⚠️【非推奨】FIX API完全版①|2セッション構成→ Python cBotを使おう
🚨 重要:FIX APIは非推奨です
この記事で紹介しているFIX APIは、以下の理由から非推奨です:
- 😱 17個のハマりポイントがあり、ドキュメントに書いてない罠が多すぎる
- 💀 実装に1週間以上かかる(Python cBotなら数時間)
- 🤯 800行以上のコードが必要(Python cBotなら数十行)
- ❌ コントラクトサイズ等の情報が取得できない
🐍 代わりにPython cBotを使いましょう!
cTrader 5.4以降、ネイティブPythonサポートが追加されました。
👉 Python cBot入門ガイド - 1行で注文できる世界へ
👉 cTrader 5.4 Python完全ガイド - 環境構築から実践まで
以下の内容は参考資料として残しています。FIX APIをどうしても使いたい方のみお読みください。
【参考】FIX API完全版①|2セッション構成とパスワードの真実
この記事で解決すること
- 「ログオンしても無応答」 → セッション構成の理解
- 「RET_INVALID_DATA」 → パスワードの種類の理解
- 「価格データが取れない」 → TRADE/QUOTE分離の理解
ライブ口座(Fundora)で全て検証済みの情報です。
🎯 cTrader FIX API の全体像
FIX APIとは
FIX(Financial Information eXchange)は、金融業界の標準プロトコル。
- 高速: 機関投資家レベルの低レイテンシ
- 信頼性: 業界標準で20年以上の実績
- シンプル: テキストベースのプロトコル
cTraderはFIX 4.4をベースに独自拡張を加えている。
🚨 最重要: 2セッション構成
cTraderは2つのセッションが完全分離
┌─────────────────────────────────────────────────────────┐│ cTrader FIX API │├─────────────────────────┬───────────────────────────────┤│ TRADEセッション │ QUOTEセッション ││ (Port 5212) │ (Port 5211) │├─────────────────────────┼───────────────────────────────┤│ ・新規注文 │ ・Bid/Ask価格取得 ││ ・決済 │ ・マーケットデータ購読 ││ ・ポジション照会 │ ││ ・オーダー照会 │ ││ ・シンボル情報取得 │ │└─────────────────────────┴───────────────────────────────┘同一ホストでもポートが違う
# 例: Fundoraの場合HOST = "live-uk-eqx-01.p.c-trader.com"TRADE_PORT = 5212 # 注文・ポジション系QUOTE_PORT = 5211 # 価格データ系なぜ分離されているのか
| 理由 | 説明 |
|---|---|
| 負荷分散 | 価格データは高頻度(毎秒数十〜数百回)、注文は低頻度 |
| セキュリティ | 注文系は厳格な認証、価格系は読み取り専用 |
| 信頼性 | 片方が落ちても、もう片方は稼働継続 |
| HFT対応 | 価格データだけ超高速に取得する用途に対応 |
❌ よくある間違い
# ❌ 1つの接続で全部やろうとするsock = connect(HOST, TRADE_PORT)send_logon(sock)send_market_data_request(sock) # ← 動かない!
# ✅ 2つの接続を別々に管理trade_sock = connect(HOST, TRADE_PORT)quote_sock = connect(HOST, QUOTE_PORT)send_logon(trade_sock, "TRADE")send_logon(quote_sock, "QUOTE")🔑 最重要: 2種類のパスワード
cTraderには2つのパスワードがある
| パスワード | 用途 | FIX APIで使えるか |
|---|---|---|
| CTIDパスワード | cTraderアプリのログイン | ❌ 使えない |
| FIXパスワード | FIX API専用 | ✅ これを使う |
CTIDパスワードを使うとどうなるか
→ 送信: Logon (35=A)← 受信: Logout (35=5) with 58=RET_INVALID_DATA
「パスワード合ってるのに!」→ 違うパスワードを使ってるこれがFIX APIで一番最初にハマるポイント。
FIXパスワードの設定方法
-
cTrader Web版にログイン
https://ct.○○○.com/(ブローカーのcTrader Web)
-
設定 → FIX APIを開く
-
FIX API専用パスワードを設定
- CTIDパスワードとは別のものを設定
-
接続情報を確認
- ホスト名
- ポート番号
- SenderCompID(通常は
live.broker.口座番号形式)
認証情報の構成
# 環境変数または設定ファイルから読み込むFIX_HOST = "live-uk-eqx-01.p.c-trader.com"FIX_TRADE_PORT = 5212FIX_QUOTE_PORT = 5211FIX_SENDER_COMP_ID = "live.fundoratrading.5006639" # 口座固有FIX_PASSWORD = "your_fix_specific_password" # FIX専用!FIX_TRADER_LOGIN = "5006639" # 口座番号🔌 SSL接続の実装
基本的なSSL接続コード
import socketimport ssl
def connect_session(host: str, port: int, session_type: str) -> ssl.SSLSocket: """ SSL接続を確立
Args: host: 接続先ホスト port: ポート番号(TRADE=5212, QUOTE=5211) session_type: "TRADE" or "QUOTE"
Returns: ssl.SSLSocket: 接続済みソケット """ print(f"🔗 {session_type}セッション {host}:{port} に接続中...")
# 生ソケット作成 raw_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) raw_sock.settimeout(10) # 接続タイムアウト
# SSL/TLSでラップ context = ssl.create_default_context() # cTraderは正規証明書を使用、検証無効化は不要 # context.check_hostname = False # ❌ 本番では使わない # context.verify_mode = ssl.CERT_NONE # ❌ 本番では使わない
wrapped_sock = context.wrap_socket(raw_sock, server_hostname=host) wrapped_sock.connect((host, port)) wrapped_sock.settimeout(1) # 受信タイムアウト(短め)
print(f"✅ {session_type}セッション SSL接続完了") return wrapped_sock両セッションへの接続
# TRADE接続trade_sock = connect_session(FIX_HOST, FIX_TRADE_PORT, "TRADE")
# QUOTE接続quote_sock = connect_session(FIX_HOST, FIX_QUOTE_PORT, "QUOTE")📝 ログオンメッセージの構成
必須フィールド
┌─────┬──────────────────┬─────────────────────────────────┐│ Tag │ 名称 │ 値の例 │├─────┼──────────────────┼─────────────────────────────────┤│ 35 │ MsgType │ A(ログオン) ││ 49 │ SenderCompID │ live.fundoratrading.5006639 ││ 56 │ TargetCompID │ cServer ││ 57 │ TargetSubID │ TRADE または QUOTE ← 超重要! ││ 50 │ SenderSubID │ TRADE または QUOTE ││ 34 │ MsgSeqNum │ 1 ││ 52 │ SendingTime │ 20260107-14:00:00 ││ 98 │ EncryptMethod │ 0(暗号化なし) ││ 108 │ HeartBtInt │ 30 ││ 141 │ ResetSeqNumFlag │ Y ││ 553 │ Username │ 5006639(口座番号) ││ 554 │ Password │ FIX専用パスワード │└─────┴──────────────────┴─────────────────────────────────┘超重要: TargetSubID(タグ57)
# ❌ TargetSubIDなし → 無応答f"49={SENDER}{SOH}56={TARGET}{SOH}34=1{SOH}..."
# ✅ TargetSubIDあり → 動くf"49={SENDER}{SOH}56={TARGET}{SOH}57=TRADE{SOH}34=1{SOH}..."タグ57がないと、サーバーはどのセッションかわからず無視する。
Python実装
SOH = "\x01"
def build_logon_message(session_type: str, seq_num: int) -> str: """ ログオンメッセージを構築
Args: session_type: "TRADE" or "QUOTE" seq_num: シーケンス番号
Returns: str: FIXメッセージ文字列 """ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H:%M:%S.%f")[:-3]
body = ( f"35=A{SOH}" f"49={FIX_SENDER_COMP_ID}{SOH}" f"56=cServer{SOH}" f"57={session_type}{SOH}" # ← 重要! f"50={session_type}{SOH}" f"34={seq_num}{SOH}" f"52={timestamp}{SOH}" f"98=0{SOH}" # 暗号化なし f"108=30{SOH}" # ハートビート30秒 f"141=Y{SOH}" # シーケンスリセット f"553={FIX_TRADER_LOGIN}{SOH}" f"554={FIX_PASSWORD}{SOH}" )
# ヘッダー(8=, 9=) body_length = len(body.encode('utf-8')) header = f"8=FIX.4.4{SOH}9={body_length}{SOH}"
# チェックサム計算 msg_without_checksum = header + body checksum = sum(ord(c) for c in msg_without_checksum) % 256 checksum_str = f"10={checksum:03d}{SOH}"
return msg_without_checksum + checksum_str📨 メッセージタイプ一覧
システムメッセージ
| MsgType | 名称 | 方向 | 説明 |
|---|---|---|---|
| A | Logon | 送受信 | セッション開始 |
| 5 | Logout | 送受信 | セッション終了 |
| 0 | Heartbeat | 送受信 | 生存確認 |
| 1 | TestRequest | 受信 | ハートビート要求 |
| 3 | Reject | 受信 | セッションレベル拒否 |
アプリケーションメッセージ(TRADE)
| MsgType | 名称 | 方向 | 説明 |
|---|---|---|---|
| D | NewOrderSingle | 送信 | 新規注文 |
| F | OrderCancelRequest | 送信 | 注文キャンセル |
| H | OrderStatusRequest | 送信 | 注文状態照会 |
| AF | OrderMassStatusRequest | 送信 | 全注文状態照会 |
| 8 | ExecutionReport | 受信 | 約定レポート |
| 9 | OrderCancelReject | 受信 | キャンセル拒否 |
| AN | RequestForPositions | 送信 | ポジション照会 |
| AP | PositionReport | 受信 | ポジション情報 |
| AO | RequestForPositionsAck | 受信 | ポジション照会応答 |
| x | SecurityListRequest | 送信 | シンボル情報要求 |
| y | SecurityList | 受信 | シンボル情報 |
| j | BusinessReject | 受信 | ビジネスロジック拒否 |
アプリケーションメッセージ(QUOTE)
| MsgType | 名称 | 方向 | 説明 |
|---|---|---|---|
| V | MarketDataRequest | 送信 | 価格データ購読 |
| W | MarketDataSnapshot | 受信 | 価格スナップショット |
| X | MarketDataIncremental | 受信 | 価格更新 |
| Y | MarketDataReject | 受信 | 価格データ拒否 |
🆔 シンボルIDについて
重要: シンボルは文字列ではなく数値ID
# ❌ 文字列(動かない)f"55=XAUUSD{SOH}"
# ✅ 数値IDf"55=41{SOH}"シンボルIDの確認方法
- cTrader Web版で対象シンボルを開く
- 詳細/情報タブを表示
- **「FIX通貨ペアID」**を確認
既知のシンボルID例(Fundora)
| シンボル | ID | 説明 |
|---|---|---|
| XAUUSD | 41 | ゴールド/ドル |
| EURUSD | 1 | ユーロ/ドル |
| GBPUSD | 2 | ポンド/ドル |
| USDJPY | 3 | ドル/円 |
⚠️ ブローカーによってIDは異なる!必ず確認すること。
💡 FIX API の制限事項
FIX APIでできないこと
-
口座情報の取得
- 残高、証拠金、レバレッジは取得不可
- → Open APIを併用する必要あり
-
ヒストリカルデータの取得
- 過去の価格データは取得不可
- FIXは「最速の接続」に特化
-
コントラクトサイズの取得
- 商品のコントラクトサイズは取得不可
- → 事前に調べておく必要あり
-
全シンボル一覧の取得
SecurityListRequestType=4(全シンボル)は非対応- → 個別シンボルのみ取得可能
cTrader独自の制限
# 標準FIX 4.4で使えるはずのタグが使えない
# ❌ ポジション照会で使えないタグf"1={ACCOUNT}{SOH}" # Accountf"263=1{SOH}" # SubscriptionRequestTypef"453=1{SOH}" # NoPartyIDsf"724=0{SOH}" # PosReqType
# ✅ cTraderではタグ710だけf"710={pos_req_id}{SOH}"🔄 接続フロー
完全な接続手順
1. TRADEソケット作成(SSL) └─> 5212ポートに接続
2. TRADEログオン送信 └─> 35=A with 57=TRADE
3. TRADEログオン応答待ち └─> 35=A が返ってきたら成功
4. QUOTEソケット作成(SSL) └─> 5211ポートに接続
5. QUOTEログオン送信 └─> 35=A with 57=QUOTE
6. QUOTEログオン応答待ち └─> 35=A が返ってきたら成功
7. 準備完了 └─> TRADE: 注文・ポジション操作可能 └─> QUOTE: 価格データ取得可能受信スレッドの構成
メインスレッド├── スケジュール管理├── 注文判断ロジック│├── TRADE受信スレッド│ └── 約定・ポジション・エラー受信│└── QUOTE受信スレッド └── 価格データ受信🏥 ハートビート対応
30秒ごとにハートビートを送信
サーバーは30秒以上無通信だと切断する可能性がある。
def send_heartbeat(sock, session_type: str, test_req_id: str = ""): """ハートビート送信""" body = f"35=0{SOH}..." if test_req_id: body += f"112={test_req_id}{SOH}" # TestRequest応答時 # ... メッセージ構築して送信TestRequest(35=1)への応答
サーバーからTestRequestが来たら、即座にハートビートで応答:
def handle_message(msg: str, sock, session_type: str): fields = parse_fix_message(msg) msg_type = fields.get("35", "")
if msg_type == "1": # TestRequest test_req_id = fields.get("112", "") send_heartbeat(sock, session_type, test_req_id)❌ よくあるエラーと対処
RET_INVALID_DATA
58=RET_INVALID_DATA原因: CTIDパスワードを使用している 対処: cTrader Web → 設定 → FIX APIでFIX専用パスワードを設定
Invalid tag number
58=Invalid tag number, field=158=Invalid tag number, field=724原因: cTrader非対応のFIXタグを使用 対処: 公式ドキュメントで対応タグを確認
無応答
原因:
- TargetSubID(57)の欠落
- ボディ長(9)の計算ミス
- チェックサム(10)の計算ミス
対処: ログを出力してメッセージ内容を確認
📝 まとめ
覚えておくべき3つのこと
-
TRADEとQUOTEは完全に別セッション
- ポート5212: 注文・ポジション
- ポート5211: 価格データ
-
パスワードは2種類ある
- CTIDパスワード: アプリ用(FIX APIでは使えない)
- FIXパスワード: FIX API専用(これを使う)
-
TargetSubID(57)は必須
- TRADEセッション:
57=TRADE - QUOTEセッション:
57=QUOTE
- TRADEセッション:
次のステップ
この記事では接続の基礎を解説しました。次の記事では:
- 価格取得編: QUOTEセッションでBid/Askをリアルタイム取得
- ポジション・オーダー照会編: RequestForPositions、OrderMassStatusRequest
- 注文・決済編: NewOrderSingle、ポジション決済
📚 参考リンク
- cTrader FIX API 公式ドキュメント
- cTrader FIX API 仕様書
- cTrader FIX API PDF
- GitHub: cTraderFixPy(公式)
- cTrader Community Forum - FIX API
検証環境: Fundora (cTrader) ライブ口座 最終更新: 2026-01-08
🎁おすすめツール・サービス
※上記リンクにはプロモーション(広告)が含まれています
量子相場謀略|ホソノP
ブログでは書けない本音のトレード戦略とEA開発の裏側を公開。 データに基づいた検証で、再現性のある手法だけをお届けします。