Web Lukes
📄

⚠️【非推奨】FIX API入門②|PythonでLogon→ Python cBotを使おう

公開日: 2026/1/8

🚨 重要: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プロトコルの基礎を学んだ。

「じゃあPythonでログオンしてみよう」

…3時間かかった。

Code
❌ ログオン失敗
❌ 接続切断
❌ チェックサム不正
❌ ボディ長が違う
❌ パスワードが違う

地獄だった。


🎯 この記事でできるようになること

  1. PythonでSSL接続を確立
  2. FIXメッセージを正しく組み立て
  3. チェックサムを計算
  4. ログオンが通る

サンプルコード全文付き。


📋 必要な情報

ログオン前に、以下の情報を準備:

Python
# 接続先
TRADE_HOST = "h52.p.ctrader.com" # ブローカーによる
TRADE_PORT = 5212 # 固定
# 認証情報
SENDER_COMP_ID = "your_login_id" # cTraderのログインID
TARGET_COMP_ID = "cServer" # 通常はcServer
FIX_PASSWORD = "your_fix_password" # FIX専用パスワード
# プロトコル
FIX_VERSION = "FIX.4.4"
HEARTBEAT_INTERVAL = 30

⚠️ 重要な注意

情報取得場所
TRADE_HOSTブローカーのFIX API設定ページ
SENDER_COMP_IDcTraderのログインID(メアドじゃない)
TARGET_COMP_ID通常は "cServer"
FIX_PASSWORDcTrader Web → 設定 → FIX API

CTIDパスワード(普段使うやつ)は使えない!


🔌 Step 1: SSL接続

コード

Python
import socket
import ssl
def connect(host: str, port: int):
"""SSL接続を確立"""
# 生ソケット作成
raw_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# SSL/TLSでラップ
context = ssl.create_default_context()
wrapped_sock = context.wrap_socket(raw_sock, server_hostname=host)
# 接続
wrapped_sock.connect((host, port))
print(f"✅ 接続成功: {host}:{port}")
return wrapped_sock

使い方

Python
sock = connect(TRADE_HOST, TRADE_PORT)

ハマりポイント①: SSL証明書検証

Python
# ❌ 検証を無効にしたくなる誘惑
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
# ✅ デフォルト設定で十分(cTraderは正規証明書)
context = ssl.create_default_context()

cTraderサーバーは正規SSL証明書を使用。検証無効化は不要。


📝 Step 2: FIXメッセージの構造

基本構造

Code
ヘッダー | ボディ | トレーラー
ヘッダー:
8=FIX.4.4 (BeginString)
9=123 (BodyLength)
35=A (MsgType)
49=SENDER (SenderCompID)
56=TARGET (TargetCompID)
57=TRADE (TargetSubID) ← 重要!
34=1 (MsgSeqNum)
52=20260106-12:00:00 (SendingTime)
ボディ:
(メッセージ固有のフィールド)
トレーラー:
10=234 (CheckSum)

区切り文字: SOH(\x01)

見た目は「|」だけど、実際は制御文字。

Python
SOH = "\x01" # ASCII 1 = Start of Header
# メッセージ例
msg = f"8=FIX.4.4{SOH}9=123{SOH}35=A{SOH}..."

ハマりポイント②: SOHを忘れる

Python
# ❌ パイプで区切る(動かない)
msg = "8=FIX.4.4|9=123|35=A"
# ✅ SOH(\x01)で区切る
msg = f"8=FIX.4.4{SOH}9=123{SOH}35=A{SOH}"

🔢 Step 3: ボディ長の計算

ルール

Code
BodyLength = タグ35から最後のSOH(タグ10の前)までのバイト数

タグ8、タグ9は含まない。タグ10も含まない。

コード

Python
def calc_body_length(body_with_header: str) -> int:
"""
ボディ長を計算
35=... から最後のSOHまでのバイト数
"""
# 8=FIX.4.4\x01 と 9=XXX\x01 を除いた部分
# つまり35=から始まる部分
return len(body_with_header.encode('ascii'))

使い方

Python
# 35=から10=の前まで
body_part = f"35=A{SOH}49={SENDER}{SOH}56={TARGET}{SOH}..."
body_length = calc_body_length(body_part)

ハマりポイント③: ボディ長計算ミス

Python
# ❌ 全体の長さを計算(間違い)
body_length = len(f"8=FIX.4.4{SOH}9=XXX{SOH}35=A{SOH}...")
# ❌ SOHを数えない(間違い)
body_length = len("35=A49=SENDER56=TARGET...")
# ✅ 35=から10=の前までのバイト数(SOH含む)
body_part = f"35=A{SOH}49={SENDER}{SOH}56={TARGET}{SOH}..."
body_length = len(body_part.encode('ascii'))

✅ Step 4: チェックサム計算

ルール

Code
CheckSum = (全バイトの合計) mod 256

タグ10=より前の全バイト(8=から始まる)の合計。

コード

Python
def calc_checksum(msg_without_checksum: str) -> str:
"""
チェックサムを計算
全バイトの合計 mod 256 を3桁ゼロ埋めで返す
"""
checksum = sum(ord(c) for c in msg_without_checksum) % 256
return f"10={checksum:03d}{SOH}"

使い方

Python
# チェックサム以外を組み立て
msg_without_checksum = f"8=FIX.4.4{SOH}9={body_length}{SOH}{body_part}"
# チェックサムを計算して追加
checksum = calc_checksum(msg_without_checksum)
full_msg = msg_without_checksum + checksum

ハマりポイント④: チェックサム形式

Python
# ❌ ゼロ埋めなし(間違い)
f"10={checksum}{SOH}" # → "10=23" (2桁)
# ✅ 3桁ゼロ埋め
f"10={checksum:03d}{SOH}" # → "10=023"

チェックサムは必ず3桁。


🔑 Step 5: Logonメッセージ組み立て

Logon(35=A)の必須フィールド

タグ名前値
35MsgTypeA (Logon)
49SenderCompIDログインID
56TargetCompIDcServer
57TargetSubIDTRADE
34MsgSeqNum1(最初は1)
52SendingTimeUTC時刻
98EncryptMethod0(暗号化なし)
108HeartBtInt30(秒)
141ResetSeqNumFlagY(推奨)
553UsernameログインID
554PasswordFIXパスワード

ハマりポイント⑤: TargetSubIDを忘れる

Python
# ❌ 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(TargetSubID)がないとログオンが無視される!

ハマりポイント⑥: UTC時刻フォーマット

Python
from datetime import datetime, timezone
# ❌ ローカル時刻(間違い)
timestamp = datetime.now().strftime("%Y%m%d-%H:%M:%S")
# ❌ フォーマット間違い
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
# ✅ UTC時刻・正しいフォーマット
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H:%M:%S")
# 例: "20260106-03:00:00"

🎯 Step 6: 完全なLogon送信コード

Python
import socket
import ssl
from datetime import datetime, timezone
SOH = "\x01"
# 設定
TRADE_HOST = "h52.p.ctrader.com" # ブローカーによる
TRADE_PORT = 5212
SENDER_COMP_ID = "your_login_id"
TARGET_COMP_ID = "cServer"
FIX_PASSWORD = "your_fix_password"
# シーケンス番号(セッション内でインクリメント)
seq_num = 1
def get_utc_timestamp():
"""UTC時刻を取得"""
return datetime.now(timezone.utc).strftime("%Y%m%d-%H:%M:%S")
def calc_checksum(msg: str) -> str:
"""チェックサムを計算"""
checksum = sum(ord(c) for c in msg) % 256
return f"10={checksum:03d}{SOH}"
def build_logon_message():
"""Logonメッセージを組み立て"""
global seq_num
# ボディ部分(35=から)
body = (
f"35=A{SOH}"
f"49={SENDER_COMP_ID}{SOH}"
f"56={TARGET_COMP_ID}{SOH}"
f"57=TRADE{SOH}" # ← 重要!
f"34={seq_num}{SOH}"
f"52={get_utc_timestamp()}{SOH}"
f"98=0{SOH}" # 暗号化なし
f"108=30{SOH}" # ハートビート30秒
f"141=Y{SOH}" # シーケンスリセット
f"553={SENDER_COMP_ID}{SOH}" # Username
f"554={FIX_PASSWORD}{SOH}" # Password
)
# ヘッダー(8=, 9=)
body_length = len(body.encode('ascii'))
header = f"8=FIX.4.4{SOH}9={body_length}{SOH}"
# チェックサム計算
msg_without_checksum = header + body
checksum = calc_checksum(msg_without_checksum)
# 完成
seq_num += 1
return msg_without_checksum + checksum
def send_message(sock, msg: str):
"""メッセージを送信"""
sock.send(msg.encode('ascii'))
print(f"→ 送信: {msg.replace(SOH, '|')}")
def receive_message(sock) -> str:
"""メッセージを受信"""
data = sock.recv(4096)
msg = data.decode('ascii')
print(f"← 受信: {msg.replace(SOH, '|')}")
return msg
def main():
# 接続
raw_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
context = ssl.create_default_context()
sock = context.wrap_socket(raw_sock, server_hostname=TRADE_HOST)
sock.connect((TRADE_HOST, TRADE_PORT))
print(f"✅ 接続成功: {TRADE_HOST}:{TRADE_PORT}")
# Logon送信
logon_msg = build_logon_message()
send_message(sock, logon_msg)
# 応答受信
response = receive_message(sock)
# 結果判定
if "35=A" in response:
print("🎉 ログオン成功!")
elif "35=3" in response:
print("❌ Reject: メッセージ形式エラー")
elif "35=j" in response:
print("❌ BusinessReject: 認証エラーなど")
elif "35=5" in response:
print("❌ Logout: サーバーから切断")
# 切断
sock.close()
if __name__ == "__main__":
main()

✅ 成功したときの応答

Code
→ 送信: 8=FIX.4.4|9=156|35=A|49=12345678|56=cServer|57=TRADE|34=1|52=20260106-03:00:00|98=0|108=30|141=Y|553=12345678|554=MyPassword123|10=234|
← 受信: 8=FIX.4.4|9=98|35=A|49=cServer|56=12345678|34=1|52=20260106-03:00:00.123|98=0|108=30|10=187|
🎉 ログオン成功!

35=A が返ってきたら成功!


❌ 失敗パターンと対処

パターン1: RET_INVALID_DATA

Code
← 受信: 35=j|58=RET_INVALID_DATA|...

原因: CTIDパスワードを使用

対処: cTrader Web → FIX API設定でFIX専用パスワードを設定


パターン2: Reject(35=3)

Code
← 受信: 35=3|58=Required tag missing|...

原因: 必須タグの欠落

対処: タグ57(TargetSubID)を追加


パターン3: 無応答

原因: ボディ長またはチェックサムの計算ミス

対処: 計算ロジックを確認


パターン4: 接続切断

原因: ホスト名/ポート番号が違う

対処: ブローカーのFIX API設定ページで確認


🔄 ハートビート対応

ログオン成功後、30秒ごとにハートビートを送る必要がある。

Python
def build_heartbeat(test_req_id: str = None):
"""ハートビートメッセージ"""
global seq_num
body = (
f"35=0{SOH}"
f"49={SENDER_COMP_ID}{SOH}"
f"56={TARGET_COMP_ID}{SOH}"
f"57=TRADE{SOH}"
f"34={seq_num}{SOH}"
f"52={get_utc_timestamp()}{SOH}"
)
# TestReqIDがあれば追加(TestRequest応答時)
if test_req_id:
body += f"112={test_req_id}{SOH}"
body_length = len(body.encode('ascii'))
header = f"8=FIX.4.4{SOH}9={body_length}{SOH}"
msg_without_checksum = header + body
checksum = calc_checksum(msg_without_checksum)
seq_num += 1
return msg_without_checksum + checksum

TestRequest(35=1)への応答

サーバーからTestRequestが来たら、即座にハートビートで応答:

Python
def handle_message(msg: str, sock):
"""受信メッセージを処理"""
if "35=1" in msg: # TestRequest
# タグ112(TestReqID)を取得
test_req_id = extract_tag(msg, "112")
# ハートビートで応答
heartbeat = build_heartbeat(test_req_id)
send_message(sock, heartbeat)

📊 Logonで踏むハマりポイントまとめ

#ハマりポイント症状
1CTIDパスワード使用RET_INVALID_DATA
2SOHの代わりに|使用接続切断
3ボディ長計算ミス無応答
4チェックサム計算ミス無応答
5チェックサム3桁ゼロ埋め忘れReject
6TargetSubID(57)欠落無応答
7UTC時刻フォーマット間違いReject
8シーケンス番号の管理ミス接続切断

8個中3個はLogonで踏む。


📝 まとめ

必須知識

  1. SOH(\x01) で区切る(|じゃない)
  2. ボディ長 は35=から10=の前まで
  3. チェックサム は全バイトmod256の3桁
  4. TargetSubID は必須
  5. UTC時刻 をYYYYMMDD-HH:MM:SS形式で

やっとスタートライン

ログオンが通っただけ。

これで「注文が送れる状態」になった。

次は実際に注文を送る。


🔥 次回予告

【FIX API入門③】Pythonで注文送信!0.01ロットが10ロットになった事件

  • NewOrderSingle(35=D)の組み立て
  • OrderQtyの単位の罠
  • ExecutionReportの解析
  • ポジションの確認方法

15個のハマりポイントの大半はここで踏む。


📚 関連記事

FIX API完全版シリーズ(2026年版・Fundora検証済み)

その他


📖 参考リンク


ログオンが通った瞬間の喜びは格別。 🎉

だがこれはまだ序章に過ぎない。

🎁おすすめツール・サービス

FX自動売買VPSなら50%OFF

キャンペーンで月1,790円〜。MT4/MT5プリインストール済み、自動復旧機能付き。14日間無料お試し。

シンクラウドデスクトップ for FX
ゴトー日EAを探しているならソースコード付

4年で資金4.7倍のバックテスト実績。ソースコード付きで自由にカスタマイズ可能。

ゴトー日EA Ultimate版(3,980円)

※上記リンクにはプロモーション(広告)が含まれています

🔮
MEMBERSHIP

量子相場謀略|ホソノP

ブログでは書けない本音のトレード戦略とEA開発の裏側を公開。 データに基づいた検証で、再現性のある手法だけをお届けします。

✓EAソースコード公開
✓バックテスト生データ共有
✓リアルタイム相場分析
✓メンバー限定記事
メンバーシップに参加する※ noteアカウントで簡単登録

📚あわせて読みたい

📄

🚨【結論】FIX APIは非推奨!Python cBotを使いましょう - 17個の地獄を経験した結論

FIX APIは17個のハマりポイントがあり非推奨です。cTrader 5.4以降のPython cBotなら1行で注文可能。FIX APIで813行書いた経験者が断言します。

fx2026/1/8
📄

⚠️【非推奨】FIX API入門①|FIXプロトコルとは?→ Python cBotを使おう

【非推奨】FIX APIは17個のハマりポイントがあり初心者には推奨しません。代わりにPython cBotを使いましょう。この記事は参考資料として残しています。

fx2026/1/8
📄

⚠️【非推奨】FIX API入門④|17個のハマりポイント→ Python cBotを使おう

【非推奨】FIX APIは17個のハマりポイントがあり初心者には推奨しません。代わりにPython cBotを使いましょう。この記事は参考資料として残しています。

fx2026/1/8
📄

⚠️【非推奨】FIX API入門③|Python注文送信→ Python cBotを使おう

【非推奨】FIX APIは17個のハマりポイントがあり初心者には推奨しません。代わりにPython cBotを使いましょう。この記事は参考資料として残しています。

fx2026/1/8
📄

🚨【結論】cTrader API比較:FIX APIは非推奨→Python cBotを使おう

【FIX APIは非推奨】cTrader 5.4以降はPython cBotがベスト。Open APIよりもさらに簡単。FIX APIで17個のハマりポイントを踏んだ経験者が断言します。

fx2026/1/8
📄

⚠️【非推奨】FIX APIでゴールド注文失敗「証拠金不足」の原因→ Python cBotを使おう

【非推奨】FIX APIは17個のハマりポイントがあり初心者には推奨しません。代わりにPython cBotを使いましょう。この記事は参考資料として残しています。

fx2026/1/8