noteのタイトル画像

「ビットコイン自動売買用ccxt拡張」 リアクティブ・プログラミングのすすめ

目次
・リアクティブ・プログラミングって?
・すべては流れの中に
・あなたが求めているものかどうか
・興味をもってもらえたら

●リアクティブ・プログラミングって?

私のnoteを購入してくださった方や、無料部分で公開しているコードを覗いて「これ何?」って思われた方がいるようなので、少し詳説しますね。(って、私もそれほど詳しい訳ではないですけれど)

公開したプログラムを眺めていると次のような箇所があると思います。

fromEvent(em, 'buy')
    .pipe(filter(pos => pos == 0))
    .subscribe(
        async (pos) => {
            // 処理
        }
    );

jsファイルの冒頭には

// rxjs
import {fromEvent} from 'rxjs';
import {filter} from 'rxjs/operators';

のような記述もあります。

「RxJS」というライブラリを読み込んで、イベントの発火を受けてsubscribeしている部分に注目してください。

subscribeとは「購読」ということなんですが、上記の例ではイベントによる発火を待って、発火した後に内容を「購読」するという約束事を記述しています。

fromEventでem(イベント・エミッター)の「buy」イベントを待ち、フィルター(filter)でポジション(pos)が0だった場合のみ「購読」されるようにしています。

なぜわざわざこんなプログラミングにしたのか?と思われる方も多いと思います。
素直にif文を書いて条件分岐で処理を追加すれば良いじゃないですか、と。

そうです。if文の数が必要最小限に収まっているなら、なんら問題はないと思います。
ですが、処理をどんどん追加していくと、ややもするとif文の入れ子の入れ子の、そのまた入れ子になる可能性もあり、バグを産む温床になる可能性は否定できません。

if( x == 0 ) {
    if( y == 0 ) {
        if( z == 0 ) {
            ・・・ 処理 ・・・
        }
        ・・・処理・・・
    }
    ・・・処理・・・
}

手続き型のプログラムの場合、どうしても上記のような構造になりやすいと言われています。

では、リアクティブ・プログラミングとは関数型プログラミングなのか?と言われると、それも違うと言われています。

ちまたでは、次のような議論が行われていますが、

ここではさくっと概念だけ掴んでおいていただけるとありがたいです。
(私もまだ理解がストンと落ちていないところもあるので)

私は、リアクティブ・プログラミングを

時間的に変化するデータの流れを、宣言的な記述で、非同期で扱うために考案された手法

だと理解しています。

時間的に変化するデータの流れ、と言われてもピンと来ませんよね。

「ストリーム」

と言った方が理解しやすいかもしれません。

●すべては流れの中に

リアクティブ・プログラミングは最初マイクロソフトの1プロジェクトとしてスタートし、瞬く間にJavaScript, java, Ruby, pythonなどの言語に移植されました。

時間的に変化していくデータを次々に処理していく訳ですが、実装ではデータを変換器に次々に通過させ、購読者に届けるという書き方をします。

面白い点は、購読者は自分からデータを取得したりしないのです。

すべては「流れに身を任せて」、流れてきたデータを受け取るだけです。

流れてきたデータを受け取った後、購読者の関心のある部分だけ処理をします。

購読(subscribe)しなければ、データはそこに流れません。ある意味シンプルですね。

購読が必要な部分だけを追加すれば、他の処理に影響はないのです。

私の作った「ドテン君」のロジックでも、

// --------------------------------
// buy && position == 0
// --------------------------------
fromEvent(em, 'buy')
    .pipe(filter(pos => pos == 0))
    .subscribe(
        async (pos) => {
            //console.log('pos == 0 && buy');
        }
    );

// --------------------------------
// buy && position < 0
// --------------------------------
fromEvent(em, 'buy')
    .pipe(filter(pos => pos < 0))
    .subscribe(
        async (pos) => {
            //console.log('pos < 0 && buy');
            // ポジションがー側(売り先行状態からの反転上昇開始)
        }
    );

のように、2つの処理(ポジション有りと無しの場合の「買い」判定)はお互いに干渉しません。

if文の条件分岐を間違えてしまい、他の処理に飛んでいってしまうこともないのです。

今回は、扱いが簡単なfromEventでイベントを 受けています。
これなら簡単にマルチキャスト(一度に複数の受信先に処理を委託する)も容易です。
本来なら、observableを継承したsubjectクラスを使用すべきでしょうが、使用するオペレータが多岐にわたるので、今回はイベントベースで記述してあります。

そのうちにまた改修する時に別途説明させていただきます。

●あなたが求めているものかどうか

もっとも、リアクティブ・プログラミングはプログラミングの一手法であって、すべての実装に有効という訳ではありません。

私は最初、ポジションの値や、買いと売りの方向、レンジ期間の損切り処理等を通常のif文やswitch文等の分岐を使用して処理を記述していました。

一つ処理を思いつく毎に、if文のネストが深くなり、switch文の内部でさらにif文、switch文を乱発することもありました。

botは基本的に常時データをガシガシと集め、そのデータの流れの中で処理を進めます。

その時「これってストリーム?」と思い立ち、リアクティブ・プログラミングの記憶を辿ってトライしようと思ったのです。

リアクティブ・プログラミングの概念は昔からありましたが、実用に耐えるライブラリが見当たらず、ほぼ放棄していたところでした。

私があーだこーだと解説するよりも、上記の記事を読んだ方がスッキリするかもしれません。

ちょっと古い記事では

や、新しいところでは、

があります。

●興味をもってもらえたら

次のサイトで色々なサンプルが公開されています。

上記はjavascriptでのサンプルですが、探せば他の言語もあるでしょう。

RxJSの最新バージョンは6系です。
(ver.5.5系の記事もちらほらありますが、最新は6系です。pipe周り等が変更されているので、5.5系の記述方法では動かないケースも多々有ります)

RxJSで中心的な役割を果たす「Observable」はECMAScriptで正式に採用される見通しらしいので、ここでマスターしておくことは無駄ではないと思います。

あなたもこれから楽しいリアクティブ・プログラミング・ライフを!


ソフトウェア・エンジニアを40年以上やってます。 「Botを作りたいけど敷居が高い」と思われている方にも「わかる」「できる」を感じてもらえるように頑張ります。 よろしくお願い致します。