Bot

開発記録#129(2025/2/16)「Rust×Pythonで行うbot作り」

前回の記事に引き続き、今回も仮想通貨botの開発状況をまとめていきます。

今回は「Rust×Pythonで行うbot作り」のアイデアをまとめました。

新しいbotの開発ネタとして、記録を残しておきます。

Rustとは?

Rustは、高速性・安全性・並行性を兼ね備えたシステムプログラミング言語です。Mozillaが開発し、現在はRust財団が管理しています。

Rustの特徴

  1. メモリ安全性が高い
    • 所有権システム(Ownership System) により、ガベージコレクションなしでメモリ管理を行う。
    • 借用(Borrowing)とライフタイム(Lifetimes) により、メモリの不正アクセスを防ぐ。
    • Nullポインタやデータ競合が発生しにくい 仕組みになっている。
  2. 高速な実行速度
    • CやC++と同等のパフォーマンスを発揮。
    • ゼロコスト抽象化 により、抽象度の高いコードでもオーバーヘッドがほぼゼロ。
  3. 安全な並行処理
    • Fearless Concurrency(恐れない並行処理) を目指し、スレッド競合をコンパイル時に防ぐ。
    • std::synctokio などの強力な並列・非同期処理機能をサポート。
  4. エコシステムが充実
    • パッケージマネージャー Cargo により、依存関係管理が簡単。
    • 高性能なライブラリ(serde でシリアライズ、tokio で非同期処理など)。
  5. WebAssembly(Wasm)との親和性
    • RustでWebAssemblyを記述し、フロントエンド(Webブラウザ)でも高性能なアプリが動作可能。
  6. C言語との互換性
    • FFI(Foreign Function Interface) を利用し、Cライブラリと簡単に連携できる。

Rustの用途

  • システムプログラミング(OS、組み込みシステム)
  • Webバックエンド(Actix、Rocketなどのフレームワーク)
  • ブロックチェーン・仮想通貨(Solana、Parity Substrate)
  • ゲーム開発(Bevy、Amethyst)
  • 分散システム(TiKV、Vector)
  • 機械学習(tch-rs、rust-ml)

RustとPythonの組み合わせ

RustはPythonと組み合わせて使うことも可能。たとえば:

  • データストリーミングや高速な数値計算をRustで処理し、Pythonで機械学習モデルを実装
  • PyO3maturin を使って、RustでPythonの拡張モジュールを作成
Yodaka

私の自動取引botでも、Rustでデータストリーミングや価格計算を高速化し、Pythonで取引ロジックを実装するという形が活かせそうです。

📌 PythonとRustの棲み分け

Yodaka

PythonとRustを組み合わせて仮想通貨の自動取引botを作る場合、それぞれの特性を活かして適切な棲み分けをすることが重要です。以下のような役割分担が考えられます。

機能Rustで実装Pythonで実装
データ取得・ストリーミング高速なWebSocket処理 (tokio)簡易なAPIアクセス (requests / websockets)
データ前処理高速フィルタリング・集計 (polars)Pandasでデータ解析
価格予測数値計算 (ndarray, nalgebra)機械学習 (scikit-learn, PyTorch)
取引戦略の実装シンプルな戦略のバックテスト (backtest-rs)複雑なMLモデルの適用
注文管理・リスク管理低レイテンシな注文処理 (reqwest, serde)ロジックの組み合わせ (ccxt)
ログ管理・監視効率的なロギング (tracing, log)視覚化 (matplotlib, dash)
バックテスト高速なシミュレーション柔軟な分析

🌟 各モジュールの具体的な設計

1️⃣ データ取得・ストリーミング

  • Rust
    • 高速WebSocket接続(tokio-tungstenite
    • マルチスレッドで並行処理
    • serde を使ったJSONパース
  • Python
    • ccxt を使ってスポットデータ取得(低頻度)
    • 過去データのロード・解析

2️⃣ データ処理・特徴量エンジニアリング

  • Rust
    • polarsndarray を使って超高速データ処理
    • 並列処理でリアルタイムフィルタリング
  • Python
    • pandas でデータ解析
    • 時系列データの特徴量エンジニアリング

3️⃣ 価格予測

  • Rust
    • ndarray, tch-rs (PyTorch for Rust) を使って数値計算
    • 簡単な統計手法(移動平均、ボリンジャーバンド)
  • Python
    • scikit-learn, tensorflow, PyTorch を使って機械学習モデルを構築
    • LSTM, Transformer, DQN などを利用

4️⃣ 取引戦略

  • Rust
    • シンプルなロジック(スプレッドトレード、マーケットメイキング)
    • 低レイテンシなアルゴリズム
  • Python
    • 複雑なアルゴリズム(リスク制御、アービトラージ)
    • MLを用いた動的戦略変更

5️⃣ 注文処理

  • Rust
    • API通信の最適化 (reqwest)
    • 非同期注文 (tokio)
    • 超低レイテンシな発注
  • Python
    • ccxt でマルチ取引所対応
    • デバッグしやすい発注ロジック

6️⃣ ログ管理・監視

  • Rust
    • tracing を使って軽量ログ記録
    • バイナリログフォーマット(パフォーマンス重視)
  • Python
    • matplotlib, dash で視覚化
    • Slack, Discord, Telegram へのアラート送信

🚀 RustとPythonの統合方法

1. PyO3 を使う

RustでPythonのモジュールを作成し、PythonからRustの関数を呼び出す方法。
メリット:Rustの高速性をPythonに統合できる。

use pyo3::prelude::*;

#[pyfunction]
fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[pymodule]
fn my_rust_module(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(add, m)?)?;
    Ok(())
}

Pythonで利用:

import my_rust_module
print(my_rust_module.add(3, 4))  # 7

2. FFI (cbindgen) を使う

Rustで共有ライブラリ (.so, .dll) を作成し、Pythonから ctypescffi で呼び出す。
メリット:さらに低レベルな最適化が可能。

3. maturin でPythonパッケージを作る

RustをPythonのネイティブ拡張としてビルドし、PyPIパッケージとして配布可能。


📌 まとめ

役割RustPython
データ取得WebSocketでリアルタイム処理API経由でデータ取得
データ処理高速処理 (polars)柔軟な解析 (pandas)
価格予測数値計算 (ndarray)MLモデル (PyTorch)
戦略実装低レイテンシ戦略高度なロジック
注文処理高速APIリクエスト (reqwest)ccxt を活用
ログ管理tracing で軽量記録matplotlib で視覚化

Pythonの強み

  • 柔軟な開発
  • 豊富な機械学習ライブラリ

Rustの強み

  • 高速処理
  • 低レイテンシな通信
  • 安全な並行処理

Rustでリアルタイム処理 & 取引API、PythonでMLとデータ解析 という棲み分けが最適!


Yodaka

この構成で開発を進めると、Pythonの機械学習の柔軟性Rustの低レイテンシ高速処理 の両方を活かせます。

実装の例

PythonとRustを組み合わせた仮想通貨自動取引botの設計について、リアルタイムデータ取得(Rust)→ ML戦略処理(Python)→ 取引実行(Rust) という流れで、実際のコード例を交えながら説明します。


🚀 RustでWebSocketデータ取得(リアルタイム)

Rustの tokio を使って、仮想通貨取引所(Bybitなど)からリアルタイムのWebSocketデータを取得します。

📌 Rust(データ取得用)

src/main.rs

use tokio_tungstenite::connect_async;
use tokio::stream::StreamExt;
use serde_json::Value;

#[tokio::main]
async fn main() {
    let url = "wss://stream.bybit.com/realtime";
    let (ws_stream, _) = connect_async(url).await.expect("Failed to connect");

    let (_, mut read) = ws_stream.split();

    println!("Connected to WebSocket!");

    while let Some(msg) = read.next().await {
        if let Ok(msg) = msg {
            let text = msg.to_text().unwrap();
            let json: Value = serde_json::from_str(text).unwrap();
            
            println!("Received: {:?}", json);
        }
    }
}

🔹 Rustのポイント

  • tokio_tungstenite を使って非同期でWebSocket接続
  • serde_json で受信データをJSONとして解析
  • 受信データをリアルタイムで表示(取引所APIによってフォーマットを調整)

📌 Pythonで価格予測モデル

Rustで取得したデータをPythonに渡し、LSTM(長短期記憶ネットワーク)を使った価格予測を行います。

📌 Python(機械学習モデル)

ml_model.py

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim

# LSTMモデル定義
class LSTMPredictor(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(LSTMPredictor, self).__init__()
        self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        _, (h_n, _) = self.lstm(x)
        out = self.fc(h_n[-1])
        return out

# モデルロード
def load_model():
    model = LSTMPredictor(1, 50, 1)
    model.load_state_dict(torch.load("lstm_model.pth"))
    model.eval()
    return model

# 予測関数
def predict_price(model, data):
    data = torch.tensor(data, dtype=torch.float32).unsqueeze(0).unsqueeze(2)
    with torch.no_grad():
        prediction = model(data).item()
    return prediction

🔹 Pythonのポイント

  • PyTorchでLSTMモデルを定義
  • 保存済みの学習済みモデルをロード
  • WebSocketで受け取った価格データを使って予測

🚀 RustとPythonの連携

Rustで取得したデータをPythonのMLモデルに渡し、予測結果をRustに返します。
これには PyO3 を使用します。

📌 RustからPythonの予測関数を呼び出す

src/lib.rs

use pyo3::prelude::*;
use pyo3::types::IntoPyDict;

fn predict_price(price_data: Vec<f32>) -> f32 {
    let gil = Python::acquire_gil();
    let py = gil.python();

    let model_module = PyModule::import(py, "ml_model").expect("Failed to import Python module");
    let model = model_module.call_function0("load_model").expect("Failed to load model");

    let price = price_data.clone().into_py(py);
    let result = model_module.call_function1("predict_price", (model, price))
        .expect("Failed to predict price");

    result.extract::<f32>().unwrap()
}

🔹 RustとPythonの連携のポイント

  • PyO3 を使ってPythonをRustから呼び出す
  • Rustで取得した価格データをPythonのMLモデルに渡す
  • Pythonから予測値を取得し、Rust側で処理する

📌 Rustで取引注文の実行

予測結果に基づき、RustでBybit APIを使って取引を行います。

src/trade.rs

use reqwest::Client;
use serde_json::json;

async fn place_order(api_key: &str, secret_key: &str, side: &str, qty: f64, price: f64) -> Result<(), reqwest::Error> {
    let url = "https://api.bybit.com/v2/private/order/create";

    let params = json!({
        "api_key": api_key,
        "side": side,
        "symbol": "BTCUSDT",
        "order_type": "Limit",
        "qty": qty,
        "price": price,
        "time_in_force": "GoodTillCancel"
    });

    let client = Client::new();
    let response = client.post(url)
        .json(&params)
        .send()
        .await?;

    println!("Order response: {:?}", response.text().await?);
    Ok(())
}

🔹 Rustの取引処理のポイント

  • reqwest を使ってBybit APIにHTTPリクエストを送信
  • APIキーとシークレットキー を使って認証
  • 予測価格に基づき注文を発注(売買ロジックは後で追加可能)

🔗 RustとPythonの統合フロー

  1. Rust (tokio) でリアルタイム価格データを取得
  2. Rust (PyO3) で Python の ML モデル (predict_price) を呼び出し、予測価格を取得
  3. 予測価格をもとに、Rust (reqwest) で取引注文を実行
  4. 結果をログに記録し、次の取引機会を待つ

🚀 まとめ

機能RustPython
リアルタイムデータ取得tokio + serde_jsonccxt(バックアップ用)
データ処理polarspandas
価格予測なし(Rustでも可能)PyTorch の LSTM モデル
取引実行reqwest でBybit APIへ発注なし
ログ管理tracingmatplotlib で可視化
Yodaka

この設計で、Rustの超高速処理とPythonの柔軟な機械学習 を最大限に活かすことができます。
しかし、これだけで終わらせるのは面白くないので、もう一工夫してみましょう。

より堅牢なコード

Yodaka

ロジックをさらに改善し、効率的な取引戦略パフォーマンスの向上 を図ります。

🛠 改善ポイント

  1. リアルタイムデータ処理の効率化
    • RustでWebSocketを非同期ストリーミング処理
    • データのバッファリング で過去データを一定期間保持(時間足データの作成)
    • 価格変動率やボラティリティを計算
  2. 価格予測モデルの最適化
    • 過去N件の価格をベクトルとして入力し、LSTMで予測
    • 価格予測の信頼度スコアを導入し、エントリー判断を最適化
  3. 取引戦略の強化
    • 注文執行ロジックを改善(指値注文 + 成行注文の組み合わせ)
    • ポジションサイズの動的調整(資金管理)
    • 取引履歴を記録し、トレード評価を自動化

🚀 改善後のコード

1️⃣ Rustでリアルタイムデータ処理

過去のデータを一定数保持し、価格変動率を計算できるようにします。

📌 Rust(データバッファ付きWebSocket処理)

src/main.rs

use tokio_tungstenite::connect_async;
use tokio::sync::mpsc;
use tokio::task;
use tokio::time::{sleep, Duration};
use serde_json::Value;
use std::collections::VecDeque;

const BUFFER_SIZE: usize = 100; // 過去100件の価格を保持

#[tokio::main]
async fn main() {
    let url = "wss://stream.bybit.com/realtime";
    let (ws_stream, _) = connect_async(url).await.expect("Failed to connect");
    let (_, mut read) = ws_stream.split();

    let mut price_buffer: VecDeque<f64> = VecDeque::with_capacity(BUFFER_SIZE);

    while let Some(msg) = read.next().await {
        if let Ok(msg) = msg {
            let text = msg.to_text().unwrap();
            let json: Value = serde_json::from_str(text).unwrap();

            if let Some(price) = json["price"].as_f64() {
                if price_buffer.len() == BUFFER_SIZE {
                    price_buffer.pop_front(); // 古いデータを削除
                }
                price_buffer.push_back(price);

                // ボラティリティ計算
                if price_buffer.len() > 10 {
                    let volatility = calculate_volatility(&price_buffer);
                    println!("Current Price: {}, Volatility: {:.4}", price, volatility);
                }
            }
        }
    }
}

// ボラティリティ計算(標準偏差)
fn calculate_volatility(data: &VecDeque<f64>) -> f64 {
    let mean = data.iter().sum::<f64>() / data.len() as f64;
    let variance = data.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / data.len() as f64;
    variance.sqrt()
}

🔹 改善点

  • 価格データをバッファリング → 過去の価格を保持し、傾向分析可能
  • ボラティリティ計算 → 価格の変動率を分析し、リスク評価に利用

2️⃣ Pythonで価格予測(信頼度スコア付き)

予測モデルの信頼度スコアを計算し、取引判断を強化します。

📌 Python(価格予測モデル)

ml_model.py

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim

# LSTMモデル定義
class LSTMPredictor(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(LSTMPredictor, self).__init__()
        self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        _, (h_n, _) = self.lstm(x)
        out = self.fc(h_n[-1])
        return out

# モデルロード
def load_model():
    model = LSTMPredictor(1, 50, 1)
    model.load_state_dict(torch.load("lstm_model.pth"))
    model.eval()
    return model

# 予測と信頼度スコアの計算
def predict_price(model, data):
    data = torch.tensor(data, dtype=torch.float32).unsqueeze(0).unsqueeze(2)
    with torch.no_grad():
        prediction = model(data).item()
        confidence = np.std(data.numpy())  # 標準偏差を信頼度スコアとして使用
    return prediction, confidence

🔹 改善点

  • 信頼度スコア を導入(ボラティリティが高いと信頼度を下げる)
  • モデルの柔軟性向上(データを正規化して入力)

3️⃣ Rustで取引注文(動的ポジション管理)

価格変動と信頼度スコアを考慮し、注文量を調整する。

📌 Rust(リスク管理を考慮した発注)

src/trade.rs

use reqwest::Client;
use serde_json::json;

// 注文実行
async fn place_order(api_key: &str, secret_key: &str, side: &str, qty: f64, price: f64, confidence: f64) -> Result<(), reqwest::Error> {
    let url = "https://api.bybit.com/v2/private/order/create";

    // 信頼度スコアに基づいてポジションサイズを調整
    let adjusted_qty = if confidence < 0.01 {
        qty * 0.5 // 信頼度が低い場合はポジションを減らす
    } else {
        qty
    };

    let params = json!({
        "api_key": api_key,
        "side": side,
        "symbol": "BTCUSDT",
        "order_type": "Limit",
        "qty": adjusted_qty,
        "price": price,
        "time_in_force": "GoodTillCancel"
    });

    let client = Client::new();
    let response = client.post(url)
        .json(&params)
        .send()
        .await?;

    println!("Order response: {:?}", response.text().await?);
    Ok(())
}

🔹 改善点

  • 信頼度スコアに応じて注文量を調整
  • リスク管理を強化(不確実な相場では取引量を減らす)

🚀 改善後の全体フロー

  1. Rust (tokio) でリアルタイムデータ取得(過去データをバッファリング)
  2. 価格変動率(ボラティリティ)を計算
  3. Rust (PyO3) で Python の ML モデルを呼び出し、価格予測 + 信頼度スコアを取得
  4. Rust (reqwest) で APIを呼び出し、信頼度に応じたポジションサイズで注文
  5. 取引結果を記録し、次の取引戦略にフィードバック

🎯 最適化のメリット

改善点効果
データのバッファリング過去の価格変動を分析しやすくなる
ボラティリティ計算相場の安定性を評価可能
信頼度スコア導入取引の精度向上
リスク管理の強化価格変動が大きいときはポジションを減らす
Yodaka

この改良で、より堅牢な自動取引ボット になります! 🚀

まとめ

今回、初めてRustを使ったbot開発にチャレンジしてみました。

高速実行が可能なプログラミング言語として採用したRustでしたが、実際に調べて使ってみると、多くの使用方法があることにも気がつきました。

今後の仮想通貨bot開発をより広く展開していくためにも、Rustの習得を進めていきます。

-Bot