シストレどうですか

  Algorithmic Trading for Dummies

OANDA API 応用編 すべての銘柄(通貨ペア)のレートを取得する

前回のストリーミングでのレート取得の際に、自分の口座で取引可能なすべての通貨ペアに対してどんなデータが出力されるのか調べようと思った際に、プライスエンドポイントの銘柄("instruments")引数にはALLのようなすべての銘柄を指定できる値がない事に気が付きました。ですから複数指定するには、銘柄をカンマ区切りでひとつひとつ指定する必要があります。
一度どこかでタイプしたものをコピペで貼り付ければ使いまわしができるという方法もありますが、せっかくですからプログラムでやってみたいと思います。

加えて、そこで取得できた通貨ペア毎の売値("bids")や買値("asks")のキーのメンバーの内、以下のマニュアルの例にあるようなサイズ("liquidity")毎に違うレート("price")をもつ通貨ペアがあるかどうかを調べてみます。

{
      "asks": [
        {
          "liquidity": 1000000,   👈
          "price": "1.28260"
        }, 
        {
          "liquidity": 2000000,  👈 
          "price": "1.28261"
        }, 
        {
          "liquidity": 5000000,   👈
          "price": "1.28262"
        }, 
        {
          "liquidity": 10000000,   👈
          "price": "1.28264"
        }
      ], 
      "bids": [
        {
          "liquidity": 1000000, 
          "price": "1.28241"
        }, 
        {
          "liquidity": 2000000, 
          "price": "1.28240"
        }, 
        {
          "liquidity": 5000000, 
          "price": "1.28239"
        }, 
        {
          "liquidity": 10000000, 
          "price": "1.28237"
        }
      ], 
      "closeoutAsk": "1.28264", 
      "closeoutBid": "1.28237", 
      "instrument": "USD_CAD", 

すべての通貨ペアを一度に取得するようなシチュエーションはあまりないと思いますので実用的ではないですが、その過程でPythonのプログラミングについても学ぶことがありあましたので書いてみました。

すべての銘柄(通貨ペア)のレートを取得する

処理概要

はじめに書いた通り、取引可能な銘柄に対してどんな形のレートが戻ってくるかを検証するために、銘柄情報からすべての銘柄を取得し、そこからすべての銘柄のプライス情報を調べるという事をやってみたいと思います。
その為に4つの段階にわけてみました。

  • 銘柄情報の取得
      取引可能なすべての銘柄(通貨ペア)情報を取得する。
  • 銘柄名の編集
      取得したJSON型式のデータから銘柄(通貨ペア)名をとりだし、CSV型式(カンマ区切り)の文字列に変換する。
  • レート情報の取得
      取得した銘柄名を引数にすべての銘柄のレートを取得する。
  • 取得したレート情報の検索
      取得したレート情報を検索し、該当データが存在するか確認する。

銘柄情報の取得

銘柄情報の取得方法は以前に書いたブログにありますのでそれを参考にしてください。
jantzen.hatenablog.com

"instruments"引数を指定しなければ全ての銘柄情報を取得できます。

#外部モジュール
import requests
import json
  
#口座情報(自分の番号に置き換える)
#API_Token = '9999999999999999999999999999999999999999999999999999999999999999'
#API_AccountID = '999-999-99999999-999'
  
# URLの設定 (デモ口座用ストリーミングURL)
API_URL =  "https://api-fxpractice.oanda.com"
url = API_URL + "/v3/accounts/%s/instruments" %str(API_AccountID)
  
# ヘッダー
headers = {
        'Authorization' : 'Bearer ' + API_Token
    }
  
#銘柄情報データの要求
response_body = requests.get(url, headers=headers)
  
inst_info = response_body.json()['instruments']  👈
print(json.dumps(inst_info, indent=2))
  

ここまでを実行すると次のようなデータが戻ります。(かなり長いので途中で省略)

  "instruments": [
    {
      "name": "CAD_SGD", 👈
      "type": "CURRENCY",
      "displayName": "CAD/SGD",
      "pipLocation": -4,
      "displayPrecision": 5,
      "tradeUnitsPrecision": 0,
      "minimumTradeSize": "1",
      "maximumTrailingStopDistance": "1.00000", 
      "minimumTrailingStopDistance": "0.00050", 
      "maximumPositionSize": "0",
      "maximumOrderUnits": "100000000",
      "marginRate": "0.05",
      "guaranteedStopLossOrderMode": "DISABLED",
      "tags": [
        {
          "type": "ASSET_CLASS",
          "name": "CURRENCY"
        }
      ],
      "financing": {
        "longRate": "0",
        "shortRate": "0",
        "financingDaysOfWeek": [
          {
            "dayOfWeek": "MONDAY",
            "daysCharged": 1
          },
          {
            "dayOfWeek": "TUESDAY",
            "daysCharged": 1
          },
          {
            "dayOfWeek": "WEDNESDAY",
            "daysCharged": 1
          },
          {
            "dayOfWeek": "THURSDAY",
            "daysCharged": 1
          },
          {
            "dayOfWeek": "FRIDAY",
            "daysCharged": 1
          },
          {
            "dayOfWeek": "SATURDAY",
            "daysCharged": 0
          },
          {
            "dayOfWeek": "SUNDAY",
            "daysCharged": 0
          }
        ]
      }
    },
    {
      "name": "GBP_NZD",  👈
      "type": "CURRENCY",
      "displayName": "GBP/NZD",
      "pipLocation": -4,
      "displayPrecision": 5,
      "tradeUnitsPrecision": 0,
      "minimumTradeSize": "1",
      "maximumTrailingStopDistance": "1.00000",
      "minimumTrailingStopDistance": "0.00050",
      "maximumPositionSize": "0",
      "maximumOrderUnits": "100000000",
      "marginRate": "0.05",
      "guaranteedStopLossOrderMode": "DISABLED",
      "tags": [
        {
          "type": "ASSET_CLASS",
          "name": "CURRENCY"
        }
      ],
      "financing": {
        "longRate": "0",
        "shortRate": "0",
        "financingDaysOfWeek": [
          {
            "dayOfWeek": "MONDAY",
            "daysCharged": 1
          },
          {
            "dayOfWeek": "TUESDAY",
            "daysCharged": 1
          },
          {
            "dayOfWeek": "WEDNESDAY",
            "daysCharged": 1
          },
          {
            "dayOfWeek": "THURSDAY",
            "daysCharged": 1
          },
          {
            "dayOfWeek": "FRIDAY",
            "daysCharged": 1
          },
          {
            "dayOfWeek": "SATURDAY",
            "daysCharged": 0
          },
          {
            "dayOfWeek": "SUNDAY",
            "daysCharged": 0
          }
        ]
      }
    },
    {
      "name": "ZAR_JPY",  👈
      "type": "CURRENCY",
      "displayName": "ZAR/
     |
     |

今回は銘柄の名前があればよいので、次のステップでは銘柄("instruments”)キーのメンバーの中から銘柄名(”name")を抽出します。

銘柄名の編集

ここでは取得したJSON型式のデータをCSV型式(カンマ区切り)の文字列に変換します。
まずは検索対象となるキーのメンバーから対象の値を見つけるためのループを行います。
今回の例では"instrument"キーのメンバーを検索すればよいので、先程の銘柄情報から取得した"inst_info"変数から銘柄名("name")を取り出します。

all_inst = [i["name"] for i in inst_info]
print(all_inst)

リスト内包表記で記述すればたった一行で書けてしまいます。
"all_inst"変数内には以下のようなリストが作成されました。

['CAD_SGD', 'GBP_NZD', 'ZAR_JPY', 'EUR_HUF', 'EUR_DKK', 'USD_MXN', 'GBP_USD', 'NZD_HKD', 'AUD_CHF', 'CAD_JPY', 'GBP_SGD', 'USD_SEK', 'AUD_HKD', 'AUD_NZD', 'AUD_JPY', 'EUR_ZAR', 'SGD_CHF', 'AUD_SGD', 'EUR_JPY', 'USD_CHF', 'USD_TRY', 'GBP_JPY', 'EUR_CZK', 'CHF_ZAR', 'EUR_TRY', 'USD_JPY', 'USD_NOK', 'TRY_JPY', 'USD_DKK', 'CHF_JPY', 'EUR_PLN', 'SGD_JPY', 'AUD_CAD', 'NZD_USD', 'EUR_CHF', 'NZD_SGD', 'USD_HKD', 'CHF_HKD', 'USD_CAD', 'USD_CNH', 'USD_CZK', 'GBP_ZAR', 'EUR_HKD', 'HKD_JPY', 'EUR_AUD', 'USD_SGD', 'EUR_SEK', 'GBP_HKD', 'EUR_NZD', 'EUR_CAD', 'USD_HUF', 'NZD_CAD', 'EUR_SGD', 'AUD_USD', 'EUR_USD', 'GBP_AUD', 'USD_PLN', 'SGD_HKD', 'CAD_HKD', 'GBP_CAD', 'USD_SAR', 'GBP_PLN', 'EUR_NOK', 'NZD_CHF', 'USD_ZAR', 'NZD_JPY', 'USD_THB', 'GBP_CHF', 'EUR_GBP', 'CAD_CHF']

カンマ区切りのようには見えますがあくまでも配列の要素ですので、次にjoin関数を使い文字列になるようにカンマで区切りなおします。

all_inst2 = ','.join([i for i in all_inst])
print(all_inst2)

するとCSV型式の文字列に変換する事ができました。

CAD_SGD,GBP_NZD,ZAR_JPY,EUR_HUF,EUR_DKK,USD_MXN,GBP_USD,NZD_HKD,AUD_CHF,CAD_JPY,GBP_SGD,USD_SEK,AUD_HKD,AUD_NZD,AUD_JPY,EUR_ZAR,SGD_CHF,AUD_SGD,EUR_JPY,USD_CHF,USD_TRY,GBP_JPY,EUR_CZK,CHF_ZAR,EUR_TRY,USD_JPY,USD_NOK,TRY_JPY,USD_DKK,CHF_JPY,EUR_PLN,SGD_JPY,AUD_CAD,NZD_USD,EUR_CHF,NZD_SGD,USD_HKD,CHF_HKD,USD_CAD,USD_CNH,USD_CZK,GBP_ZAR,EUR_HKD,HKD_JPY,EUR_AUD,USD_SGD,EUR_SEK,GBP_HKD,EUR_NZD,EUR_CAD,USD_HUF,NZD_CAD,EUR_SGD,AUD_USD,EUR_USD,GBP_AUD,USD_PLN,SGD_HKD,CAD_HKD,GBP_CAD,USD_SAR,GBP_PLN,EUR_NOK,NZD_CHF,USD_ZAR,NZD_JPY,USD_THB,GBP_CHF,EUR_GBP,CAD_CHF

以上の二つの処理をさらに一行で書いてみます。

all_inst = ','.join([i["name"] for i in inst_info])

とてもシンプルな記述になりました。

レート情報の取得

次の段階として、取得できたすべての通貨ペア名を使って最新のレートを取得します。
詳細は以下を参考にして下さい。 jantzen.hatenablog.com

前のステップでセットした"all_inst"変数に格納されている値を指定すればよいだけです。

url = API_URL + "/v3/accounts/%s/pricing?instruments=%s" % (str(API_AccountID), all_inst)  👈
response_body = requests.get(url, headers=headers)
print(json.dumps(response_body.json(), indent=2))

取得したレート情報の検索

最後のステップとして取得できたレート情報の中に"liquidity"が異なるような値を複数もつ”asks"や'bids"がある通貨ペアがあるかどうか調べます。

これも銘柄名をCSVに変換した時と同じように、"asks"や”bids"情報がある"prices"キーのメンバーの中をループさせます。

"liquidity"毎に"price"があるかどうかは、"asks"や”bids"がリストのメンバー("asks": [{...}, {...},~{...}],)を持つことを利用して、リストの要素数が1より大きい場合に複数あると考えます。
複数あると判定された場合は、その通貨ペアを後で詳細に調べられるように通貨ペア名を表示させます。

check_result=[]
for liquidity_check in response_body.json()['prices']:
        if len(liquidity_check["bids"]) != 1 or len(liquidity_check["asks"]) != 1:
                check_result.append(liquidity_check["instrument"])
print("結果: %s" %check_result)

やはりここでもリスト内包表記でまとめてみました。

liquidity_check = response_body.json()['prices']
check_result = [i["instrument"] for i in liquidity_check if len(i["bids"]) != 1 or len(i["asks"]) != 1]
print("結果: %s" %check_result)

実行結果としては、複数もつようなレートはとりあえずなさそうです。

結果: []


まとめ

最新のレートを取得する際にどんなデータを受け取るのか調べて見ました。
とりあえずはマニュアルにあるようなデータは見つかりませんでしたが、いつもそうなっているとは限りませんのでストリーミングでレートを拾って調べてみてもよさそうです。
ストリーミング用レートでも受け取りデータのフォーマットが多少違いますが同じように調べることができます。

最後に今回の例の全体のコードを掲載しておきます。

全体のコード

#外部モジュール
import requests
import json
  
#口座情報
API_Token = '9999999999999999999999999999999999999999999999999999999999999999'
API_AccountID = '999-999-99999999-999'
  
#-- 銘柄情報の取得 --
API_URL =  "https://api-fxpractice.oanda.com"
url = API_URL + "/v3/accounts/%s/instruments" %str(API_AccountID)
  
headers = {
        'Authorization' : 'Bearer ' + API_Token
    }
    
response_body = requests.get(url, headers=headers)
inst_info = response_body.json()['instruments']
  
#-- 銘柄名の編集 --
all_inst = ','.join([i["name"] for i in inst_info])
print(all_inst)
  
#-- レート情報の取得 --
url = API_URL + "/v3/accounts/%s/pricing?instruments=%s" % (str(API_AccountID), all_inst)
response_body = requests.get(url, headers=headers)
  
#-- 取得したレート情報の検索 --
liquidity_check = response_body.json()['prices']
check_result = [i["instrument"] for i in liquidity_check if len(i["bids"]) != 1 or len(i["asks"]) != 1]
  
print("結果: %s" %check_result)