⚠️【非推奨】FIX API入門④|17個のハマりポイント→ 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を実装して1週間。
17個のハマりポイントを発見した。
公式ドキュメント: 「これがFIXプロトコルです」現実: 「標準FIXタグ?使えません」全部、ドキュメントに書いてない。
🎯 この記事の内容
- ログオン系 - 3個
- 注文系 - 5個
- ポジション系 - 2個
- データ取得系 - 2個
- その他 - 5個 (2026-01-08追加2個)
合計17個。全部実体験。
🔐 ログオン系(3個)
❌ ハマり #1: CTIDパスワードを使用
症状:
ログオン送信 → 即座にLogout → RET_INVALID_DATA原因: cTraderアプリのログインパスワード(CTID)を使用
解決: cTrader Web → 設定 → FIX API → FIX専用パスワードを設定
# ❌ 間違いPASSWORD = "ctrader_app_password"
# ✅ 正解PASSWORD = "fix_api_specific_password"教訓: cTraderには2種類のパスワードがある
❌ ハマり #2: TRADEとQUOTEを1セッションと勘違い
症状:
TRADEセッションで価格データを要求 → 無応答原因: TRADE(注文)とQUOTE(価格)は完全に別セッション
解決: 2つの接続を別々に管理
# ✅ 正解trade_sock = connect(TRADE_HOST, 5212) # 注文・約定quote_sock = connect(QUOTE_HOST, 5211) # 価格データ
# Logonも別々に送るsend_logon(trade_sock, "TRADE")send_logon(quote_sock, "QUOTE")教訓: 1つのソケットで全部やろうとしない
❌ ハマり #3: TargetSubID(57)を忘れる
症状:
Logon送信 → 応答なし → タイムアウト原因: タグ57(TargetSubID)がないと無視される
解決:
# ❌ 間違いf"49={SENDER}{SOH}56={TARGET}{SOH}34=1{SOH}..."
# ✅ 正解f"49={SENDER}{SOH}56={TARGET}{SOH}57=TRADE{SOH}34=1{SOH}..."教訓: TargetSubIDは「TRADE」または「QUOTE」を指定
📝 注文系(5個)
❌ ハマり #4: シンボルを文字列で指定
症状:
注文送信 → Unknown error / Reject原因:
55=XAUUSD のように文字列で指定
解決: cTrader Webで「FIX通貨ペアID」を確認し、数値で指定
# ❌ 間違いf"55=XAUUSD{SOH}"
# ✅ 正解f"55=41{SOH}" # ゴールドのID| シンボル | ID(例) |
|---|---|
| EURUSD | 1 |
| GBPUSD | 2 |
| USDJPY | 3 |
| XAUUSD | 41 |
教訓: ブローカーによってIDが違う
❌ ハマり #5: OrderQtyをロット数で指定
症状:
58=NOT_ENOUGH_MONEY(証拠金は十分あるのに)原因: OrderQty(タグ38)はロット数じゃなくVolume
# ❌ 間違いf"38=0.01{SOH}" # ロット数
# ❌ 間違い(FXの計算式をゴールドに適用)qty = 0.01 * 100000 # = 1000f"38=1000{SOH}" # → 10ロット分として解釈される!解決: 商品ごとに計算式が違う
# FX通貨ペアqty = int(lot_size * 100000) # 0.01 = 1000
# ゴールド(XAUUSD)qty = int(lot_size * 100) # 0.01 = 1
# シルバー(XAGUSD)qty = int(lot_size * 5000) # 0.01 = 50教訓: 0.01ロットのつもりが10ロットになりうる
❌ ハマり #6: 数量の単位変換忘れ
症状:
決済時に「数量不足」または「過剰数量」原因: ExecutionReportで受け取った数量をそのまま使用
解決: 開いた時の数量を保存しておく
# 約定時positions[position_id] = { "qty": sent_qty, # 送信した数量を保存 "side": side, "symbol_id": symbol_id}
# 決済時close_qty = positions[position_id]["qty"] # 保存した数量を使用教訓: 単位変換が複雑なので、送信した値を保存
❌ ハマり #7: UTC時刻フォーマット間違い
症状:
Reject: Invalid format原因: タイムスタンプの形式が違う
# ❌ 間違い(ローカル時刻)datetime.now().strftime("%Y%m%d-%H:%M:%S")
# ❌ 間違い(フォーマット違い)datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")解決:
# ✅ 正解datetime.now(timezone.utc).strftime("%Y%m%d-%H:%M:%S")# 例: "20260106-03:00:00"教訓: UTCで、ハイフンはYYYYMMDDとHH:MM:SSの間だけ
❌ ハマり #8: シーケンス番号の管理ミス
症状:
突然接続が切れる / Reject: MsgSeqNum too low原因: シーケンス番号(34)を正しくインクリメントしていない
解決:
# グローバルまたはセッションごとに管理trade_seq_num = 1quote_seq_num = 1
def get_next_seq(session_type: str) -> int: global trade_seq_num, quote_seq_num if session_type == "TRADE": trade_seq_num += 1 return trade_seq_num - 1 else: quote_seq_num += 1 return quote_seq_num - 1教訓: TRADE/QUOTE別にシーケンス番号を管理
📊 ポジション系(2個)
❌ ハマり #9: ポジション照会に非対応タグ使用
症状:
Invalid tag number, field=1Invalid tag number, field=724原因: 標準FIX 4.4のタグを使用
# ❌ 全部非対応!f"1={ACCOUNT}{SOH}" # Accountf"263=1{SOH}" # SubscriptionRequestTypef"453=1{SOH}" # NoPartyIDsf"724=0{SOH}" # PosReqTypef"447=D{SOH}" # PartyIDSource解決: タグ710(PosReqID)だけ使う
# ✅ 正解f"710={int(time.time())}{SOH}" # これだけ!教訓: cTrader FIX APIは独自実装が多い
❌ ハマり #10: ExecutionReportのポジションID取得漏れ
症状:
決済時にポジションIDがわからない原因: ExecutionReport(35=8)のタグ721を取得していなかった
解決:
def parse_execution_report(msg: str): fields = parse_fix_message(msg)
if fields.get("150") == "F": # Trade position_id = fields.get("721", "") # ← これを保存! price = fields.get("31", "") save_position(position_id, price)教訓: 約定時にポジションID(721)を必ず保存
📈 データ取得系(2個)
❌ ハマり #11: SecurityListRequestType=4を使用
症状:
Only SecurityListRequestType = SYMBOL(0) supported原因: 全シンボル一覧取得(559=4)は非対応
解決: 個別シンボルのみ取得可能(ID 1〜200等をループでスキャン)
# ❌ 間違い(全シンボル一括取得)f"559=4{SOH}"
# ✅ 正解(個別シンボル照会)f"559=0{SOH}55=41{SOH}" # → Tag 1007でシンボル名が返る取得できる情報:
- シンボルID(Tag 55)
- シンボル名(Tag 1007)← これは取得可能!
- 桁数(Tag 1008)
教訓: 一括取得はできないが、ループでスキャンすれば全シンボル取得は可能
❌ ハマり #12: コントラクトサイズが取得できないことを知らない
症状:
どこを探してもコントラクトサイズの情報がない原因: FIX APIにはコントラクトサイズを返すAPIがない
公式フォーラムの回答:
"Unfortunately this information is not available in FIX API. You can consider Open API instead."
解決:
- 事前にブローカーの仕様を調べる
- または Open API を併用する
教訓: FIX APIは情報取得が限定的
⚙️ その他(3個)
❌ ハマり #13: ボディ長(9)の計算ミス
症状:
無応答 / 接続切断原因: BodyLength(タグ9)の計算が間違っている
# ❌ 間違い(全体の長さ)body_length = len(f"8=FIX.4.4{SOH}9=XXX{SOH}35=A{SOH}...")
# ❌ 間違い(SOHを含めない)body_length = len("35=A49=SENDER...")解決:
# ✅ 正解(35=から10=の前までのバイト数、SOH含む)body = f"35=A{SOH}49={SENDER}{SOH}56={TARGET}{SOH}..."body_length = len(body.encode('ascii'))教訓: 8=と9=は含まない、10=も含まない
❌ ハマり #14: チェックサム(10)の計算ミス
症状:
無応答 / 接続切断原因: CheckSum計算が間違っている
# ❌ 間違い(3桁ゼロ埋めなし)f"10={checksum}{SOH}" # → "10=23"
# ❌ 間違い(チェックサム自体も含めている)checksum = sum(ord(c) for c in full_message) % 256解決:
# ✅ 正解def calc_checksum(msg_without_checksum: str) -> str: checksum = sum(ord(c) for c in msg_without_checksum) % 256 return f"10={checksum:03d}{SOH}" # 3桁ゼロ埋め教訓: 10=の前までの全バイトを合計、結果は3桁ゼロ埋め
❌ ハマり #15: SSL証明書検証を無効にしようとする
症状:
SSL証明書エラー → 検証を無効にしたくなる原因: 古いサンプルコードや、テスト環境の設定を本番に使用
# ❌ 危険(本番で使うな)context.check_hostname = Falsecontext.verify_mode = ssl.CERT_NONE解決: cTraderサーバーは正規SSL証明書を使用。デフォルト設定で十分。
# ✅ 正解context = ssl.create_default_context()教訓: セキュリティを犠牲にするな
❌ ハマり #16: FIXクライアントのログが出力されない
症状:
main.pyからctrader_fix_client.pyを呼び出すと、FIXクライアント内のログが出力されないポジション確認がタイムアウトするが原因がわからない原因: Pythonのロガーは名前ごとに独立しているため、モジュールごとにハンドラ設定が必要
# ctrader_fix_client.pylog = logging.getLogger("CTraderFIX") # ← このロガー
# main.pylog = logging.getLogger("YourApp") # ← 別のロガー# CTraderFIXロガーにはハンドラがない!解決:
def setup_logger(): # メインロガー設定 main_logger = logging.getLogger("YourApp") main_logger.setLevel(logging.INFO) main_logger.addHandler(console_handler)
# FIXクライアントロガーも設定(これが重要!) fix_logger = logging.getLogger("CTraderFIX") fix_logger.setLevel(logging.INFO) fix_logger.addHandler(console_handler) # 同じハンドラを追加教訓: モジュール分離時はロガー設定も分離される
❌ ハマり #17: 貴金属とFXでメンテナンス時間が違う(2026-01-08追加)
症状:
07:00 JSTにXAUUSDで注文 → MARKET_CLOSED同じ時間にUSDJPYで注文 → 正常に約定原因: 商品カテゴリによってメンテナンス時間が異なる
貴金属(XAUUSD等): 06:00-07:00 JST メンテナンスFX通貨ペア: 同時間帯でも取引可能な場合が多い解決:
def is_market_open(symbol_name: str) -> bool: now_jst = datetime.now(timezone(timedelta(hours=9))) hour = now_jst.hour
# 貴金属: 06:00-07:00 JST はメンテナンス if "XAU" in symbol_name or "XAG" in symbol_name: if 6 <= hour < 7: return False return True教訓: 「市場クローズ」は商品によって時間が違う
📋 ハマりポイント一覧表
| # | カテゴリ | ハマりポイント | 症状 |
|---|---|---|---|
| 1 | ログオン | CTIDパスワード使用 | RET_INVALID_DATA |
| 2 | ログオン | TRADE/QUOTE 1セッション | 無応答 |
| 3 | ログオン | TargetSubID欠落 | 無応答 |
| 4 | 注文 | シンボル文字列指定 | Unknown error |
| 5 | 注文 | OrderQtyロット数指定 | NOT_ENOUGH_MONEY |
| 6 | 注文 | 数量単位変換忘れ | 数量不一致 |
| 7 | 注文 | UTC時刻フォーマット | Reject |
| 8 | 注文 | シーケンス番号ミス | 切断 |
| 9 | ポジション | 非対応タグ使用 | Invalid tag |
| 10 | ポジション | ポジションID取得漏れ | 決済不可 |
| 11 | データ | 559=4使用 | 非対応エラー |
| 12 | データ | コントラクトサイズ | 取得不可 |
| 13 | その他 | ボディ長計算ミス | 無応答 |
| 14 | その他 | チェックサム計算ミス | 無応答 |
| 15 | その他 | SSL検証無効化 | セキュリティリスク |
| 16 | その他 | ロガー設定漏れ | ログ出力なし |
| 17 | その他 | メンテナンス時間差 | MARKET_CLOSED |
🎯 デバッグのコツ
1. ログを詳細に出力
def send_message(sock, msg: str): print(f"→ 送信: {msg.replace(SOH, '|')}") sock.send(msg.encode('ascii'))
def receive_message(sock) -> str: data = sock.recv(4096) msg = data.decode('ascii') print(f"← 受信: {msg.replace(SOH, '|')}") return msg2. タグごとにパース
def parse_fix_message(msg: str) -> dict: fields = {} for part in msg.split(SOH): if "=" in part: tag, value = part.split("=", 1) fields[tag] = value print(f" {tag} = {value}") return fields3. テストスクリプトで検証
本番コードではなく、小さなテストスクリプトで試す:
# test_order.py# 単一の注文だけをテスト
# test_close.py# 単一の決済だけをテスト4. 公式フォーラムを検索
エラーメッセージで検索すると、同じ問題にハマった人がいる。
🆚 FIX API vs Python cBot
比較
| 項目 | FIX API | Python cBot |
|---|---|---|
| ハマりポイント | 17個 | 0個 |
| 実装時間 | 1週間〜 | 数時間 |
| コード量 | 800行以上 | 数十行 |
| 情報取得 | 限定的 | 豊富 |
| 公式ライブラリ | なし | あり |
Python cBotで同じことをやると
# Python cBotapi.ExecuteMarketOrder(TradeType.Buy, "XAUUSD", 1000)# ↑ これだけ。FIX API:
- ログオン実装
- メッセージ組み立て
- チェックサム計算
- ボディ長計算
- シンボルID調査
- OrderQty計算
- ExecutionReport解析
- ポジションID管理
- ハートビート実装
- エラーハンドリング
- ...
800行 vs 1行。
📝 最終結論
FIX APIを使うべき人
✅ 機関レベルの低レイテンシが必要✅ FIXプロトコルの勉強をしたい✅ 苦労を楽しめるマゾ気質✅ 既存のFIXインフラがあるFIX APIを避けるべき人
❌ 普通に自動売買したいだけ❌ 詳細な情報が欲しい❌ すぐに動くものが欲しい❌ デバッグ時間を節約したい本音
「Python cBotでええやん」
🔥 最後に
17個のハマりポイントを踏み抜いた。
ドキュメントには書いてない。
フォーラムで探し回ってやっと見つかる。
それがFIX API。
この記事が誰かの時間を節約できれば幸いです。
同じ地雷を踏む人が一人でも減りますように。 🙏
📚 関連記事
FIX API完全版シリーズ(2026年版・Fundora検証済み)
- FIX API完全版① 2セッション構成とパスワードの真実
- FIX API完全版② リアルタイム価格取得編
- FIX API完全版③ ポジション・オーダー照会編
- FIX API完全版④ エントリー・決済完全実装
- FIX API完全版⑤ 全機能クラスの実装
その他
- FIX API入門①:FIXプロトコルとは
- FIX API入門②:PythonでLogon
- FIX API入門③:注文送信
- FIX APIでゴールド注文したら「証拠金不足」!?
- FIX API vs Python cBot完全比較
📖 参考リンク
FIX APIの苦労話、コメントで共有してください! 😭
🎁おすすめツール・サービス
※上記リンクにはプロモーション(広告)が含まれています
量子相場謀略|ホソノP
ブログでは書けない本音のトレード戦略とEA開発の裏側を公開。 データに基づいた検証で、再現性のある手法だけをお届けします。