Web Lukes
📄

🔥 cTrader Open APIで自動売買!Pythonで注文を出す完全ガイド【Market/Limit/SL/TP対応】

公開日: 2026/1/7

🔥 cTrader Open APIで自動売買!Pythonで注文を出す完全ガイド

ついに注文を出す時が来た!

ここまでの記事で:

  • ✅ Open APIへの接続
  • ✅ リアルタイム価格の取得

ができるようになりました。

そして今回、ついに実際に注文を出します!

MQLで言うところの OrderSend() ですね。ワクワクしますね 😆

⚠️ 重要: 必ずデモ口座でテストしてから本番に移行してください!


注文の種類を理解する

Open APIで出せる注文タイプ:

注文タイプMQL相当説明
MARKETOP_BUY, OP_SELL成行注文(即時約定)
LIMITOP_BUYLIMIT, OP_SELLLIMIT指値注文
STOPOP_BUYSTOP, OP_SELLSTOP逆指値注文
STOP_LIMIT-ストップリミット注文

Step 1: 成行注文(Market Order)

基本的な成行注文

Python
from ctrader_open_api.messages.OpenApiMessages_pb2 import *
def sendMarketOrder(symbol_id, volume, is_buy):
"""
成行注文を送信
Args:
symbol_id: シンボルID(例:1 = EURUSD)
volume: ロット数 × 100(0.01ロット = 1000)
is_buy: True=買い, False=売り
"""
request = ProtoOANewOrderReq()
request.ctidTraderAccountId = ACCOUNT_ID
request.symbolId = symbol_id
request.orderType = ProtoOAOrderType.MARKET
request.tradeSide = ProtoOATradeSide.BUY if is_buy else ProtoOATradeSide.SELL
request.volume = volume # 0.01ロット = 1000
deferred = client.send(request)
deferred.addCallback(onOrderResult)
deferred.addErrback(onOrderError)
def onOrderResult(result):
print("✅ 注文成功!")
print(f" ポジションID: {result.position.positionId}")
print(f" 約定価格: {result.position.price}")
def onOrderError(failure):
print(f"❌ 注文失敗: {failure}")

🚨 超重要:ボリュームの単位

MQLでは 0.01 と書けばOKでしたが、Open APIでは整数値で指定します:

ロット数Open APIのvolume値
0.011,000
0.110,000
1.0100,000
Python
# 例:0.01ロットを買う
volume = 1000 # 0.01 × 100000 = 1000
sendMarketOrder(symbol_id=1, volume=volume, is_buy=True)

Step 2: SL/TP付きの成行注文

実戦では、SL(ストップロス)とTP(テイクプロフィット)が必須ですよね。

Python
def sendMarketOrderWithSLTP(symbol_id, volume, is_buy, sl_pips=None, tp_pips=None, current_price=None):
"""
SL/TP付き成行注文
Args:
symbol_id: シンボルID
volume: ボリューム(0.01ロット = 1000)
is_buy: True=買い, False=売り
sl_pips: SL幅(pips)
tp_pips: TP幅(pips)
current_price: 現在価格(SL/TP計算用)
"""
request = ProtoOANewOrderReq()
request.ctidTraderAccountId = ACCOUNT_ID
request.symbolId = symbol_id
request.orderType = ProtoOAOrderType.MARKET
request.tradeSide = ProtoOATradeSide.BUY if is_buy else ProtoOATradeSide.SELL
request.volume = volume
# SL/TPを設定
if sl_pips and current_price:
pip_value = 0.0001 # EURUSDの場合
if is_buy:
sl_price = current_price - (sl_pips * pip_value)
else:
sl_price = current_price + (sl_pips * pip_value)
request.stopLoss = sl_price
if tp_pips and current_price:
pip_value = 0.0001
if is_buy:
tp_price = current_price + (tp_pips * pip_value)
else:
tp_price = current_price - (tp_pips * pip_value)
request.takeProfit = tp_price
deferred = client.send(request)
deferred.addCallback(onOrderResult)
return deferred
# 使用例:20pips SL、40pips TPで買い
sendMarketOrderWithSLTP(
symbol_id=1, # EURUSD
volume=1000, # 0.01ロット
is_buy=True,
sl_pips=20,
tp_pips=40,
current_price=1.10500
)

Step 3: 指値注文(Limit Order)

指値注文は、指定した価格で約定させる注文です。

Python
def sendLimitOrder(symbol_id, volume, is_buy, target_price, sl_price=None, tp_price=None):
"""
指値注文を送信
Args:
symbol_id: シンボルID
volume: ボリューム
is_buy: True=買い指値, False=売り指値
target_price: 指値価格
sl_price: ストップロス価格(オプション)
tp_price: テイクプロフィット価格(オプション)
"""
request = ProtoOANewOrderReq()
request.ctidTraderAccountId = ACCOUNT_ID
request.symbolId = symbol_id
request.orderType = ProtoOAOrderType.LIMIT
request.tradeSide = ProtoOATradeSide.BUY if is_buy else ProtoOATradeSide.SELL
request.volume = volume
request.limitPrice = target_price
if sl_price:
request.stopLoss = sl_price
if tp_price:
request.takeProfit = tp_price
deferred = client.send(request)
deferred.addCallback(onPendingOrderResult)
return deferred
def onPendingOrderResult(result):
print("✅ 指値注文を設置しました!")
print(f" 注文ID: {result.order.orderId}")
print(f" 価格: {result.order.limitPrice}")
# 使用例:1.10000で買い指値
sendLimitOrder(
symbol_id=1,
volume=1000,
is_buy=True,
target_price=1.10000,
sl_price=1.09800,
tp_price=1.10400
)

Step 4: 逆指値注文(Stop Order)

ブレイクアウト戦略で使う逆指値注文です。

Python
def sendStopOrder(symbol_id, volume, is_buy, stop_price, sl_price=None, tp_price=None):
"""
逆指値注文(ストップ注文)を送信
Args:
symbol_id: シンボルID
volume: ボリューム
is_buy: True=買い逆指値, False=売り逆指値
stop_price: トリガー価格
"""
request = ProtoOANewOrderReq()
request.ctidTraderAccountId = ACCOUNT_ID
request.symbolId = symbol_id
request.orderType = ProtoOAOrderType.STOP
request.tradeSide = ProtoOATradeSide.BUY if is_buy else ProtoOATradeSide.SELL
request.volume = volume
request.stopPrice = stop_price
if sl_price:
request.stopLoss = sl_price
if tp_price:
request.takeProfit = tp_price
deferred = client.send(request)
deferred.addCallback(onPendingOrderResult)
return deferred
# 使用例:1.11000を上抜けたら買い
sendStopOrder(
symbol_id=1,
volume=1000,
is_buy=True,
stop_price=1.11000,
sl_price=1.10800,
tp_price=1.11400
)

Step 5: ポジション決済

利確・損切り、または手動決済の方法です。

全決済

Python
def closePosition(position_id, volume):
"""
ポジションを決済
Args:
position_id: 決済するポジションのID
volume: 決済数量(全決済なら元のvolume)
"""
request = ProtoOAClosePositionReq()
request.ctidTraderAccountId = ACCOUNT_ID
request.positionId = position_id
request.volume = volume
deferred = client.send(request)
deferred.addCallback(onCloseResult)
return deferred
def onCloseResult(result):
print("✅ 決済完了!")
print(f" 決済価格: {result.position.price}")

部分決済

Python
# 元のポジションが0.1ロット(volume=10000)の場合
# 半分だけ決済
closePosition(position_id=123456, volume=5000) # 0.05ロット決済

Step 6: SL/TPの変更

ポジション保有中にSL/TPを変更する方法です。

Python
def modifySLTP(position_id, new_sl=None, new_tp=None):
"""
ポジションのSL/TPを変更
Args:
position_id: ポジションID
new_sl: 新しいSL価格
new_tp: 新しいTP価格
"""
request = ProtoOAAmendPositionSLTPReq()
request.ctidTraderAccountId = ACCOUNT_ID
request.positionId = position_id
if new_sl:
request.stopLoss = new_sl
if new_tp:
request.takeProfit = new_tp
deferred = client.send(request)
deferred.addCallback(lambda r: print("✅ SL/TP変更完了!"))
return deferred
# 使用例:トレーリングストップ的な使い方
modifySLTP(position_id=123456, new_sl=1.10200, new_tp=1.10800)

Step 7: 保有ポジション一覧の取得

現在の保有ポジションを確認します。

Python
def getPositions():
"""保有ポジション一覧を取得"""
request = ProtoOAReconcileReq()
request.ctidTraderAccountId = ACCOUNT_ID
deferred = client.send(request)
deferred.addCallback(onPositionsList)
return deferred
def onPositionsList(result):
print(f"\n📊 保有ポジション: {len(result.position)} 件")
print("=" * 60)
for pos in result.position:
side = "BUY" if pos.tradeData.tradeSide == ProtoOATradeSide.BUY else "SELL"
volume_lot = pos.tradeData.volume / 100000
print(f"🔹 ポジションID: {pos.positionId}")
print(f" シンボルID: {pos.tradeData.symbolId}")
print(f" 方向: {side}")
print(f" ロット: {volume_lot:.2f}")
print(f" エントリー価格: {pos.price}")
if pos.HasField('stopLoss'):
print(f" SL: {pos.stopLoss}")
if pos.HasField('takeProfit'):
print(f" TP: {pos.takeProfit}")
print()

完全版コード:自動売買ボット

全部を組み合わせた、実際に動く自動売買ボットです!

Python
from ctrader_open_api import Client, Protobuf, TcpProtocol, EndPoints
from ctrader_open_api.messages.OpenApiCommonMessages_pb2 import *
from ctrader_open_api.messages.OpenApiMessages_pb2 import *
from twisted.internet import reactor, task
import time
# ============================================
# 設定
# ============================================
CLIENT_ID = "あなたのClient ID"
CLIENT_SECRET = "あなたのClient Secret"
ACCESS_TOKEN = "取得したアクセストークン"
ACCOUNT_ID = 12345678
# トレード設定
SYMBOL_ID = 1 # EURUSD(あなたの口座で確認してください)
TRADE_VOLUME = 1000 # 0.01ロット
SL_PIPS = 20
TP_PIPS = 40
# ============================================
client = Client(EndPoints.PROTOBUF_LIVE_HOST, EndPoints.PROTOBUF_PORT, TcpProtocol)
# 状態管理
current_bid = None
current_ask = None
has_position = False
def onConnected(client):
print("🔗 接続成功!")
request = ProtoOAApplicationAuthReq()
request.clientId = CLIENT_ID
request.clientSecret = CLIENT_SECRET
deferred = client.send(request)
deferred.addCallback(onAppAuth)
def onAppAuth(result):
print("✅ アプリ認証完了")
request = ProtoOAAccountAuthReq()
request.ctidTraderAccountId = ACCOUNT_ID
request.accessToken = ACCESS_TOKEN
deferred = client.send(request)
deferred.addCallback(onAccountAuth)
def onAccountAuth(result):
print("✅ アカウント認証完了")
# 現在のポジションを確認
request = ProtoOAReconcileReq()
request.ctidTraderAccountId = ACCOUNT_ID
deferred = client.send(request)
deferred.addCallback(onReconcile)
def onReconcile(result):
global has_position
has_position = len(result.position) > 0
print(f"📊 現在のポジション数: {len(result.position)}")
# 価格をサブスクライブ
request = ProtoOASubscribeSpotsReq()
request.ctidTraderAccountId = ACCOUNT_ID
request.symbolId.append(SYMBOL_ID)
deferred = client.send(request)
deferred.addCallback(lambda r: print("📡 価格監視を開始!"))
def sendBuyOrder():
"""買い注文を送信"""
global has_position, current_ask
if has_position:
print("⏸️ 既にポジションがあるため注文をスキップ")
return
if not current_ask:
print("⏸️ 価格データがまだ取得できていません")
return
print(f"\n🔥 買い注文を送信中...")
request = ProtoOANewOrderReq()
request.ctidTraderAccountId = ACCOUNT_ID
request.symbolId = SYMBOL_ID
request.orderType = ProtoOAOrderType.MARKET
request.tradeSide = ProtoOATradeSide.BUY
request.volume = TRADE_VOLUME
# SL/TP設定
pip_value = 0.0001 # EURUSD用
request.stopLoss = current_ask - (SL_PIPS * pip_value)
request.takeProfit = current_ask + (TP_PIPS * pip_value)
deferred = client.send(request)
deferred.addCallback(onOrderResult)
deferred.addErrback(onOrderError)
def sendSellOrder():
"""売り注文を送信"""
global has_position, current_bid
if has_position:
print("⏸️ 既にポジションがあるため注文をスキップ")
return
if not current_bid:
print("⏸️ 価格データがまだ取得できていません")
return
print(f"\n🔥 売り注文を送信中...")
request = ProtoOANewOrderReq()
request.ctidTraderAccountId = ACCOUNT_ID
request.symbolId = SYMBOL_ID
request.orderType = ProtoOAOrderType.MARKET
request.tradeSide = ProtoOATradeSide.SELL
request.volume = TRADE_VOLUME
pip_value = 0.0001
request.stopLoss = current_bid + (SL_PIPS * pip_value)
request.takeProfit = current_bid - (TP_PIPS * pip_value)
deferred = client.send(request)
deferred.addCallback(onOrderResult)
deferred.addErrback(onOrderError)
def onOrderResult(result):
global has_position
has_position = True
print("✅ 注文が約定しました!")
print(f" ポジションID: {result.position.positionId}")
print(f" 約定価格: {result.position.price}")
def onOrderError(failure):
print(f"❌ 注文エラー: {failure}")
def onMessageReceived(client, message):
global current_bid, current_ask
# 価格更新
if message.payloadType == 2131: # ProtoOASpotEvent
spot = Protobuf.extract(message)
if spot.symbolId == SYMBOL_ID:
if spot.HasField('bid'):
current_bid = spot.bid / 100000
if spot.HasField('ask'):
current_ask = spot.ask / 100000
timestamp = time.strftime("%H:%M:%S")
print(f"[{timestamp}] Bid: {current_bid:.5f} Ask: {current_ask:.5f}", end='\r')
# ポジションクローズの検知
elif message.payloadType == 2126: # ProtoOAExecutionEvent
global has_position
event = Protobuf.extract(message)
if event.executionType == ProtoOAExecutionType.ORDER_FILLED:
pass # 新規約定
elif event.executionType == ProtoOAExecutionType.ORDER_CANCELLED:
pass # キャンセル
# コマンド入力用
def checkInput():
"""標準入力をチェック(簡易的なコマンド処理)"""
# 実際のボットでは、条件に基づいて自動で発注します
pass
# コールバック設定
client.setConnectedCallback(onConnected)
client.setDisconnectedCallback(lambda c, r: print(f"\n切断: {r}"))
client.setMessageReceivedCallback(onMessageReceived)
print("=" * 60)
print("🤖 cTrader Open API 自動売買ボット")
print("=" * 60)
print(f" シンボルID: {SYMBOL_ID}")
print(f" ロット: {TRADE_VOLUME / 100000:.2f}")
print(f" SL: {SL_PIPS} pips")
print(f" TP: {TP_PIPS} pips")
print("=" * 60)
print("\n接続中...\n")
client.startService()
reactor.run()

MQLとの比較:注文処理

操作MQLOpen API (Python)
成行注文OrderSend(OP_BUY)ProtoOANewOrderReq (MARKET)
指値注文OrderSend(OP_BUYLIMIT)ProtoOANewOrderReq (LIMIT)
決済OrderClose()ProtoOAClosePositionReq
SL/TP変更OrderModify()ProtoOAAmendPositionSLTPReq
ロット指定0.011000
価格指定1.105001.10500(そのまま)

エラーハンドリング

よくあるエラーと対処法:

「NOT_ENOUGH_MONEY」

Python
def onOrderError(failure):
error_message = str(failure)
if "NOT_ENOUGH_MONEY" in error_message:
print("❌ 証拠金不足です!ロット数を減らしてください")
elif "SYMBOL_NOT_FOUND" in error_message:
print("❌ シンボルIDが間違っています")
elif "TRADING_DISABLED" in error_message:
print("❌ 取引が無効です(市場クローズ or 口座制限)")
else:
print(f"❌ エラー: {error_message}")

「INVALID_VOLUME」

ボリュームが口座の最小/最大ロットを超えていないか確認:

Python
# シンボル詳細を取得して確認
request = ProtoOASymbolByIdReq()
request.ctidTraderAccountId = ACCOUNT_ID
request.symbolId.append(SYMBOL_ID)

注意点とベストプラクティス

1. 必ずデモ口座でテスト

Python
# デモサーバーを使う
HOST = EndPoints.PROTOBUF_DEMO_HOST # デモ
# HOST = EndPoints.PROTOBUF_LIVE_HOST # ライブ(本番用)

2. リスク管理を忘れずに

Python
# 1トレードの最大リスクを口座の2%に制限
account_balance = 10000 # ドル
max_risk = account_balance * 0.02 # 200ドル
pip_value = 10 # 1ロットあたり1pipのドル価値
sl_pips = 20
max_lots = max_risk / (sl_pips * pip_value) # 1.0ロット

3. 重複注文を防ぐ

Python
# フラグで管理
if not has_position and not pending_order:
sendBuyOrder()

次のステップ

この記事で学んだこと:

  • ✅ 成行注文の出し方
  • ✅ SL/TP付き注文
  • ✅ 指値・逆指値注文
  • ✅ ポジション決済
  • ✅ SL/TPの変更
  • ✅ ポジション一覧取得

これで、Open APIで自動売買ができるようになりました!🎉

次の記事では、cTrader 5.4で導入されたネイティブPython cBotについて解説します。Open APIとの違いや使い分けも説明しますね。


まとめ

ついに注文が出せるようになりましたね!

MQLの OrderSend() に慣れていると、最初は「なんでこんなに複雑なの?」と思うかもしれません。

でも、この複雑さには理由があります:

  • 非同期処理で複数の操作を同時に
  • 詳細なエラー情報でデバッグが楽
  • 柔軟な拡張性で何でも作れる

一度慣れてしまえば、MQLより快適に開発できるようになります。

さあ、あなたの自動売買ボットを作りましょう! 🤖


参考リンク

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

FX自動売買VPSなら50%OFF

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

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

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

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

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

🔮
MEMBERSHIP

量子相場謀略|ホソノP

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

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

📚あわせて読みたい

📄

😤「MQLは書けるのにcTraderで自動売買できない!」そんなあなたへ贈る cTrader Open API × Python 入門ガイド

MQL経験者がcTrader Open APIとPythonで自動売買を始めるための完全入門ガイド。環境構築からアプリ登録、認証の仕組みまで丁寧に解説します。

trading2026/1/7
📄

🎊 cTrader 5.4でPython cBotが書ける!ネイティブPythonサポート完全ガイド【Open APIとの違いも解説】

cTrader 5.4で追加されたネイティブPythonサポートを徹底解説。cBot、インジケーターをPythonで作成する方法、Open APIとの違い、MQL経験者のための移行ガイド。

trading2026/1/7
📄

🎯 cTrader Open APIでリアルタイム価格を取得!Python実践チュートリアル【コピペでOK】

cTrader Open APIとPythonでリアルタイムの為替レートを取得する方法を解説。シンボル一覧取得からティックデータの受信まで、実際に動くコードで学べます。

trading2026/1/7
📄

⚠️【非推奨】FIX API完全版①|2セッション構成→ Python cBotを使おう

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

trading2026/1/8
📄

🚀【神機能】cTrader Open APIはプラットフォーム不要!MT5との決定的な違いとおすすめブローカー・プロップファーム

cTrader Open APIはMT5と違ってデスクトップアプリ不要!Pythonだけで直接サーバーに接続できる革命的なAPI。VPSでヘッドレス運用したいトレーダー必見。日本人におすすめのcTraderブローカーとプロップファームも徹底解説。

ctrader2026/1/6
📄

😤「ピラミッティングで爆益!」は本当か?60日ブレイクアウトで徹底検証した結果...

60日高値安値ブレイクアウトで単ポジションとピラミッティングを比較検証。「積みながら建値ガードで安心!トレンド乗れれば爆益!」という夢は実現するのか?7年分のデータで検証した衝撃の結果をお届けします。

trading2025/12/20