【シストレ開発#1】為替レートを取得するROSノードを作成してみる。

 ※ 本記事は広告・プロモーションを含みます。
シストレ開発

ガチでシストレ開発始めます!たっきん(Twitter)です!

今まではOANDA APIを使って窓埋め仲値&ゴトー日の解析を行ってきてチャートにある程度の傾向があることを確認してきました。

他にも三角待合いや東京、欧州、ニューヨーク時間帯の相場傾向の解析など、色んな条件での解析シリーズもやっていこうと思っているのですが、それとは別に前々からやりたいと思っていたことを今回からやっていこうと思います。

それは、システムトレードの開発です。

こんなこと言うとプロのトレーダーに怒られるかもしれないのであんまり言いたくないのですが、パソコンの画面に1日中張り付いてトレードするスタイルって正直バカバカしくてやりたくないんですよね。

だって時間の無駄じゃないですか?

自分の中でトレードルール(条件)を決めてその条件にマッチするときが来るまで画面の前でひたすら待ち続ける時間の使い方なんて僕はしたくない。

そもそもトレードルールを決めてるなら自分の代わりにコンピュータにトレードさせればいいじゃん。

言うは易し、するは難しなんて言葉があるので、これ以上言うのは止めにするとして、そんな事情もありシステムトレード開発をやっていこうと思います。

今回はシステムトレード開発第1段として、僕が考えるシストレ実行環境を紹介していこうと思います。

シストレ実行環境

実行環境についてはそこそこ時間を費やして色々と調べてみましたが、最終的には以下の環境で動かしていこうと思います。

  • OS:Linux(Ubuntu)
  • プログラミング言語:Python
  • ミドルウェア:ROS

上記の中でも特に注目されるのがミドルウェアのROSを使用する点だと思います。

ROS (Robot Operating System)

ROSというのはRobot Operating Systemの略で、ロボット開発向けのソフトウェアプラットフォームになります。

ちなみに、Operating Systemと謳っていますがWindowsやLinuxのようなOSではなく、ミドルウェア的な位置づけとなります。

ROSについての紹介記事はググるとたくさん出てくるのでそちらを参照してみてください。

今回開発するのはシストレなのに、なぜロボット開発向けのROSを使うのか?と疑問に思った方も多いと思います。

そのROSを今回採用しようと決めた理由ですが、単純にシストレ開発を行っていく上で想定される課題が3つあり、その3つ全てを解決できそうだったのがROSだったからです。

 課題①:機能拡張を容易に行えるか?

 課題②:容易に分散処理を実現できるか?

 課題③:課題①②をお金をかけずに解決できるか?

課題①:機能拡張を容易に行えるか?

シストレは常にアルゴリズムを更新していく必要があり、頻繁に仕様変更もしくは機能拡張していくことが予想されます。

よって機能拡張に堅牢なフレームワークが必要になると考えました。

ROSはノードと呼ばれる複数の実行可能ファイルを環境内で実行させ、各ノード同士が相互に通信することで1つのアプリケーションを実現しています。

よって機能拡張をする際は、ノードとノード間の通信を追加するだけでよく、仕様変更による他のノードへの影響を最小限に抑えることができるんじゃないかと思います。

課題②:容易に分散処理を実現できるか?

今回開発するシストレはどこまでの機能を実装するかは未知数です。よってデプロイ先のコンピュータの負荷もどこまで大きくなるのかの見通しも立っていません。

最悪1台のコンピュータでは処理が追い付かず、複数台のコンピュータを容易する必要があるかもしれません。

そのような場合でもROSの散処理機を使うことでこの課題を容易に解決することができそうです。

ROSは複数のコンピュータによる分散処理をも容易に実行できるよう設計されており、自身の環境のリソースに合わせて処理負荷を柔軟にコントロールすることができると考えました。

課題③:課題①②をお金をかけずに解決できるか?

ROSは無料で入手できます。

これは地味に嬉しい。

ROSで現在の為替の値を取得するノードを作ってみる。

実際にROSを使って為替の値を取得するノードを作成してみます。

ベースのソースコードは以下の記事で作成したOANDA APIによる「リアルタイム為替レート取得」のプログラム(pricing_stream.py)を用いることにします。

実際にROSノード化したソースコードは以下のようになりました。

import rclpy
from rclpy.node import Node
from std_msgs.msg import Float32
from oandapyV20 import API
from oandapyV20.endpoints.pricing import PricingStream
from oandapyV20.exceptions import V20Error


class StreamApi(Node):

    def __init__(self):
        super().__init__("stream_api")

        # Set logger lebel
        logger = super().get_logger()
        logger.set_level(rclpy.logging.LoggingSeverity.DEBUG)

        PRMNM_ACCOUNT_NUMBER = "account_number"
        PRMNM_ACCESS_TOKEN = "access_token"
        TPCNM_BIDS_PRICE = "bids_price"
        TPCNM_ASKS_PRICE = "asks_price"

        # Declare parameter
        self.declare_parameter(PRMNM_ACCOUNT_NUMBER)
        self.declare_parameter(PRMNM_ACCESS_TOKEN)

        account_number = self.get_parameter(PRMNM_ACCOUNT_NUMBER).value
        access_token = self.get_parameter(PRMNM_ACCESS_TOKEN).value
        self.get_logger().debug("[OANDA]Account Number:%s" % account_number)
        self.get_logger().debug("[OANDA]Access Token:%s" % access_token)

        self.__api = API(access_token=access_token)
        params = {"instruments": "USD_JPY"}
        self.__ps = PricingStream(account_number, params)

        # Declare publisher
        self.__pub_bids = self.create_publisher(Float32, TPCNM_BIDS_PRICE)
        self.__pub_asks = self.create_publisher(Float32, TPCNM_ASKS_PRICE)

    def request(self):
        BIDS = "bids"
        ASKS = "asks"
        PRICE = "price"
        bids = Float32()
        asks = Float32()
        try:
            for rsp in self.__api.request(self.__ps):
                if (BIDS and ASKS) in rsp.keys():
                    bids.data = float(rsp[BIDS][0][PRICE])
                    asks.data = float(rsp[ASKS][0][PRICE])
                    # Publish topics
                    self.__pub_bids.publish(bids)
                    self.__pub_asks.publish(asks)
                    # output log
                    self.get_logger().info(str(bids.data))
                    self.get_logger().info(str(asks.data))
        except V20Error as e:
            print("Error: {}".format(e))


def main(args=None):
    rclpy.init(args=args)
    stream_api = StreamApi()
    stream_api.request()
    rclpy.shutdown()

ノード名は「stream_api」としました。

ノードの内部処理としては、OANDA STREAM APIによって取得したBID/ASKの価格をトピック「bids_price」、「asks_price」としてPublishするシンプルな構成にしました。

※トピックを簡単に説明すると、ノード間で通信されるメッセージ仕様だと思ってください。

てなわけで、さっそく今回作成したノード「stream_api」を動かしてみて、Publishされた「bids_price」、「asks_price」をグラフにプロットしてみました。

いい感じに動いてくれてるみたいですね!

さいごに

今回はシストレ開発第1回ということで、ROSを使った為替レートを取得するノードをお試しで作成してみました。

お試しとは言うものの、既に「為替レートを取得するノード」としては十分完成形に近いものになっている気もします。

まだまだ駆け出しではありますが、少しづつ機能を拡張しながら自分の作ったシストレを実践投入できるまで作りこんでいきたいと思います。

また、今回作成したROSノードはGitHubで公開しています。

スポンサーリンク

コメント

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