XM 500ドル入金ボーナスの詳細はこちら ▶︎

無料レイテンシー取引ツール(MT5-バイナリー業者)のソースコード・使い方

  • URLをコピーしました!

レイテンシーアービトラージはFXの中でもトップクラスに期待値の高いトレード手法です。かつては数ヶ月で数百万円から数千万円以上の利益を出すことも可能でした。

ただ現在ではブローカー側の対策が厳しくなっており、この手の超高速売買をすると不正取引として口座凍結・利益没収されるリスクが高くなっています。

なおこれは国内/海外FXブローカーの話です。レイテンシー対策が共有されていないバイナリーオプション業者であれば付け入る隙はあるかもしれません。

この記事ではバイナリーオプション業者向けのレイテンシー取引ツールを紹介していきます。無料公開しているソースコードなのでLLMで改造することも可能です。

目次

レイテンシーツールの無料公開ソースコード

バイナリー業者向けのレイテンシー取引ツールのソースコードはこちら。(ソースコード引用元

これはいわゆるクリック系ツールで、Chromeブラウザを手動操作に見せかけて自動売買することができます。

import json
import asyncio
import re
import requests
import websockets
import pychrome
import time
from collections import deque
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import MetaTrader5 as mt5
import psutil
import sys
import pyautogui # ▼▼▼ 追加: pyautogui をインポート ▼▼▼

# --- ▼ ユーザー設定 ▼ ---
YOUR_HASH = ""  # 自身のハッシュ値に書き換えてください
TRADE_AMOUNT = "500"
TIMEFRAME_ID = "15" # 30秒取引:"15", 1分取引:"16", ...
TRADE_MODE = "demo"
MT5_SYMBOL = "USDJPY"

# ▼▼▼ 追加・変更箇所: マウスクリック用の座標設定 ▼▼▼
# 【超重要】これらの座標は**仮の値**です。
# 実際の取引プラットフォームの「PUT」と「CALL」ボタンの正確な中心座標に設定してください。
# 設定を誤るとクリックが全く機能しません。
#
# ★ 座標特定の手順 ★
# 1. 取引プラットフォームのページをChromeで開く。
# 2. Chromeの**ズームレベルを必ず100%**に設定。
# 3. ブラウザの**ウィンドウサイズを固定**し、常にその位置に配置する。
# 4. マウスカーソルを「PUT」または「CALL」ボタンの中心に手動で合わせる。
# 5. Pythonのインタラクティブシェルや簡単なスクリプトで `import pyautogui; pyautogui.position()` を実行し、
#    その時点のマウスカーソルのX, Y座標を取得する。
#    例:
#    >>> import pyautogui
#    >>> pyautogui.position()
#    Point(x=900, y=500)
#
# ★ その他重要な注意点 (pyautogui 使用時) ★
# ・プログラム実行中は**ブラウザウィンドウを最小化せず、フォアグラウンドに表示**してください。
# ・プログラム実行中に**マウスやキーボードを操作しないでください**。誤動作の原因になります。
# ・これはOSレベルでの物理クリックであり、画面に表示されているものをクリックします。
PUT_BUTTON_COORD_X = 1742  # 例: PUTボタンのX座標 (画面左上を(0,0)とするピクセル値)
PUT_BUTTON_COORD_Y = 575  # 例: PUTボタンのY座標
CALL_BUTTON_COORD_X = 1732  # 例: CALLボタンのX座標
CALL_BUTTON_COORD_Y = 359  # 例: CALLボンのY座標

# --- ▼ エントリーロジック設定 ▼ ---
# 価格差の移動平均を計算するために、何回分のデータを保持するか
AVERAGE_PERIOD = 50
# 平均の価格差から、何pips乖離したらエントリーするか (例: 0.5pips = 0.005)
ENTRY_THRESHOLD_PIPS = 0.005
# 一度エントリーしてから、次にエントリーするまでの待機時間(秒)
ENTRY_COOLDOWN_SECONDS = 15
# 注文失敗時に再試行する最大回数
MAX_ORDER_RETRY_ATTEMPTS = 3
# 注文再試行までの待機時間(秒)
ORDER_RETRY_DELAY_SECONDS = 5
# --- ▲ エントリーロジック設定 ▲ ---


# --- 以下は基本的に編集不要 ---

# グローバル変数
diff_history = deque(maxlen=AVERAGE_PERIOD)
last_entry_time = 0

def log_message(message):
    print(f"[INFO] {message}")

def find_mt5_instances():
    instances = []
    for proc in psutil.process_iter(['name', 'exe']):
        try:
            if proc.info['name'] in ['terminal64.exe', 'terminal.exe']:
                if proc.info['exe'] and proc.info['exe'] not in instances:
                    instances.append(proc.info['exe'])
        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
            pass
    return instances

def select_mt5_instance(instances):
    if not instances:
        log_message("MT5: 起動しているMT5が見つかりませんでした。")
        return None
    if len(instances) == 1:
        log_message(f"MT5: 起動中のMT5を1つ検出しました: {instances[0]}")
        return instances[0]
    print("-" * 50)
    log_message("複数のMT5が起動しています。接続するMT5を選択してください:")
    for i, path in enumerate(instances):
        print(f"  [{i + 1}] {path}")
    while True:
        try:
            choice = int(input(f"番号を入力してください (1-{len(instances)}): ")) - 1
            if 0 <= choice < len(instances): return instances[choice]
            else: print("無効な番号です。")
        except ValueError: print("数値を入力してください。")
        except (KeyboardInterrupt, EOFError): return None

# --- ブラウザへの接続処理 ---
# SeleniumとPyChromeはWebSocketによる価格データ取得のために引き続き使用します。
# クリックにはpyautoguiを使用するため、pychromeでのInput.dispatchMouseEventは不要になります。
try:
    chrome_options = Options()
    chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
    driver = webdriver.Chrome(options=chrome_options)
    log_message("Selenium: 既存のChromeセッションに接続しました (port 9222)")
except Exception as e:
    log_message(f"Selenium: 接続に失敗しました: {e}"); exit()
try:
    browser = pychrome.Browser(url="http://127.0.0.1:9222")
    tabs = browser.list_tab()
    tab = tabs[0] if tabs else None
    if tab: tab.start(); log_message("PyChrome: 接続に成功しました (port 9222)")
    else: log_message("PyChrome: 利用可能なタブが見つかりません")
except Exception as e:
    log_message(f"PyChrome: 接続に失敗しました: {e}"); tab = None


async def setup_websocket_connection():
    # この関数は、pyautoguiによるクリックとは直接関係ありませんが、
    # 価格データ取得のために既存のWebSocket接続設定を維持します。
    if not tab:
        log_message("BO: PyChrome タブが利用できないためWebSocket設定をスキップします。")
        return False

    js_websocket_setup = f"""
    (async function() {{
        if (typeof window.boWebSocket !== 'undefined' && window.boWebSocket.readyState !== WebSocket.CLOSED) {{
            console.log("Closing existing BO WebSocket before re-setup.");
            window.boWebSocket.close();
            await new Promise(r => setTimeout(r, 100));
        }}

        window.boWebSocket = new WebSocket("wss://bo.zentrader.com:27017/");

        window.boWebSocket.onclose = (event) => {{
            console.warn("BO WebSocket closed. Code:", event.code, "Reason:", event.reason);
        }};
        window.boWebSocket.onerror = (error) => {{
            console.error("BO WebSocket error:", error);
        }};

        let timeout = 10000;
        let interval = 500;
        let elapsed = 0;

        while (window.boWebSocket.readyState !== WebSocket.OPEN && elapsed < timeout) {{
            await new Promise(r => setTimeout(r, interval));
            elapsed += interval;
        }}

        if (window.boWebSocket.readyState === WebSocket.OPEN) {{
            console.log("BO WebSocket opened and ready for initial commands.");
            const now = Math.floor(Date.now() / 1000), tE = now, tB = now - 500;

            window.boWebSocket.send(JSON.stringify({{
                command: "connect",
                bo: "{TRADE_MODE}",
                hash: "{YOUR_HASH}",
                platform: "mt4",
                source: "site"
            }}));
            console.log("connect sent");

            await new Promise(r => setTimeout(r, 300));
            window.boWebSocket.send(JSON.stringify({{ command: "get_user_data" }}));
            console.log("get_user_data sent");

            await new Promise(r => setTimeout(r, 300));
            window.boWebSocket.send(JSON.stringify({{ command: "hook_time", enable: "true" }}));
            console.log("hook_time sent");

            await new Promise(r => setTimeout(r, 300));
            window.boWebSocket.send(JSON.stringify({{ command: "get_cfg_trade", language: "ja" }}));
            console.log("get_cfg_trade sent");

            await new Promise(r => setTimeout(r, 300));
            window.boWebSocket.send(JSON.stringify({{ command: "get_user_settings" }}));
            console.log("get_user_settings sent");

            await new Promise(r => setTimeout(r, 300));
            window.boWebSocket.send(JSON.stringify({{
                command: "hook_timeframes",
                option_kind: "1",
                tool_id: "4",
                timeframe_id: "{TIMEFRAME_ID}",
                bo: "{TRADE_MODE}",
                enable: "true",
                interval: "1000",
                source: "site"
            }}));
            console.log("hook_timeframes sent");

            await new Promise(r => setTimeout(r, 300));
            window.boWebSocket.send(JSON.stringify({{
                command: "get_quotes_history",
                tool_id: "4",
                time_begin: tB.toString(),
                time_end: tE.toString(),
                time_size: "S1",
                source: "site",
                request_id: "4_500"
            }}));
            console.log("get_quotes_history sent", {{
                time_begin: new Date(tB * 1000).toISOString(),
                time_end: new Date(tE * 1000).toISOString(),
                duration_sec: tE - tB
            }});

            await new Promise(r => setTimeout(r, 300));
            window.boWebSocket.send(JSON.stringify({{ command: "hook_user_status", enable: "true" }}));
            console.log("hook_user_status sent");

            await new Promise(r => setTimeout(r, 300));
            window.boWebSocket.send(JSON.stringify({{
                command: "hook_options",
                source: "site",
                enable: "true",
                interval: "100"
            }}));
            console.log("hook_options sent");

            return true;
        }} else {{
            console.error("BO WebSocket failed to open within timeout. readyState:", window.boWebSocket.readyState);
            return false;
        }}
    }})();
    """
    try:
        result_eval = tab.Runtime.evaluate(expression=js_websocket_setup, awaitPromise=True)

        js_return_value = result_eval.get('result', {}).get('value')

        if js_return_value is True:
            log_message("BO: WebSocket接続スクリプトの実行とOPEN状態への待機に成功しました。")
            return True
        else:
            log_message("BO: WebSocket接続スクリプトの実行に失敗、またはOPEN状態になりませんでした。")
            if js_return_value is False:
                pass
            else:
                log_message(f"BO: JavaScriptからの予期せぬ戻り値: {js_return_value}")
            return False
    except Exception as e:
        log_message(f"BO: WebSocket接続スクリプトの実行中にエラー: {e}")
        return False


def send_bo_order(direction, price_open):
    """バイナリーオプションの注文をPC画面上のマウスクリックで送信する関数"""
    target_x = 0
    target_y = 0
    button_name = ""

    if direction == "PUT":
        target_x = PUT_BUTTON_COORD_X
        target_y = PUT_BUTTON_COORD_Y
        button_name = "PUT"
    elif direction == "CALL":
        target_x = CALL_BUTTON_COORD_X
        target_y = CALL_BUTTON_COORD_Y
        button_name = "CALL"
    else:
        log_message(f"BO: 不明な注文方向: {direction}")
        return False

    log_message(f"BO: {button_name} ボタンを物理座標 ({target_x}, {target_y}) でクリックします。現在の価格表示(参考): {price_open}")
    log_message(f"【超重要】ブラウザのウィンドウ位置、ズームレベル100%を確認し、実行中はPCを操作しないでください。")

    try:
        # pyautoguiで指定された座標をクリック
        pyautogui.click(x=target_x, y=target_y)
        log_message(f"★★★ BO: {button_name} ボタンの物理クリックイベント送信成功 (座標: {target_x}, {target_y}) ★★★")
        return True

    except pyautogui.FailSafeException:
        log_message(f"BO: エラー - マウスが画面隅に移動したため、pyautoguiがFail-Safeをトリガーしました。")
        log_message(f"BO: Fail-Safeは緊急停止機能です。プログラムを終了します。")
        sys.exit(1) # プログラムを強制終了
    except Exception as e:
        log_message(f"BO: エラー - {button_name} ボタンの物理クリック中にエラーが発生しました: {e}")
        return False



def get_price_from_mt5():
    tick = mt5.symbol_info_tick(MT5_SYMBOL)
    return tick.ask if tick else None

async def price_monitoring_and_trading_logic():
    """ブラウザ価格を監視し、乖離エントリーロジックを実行する"""
    global last_entry_time
    port = 9222
    try:
        response = requests.get(f'http://localhost:{port}/json')
        ws_debugger_url = next(item['webSocketDebuggerUrl'] for item in response.json() if 'webSocketDebuggerUrl' in item and item['type'] == 'page')
    except Exception as e:
        log_message(f"CDP: DevTools WebSocket URL 取得に失敗: {e}");
        log_message("CDP: プログラムを終了します。ブラウザが起動していないか、デバッグポートが利用できません。")
        sys.exit(1) # 強制終了

    log_message("CDP: DevTools WebSocket に接続試行中...")
    try:
        websocket_cdp = await websockets.connect(ws_debugger_url)
        await websocket_cdp.send(json.dumps({"id": 1, "method": "Network.enable"}))
        log_message("CDP: DevTools WebSocket に接続成功。価格差の監視を開始...")
    except Exception as e:
        log_message(f"CDP: DevTools WebSocket への接続に失敗しました: {e}")
        log_message("CDP: プログラムを終了します。")
        sys.exit(1) # 強制終了


    latest_browser_price = 0.0
    while True:
        try:
            message = await websocket_cdp.recv() # CDP WebSocketを使用
            data = json.loads(message)
            if data.get("method") != "Network.webSocketFrameReceived": continue

            # WebSocketフレームのペイロードから価格情報を抽出
            payload = data["params"]["response"].get("payloadData", "")
            if not payload.startswith("U,1,1,USD/JPY"): continue

            match = re.search(r"USD/JPY\|([\d.]+)\|", payload)
            if not match: continue

            browser_price = float(match.group(1))
            if latest_browser_price == browser_price: continue
            latest_browser_price = browser_price

            mt5_ask = get_price_from_mt5()
            if not mt5_ask: continue

            current_diff = mt5_ask - browser_price
            diff_history.append(current_diff)

            if len(diff_history) < AVERAGE_PERIOD:
                log_message(f"[データ収集中 {len(diff_history)}/{AVERAGE_PERIOD}] MT5: {mt5_ask} | Browser: {browser_price} | 差: {current_diff:+.5f}")
                continue

            average_diff = sum(diff_history) / len(diff_history)
            log_message(f"[監視中] MT5: {mt5_ask} | Browser: {browser_price} | 現在差: {current_diff:+.5f} | 平均差: {average_diff:+.5f}")

            # --- ▼▼▼ エントリー判定 ▼▼▼ ---

            if time.time() - last_entry_time < ENTRY_COOLDOWN_SECONDS:
                log_message(f"【エントリー対象外】クールダウン中 ({ENTRY_COOLDOWN_SECONDS - (time.time() - last_entry_time):.1f}秒残り) ✕")
                continue

            order_triggered = False
            order_direction = None
            order_price = None

            if current_diff > average_diff + ENTRY_THRESHOLD_PIPS:
                log_message(f"【PUT条件成立】現在差({current_diff:+.5f}) > 平均差({average_diff:+.5f}) + 閾値({ENTRY_THRESHOLD_PIPS}) ○")
                order_direction = "PUT"
                order_price = browser_price
                order_triggered = True
            elif current_diff < average_diff - ENTRY_THRESHOLD_PIPS:
                log_message(f"【CALL条件成立】現在差({current_diff:+.5f}) < 平均差({average_diff:+.5f}) - 閾値({ENTRY_THRESHOLD_PIPS}) ○")
                order_direction = "CALL"
                order_price = browser_price
                order_triggered = True
            else:
                log_message(f"【エントリー対象外】条件不成立 (現在差: {current_diff:+.5f}, 平均差: {average_diff:+.5f}, 閾値: {ENTRY_THRESHOLD_PIPS}) ✕")

            # 注文がトリガーされた場合の再試行ロジック
            if order_triggered:
                order_successful = False
                for attempt in range(MAX_ORDER_RETRY_ATTEMPTS):
                    log_message(f"BO: 注文試行中... ({attempt + 1}/{MAX_ORDER_RETRY_ATTEMPTS}回目)")
                    # pyautoguiによる物理クリックを試行
                    order_status = send_bo_order(order_direction, order_price)

                    if order_status is True: # クリックイベント送信が成功
                        order_successful = True
                        break # 再試行ループを抜ける
                    else: # クリックイベント送信が失敗した場合 (FailSafeなどで中断された場合も含む)
                        if attempt < MAX_ORDER_RETRY_ATTEMPTS - 1:
                            log_message(f"BO: 注文クリック失敗。{ORDER_RETRY_DELAY_SECONDS}秒後に再試行します。")
                            await asyncio.sleep(ORDER_RETRY_DELAY_SECONDS)
                        else:
                            log_message(f"BO: 注文クリック失敗。最大試行回数 ({MAX_ORDER_RETRY_ATTEMPTS}) に達しました。")

                if order_successful:
                    log_message(f"BO: 注文クリック処理が正常に完了しました。クールダウンを開始します。")
                    last_entry_time = time.time() # 注文成功時のみ最終エントリー時間を更新
                else:
                    log_message(f"BO: 注文の再試行がすべて失敗しました。次のティックで処理を継続します。")


        except websockets.exceptions.ConnectionClosed as e:
            log_message(f"CDP: DevTools WebSocket接続が閉じられました: {e}。これは致命的な問題です。")
            log_message("CDP: プログラムを終了します。ブラウザのデバッグ接続が失われました。")
            break
        except Exception as e:
            log_message(f"CDP: WebSocket 受信または処理中にエラー: {e}");
            await asyncio.sleep(1)
            continue

async def main():
    mt5_path = select_mt5_instance(find_mt5_instances())
    if not mt5_path: log_message("プログラムを終了します。"); return
    if not mt5.initialize(path=mt5_path):
        log_message(f"MT5: initialize() failed, error code = {mt5.last_error()}"); return
    log_message(f"MT5: 接続成功 (Path: {mt5_path}, Version: {mt5.version()})")

    log_message("BO: 初期WebSocket接続設定を開始します。")
    if not await setup_websocket_connection():
        log_message("BO: WebSocketの初期接続設定に失敗しました。プログラムを終了します。"); return
    log_message("BO: 初期WebSocket接続が正常に確立されました。")

    await price_monitoring_and_trading_logic()

if __name__ == "__main__":
    # pyautogui のFail-Safe機能を有効にする (デフォルトで有効ですが念のため)
    # マウスを画面の四隅のいずれかに移動させると、pyautoguiの制御が停止します。
    pyautogui.FAILSAFE = True
    pyautogui.PAUSE = 0.1 # 各pyautogui呼び出し間の0.1秒の一時停止を設定

    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        log_message("プログラムが中断されました。(KeyboardInterrupt)")
    finally:
        if mt5.terminal_info():
            mt5.shutdown(); log_message("MT5: 接続をシャットダウンしました。")

calibrate用のファイルはこちら。

# -*- coding: utf-8 -*-
import pyautogui
import time

def calibrate():
    print("=== Calibration Mode ===")
    time.sleep(0.2)

    input("Move the mouse cursor over the UP button and press Enter�c")
    up = pyautogui.position()
    print(f"UP_POS = ({up.x}, {up.y})")

    input("Move the mouse cursor over the DOWN button and press Enter�c")
    down = pyautogui.position()
    print(f"DOWN_POS = ({down.x}, {down.y})")

    print("\nCopy these tuples into auto_trader.py as UP_POS and DOWN_POS.")

if __name__ == "__main__":
    calibrate()

説明書・使い方はこちら。

1.PowerShellを立ち上げます。

2.cd C:\Users\自分のフォルダ\Desktop\tradebot

3.
pip install requests
pip install websockets
pip install pychrome
pip install selenium
pip install MetaTrader5
pip install psutil

  1. Chrome のリモートデバッグ起動
    自動接続のため、別ウィンドウで次を実行し、デバッグモードの Chrome を立ち上げます:

& “C:/Program Files/Google/Chrome/Application/chrome.exe” --remote-debugging-port=9222
–user-data-dir=”C:/chromebot_profile”

5.https://demo.zentrader.com/を、立ち上げたURL欄に貼る。
(デモ口座登録してない人はしてください)

6.py tradebot.py

エントリー部分は、ChatGPTと話しながらするといいと思います。
抜けてるところあるかもなので、わからないことあったらDMで聞いてください

HASHの見つけ方

ステップ1でデバッグモードで起動したChromeで、取引サイトにログインします。
キーボードの F12 キーを押して、「デベロッパーツール(開発者ツール)」を開きます。
「ネットワーク(Network)」タブを選択し、その中にある「WS (WebSocket)」をクリックして絞り込みます。
ページをリロード(再読み込み)すると、WebSocketの通信が表示されます。bo.zentrader.com のような名前の通信を探してください。
その通信名をクリックし、「メッセージ(Messages)」タブを開きます。
緑色の上向き矢印(送信されたデータ)の中に、”command”: “connect” から始まるデータがあります。その中にある “hash”: “xxxxxxxx…” の、xxxx… の部分があなたのログイン情報です。これをコピーします。

ソースコードの使い方(Gemini Pro説明バージョン)

このPythonコードは、MetaTrader 5 (MT5) の価格と、ブラウザ上のバイナリーオプションプラットフォームの価格差を利用して、自動でエントリー(取引注文)を行うためのツールです。

注文は、pyautoguiというライブラリを使って、PCの画面上のボタンを物理的にマウスクリックすることで行われます。

以下に、このコードを使用するための準備から実行までの手順を説明します。

1. このコードの目的と仕組み

  • 目的: MT5から配信されるレートと、ブラウザ上の取引プラットフォームのレートの間に発生するわずかな「ズレ(乖離)」を検知し、そのズレが元に戻る動きを狙って自動で取引します。
  • 仕組み:
    1. MT5とブラウザの両方からリアルタイムに価格を取得します。
    2. 2つの価格の差を常に計算し、その差の「移動平均値」を算出します。
    3. 現在の価格差が、計算された移動平均値から設定した閾値(ENTRY_THRESHOLD_PIPS)以上に広がった瞬間に、エントリーチャンスと判断します。
    4. エントリーチャンスと判断すると、pyautoguiが事前に設定された座標(PUTボタンまたはCALLボタンの位置)を自動でマウスクリックして注文を出します。

2. 必要なもの(実行環境)

  1. Python環境: PythonがPCにインストールされている必要があります。
  2. 必要なライブラリ: 以下のライブラリをインストールしてください。コマンドプロンプトやターミナルで以下のコマンドを実行します。
    bash pip install requests websockets pychrome-async selenium MetaTrader5 psutil pyautogui
  3. MetaTrader 5 (MT5): PCにインストールされ、起動している必要があります。
  4. Google Chrome: PCにインストールされている必要があります。
  5. バイナリーオプションプラットフォームのアカウント: コードはZentraderを想定しているようですが、他のプラットフォームでも座標設定を正しく行えば利用できる可能性があります。

3. 準備手順(プログラム実行前)

プログラムを実行する前に、以下の準備を必ず行ってください。

Step 1: Chromeをデバッグモードで起動する

このコードは、起動中のChromeに接続して情報を取得する必要があります。

  1. 現在開いているChromeをすべて閉じます。
  2. コマンドプロンプト(Windowsの場合)またはターミナル(Macの場合)を開き、以下のコマンドを実行してChromeを起動します。
    • Windowsの場合: "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 ※Chromeのインストール場所が異なる場合は、パスを修正してください。
    • Macの場合:
      bash /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222

Step 2: 取引プラットフォームにログインする

デバッグモードで起動したChromeで、取引したいバイナリーオプションのプラットフォームのページを開き、ログインを済ませておきます。

Step 3: MT5を起動する

取引に使用するMT5を起動し、ログインしておきます。

Step 4: 【最重要】コードのユーザー設定を行う

コードの冒頭にある「ユーザー設定」セクションを、ご自身の環境に合わせて編集します。

  1. YOUR_HASH = "": あなたの取引アカウントのハッシュ値を設定します。これはプラットフォームにログインした際の認証情報の一部です。ブラウザの開発者ツール(F12キー)の「ネットワーク」タブなどで確認する必要があります。
  2. TRADE_AMOUNT = "500": 1回あたりの取引金額を設定します。
  3. TIMEFRAME_ID = "15": 取引時間を選択します。コードのコメントによると "15" は30秒取引のようです。
  4. TRADE_MODE = "demo": デモ口座を使うか、リアル口座を使うかを設定します。("demo" または "real"
  5. MT5_SYMBOL = "USDJPY": MT5で価格を取得する通貨ペアを設定します。
  6. 【超重要】マウスクリック用の座標設定
    このプログラムが正しく動作するための最も重要な設定です。PUTボタンとCALLボタンの画面上の正確な座標を設定する必要があります。
    • ウィンドウサイズとズームレベルの固定:
      • 取引プラットフォームを開いているChromeのズームレベルを必ず100%にしてください。
      • ブラウザのウィンドウサイズと位置を固定し、プログラム実行中はその状態を維持してください。
    • 座標の特定方法:
      1. マウスカーソルを取引画面の「PUT」ボタンの真ん中に手で合わせます。
      2. コマンドプロンプトやターミナルでPythonを起動し(pythonと入力)、以下のコマンドを実行して座標(X, Y)を取得します。
        python >>> import pyautogui >>> print(pyautogui.position()) # Point(x=1742, y=575) のように表示されます
      3. 表示されたX座標とY座標を、コードの PUT_BUTTON_COORD_XPUT_BUTTON_COORD_Y に設定します。
      4. 同様に、「CALL」ボタンの座標も特定し、CALL_BUTTON_COORD_XCALL_BUTTON_COORD_Y に設定します。
    ※この座標設定を間違えると、意図しない場所がクリックされたり、全くクリックされなかったりします。

4. 実行方法

すべての準備が完了したら、コマンドプロンプトやターミナルで、このPythonファイルが保存されているフォルダに移動し、以下のコマンドでプログラムを実行します。

python <ファイル名>.py

<ファイル名>は、このコードを保存したファイル名に置き換えてください)

  • MT5の選択: 複数のMT5を起動している場合、どのMT5に接続するかを選択するためのプロンプトが表示されます。番号を入力してEnterキーを押してください。
  • 監視開始: 正常に起動すると、ターミナルにログメッセージが表示され、価格差の監視が始まります。

5. 実行中の注意点

  • 【絶対にPCを操作しない】: プログラム実行中は、マウスやキーボードに触らないでください。予期せぬ場所をクリックしてしまい、誤った取引や操作につながる危険があります。
  • 【ブラウザを前面に表示】: 取引画面のChromeウィンドウは、常に最前面に表示し、最小化しないでくださいpyautoguiは画面に見えているものをクリックするため、ウィンドウが隠れていると機能しません。
  • プログラムの停止方法:
    • 通常停止: プログラムを実行しているターミナルを選択し、Ctrl + C キーを押します。
    • 緊急停止(フェイルセーフ): マウスカーソルを画面の四隅(右上、右下、左上、左下)のいずれかに素早く移動させると、pyautoguiの安全機能が作動し、プログラムが強制的に停止します。

このコードは高度な自動化ツールであり、設定ミスや不適切な使用は意図しない損失につながる可能性があります。まずはデモ口座で十分にテストし、仕組みをよく理解した上でご利用ください。

calibrateファイルの使い方

このPythonスクリプトは、画面上の特定の位置の座標(X, Y)を正確に取得するための補助ツールです。

具体的には、pyautoguiを使った自動化スクリプト(例えば、先ほど質問された自動取引ボット)で、クリックさせたいボタン(このコードでは「UPボタン」と「DOWNボタン」)の正確な座標を簡単に見つけ出すために使います。

手動で座標を調べる手間を省き、より正確な設定を支援することが目的の「キャリブレーション(位置調整)用スクリプト」です。

コードの主な機能と解説

このコードは非常にシンプルで、以下の3つの主要な部分から成り立っています。

  1. ライブラリのインポートimport pyautogui import time
    • pyautogui: マウスやキーボードを自動で操作するためのライブラリです。このコードでは、マウスカーソルの現在位置を取得する pyautogui.position() という機能を使っています。
    • time: 時間に関連する機能を提供します。time.sleep(0.2) でプログラムの開始をわずかに遅らせ、ユーザーが準備する時間を与えています。
  2. 対話的な座標取得input("Move the mouse cursor over the UP button and press Enter…") up = pyautogui.position() print(f"UP_POS = ({up.x}, {up.y})")
    • input(...): 画面に (...) 内のメッセージを表示し、ユーザーがEnterキーを押すまでプログラムの実行を一時停止します。
    • 処理の流れ:
      1. 「UPボタンの上にマウスカーソルを移動させて、Enterキーを押してください」というメッセージが表示されます。
      2. ユーザーは、自動クリックさせたいアプリケーションの「UP」ボタンの上にマウスカーソルを合わせます。
      3. その後、このスクリプトを実行しているターミナル(黒い画面)に戻り、Enterキーを押します。
      4. Enterキーが押された瞬間に pyautogui.position() が実行され、その時点でのマウスカーソルのX座標とY座標が変数 up に保存されます。
      5. print(...) で、取得した座標を UP_POS = (123, 456) のような形式で画面に表示します。
    • この一連の処理が、「DOWN」ボタンについても繰り返されます。
  3. 実行部分
    python if __name__ == "__main__": calibrate()
    • これはPythonの決まり文句で、「このスクリプトが直接実行された場合に calibrate() 関数を呼び出す」という意味です。これにより、このファイルを実行するだけでキャリブレーションが開始されます。

使い方

このスクリプトの使い方は以下の通りです。

  1. 準備:
    • 自動クリックさせたいアプリケーション(例: 取引プラットフォームの画面)をPCの画面に表示させます。
    • このコードを calibrate.py のような名前で保存します。
  2. 実行:
    • コマンドプロンプトやターミナルを開き、保存したファイルがあるフォルダに移動します。
    • python calibrate.py と入力してスクリプトを実行します。
  3. 操作:
    1. ターミナルに Move the mouse cursor over the UP button and press Enter と表示されます。
    2. あなたはマウスを操作して、クリックさせたい「UP」ボタン(例えば、取引プラットフォームの「CALL」ボタン)の真ん中にカーソルを合わせます。
    3. カーソルを合わせたまま、ターミナルに戻って Enterキー を押します。
    4. すると、ターミナルに UP_POS = (1732, 359) のような座標が表示されます。
    5. 次に Move the mouse cursor over the DOWN button and press Enter と表示されるので、今度は「DOWN」ボタン(例えば「PUT」ボタン)で同じ操作を繰り返します。
  4. 結果の利用:
    • すべての操作が終わると、ターミナルに以下のような最終結果が表示されます。
      UP_POS = (1732, 359) DOWN_POS = (1742, 575)
    • この (1732, 359)(1742, 575) という座標を、あなたのメインの自動取引スクリプト(auto_trader.pyなど)の座標設定部分にコピー&ペーストします。

まとめ

このスクリプトは、自動化プログラムの「目」や「指」となるマウスカーソルの位置を正確に教えるための、非常に便利で重要な準備ツールです。これにより、設定ミスを防ぎ、自動化の信頼性を大幅に向上させることができます。

派生元のソースコード

抜けるバイナリー&ソースコード

天国草さんが最初に紹介したバイナリー業者向けのレイテンシーアービトラージツールです。

該当noteは削除済みで、購入者のみ閲覧できます。

    # ---------------------------------------------------------------------
    #                     ENTRY TIMER & ODDS FETCH (SELENIUM)
    # ---------------------------------------------------------------------

    def init_driver_with_iframe(self):
        """Attach to an existing Chrome (debug port 9230) and switch to iframe containing odds."""
        opts = Options()
        opts.debugger_address = "localhost:9230"  # assumes user started Chrome with --remote-debugging-port=9230
        try:
            self.driver = webdriver.Chrome(options=opts)
            print("✅ Connected to Chrome debugging session")
            self.find_and_switch_to_odds_iframe()
        except Exception as exc:
            print(f"❌ Could not connect to Chrome: {exc}")
            self.driver = None

    def find_and_switch_to_odds_iframe(self):
        """Loop through iframes until BUY_ODDS_CLASS element found."""
        if self.driver is None:
            return
        while True:
            try:
                self.driver.switch_to.default_content()
                iframes = self.driver.find_elements(By.TAG_NAME, "iframe")
                for idx, iframe in enumerate(iframes):
                    try:
                        self.driver.switch_to.default_content()
                        self.driver.switch_to.frame(iframe)
                        if self.driver.find_elements(By.CLASS_NAME, BUY_ODDS_CLASS):
                            #print(f"✅ Odds found in iframe[{idx}]")
                            return
                    except Exception:
                        continue
                time.sleep(0)
            except Exception as exc:
                print(f"frame scan error: {exc}")
                time.sleep(0)

    def get_latest_buy_odds(self):
        if not self.driver:
            return None
        try:
            elements = self.driver.find_elements(By.CLASS_NAME, BUY_ODDS_CLASS)
            for el in elements:
                txt = el.text.strip()
                if txt:
                    return float(txt)
            #print("⚠️ BUYオッズ取得できず → iframe再検索")
            self.find_and_switch_to_odds_iframe()
        except Exception:
            self.find_and_switch_to_odds_iframe()
        return None


    def get_latest_sell_odds(self):
        if not self.driver:
            return None
        try:
            el = self.driver.find_element(By.XPATH, SELL_XPATH)
            txt = el.text.strip()
            return float(txt) if txt else None
        except Exception:
            self.find_and_switch_to_odds_iframe()
            return None

    def start_entry_timer(self):
        """Starts 40‑second cycle timer logic one minute in the future."""
        if self.timer_active:
            return
        now = datetime.now()
        self.entry_start_time = (now + timedelta(minutes=1)).replace(second=0, microsecond=0)
        self.timer_active = True
        self.label_overlay(f"Entry timer will start at {self.entry_start_time.strftime('%H:%M:%S')}")
        threading.Thread(target=self._entry_timer_loop, daemon=True).start()

    def _entry_timer_loop(self):
        """Runs forever updating allow flags based on time and odds."""
        while self.timer_active:
            time.sleep(0)
            now = datetime.now()
            if self.entry_start_time is None:
                continue
            delta = (now - self.entry_start_time).total_seconds()
            if delta < 0:
                self.allow_buy_click = self.allow_sell_click = False
                self.label_overlay("⏳ Waiting for entry start …")
                continue

            # inside cycles 37is best
            time_in_cycle = delta % CYCLE_SECONDS
            if 37 <= time_in_cycle <= 40:  # active 4‑second window each cycle
                buy_odds = self.get_latest_buy_odds()
                sell_odds = self.get_latest_sell_odds()
                self.allow_buy_click = buy_odds is not None and buy_odds >= ODDS_THRESHOLD
                self.allow_sell_click = sell_odds is not None and sell_odds >= ODDS_THRESHOLD
                status = (
                    f"[{now.strftime('%H:%M:%S')}] Window 37‑40s  BUY: {buy_odds} {'✅' if self.allow_buy_click else '❌'} | "
                    f"SELL: {sell_odds} {'✅' if self.allow_sell_click else '❌'}"
                )
                self.label_overlay(status)
            else:
                self.allow_buy_click = self.allow_sell_click = False
                self.label_overlay(f"Wait cycle … {int(time_in_cycle)}s")

WS接続してエントリーするコード

天国草さんの2つ目のレイテンシー取引用ソースコードです。現在は有料noteになっています。

# Selenium ドライバ設定(port 9222)
chrome_options = Options()
chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
driver = webdriver.Chrome(options=chrome_options)

# PyChrome 設定(port 9222 使用)
try:
    browser = pychrome.Browser(url="http://127.0.0.1:9222")
    tabs = browser.list_tab()
    if tabs:
        tab = tabs[0]
        tab.start()
        log_queue.put("PyChrome: 接続に成功しました (port 9222)")
    else:
        log_queue.put("PyChrome: 利用可能なタブが見つかりません")
        tab = None
except Exception as e:
    log_queue.put(f"PyChrome: 接続に失敗しました: {e}")
    tab = None

# WebSocket イベントハンドラ
def on_ws_created(**kwargs):
    log_queue.put(f"WS created: {kwargs}")

def on_ws_frame_sent(**kwargs):
    response = kwargs.get("response", {})
    payload = response.get("payloadData", "")
    if "livechat" in payload and "disconnect_timeout" in payload:
        log_queue.put("WS: livechat の disconnect_timeout を送信")

def on_ws_frame_received(**kwargs):
    pass

# PyChrome 初期化
if tab:
    try:
        tab.Network.enable()
        tab.Network.webSocketCreated = on_ws_created
        tab.Network.webSocketFrameSent = on_ws_frame_sent
        tab.Network.webSocketFrameReceived = on_ws_frame_received
        log_queue.put("PyChrome: Network 監視を開始")
    except Exception as e:
        log_queue.put(f"PyChrome: Network 設定に失敗しました: {e}")

# BO 注文送信関数(CALL / PUT)
def send_bo_order(direction, price_open):
    if not tab:
        log_queue.put("BO: PyChrome 未接続のため送信をスキップ")
        return

    try:
        # CALL(BUY)と PUT(SELL)で構造は同一(direction のみ差し替え)
        if direction == "CALL":
            order_data = {
                "command": "open_option",
                "plugin": "site",
                "sum": "5000",
                "tool_id": "4",
                "direction": direction,
                "price_open": str(price_open),
                "timeframe_id": "15",
                "option_kind": "1"
            }
        else:
            order_data = {
                "command": "open_option",
                "plugin": "site",
                "sum": "5000",
                "tool_id": "4",
                "direction": direction,
                "price_open": str(price_open),
                "timeframe_id": "15",
                "option_kind": "1"
            }

        # JavaScript 経由で WebSocket 送信
        js_code = f"""
        if (typeof window.boWebSocket !== 'undefined' && window.boWebSocket.readyState === WebSocket.OPEN) {{
            var orderData = {json.dumps(order_data)};
            window.boWebSocket.send(JSON.stringify(orderData));
            console.log('BO order sent:', orderData);
            true;
        }} else {{
            console.log('BO WebSocket connection is not available');
            false;
        }}
        """

        result = tab.Runtime.evaluate(expression=js_code)
        if result.get('result', {}).get('value'):
            log_queue.put(f"BO: 注文送信成功 - {direction} @ {price_open}")
            log_queue.put(f"BO: 注文詳細 - {order_data}")
        else:
            log_queue.put("BO: 注文送信失敗(WebSocket 未接続)")

    except Exception as e:
        log_queue.put(f"BO: 注文送信エラー: {e}")

# WebSocket 接続設定(BO 用): 履歴データ取得 + hook_user_status / hook_options
js_websocket_setup = """
try {
    if (typeof window.boWebSocket !== 'undefined') {
        window.boWebSocket.close();
    }

    window.boWebSocket = new WebSocket("wss://bo.zentrader.com:27017/");

    window.boWebSocket.onopen = () => {
        const now = Math.floor(Date.now() / 1000);
        const timeEnd = now;
        const timeBegin = now - 500;

        // 1) connect
        window.boWebSocket.send(JSON.stringify({
            command: "connect",
            bo: "demo",
            hash: "ここにログイン情報いれてください。",
            platform: "mt4",
            source: "site"
        }));
        console.log("connect sent");

        // 2) get_user_data
        setTimeout(() => {
            window.boWebSocket.send(JSON.stringify({ command: "get_user_data" }));
            console.log("get_user_data sent");
        }, 300);

        // 3) hook_time
        setTimeout(() => {
            window.boWebSocket.send(JSON.stringify({ command: "hook_time", enable: "true" }));
            console.log("hook_time sent");
        }, 600);

        // 4) get_cfg_trade
        setTimeout(() => {
            window.boWebSocket.send(JSON.stringify({ command: "get_cfg_trade", language: "ja" }));
            console.log("get_cfg_trade sent");
        }, 900);

        // 5) get_user_settings
        setTimeout(() => {
            window.boWebSocket.send(JSON.stringify({ command: "get_user_settings" }));
            console.log("get_user_settings sent");
        }, 1200);

        // 6) hook_timeframes
        setTimeout(() => {
            window.boWebSocket.send(JSON.stringify({
                command: "hook_timeframes",
                option_kind: "1",
                tool_id: "4",
                timeframe_id: "15",
                bo: "demo",
                enable: "true",
                interval: "1000",
                source: "site"
            }));
            console.log("hook_timeframes sent");
        }, 1500);

        // 7) get_quotes_history
        setTimeout(() => {
            window.boWebSocket.send(JSON.stringify({
                command: "get_quotes_history",
                tool_id: "4",
                time_begin: timeBegin.toString(),
                time_end: timeEnd.toString(),
                time_size: "S1",
                source: "site",
                request_id: "4_500"
            }));
            console.log("get_quotes_history sent", {
                time_begin: new Date(timeBegin * 1000).toISOString(),
                time_end: new Date(timeEnd * 1000).toISOString(),
                duration_sec: timeEnd - timeBegin
            });
        }, 1800);

        // 8) hook_user_status
        setTimeout(() => {
            window.boWebSocket.send(JSON.stringify({ command: "hook_user_status", enable: "true" }));
            console.log("hook_user_status sent");
        }, 2100);

        // 9) hook_options
        setTimeout(() => {
            window.boWebSocket.send(JSON.stringify({
                command: "hook_options",
                source: "site",
                enable: "true",
                interval: "100"
            }));
            console.log("hook_options sent");
        }, 2400);
    };

    window.boWebSocket.onmessage = (event) => {
        console.log("BO WS message:", event.data);
    };

    window.boWebSocket.onerror = (error) => {
        console.log("BO WS error:", error);
    };

    window.boWebSocket.onclose = (event) => {
        console.log("BO WS closed:", event.code, event.reason);
    };

    true;
} catch (error) {
    console.log("BO WS setup error:", error);
    false;
}
"""

# WebSocket でブラウザ価格を監視(port 9222)
def start_cdp_price_listener():
    async def listen_price_from_cdp():
        port = 9222
        try:
            response = requests.get(f'http://localhost:{port}/json')
            targets = response.json()
            ws_debugger_url = next(item['webSocketDebuggerUrl'] for item in targets if 'webSocketDebuggerUrl' in item)
        except Exception as e:
            log_queue.put(f"CDP: DevTools WebSocket URL 取得に失敗: {e}")
            return

        async with websockets.connect(ws_debugger_url) as websocket:
            await websocket.send(json.dumps({"id": 1, "method": "Network.enable"}))
            log_queue.put("CDP: DevTools WebSocket に接続。価格監視を開始 (port 9222)")

            while True:
                try:
                    message = await websocket.recv()
                    data = json.loads(message)

                    if data.get("method") == "Network.webSocketFrameReceived":
                        payload = data["params"]["response"].get("payloadData", "")
                        if payload.startswith("U,1,1,USDJPY.FXCM"):
                            match = re.search(r"USDJPY\\.FXCM\\|([\\d.]+)\\|", payload)
                            if match:
                                latest_price["ask"] = float(match.group(1))
                except Exception as e:
                    log_queue.put(f"CDP: WebSocket 受信エラー: {e}")
                    break

    asyncio.run(listen_price_from_cdp())

他の無料入手可能なレイテンシー取引ツール

レイテンシーアービトラージの取引ツールは他の方法でも無料入手できます。

上級者にはHFT Arbitrage Platformが人気です。以下のリンクでは無料の海賊版をダウンロードできます。

あるいはGitHubでも無料入手できます。ただMT4/MT5は約定速度が遅いため、メインで使われることは少ないです。あくまで勉強用のソースコードと考えましょう。

以下のクラックサイトでも無料入手できます。本来の価格は499ドルと高額ですが、クラックサイトなら良質なEAを無料で入手できます。

FX初心者にレイテンシーアービトラージをおすすめしない理由

レイテンシーアービトラージは利益率が非常に高いですが、以下の理由からFX初心者にはおすすめしていません。

  • ブローカー・バイナリー業者から口座凍結・利益没収されやすい
  • ロットを上げると口座凍結されるため、スケール化しにくい
  • 大量のリアル口座を使い潰すため、新規口座登録などの事務的コストも大きい
  • ブローカーごとのレイテンシーを計測する負担も大きい
  • ノウハウの陳腐化が激しい(現在はレイテンシーアービトラージのノウハウを売るほうが稼げる)
  • レートの遅い業者の情報は希少なので、他のトレーダーから情報を得られない
  • 取引環境の良いA-bookブローカーでは利益を出せない
  • リクイディティプロバイダー(LP)のラストルック(Last Look)仕様により、LPに不利なタイミングでは決済させてもらえない(約定が遅れやすい)

まず前提として、B-bookブローカーやバイナリー業者はトレーダーと利益相反の関係にあり、トレーダーが大きな利益を出すと、ブローカー・バイナリー業者側は損失を出すことになります。

そのためレイテンシーアービトラージのような利益率の高いトレードを行うトレーダーには、不正取引などの難癖をつけて口座凍結したり、利益没収してきたりすることがあります。

2015年から2020年あたりまでなら楽に稼げたかもしれませんが、2025年現在だとブローカー・バイナリー業者側の対策も進んでいます。初心者が運用しても、数日もしないうちに口座凍結されてしまうでしょう。

こういった裏事情はFX初心者の間では知られていません。現在はレイテンシー取引をするよりもノウハウを売った方が楽に稼げます。(ももんがさんのちゃっち倶楽部ピル山めろ子さんのnoteうみさんのnoteなど)

レイテンシーアービトラージはレート配信の遅いブローカーを見つけたり、通貨ごとのレイテンシーを計測したり、口座凍結されないようパラメーターを調整したり、口座凍結された後に口座を作り直したりする必要があります。これらの作業も膨大な労力コストになります。

レイテンシー取引に精通しているFX上級者、あるいはブローカーの選定に時間をかけられる専業トレーダーなら続ける価値はあるでしょう。ただ兼業トレーダーやFX初心者が今から参入するのは労力的に割に合わないです。

今から高頻度売買で稼ぐなら、Stop Grid Traderによる指標トレードがおすすめ。こちらはレートのズレを利用しないため、A-bookブローカーでも利益が出せます。

A-bookブローカーの収益源は取引手数料で、トレーダーとはあまり利益相反しません。派手に稼いでもB-bookブローカーやバイナリー業者のように口座凍結・利益没収されるリスクが低いです。

役に立った記事はSNSシェア!
  • URLをコピーしました!
  • URLをコピーしました!

海外FXの稼ぎ方

海外FXの効率的な稼ぎ方はこちら。

  1. XM スタンダード口座を開設し、3種ボーナスを獲得する
  2. 海外FXの稼ぎ方を体得する
  3. 取引コストの低いTradeview cTrader口座/ILC口座に乗り換える

海外FX初心者の運用口座には、XM スタンダード口座がおすすめ。口座の最大レバレッジが1000倍で、ゴールドの銘柄レバレッジも1000倍なため、ゴールドのハイリスクトレードが可能となります。

またXMは3種類のボーナスを提供しており、10万円の入金額を17万円ほどに増やせます。ボーナスは損失カバー機能があるので、ハイリスクなトレードに使いましょう。

海外FXの稼ぎ方は、億トレーダー(Xアカウント)のコピートレード、無料bot「Stop Grid Trader」による自動売買などがおすすめ。

裁量トレードがメインなら、運用口座はTradeview cTrader口座がおすすめ。高性能FXプラットフォーム「cTrader」に対応しており、リミット注文・ストップ注文を設置しやすくなります。

botによる自動売買をするなら、運用口座はTradeview ILC口座がおすすめ。スプレッドが非常に狭く、取引手数料も1ロット往復5ドルと最安値クラスなので、取引回数の多いトレードロジックで利益を出しやすくなります。

この記事を書いた人

海外FXの情報を備忘録としてまとめています。
運用は自己責任でお願いします。
Twitterで「海外FXの有益情報bot」も運用してます。

目次