【シストレ開発#6】Qt GUIでローソク足チャートを表示させる機能を追加!

GUIで表示できるとヘタレなシステムでもカッコよく見えますよね!たっきん(Twitter)です!

前回は状態遷移表を作成して注文情報を管理する「order_manager」ノードの作りこみを行いました。

しかし、注文を出せるだけではシステムトレードとしては成り立ちません。

注文を出すには当然ながら注文を出すための判断材料が必要になります。

なので今回はその判断材料になるためのベースとなるローソク足データの取得、管理、そしてモニターするためのノード群の作りこみを行っていきます。

具体的には、以下3つのノードになります。

  • candlestick_serviceノード
  • candlestick_managerノード
  • gui_monitorノード

<シストレ・アーキテクチャ ver.0.3>

Sponsored Link

ローソク足データを取得するノード[candlestick_service]

OANDAにはローソク足データを取得するためのAPIとしてInstrumentsCandlesがありますが、このAPIをROSノード化したのが「candlestick_manager」ノードとなります。

このノードとのROS通信はServiceで行います。

現時点ではローソク足データ取得用Service「candles」しかありませんが、後々オープンオーダーとオープンポジションを取得するためのService「order_book」を追加していく予定です。

ちなみに、ローソク足データ取得用Service「candles」の入出力仕様はこんな感じ。

candles Service入出力仕様>

・Request

パラメータ名 用途
gran_msg 時間足(日足、1時間足、1分足など)
inst_msg 通貨ペア(USD/JPY、EUR/JPY、EUR/USDなど)
dt_from ローソク足データ取得開始日時
dt_to ローソク足データ取得終了日時

・Response

パラメータ名 用途
result Service処理結果(成功 or 失敗)
frc_msg Service処理失敗時の理由コード
cndl_msg_list ローソク足データのリスト

ついでに言っておくとOANDA APIのInstrumentsCandlesには取得可能なデータ数の上限が5000件までとなっていますが、ROSノード化するときにデータ数が5000件を超える場合は分割して取得できるように作っておいたので実質無制限のデータ件数を取得できます。

当然ながらOANDAのサーバー側が過去いつまでのデータを保持しているかによって限度はありますけどね。

ローソク足データを管理するノード[candlestick_manager]

candlestick_manager」はその名の通りローソク足データを管理するノードです。

1分足、1時間足、日足などの各時間足毎のローソク足データを過去数年分保持し、チャートの傾向を解析するための機能を持たせようと考えています。

現時点ではまだ実装できていませんが、このノードにも後々オープンオーダー/オープンポジションのデータの管理、解析機能を追加していく予定です。

ローソク足データをモニターするノード[gui_monitor]

「candlestick_manager」でローソク足データを管理し解析したとしても、その結果が目に見える形で出てこないとちゃんと解析できているのかわからないですよね!

そう思って急遽ローソク足データや解析結果を可視化してモニターするためのノード「gui_monitor」を作ることにしました。

このノードはアーキテクチャ設計 ver.0.3で新規に追加したノードになります。

そして、このノードの役割ですがモニター以外の機能は持たせません。

そうすることによってシストレ本体との依存関係をなくし、このノードを起動しなくてもシストレ本体のシステムを稼働できるようにしたいからです。

また、モニターするわけなので当然ながらGUIツールを使う必要があります。

今回使用したGUIツールはQtです。

Qtを選んだ理由はネット上でROSとQtを連携させて動かくことに成功した人がいるからです。

実績ってやっぱ大事ですよね!

ROSとQtを連携させる上での課題

ROSのTopic通信であるSubscriber、Service通信、タイマー割り込み処理は「rclpy.spin()」が実行されていないと、コールバック関数が呼ばれなくなりプロセスが停止してしまいます。

しかし、この関数を実行すると他の処理がブロックされてしまい、Qt側のウィンドウ更新等のプロセスが全て停止してしまいます。

一方、Qt側にもウィンドウを更新するためのコールバック関数を呼び出すためには「app.exec_()」を実行する必要がありますが、こちらもROS同様に他の処理をブロックしてしまい、今度はROS側のプロセスが停止してしまいます。

この問題についてネット上で解決策を探したところ、ノンブロッキングで処理できる「rclpy.spin_once()」、「app.processEvent()」を使えばいいらしい。

参考[技ラボ]ROS2 + Qt Quickを使ったシステム開発

パラメータ名 ROS Qt
ブロッキング rclpy.spin() app.exec_()
ノンブロッキング rclpy.spin_once() app.processEvent()

これら2つのノンブロッキング関数を同じWhileループ内で呼び出すことでROSとQtの両方のプロセスが実行できるらしい。

実装例はこんな感じ。

while rclpy.ok():
    rclpy.spin_once(node)
    app.processEvent()
    sleep()

まぁこれでも良いとは思うのですが、僕個人的にはあまりwhileを使いたくありません。

理由は処理負荷が増えるからです。

実装例のようにsleepを挟むことで処理負荷を減らせますが、その代償としてプロセスにディレイが発生してしまいます。

そこで僕が考えたのが単純にブロッキング処理の「rclpy.spin()」と「app.exec_()」をマルチスレッド処理させればいけるんじゃないか説。

物は試しでさっそく実装してみたところ、ROSとQtが互いのプロセスをブロックすることなく上手く動いた。

どうやら僕の説は間違っていなかったようですね!

しかし、マルチスレッド処理する上で気を付けなければならないのが排他制御を怠らないこと。

これは、一度バグりだすと解析が困難になることもあり厄介です。

今回作成している「gui_monitor」はROSで受信してQtで表示させるだけのシンプルな処理にしようと思っているため、排他制御も複雑にはならないと考えマルチスレッド処理させる案でいきます。

実装例はこんな感じ。

import threading

ros_th = threading.Thread(target=rclpy.spin, args=(node,))  
ros_th.start()  
app.exec_()  

動作確認

それでは今回作成した、3つのノードを動かしてみましょう!

「gui_monitor」ノードは起動した時点でウィンドウが立ち上がります。

現時点ではまだ開発途中となっているため、日足、4時間足、1時間足のローソク足チャートしか表示できませんが逐次他の機能も追加していきます。

さいごに

今回は、QtというGUIツールをROS2と連携して動かせるかどうかを確認したところ、無事に動かすことに成功しましました。

やっぱりGUIがあると見た目が良くなって開発してる感が出ますよね!(^^)

今後はこのGUIにオープンオーダー、オープンポジション、テクニカル解析結果の表示機能も追加していく予定です。

シストレ開発の進捗もいい感じで来ていますので、迅速かつ丁寧に作りこんでいきたいと思います!

また、今回作成したノードのコードはGitHubに上げてますので興味あったら覗いてみてください。

GitHub candlestick_serviceノード:candlestick_service.py

GitHub candlestick_managerノード:candlestick_manager.py

GitHub gui_monitorノード:gui_monitor.py

Sponsored Link

このサイトはreCAPTCHAによって保護されており、Googleのプライバシーポリシー利用規約が適用されます。