シストレどうですか

  Algorithmic Trading for Dummies

OANDA API 解説編 第2回 最新のレートを取得してみよう(2)

前回はOANDA APIを使って最新レートの取得をしてみましたが、いくつかの引数がありましたのでこれをもう少し掘り下げてみたいと思います。 OANDA REST-v20の開発ガイド(英語版)はこちら


最新のレートを取得する その2

GET /v3/accounts/{accountID}/pricing

保有口座内で指定された銘柄の価格情報を得る

今回は以下の引数を使っていろいろな情報を取得したいと思います。

名前 場所 必須 説明 内容
Authorization header string Y 署名なしトークンID  
Accept-Datetime-Format header AcceptDatetimeFormat 時刻フォーマット指定 初期値:RFC3339 (yyyy-mm-ddThh:mm:ss.nnnnnnnnnZ形式)
その他:UNIX (12345678.000000123形式)
accountID path AccountID Y 口座番号
instruments query List of
InstrumentName(csv)
Y 出力したい通貨ペア 例:USD_JPY 複数指定の場合はカンマ区切りUSD_JPY,EUR_USD
since query DateTime 指定時刻以降に更新されたプライスを取得 RFC3339またはUnix形式
includeUnitsAvailable*1 query boolean 初期値:True
includeHomeConversion query boolean 口座保有通貨への換算レートを含めるかどうか 初期値:False

Accept-Datetime-Format

requestする際のheadersこの引数を加える事によって、UNIX形式(12345678.000000123形式)の時刻を取得できるようになります。

# ヘッダー情報の設定
headers = {
        "Accept-Datetime-Format" : "UNIX", 
        "Authorization" : "Bearer " + API_Token
        }

"time"の部分がすべてUNIX時間に変換されました。

{
  "time": "1594150466.778711971",
  "prices": [
    {
      "type": "PRICE",
      "time": "1594150464.555806196",
      "bids": [
        {
          "price": "107.534",
          "liquidity": 10000000
        }
      ],
 #以下省略     

intsruments

複数銘柄を指定する場合はカンマで区切ります。

#例
#一銘柄のみ指定
INSTRUMENT = "USD_JPY"
#複数指定する場合
INSTRUMENT = "USD_JPY,EUR_USD,EUR_JPY"

url = API_URL + "/v3/accounts/%s/pricing?instruments=%s" % (str(API_AccountID), INSTRUMENT)

#べた打ちする時は同様にカンマ区切り
url = API_URL + "/v3/accounts/%s/pricing?instruments=USD_JPY,EUR_USD,EUR_JPY" % (str(API_AccountID)

複数銘柄を指定した場合は指定した銘柄順に出力するので、配列の部分にはその番号を指定してあげて下さい。

{
  "time": "2020-07-08T04:06:10.340040772Z",    
  "prices": [
    {
      "type": "PRICE",
      "time": "2020-07-08T04:05:15.968303862Z",
      "bids": [
        {
          "price": "107.596",
          "liquidity": 10000000
        }
      ],
      "asks": [
        {
          "price": "107.610",
          "liquidity": 10000000
        }
      ],
      "closeoutBid": "107.596",
      "closeoutAsk": "107.610",
      "status": "tradeable",
      "tradeable": true,
      "quoteHomeConversionFactors": {
        "positiveUnits": "0.00929282",
        "negativeUnits": "0.00929403"
      },
      "instrument": "USD_JPY"
    },
    {
      "type": "PRICE",
      "time": "2020-07-08T04:05:50.300004650Z",
      "bids": [
        {
          "price": "1.12768",
          "liquidity": 10000000
        }
      ],
      "asks": [
        {
          "price": "1.12782",
          "liquidity": 10000000
        }
      ],
      "closeoutBid": "1.12768",
      "closeoutAsk": "1.12782",
      "status": "tradeable",
      "tradeable": true,
      "quoteHomeConversionFactors": {
        "positiveUnits": "1.00000000",
        "negativeUnits": "1.00000000"
      },
      "instrument": "EUR_USD"
    },
    {
      "type": "PRICE",
      "time": "2020-07-08T04:05:48.483760217Z",
      "bids": [
        {
          "price": "121.339",
          "liquidity": 10000000
        }
      ],
      "asks": [
        {
          "price": "121.357",
          "liquidity": 10000000
        }
      ],
      "closeoutBid": "121.339",
      "closeoutAsk": "121.357",
      "status": "tradeable",
      "tradeable": true,
      "quoteHomeConversionFactors": {
        "positiveUnits": "0.00929282",
        "negativeUnits": "0.00929403"
      },
      "instrument": "EUR_JPY"
    }
  ]
}
print(Response_Body["prices"][0]["instrument"]) 
print(Response_Body["prices"][1]["instrument"]) 
print(Response_Body["prices"][2]["instrument"]) 
USD_JPY
EUD_USD
EUD_JPY

多くの銘柄を指定する場合は、ループを使って取り出したりpandasなどに変換した後に処理する流れになります。

since

指定した時刻後に更新されたレートを取得できる引数です。 sinceを指定せずにpricingを連続して呼び出すと同じレートが出力されることがあります。 これはサーバー側で最後に更新されたレートがセットされるためレートの更新がない時前回と同じレートが再度セットされるからです。 例えばマーケットが動いていない週末にレートを取得すると金曜日のレートがセットされます。
since引数に前回取得したレートの更新時間をセットすることによって、新しいレートがあった時にはそのレートを取得し、 新しいレートの更新がなかった場合は処理をしないというような使い方ができます。 これをうまく応用すれば、レート取得する際にストリーミングを使用しなくてもMT4で言えばOnTickイベントのような動きをするbotを作成することが可能になります。 スキャルピングのような短時間でのトレーディング用には向きませんが、5分足10分足を使ったトレードや初歩的なbotを作成してみたいのであれば十分使えると思います。 簡単な例としてそのフローチャート(すみません歳なもので)とプログラミング例を書きます。

  • 一定時間ごとにレートを取得して発注するbot
    新しいレートを取得したらシグナル計算をして売買判定を行いシグナル計算の結果により発注を行う。

以下フローチャート シンプルな<a class="keyword" href="http://d.hatena.ne.jp/keyword/bot">bot</a>

このイメージでプログラムを作成すると、こんな感じ

#外部モジュールの呼び出し
import requests
import json
import time

def OnInit():
        #初期設定
        API_TOKEN = '**********************-**********************'
        API_AccountID = '999-999-99999999-999'

        rtn_Parm = {
                'TOKEN' : API_TOKEN, 
                'AccountID': API_AccountID, 
                'URL': "https://api-fxpractice.oanda.com", 
                }
        return rtn_Parm

def Signal():
        '''
        シグナル判定
        ポジション保有の有無
        などの記述
        '''
        status = 0 # 1=Buy, -1=Sell, 0=No signal
        return status

def Order():
        '''
        注文のLotサイズの決定
        発注処理などの記述
        '''
        status = True # True=発注OK, False=失敗 
        return status

def Main():
        
        #口座情報の設定
        api = OnInit()
        #取引銘柄の指定
        instruments = "USD_JPY"
        # headres引数の設定
        headers = {
                "Authorization" : "Bearer " + api["TOKEN"]
                }
        # since用変数 初回は指定なし
        since = ""
        url = api["URL"] + "/v3/accounts/%s/pricing?instruments=%s" % (str(api["AccountID"]), instruments)
        #Loop開始 
        while True:
                # データの要求
                response = requests.get(url, headers=headers)
                Response_Body = response.json()
                
                # 新しいレートの取得の判定
                if Response_Body["prices"] != []:
                        #取引可能状態?
                        if Response_Body["prices"][0]["tradeable"] == True:
                                #シグナル計算
                                signal = Signal()
                                if signal != 0:
                                        #発注処理
                                        order = Order()
                                        if order == False:
                                                #Error処理
                                                break    
                        else:
                                print("not tradeable")

                        #最終取得時間をセット
                        since = Response_Body["prices"][0]["time"]
                        url = url + "&since=%s" %since
                        
                #新しいレートが取得できなかった場合
                else:
                        print("no rate")
                #次のレート取得まで待機(1分)
                time.sleep(60)
        #Loop戻り
                
if __name__ == "__main__":
    
    Main()

例外処理や細かい設定は記述していませんのであくまで参考程度にして下さい。 更にこの例では無限ループになっています。

includeUnitAvailable

廃止予定と記載されていますが、初期値はTrueですので取得したくない時にだけ使ってください。 記述の仕方はinstruments引数が必須ですので&でつないで指定します。

?instruments=USD_JPY&includeUnitsAvailable=False

取得したデータの中からunitsAvailableのキーの部分(下図)が表示されなくなります。

      "unitsAvailable": {
        "default": {
          "long": "2472293",
          "short": "2472293"
        },
        "openOnly": {
          "long": "2472293",
          "short": "2472293"
        },
        "reduceFirst": {
          "long": "2472293",
          "short": "2472293"
        },
        "reduceOnly": {
          "long": "0",
          "short": "0"
        }
      },

includeHomeConversion

これもあまり使うことはないとは思いますが、Lotサイズを決定する際に口座保有通貨に換算する必要がある場合に使えそうな感じですね。 今度は初期値がFalseですのでこの情報が必要な場合は以下の例のようにセットしてあげてください。

?instruments=USD_JPY&includeHomeConversion=True

私の持っていいるデモ口座はアメリカですのでUSDは常時1になります。

  "homeConversions": [
    {
      "currency": "USD",
      "accountGain": "1",
      "accountLoss": "1",
      "positionValue": "1"
    },
    {
      "currency": "JPY",
      "accountGain": "0.00927565959",
      "accountLoss": "0.0093688823",
      "positionValue": "0.009322270945"
    }



以上のようにqueryの場合はこれらの引数を複数組み合わせることができます。
"?instruments=USD_JPY,EUR_USD&includeUnitsAvailable=False&includeHomeConversion=True"
のように&を使ってつなげていきます。


まとめ

単純なエンドポイントでも初めは結構引数を理解するのに苦労しました。 一旦理解できると応用がきくようになるのでbotの基幹の部分に使えたりするアイデアについても紹介してみました。
ろうそく足の取得や発注系のエンドポイントにはさらに多くの引数が指定できるようになっています。 すべてを使いこなす必要はありませんがある程度は理解しておかないと損する場合があるかもしれません。 次回はシグナルの判定するためによく使われるろうそく足の取得の仕方を解説したいと思います。

なにか間違いを見つけた方ご指摘お願いいたします!

*1:廃止予定とあるので説明しません