OANDA REST APIとPythonでドル円の自動売買を始める方法【完全初心者向け】

ドル円を手動でやってた頃、「夜中の動きを全部見逃した!」って後悔するのが週に3回くらいあった。子供が生まれてからは、もはや深夜にチャートを見るなんて夢物語。IT会社に転職したのをきっかけに「APIで自動売買できるんじゃないか」と思って調べたら、OANDAが無料でREST APIを公開していることを知った。「え、これタダで使えるの?」ってちょっと疑ったくらい。

OANDAを選んだ理由

FX自動売買のAPIとして有名なのは、OANDA・GMOクリック証券・IG証券あたり。僕がOANDAを選んだ理由は3つ。

REST APIが無料で使える(他社はAPIに月額料金がかかることも多い)。② デモ口座でリアルタイムの実装テストができる。③ Python用ライブラリ(oandapyV20)がある。プログラミング初心者の僕には、ライブラリの存在は本当に大きい。「1からHTTPリクエストを書く」は最初の壁が高すぎる。

準備:デモ口座とAPIキーの取得

まずOANDAのデモ口座を開設する。本番口座ではなくデモで動作確認できるので安心。

① OANDA Japanのサイトでデモ口座を開設 → ② ログイン後「マイアカウント」→「APIアクセスの管理」→「APIアクセストークンの生成」でAPIキーを取得 → ③ アカウントIDはダッシュボードのURLや口座情報ページで確認できる(数字8桁くらいのやつ)。

環境構築

pip install oandapyV20 pandas

APIキーとアカウントIDは環境変数で管理するのが安全。コードにベタ書きは絶対NG(経験談あり。。。)。

# .envファイル(Gitにコミットしないこと!)
OANDA_API_KEY=あなたのAPIキー
OANDA_ACCOUNT_ID=あなたのアカウントID
import os
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv("OANDA_API_KEY")
ACCOUNT_ID = os.getenv("OANDA_ACCOUNT_ID")
ENVIRONMENT = "practice"  # デモ口座は "practice"、本番は "live"

現在レートの取得

まず「ドル円の今のレートを取れるか」から確認。

import oandapyV20
import oandapyV20.endpoints.pricing as pricing

client = oandapyV20.API(access_token=API_KEY, environment=ENVIRONMENT)

# ドル円の現在レートを取得
params = {"instruments": "USD_JPY"}
r = pricing.PricingInfo(accountID=ACCOUNT_ID, params=params)
client.request(r)

price_data = r.response["prices"][0]
bid = float(price_data["bids"][0]["price"])
ask = float(price_data["asks"][0]["price"])
spread = ask - bid

print(f"ドル円 Bid: {bid:.3f}")
print(f"ドル円 Ask: {ask:.3f}")
print(f"スプレッド: {spread*100:.1f}銭")

過去データ(ローソク足)の取得

テクニカル指標の計算に過去データが必要。OANDAのAPIで直接取れる。

import oandapyV20.endpoints.instruments as instruments
import pandas as pd

def get_candles(instrument="USD_JPY", granularity="H1", count=200):
    """
    ローソク足データを取得する
    granularity: M1(1分), M5(5分), H1(1時間), D(日足)など
    """
    params = {
        "granularity": granularity,
        "count": count,
        "price": "M"  # Mid price
    }
    r = instruments.InstrumentsCandles(instrument=instrument, params=params)
    client.request(r)

    candles = []
    for c in r.response["candles"]:
        if c["complete"]:
            candles.append({
                "time": c["time"][:19],
                "open": float(c["mid"]["o"]),
                "high": float(c["mid"]["h"]),
                "low": float(c["mid"]["l"]),
                "close": float(c["mid"]["c"]),
                "volume": c["volume"]
            })

    df = pd.DataFrame(candles)
    df["time"] = pd.to_datetime(df["time"])
    df.set_index("time", inplace=True)
    return df

# 1時間足を200本取得
df = get_candles("USD_JPY", "H1", 200)
print(df.tail())
print(f"\n取得件数: {len(df)}本")

移動平均クロスで自動売買ロジックを作る

取得したデータに移動平均を計算して、クロスシグナルが出たら注文を出す。

import oandapyV20.endpoints.orders as orders
import json

def place_market_order(instrument, units, stop_loss_pips=20):
    """
    成行注文を発注する
    units: 正数=買い、負数=売り(1ユニット=1通貨)
    """
    # ストップロス価格の計算
    current_price = get_current_price(instrument)
    pip_value = 0.01 if "JPY" in instrument else 0.0001

    if units > 0:  # 買い
        sl_price = round(current_price["bid"] - stop_loss_pips * pip_value, 3)
    else:  # 売り
        sl_price = round(current_price["ask"] + stop_loss_pips * pip_value, 3)

    data = {
        "order": {
            "type": "MARKET",
            "instrument": instrument,
            "units": str(units),
            "stopLossOnFill": {
                "price": str(sl_price)
            }
        }
    }

    r = orders.OrderCreate(accountID=ACCOUNT_ID, data=data)
    client.request(r)
    return r.response

def get_current_price(instrument):
    """現在価格を取得"""
    params = {"instruments": instrument}
    r = pricing.PricingInfo(accountID=ACCOUNT_ID, params=params)
    client.request(r)
    p = r.response["prices"][0]
    return {
        "bid": float(p["bids"][0]["price"]),
        "ask": float(p["asks"][0]["price"])
    }

def check_and_trade():
    """移動平均クロス戦略の実行"""
    df = get_candles("USD_JPY", "H1", 100)

    # 移動平均の計算
    df["ma_short"] = df["close"].rolling(10).mean()
    df["ma_long"] = df["close"].rolling(25).mean()

    # 直近2本でクロス判定
    prev_short = df["ma_short"].iloc[-2]
    prev_long = df["ma_long"].iloc[-2]
    curr_short = df["ma_short"].iloc[-1]
    curr_long = df["ma_long"].iloc[-1]

    if prev_short <= prev_long and curr_short > curr_long:
        print("ゴールデンクロス! → 買いシグナル")
        # デモでは1000通貨(最小単位)で試す
        response = place_market_order("USD_JPY", units=1000)
        print(f"注文完了: {response['orderFillTransaction']['id']}")

    elif prev_short >= prev_long and curr_short < curr_long:
        print("デッドクロス! → 売りシグナル")
        response = place_market_order("USD_JPY", units=-1000)
        print(f"注文完了: {response['orderFillTransaction']['id']}")

    else:
        print(f"シグナルなし(MA短期:{curr_short:.3f} / 長期:{curr_long:.3f})")

# 1時間ごとに実行する場合(schedule使用)
import schedule
import time

schedule.every().hour.at(":05").do(check_and_trade)
print("自動売買ボット起動中...")
while True:
    schedule.run_pending()
    time.sleep(60)

オープンポジションの確認とクローズ

import oandapyV20.endpoints.positions as positions
import oandapyV20.endpoints.trades as trades_ep

def get_open_positions():
    """オープンポジションを確認"""
    r = positions.OpenPositions(accountID=ACCOUNT_ID)
    client.request(r)
    return r.response["positions"]

def close_all_positions():
    """全ポジションを決済"""
    open_pos = get_open_positions()
    for pos in open_pos:
        instrument = pos["instrument"]
        long_units = int(pos["long"]["units"])
        short_units = int(pos["short"]["units"])

        if long_units > 0:
            r = positions.PositionClose(
                accountID=ACCOUNT_ID,
                instrument=instrument,
                data={"longUnits": "ALL"}
            )
            client.request(r)
            print(f"{instrument}: 買いポジション決済")

        if short_units < 0:
            r = positions.PositionClose(
                accountID=ACCOUNT_ID,
                instrument=instrument,
                data={"shortUnits": "ALL"}
            )
            client.request(r)
            print(f"{instrument}: 売りポジション決済")

デモで試してみた感想

最初にデモ口座でこのコードを動かしたとき、「本当に注文が入った!」って感動した。OANDAのダッシュボードを開いたら確かにドル円の買いポジションが入っていて、ちょっと「自分はすごいものを作ってしまった」感があった(笑)。

ただ実際に動かすには課題もある。接続が切れたときの再接続処理同じシグナルで二重注文しないための状態管理エラーハンドリングなど。特にAPI制限(1秒あたりのリクエスト数)には最初に引っかかった。

まとめ

OANDA REST APIとPythonを組み合わせれば、育児中でも寝ている間にドル円を自動売買できる環境が作れる。デモ口座で十分に動作を確認してから本番に移行するのが鉄則。

個人的には、今はまだデモで稼働させながらロジックを改善中。スプレッドのコストを考慮したリアルな損益計算も追加したいし、次は「ドル円が重要指標の前後どう動くか」をPythonで分析してみたいと思ってる。

タイトルとURLをコピーしました