シストレどうですか

  Algorithmic Trading for Dummies

OANDA API 解説編 第3回 ろうそく足を取得してみた (1)

前回まではOANDA APIを使って最新レートの取得をしてみましたが、今回はろうそく足(ロウソク足?・やっぱりローソク足?)の取得の仕方を学んでみたいと思います。 OANDA REST-v20の開発ガイド(英語版)はこちら


ろうそく足を取得する

GET /v3/instruments/{Instruments}/candles

ある銘柄のろうそく足情報を取得する

OANDAの開発ガイドの該当部分はこちらから。

実はPrincingの中にも同じくろうそく足を取得できるエンドポイント ( /v3/accounts/{accountID}/instruments/{instrument}/candles) があります。2つを比較してみると大きな違いはunitという引数を指定できるかどうかだけです。実際にこの引数に与える値を変えてみて結果を比較してみたのですが、得られる結果に違いがないように思えましたので、Instrumentsにあるこちらのほうを解説したいと思います。(oandapy20もろうそく足の取得にはこちらのほうを採用しているみたいだし、口座番号がいらないので短くすむし的な・・・どなたか違いを知っている方がいたら教えてください。)

名前 場所 必須 説明
Authorization header string Y トークンID
Accept-Datetime-Format header AcceptDatetimeFormat 時刻フォーマット指定 初期値:RFC3339 (yyyy-mm-ddThh:mm:ss.nnnnnnnnnZ形式)
その他:UNIX (12345678.000000123形式)
instrument path InstrumentName Y 取得したい通貨ペア 例:USD_JPY 複数指定は不可
price query PricingComponent Y プライス指定(Mid, Bid, Ask) 初期値:M(平均値) その他:B(ビッド), A(アスク)
granularity query CandlestickGranularity 時間足を指定 初期値:S5 (5秒足) 
count query integer 取得したい時間足の個数 初期値:500 最大値: 5000
from query DateTime 取得したいローソク足の開始日時 UNIXまたはRFC3339形式
両方とも指定しない場合は最新のものからcount数取得
to query DateTime 取得したいローソク足の終了日時
smooth query boolean 一つ前の足の終値始値を同一値に調整するか 初期値:True
includeFirst query boolean 開始日時に重なる足を取得するか 初期値:True
dailyAlignment query integer 時間足の開始時刻 初期値:17  最小値:0 最大値:23の整数値
alignmentTimezone query string dailyAlignment用の基準タイムゾーン 初期値:America/New_York (New York時間の17:00基準だが表示時刻はUTC)
weeklyAlignment query string granularityがW(1週間)の場合、区切りの曜日を指定 初期値:Friday その他:Monday, Tuesday, Wednesday, Thursday, Saturday, Sunday

USD_JPYで最新ものから5本分の5分足のろうそく足を取得してみます。

# 必要なモジュールの読み込み
import requests
import json

# 口座情報の設定 (口座の開設の仕方はググってね)
API_Token = '********************************-********************************'
API_AccountID = '999-999-99999999-999'

# URLの設定 (デモ口座用非ストリーミングURL)
API_URL =  "https://api-fxpractice.oanda.com"

# 通貨ペア
INSTRUMENT = "USD_JPY"

# <ろうそく足取得用URLの変数の設定>
# /v3/instruments/{Account ID}/candles 
url = API_URL + "/v3/instruments/%s/candles?count=5&price=M&granularity=M5" % INSTRUMENT 

# ヘッダー情報の変数の設定
headers = {
                "Authorization" : "Bearer " + API_Token
         }

# サーバーへの要求
response = requests.get(url, headers=headers)

# 処理結果の編集
Response_Body = response.json()
print(json.dumps(Response_Body, indent=2))

エラーがなく要求が通ると結果が戻ってきますので、json.dumpsで編集すると以下のようなデータを受け取れます。

{
  "instrument": "USD_JPY",
  "granularity": "M5",
  "candles": [
    {
      "complete": true,
      "volume": 24,
      "time": "2020-07-13T22:10:00.000000000Z",
      "mid": {
        "o": "107.304",
        "h": "107.307",
        "l": "107.289",
        "c": "107.296"
      }
    },
    {
      "complete": true,
      "volume": 35,
      "time": "2020-07-13T22:15:00.000000000Z",
      "mid": {
        "o": "107.297",
        "h": "107.313",
        "l": "107.296",
        "c": "107.308"
      }
    },
    {
      "complete": true,
      "volume": 25,
      "time": "2020-07-13T22:20:00.000000000Z",
      "mid": {
        "o": "107.310",
        "h": "107.310",
        "l": "107.298",
        "c": "107.300"
      }
    },
    {
      "complete": true,
      "volume": 24,
      "time": "2020-07-13T22:25:00.000000000Z",
      "mid": {
        "o": "107.298",
        "h": "107.302",
        "l": "107.296",
        "c": "107.300"
      }
    },
    {
      "complete": false,
      "volume": 41,
      "time": "2020-07-13T22:30:00.000000000Z",
      "mid": {
        "o": "107.298",
        "h": "107.302",
        "l": "107.288",
        "c": "107.294"
      }
    }
  ]
}

前回のprincingにくらべれば受け取るデータはとてもシンプルに見えますね。 レートを取得したい場合以下のように指定して値をとりだせます。 今回は5本分だけですので配列を変数([n]のように)でループさせればすべての値がとりあえず取得できます。

print(" Open: " + Response_Body["candles"][0]["mid"]["o"]) 
print(" High: " + Response_Body["candles"][0]["mid"]["h"]) 
print("  Low: " + Response_Body["candles"][0]["mid"]["l"]) 
print("Close: " + Response_Body["candles"][0]["mid"]["c"]) 
 Open: 107.304
 High: 107.307
  Low: 107.289
Close: 107.296

指定した銘柄(instrument)、そのろうそく足が完成しているかどうか(complete)、始値の時刻(time)の情報も取得できます。

引数で遊んでみる

というわけで前回と同様にいろいろな引数を使ってみました。

instrument

ろうそく足の取得では1銘柄しか指定できないのでUSD_JPYのように指定

price

取得したいレートを指定。B(id), A(sk), M(id)のいずれか。

granularity

なんと21種類もの時間足を取得できます。 それぞれの時間足では基準となる始値の開始時間があり、その区切りでレートが取得されます。 Minute alignmentでは0分00秒から始まりますので、5秒足の場合は例えば11:05:00➡05",10",15"...になりますし、 Daily alignmentでは初期値だとNY時間の17:00から区切りになります。 例えば6時間足を取得する場合NY時間の17時を基準として23時➡5時➡11時➡17時始値のろうそく足が出力されます。 また、Daily alignmentとWeekly alignmentでは区切りの時間を変更することができる引数も用意されています。 (dailyAlignment,alignmentTimezone,weeklyAlignment) 日本時間の朝8時を基準として日足を取得したい、日本の月曜日の朝を基準とした週足が必要とかいう人は、これらの引数を知っておく必要がありそうです。

時間足 スタート基準
S5 5秒 Minute alignment (hh:mm:00起点)
S10 10秒
S15 15秒
S30 30秒
M1 1分
M2 2分 Hourly Alignment (hh:00:00起点)
M4 4分
M5 5分
M10 10分
M15 15分
M30 30分
H1 1時間
H2 2時間 Daily Alignment (初期値の場合はNY時間17:00を起点)
H3 3時間
H4 4時間
H6 6時間
H8 8時間
H12 12時間
D 1日
W 1週 Weekly alignment (初期値:土曜日+Daily Alignment)
M 1か月 Monthly Alignment (月初日はじまり+Daily Alignment)

count

一回の命令で取得したいろうそく足の数を指定します。初期値は500ですが最大値は5000個までになります。 5000以上のろうそく足が必要な場合はfrom引数などを使って何回も呼び出しながら使います。

fromto

取得したいレンジを指定できる引数です。両方指定なしの場合は最新のろうそく足からcount引数で指定した個数取得できます。 例えばfromにとても古い日付を指定してcount=1でデータを取得すると一番古いデータを取得できます。 (あまり使い道のない例ではありますが・・・)

url = API_URL + "/v3/instruments/%s/candles?count=1&price=M&granularity=M&from=1990-01-01T00:00:00.000000000Z"  % (INSTRUMENT)
response = requests.get(url, headers=headers)
Response_Body = response.json()
print(json.dumps(Response_Body, indent=2))

どうやら月足では2002年6月のレートが一番古いようです。

{
  "instrument": "USD_JPY",
  "granularity": "M",
  "candles": [
    {
      "complete": true,
      "volume": 25,
      "time": "2002-05-31T21:00:00.000000000Z",
      "mid": {
        "o": "124.250",
        "h": "125.700",
        "l": "119.480",
        "c": "119.610"
      }
    }
  ]
}

ちなみに1分足でも2002年5月6日のレートが表示されるようですが、すべての値が同じのようなのでろうそくになってませんね。

{
  "instrument": "USD_JPY",
  "granularity": "M1",
  "candles": [
    {
      "complete": true,
      "volume": 1,
      "time": "2002-05-06T20:58:00.000000000Z",
      "mid": {
        "o": "127.150",
        "h": "127.150",
        "l": "127.150",
        "c": "127.150"
      }
    }
  ]
}

smooth

始値を一つ前の足の終値と同じ値にするかどうかを調整できます。
smooth=False(初期値)の時は、7月9日の終値(106.923)と7月12日の始値(106.940)が違う値ですが、

{
  "instrument": "USD_JPY",
  "granularity": "D",
  "candles": [
    {
      "volume": 47695,
      "time": "2020-07-09T21:00:00.000000000Z",
      "mid": {
        "o": "107.216",
        "h": "107.266",
        "l": "106.638",
        "c": "106.923" 👈ココ
      }
    },
    {
      "complete": true,
      "volume": 47475,
      "time": "2020-07-12T21:00:00.000000000Z",
      "mid": {
        "o": "106.940", 👈ココ
        "h": "107.320",
        "l": "106.788",
        "c": "107.293"
      }
    }
  ]
}

smooth=Trueの時には、7月9日の終値と7月12日の始値が同じ(106.923)に調整されました。

{
  "instrument": "USD_JPY",
  "granularity": "D",
  "candles": [
    {
      "complete": true,
      "volume": 47695,
      "time": "2020-07-09T21:00:00.000000000Z",
      "mid": {
        "o": "107.206",
        "h": "107.266",
        "l": "106.638",
        "c": "106.923" 👈ココ
      }
    },
    {
      "complete": true,
      "volume": 47475,
      "time": "2020-07-12T21:00:00.000000000Z",
      "mid": {
        "o": "106.923", 👈ココ
        "h": "107.320",
        "l": "106.788",
        "c": "107.293"
      }
    }
  ]
}

includeFirst

countの最大値である5000以上のろうそく足を取得する必要がある時に使える引数です。
ある過去の特定の日時から現在までのろうそく足を5000件以上取得する場合に1回目の最後のろうそく足を基準に次のローソク足を取得しようとすると 1回目の最後と2回目の最初のろうそく足がかぶってしまいます。
includeFirst=Falseを指定すれば重複を避ける事ができます。

url = API_URL + "/v3/instruments/%s/candles?count=3&price=M&granularity=H1&from=2020-07-15T09:00:00.000000000Z" % INSTRUMENT
headers = {
                 "Authorization" : "Bearer " + API_Token
         }
response = requests.get(url, headers=headers)
Response_Body = response.json()
print(json.dumps(Response_Body, indent=2))

#前回取得した最後の時刻を2回目の呼び出しのfrom引数にセットする。
latest_candle = Response_Body["candles"][2]["time"] 
url = API_URL + "/v3/instruments/%s/candles?count=3&price=M&granularity=H1&from=%s&includeFirst=True"  % (INSTRUMENT, latest_candle)
response = requests.get(url, headers=headers)
Response_Body = response.json()
print(json.dumps(Response_Body, indent=2))
1回目
{
  "instrument": "USD_JPY",
  "granularity": "H1",
  "candles": [
    {
      "complete": true,
      "volume": 2759,
      "time": "2020-07-15T09:00:00.000000000Z",
      "mid": {
        "o": "106.904",
        "h": "106.953",
        "l": "106.889",
        "c": "106.934"
      }
    },
    {
      "complete": true,
      "volume": 2796,
      "time": "2020-07-15T10:00:00.000000000Z",
      "mid": {
        "o": "106.935",
        "h": "106.936",
        "l": "106.870",
        "c": "106.900"
      }
    },
    {
      "complete": true,
      "volume": 3197,
      "time": "2020-07-15T11:00:00.000000000Z",  👈2回目の最初のろうそく足と時間が重複している
      "mid": {
        "o": "106.899",
        "h": "106.992",
        "l": "106.882",
        "c": "106.891"
      }
    }
  ]
}
2回目
{
  "instrument": "USD_JPY",
  "granularity": "H1",
  "candles": [
    {
      "complete": true,
      "volume": 3197,
      "time": "2020-07-15T11:00:00.000000000Z",  👈時間が重複
      "mid": {
        "o": "106.899",
        "h": "106.992",
        "l": "106.882",
        "c": "106.891"
      }
    },
    {
      "complete": true,
      "volume": 2830,
      "time": "2020-07-15T12:00:00.000000000Z",
      "mid": {
        "o": "106.890",
        "h": "106.896",
        "l": "106.755",
        "c": "106.819"
      }
    },
    {
      "complete": true,
      "volume": 4492,
      "time": "2020-07-15T13:00:00.000000000Z",
      "mid": {
        "o": "106.817",
        "h": "106.817",
        "l": "106.706",
        "c": "106.754"
      }
    }
  ]
}

includeFirst=Falseに変更して再度実行すると

#1回目
{
  "instrument": "USD_JPY",
  "granularity": "H1",
  "candles": [
    {
      "complete": true,
      "volume": 2759,
      "time": "2020-07-15T09:00:00.000000000Z",
      "mid": {
        "o": "106.904",
        "h": "106.953",
        "l": "106.889",
        "c": "106.934"
      }
    },
    {
      "complete": true,
      "volume": 2796,
      "time": "2020-07-15T10:00:00.000000000Z", 
      "mid": {
        "o": "106.935",
        "h": "106.936",
        "l": "106.870",
        "c": "106.900" 
      }
    },
    {
      "complete": true,
      "volume": 3197,
      "time": "2020-07-15T11:00:00.000000000Z", 👈2回目の初めと重複しない
      "mid": {
        "o": "106.899",
        "h": "106.992",
        "l": "106.882",
        "c": "106.891"
      }
    }
  ]
}
#2回目
{
  "instrument": "USD_JPY",
  "granularity": "H1",
  "candles": [
    {
      "complete": true,
      "volume": 2830,
      "time": "2020-07-15T12:00:00.000000000Z", 👈
      "mid": {
        "o": "106.890",
        "h": "106.896",
        "l": "106.755",
        "c": "106.819"
      }
    },
    {
      "complete": true,
      "volume": 4492,
      "time": "2020-07-15T13:00:00.000000000Z",
      "mid": {
        "o": "106.817",
        "h": "106.817",
        "l": "106.706",
        "c": "106.754"
      }
    }
  ]
}

とこのように重複がなくなります。ただし取得できる個数がひとつ減ってしまいますのでご注意ください。 今回がfrom引数を使って古いろうそく足から順次取得した場合はこの引数を使う必要がありますが、to引数を使ってあたらしいろうそく足から取得していく場合はinculedeFisrst=True(初期値)のままでも重複は発生しない事に気が付きました。(ただし取得できる順番が時系列にうまくならないのであとで並び替えが必要になります。)

dailyAlignment, alignmentTimezone,weeklyAlignment

Hourly Alignmentに属する足についてはdailyAlignmentとalignmentTimezoneの二つの引数を使って調整します。 初期値はNew York時間の17時ですので日本時間の朝8時とかにしたい場合は、"&granularity=D&dailyAlignment=8&alignmentTimezone=Japan"の様に指定します。ただ、NY時間の午後5時はすでに日本時間の朝6時(米国の夏時間採用時)です。(NY時間夕方=東京時間早朝)
同様に週足でも初期値が金曜日のNY時間の17時になりますが、既にマーケットはしまっていますので、最初に取得できるレートは日本時間の翌月曜日の朝6時になるので日本のマーケットを基準に考えると特にこの引数を利用しなくてもよさそうです。
alignmentTimezone引数で指定できる定数一覧がv20の開発ガイドには説明がありませんでしたが、ここにありました。


まとめ

引数の種類が多かったので理解するのに苦労しましたが、内容がわかるとそんなに使い道がありそうな引数がなかったですね。 なんか無駄に時間を使ってしまったような気もしますが、あくまでも基本が身についてこその応用力という精神でやってますので・・・汗
次回は取得したろうそく足をpandasに格納して、そこからできる事を考えてみたいと思います。