Web Lukes
📄

⚠️【非推奨】FIX API入門③|Python注文送信→ 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をどうしても使いたい方のみお読みください。


😤 「注文送るだけでしょ?」

ログオンが通った。

「あとは注文を送るだけ!」

Python
# 0.01ロットでゴールドを買う
new_order("XAUUSD", "BUY", 0.01)

…NOT_ENOUGH_MONEY?

口座に250万円あるのに??


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

  1. NewOrderSingle(35=D) で新規注文
  2. ExecutionReport(35=8) の約定確認
  3. 商品別のOrderQty計算
  4. ポジション決済

失敗談付きで完全解説。


📋 注文に必要な情報

シンボルID(数値!)

文字列「XAUUSD」では動かない。数値IDが必要。

シンボル文字列ID(例)
ユーロドルEURUSD1
ポンドドルGBPUSD2
ドル円USDJPY3
ゴールドXAUUSD41

ブローカーによってIDが違う!

cTrader Web → 取引 → シンボル詳細で「FIX通貨ペアID」を確認。

ハマりポイント①: シンボルを文字列で指定

Python
# ❌ 文字列(動かない)
f"55=XAUUSD{SOH}"
# ✅ 数値ID
f"55=41{SOH}"

📝 NewOrderSingle(35=D)の構造

必須フィールド

タグ名前値
35MsgTypeD(NewOrderSingle)
11ClOrdID一意な注文ID
55SymbolシンボルID(数値)
54Side1=BUY, 2=SELL
38OrderQty数量(重要!)
40OrdType1=成行, 2=指値
60TransactTimeUTC時刻

オプションフィールド

タグ名前値
44Price指値価格(OrdType=2時)
99StopPx逆指値価格
59TimeInForce1=GTC, 3=IOC, 4=FOK

😱 OrderQty(タグ38)の罠

ここが最大のハマりポイント!

間違いやすい考え方

Python
# 「0.01ロット買いたい」
# ❌ ロット数をそのまま(動かない)
f"38=0.01{SOH}"
# ❌ FXの計算式を全商品に適用(危険!)
qty = 0.01 * 100000 # = 1000
f"38=1000{SOH}"

商品ごとに単位が違う!

商品基準単位1ロット0.01ロットOrderQty
FX通貨100,0001,0001000
ゴールドオンス10011
シルバーオンス5,0005050
原油バレル1,0001010

実際に何が起きたか

Code
【送信した内容】
38=1000(1000オンス)
【ブローカーの解釈】
1000オンス ÷ 100オンス/ロット = 10ロット
【必要証拠金】
$4,450 × 1000オンス ÷ 100(レバレッジ) = $44,500
約667万円
【口座残高】
250万円
【結果】
NOT_ENOUGH_MONEY 💀

0.01ロットのつもりが10ロットになっていた!


🔧 正しいOrderQty計算

Python
def calculate_order_qty(symbol_id: int, lot_size: float) -> int:
"""商品タイプに応じたOrderQty計算"""
# ゴールド (XAUUSD) - ID=41
if symbol_id == 41:
return int(lot_size * 100) # 0.01 lot = 1
# シルバー (XAGUSD) - ID=42
elif symbol_id == 42:
return int(lot_size * 5000) # 0.01 lot = 50
# 原油 (USOIL) - ID=43
elif symbol_id == 43:
return int(lot_size * 1000) # 0.01 lot = 10
# FX通貨ペア (デフォルト)
else:
return int(lot_size * 100000) # 0.01 lot = 1000

ハマりポイント②: コントラクトサイズが取得できない

FIX APIではコントラクトサイズを取得できない!

公式フォーラムの回答:

"Unfortunately this information is not available in FIX API. You can consider Open API instead."

事前にブローカーの仕様を調べるしかない。


🎯 注文送信の完全コード

Python
def build_new_order(symbol_id: int, side: str, lot_size: float, order_type: int = 1):
"""
NewOrderSingle (35=D) を組み立て
Args:
symbol_id: シンボルID(数値)
side: "BUY" or "SELL"
lot_size: ロット数(0.01など)
order_type: 1=成行, 2=指値
"""
global seq_num
# ClOrdID(一意な注文ID)
cl_ord_id = f"{symbol_id}_{int(time.time() * 1000)}"
# Side: 1=BUY, 2=SELL
side_code = "1" if side.upper() == "BUY" else "2"
# OrderQty(商品別計算)
qty = calculate_order_qty(symbol_id, lot_size)
# ボディ組み立て
body = (
f"35=D{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"11={cl_ord_id}{SOH}" # ClOrdID
f"55={symbol_id}{SOH}" # Symbol(数値)
f"54={side_code}{SOH}" # Side
f"38={qty}{SOH}" # OrderQty(重要!)
f"40={order_type}{SOH}" # OrdType
f"60={get_utc_timestamp()}{SOH}" # TransactTime
)
# ヘッダー + チェックサム
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, cl_ord_id

使用例

Python
# ゴールド0.01ロット BUY
msg, order_id = build_new_order(symbol_id=41, side="BUY", lot_size=0.01)
send_message(trade_sock, msg)
# GBPUSD 0.05ロット SELL
msg, order_id = build_new_order(symbol_id=2, side="SELL", lot_size=0.05)
send_message(trade_sock, msg)

📨 ExecutionReport(35=8)の解析

注文送信後に返ってくる

Code
← 8=FIX.4.4|...|35=8|11=41_1704520800000|150=0|39=0|...
← 8=FIX.4.4|...|35=8|11=41_1704520800000|150=F|39=2|31=4452.02|721=1001179|...

重要なタグ

タグ名前意味
11ClOrdID送信した注文ID
150ExecType実行タイプ
39OrdStatus注文ステータス
31LastPx約定価格
32LastQty約定数量
721PosMaintRptIDポジションID
58Textエラーメッセージ

ExecType(150)の値

値意味説明
0New注文受付
4Canceledキャンセル
8Rejected拒否
FTrade約定!

OrdStatus(39)の値

値意味
0New(新規)
1PartiallyFilled(部分約定)
2Filled(全約定)
4Canceled
8Rejected

約定確認コード

Python
def parse_execution_report(msg: str) -> dict:
"""ExecutionReportを解析"""
fields = {}
for part in msg.split(SOH):
if "=" in part:
tag, value = part.split("=", 1)
fields[tag] = value
exec_type = fields.get("150", "")
ord_status = fields.get("39", "")
result = {
"cl_ord_id": fields.get("11", ""),
"exec_type": exec_type,
"ord_status": ord_status,
"price": fields.get("31", ""),
"qty": fields.get("32", ""),
"position_id": fields.get("721", ""), # ← 重要!
"text": fields.get("58", ""),
}
# 状態判定
if exec_type == "F" and ord_status == "2":
result["status"] = "FILLED"
print(f"🎉 約定成功!価格: {result['price']}, ポジションID: {result['position_id']}")
elif exec_type == "8" or ord_status == "8":
result["status"] = "REJECTED"
print(f"❌ 拒否: {result['text']}")
elif exec_type == "0":
result["status"] = "NEW"
print(f"📝 注文受付: {result['cl_ord_id']}")
return result

❌ よくあるエラーと対処

NOT_ENOUGH_MONEY

Code
58=NOT_ENOUGH_MONEY:Not enough funds

原因:

  1. 本当に証拠金不足
  2. OrderQtyの桁違い(これが多い!)

対処: calculate_order_qty()で商品別に計算


INVALID_VOLUME

Code
58=INVALID_VOLUME

原因: Volume Stepの倍数じゃない

対処: FXなら1000の倍数、ゴールドなら1の倍数


MARKET_CLOSED

Code
58=MARKET_CLOSED

原因: 市場が閉まっている

対処: 取引時間内に送信


🔄 ポジション決済

決済の仕組み

cTrader FIX APIでは、反対売買で決済する。

Code
BUYポジションを決済 → SELL注文を送信
SELLポジションを決済 → BUY注文を送信

決済に必要なもの

  1. ポジションID(721): ExecutionReportで取得
  2. 数量: 保有数量と同じ
  3. 反対のSide: BUY↔SELL

決済コード

Python
def build_close_order(symbol_id: int, position_id: str, qty: int, current_side: str):
"""
決済注文を組み立て
Args:
symbol_id: シンボルID
position_id: 決済するポジションID
qty: 決済数量(OrderQty形式)
current_side: 現在のポジション方向("BUY" or "SELL")
"""
global seq_num
# 反対売買
close_side = "2" if current_side.upper() == "BUY" else "1"
cl_ord_id = f"close_{position_id}_{int(time.time() * 1000)}"
body = (
f"35=D{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"11={cl_ord_id}{SOH}"
f"55={symbol_id}{SOH}"
f"54={close_side}{SOH}"
f"38={qty}{SOH}"
f"40=1{SOH}" # 成行
f"60={get_utc_timestamp()}{SOH}"
f"721={position_id}{SOH}" # ← ポジションIDを指定
)
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

使用例

Python
# ゴールド0.01ロットのBUYポジション(ID=1001179)を決済
close_msg = build_close_order(
symbol_id=41,
position_id="1001179",
qty=1, # 0.01ロット = 1(ゴールド)
current_side="BUY"
)
send_message(trade_sock, close_msg)

📊 ポジション照会

RequestForPositions(35=AN)

Python
def build_position_request():
"""ポジション照会リクエスト"""
global seq_num
pos_req_id = str(int(time.time()))
body = (
f"35=AN{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"710={pos_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

ハマりポイント③: 非対応タグの使用

Python
# ❌ 全部非対応!
f"1={ACCOUNT}{SOH}" # Account
f"263=1{SOH}" # SubscriptionRequestType
f"453=1{SOH}" # NoPartyIDs
f"724=0{SOH}" # PosReqType
# ✅ タグ710だけ
f"710={pos_req_id}{SOH}"

cTrader FIX APIは標準FIXタグの多くが非対応。


🎯 注文〜決済の完全フロー

Python
# 1. ログオン
logon_msg = build_logon_message()
send_message(sock, logon_msg)
response = receive_message(sock)
# 2. ログオン確認
if "35=A" not in response:
print("❌ ログオン失敗")
return
# 3. 注文送信
order_msg, order_id = build_new_order(
symbol_id=41, # XAUUSD
side="BUY",
lot_size=0.01
)
send_message(sock, order_msg)
# 4. 約定確認(受信ループ)
position_id = None
while True:
msg = receive_message(sock)
if "35=8" in msg:
result = parse_execution_report(msg)
if result["status"] == "FILLED":
position_id = result["position_id"]
print(f"✅ 約定!ポジションID: {position_id}")
break
# 5. 決済(必要なタイミングで)
if position_id:
close_msg = build_close_order(
symbol_id=41,
position_id=position_id,
qty=1,
current_side="BUY"
)
send_message(sock, close_msg)

📝 まとめ

注文送信のポイント

  1. シンボルは数値ID で指定
  2. OrderQtyは商品別に計算 が必須
  3. ExecutionReport で約定確認
  4. ポジションID(721) を保存
  5. 決済は反対売買 + ポジションID指定

OrderQty計算式

Python
# FX通貨ペア
qty = int(lot_size * 100000) # 0.01 = 1000
# ゴールド
qty = int(lot_size * 100) # 0.01 = 1
# シルバー
qty = int(lot_size * 5000) # 0.01 = 50
# 原油
qty = int(lot_size * 1000) # 0.01 = 10

🔥 次回予告

【FIX API入門④】15個のハマりポイント総まとめ

  • ログオンで踏む3個
  • 注文で踏む5個
  • 決済で踏む2個
  • その他5個

全部踏んで学んだ血の教訓集。


📚 関連記事

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

その他


0.01ロットを10ロットにされた時の絶望感。 😱

これを読んだあなたは、同じ轍を踏まないでくれ。

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

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入門④|17個のハマりポイント→ Python cBotを使おう

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

fx2026/1/8
📄

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

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

fx2026/1/8
📄

🚨【結論】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
📄

🚨【結論】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