前回の記事に引き続き、今回も仮想通貨botの開発状況をまとめていきます。
今回は「Rust×Pythonで行うbot作り」のアイデアをまとめました。
新しいbotの開発ネタとして、記録を残しておきます。
どうせやることだし、先に買っておく。https://t.co/5PhQPzdNVL
— よだか(夜鷹/yodaka) (@yodakablog) February 8, 2025
Rustとは?
Rustは、高速性・安全性・並行性を兼ね備えたシステムプログラミング言語です。Mozillaが開発し、現在はRust財団が管理しています。
Rustの特徴
- メモリ安全性が高い
- 所有権システム(Ownership System) により、ガベージコレクションなしでメモリ管理を行う。
- 借用(Borrowing)とライフタイム(Lifetimes) により、メモリの不正アクセスを防ぐ。
- Nullポインタやデータ競合が発生しにくい 仕組みになっている。
- 高速な実行速度
- CやC++と同等のパフォーマンスを発揮。
- ゼロコスト抽象化 により、抽象度の高いコードでもオーバーヘッドがほぼゼロ。
- 安全な並行処理
- Fearless Concurrency(恐れない並行処理) を目指し、スレッド競合をコンパイル時に防ぐ。
std::syncやtokioなどの強力な並列・非同期処理機能をサポート。
- エコシステムが充実
- パッケージマネージャー Cargo により、依存関係管理が簡単。
- 高性能なライブラリ(
serdeでシリアライズ、tokioで非同期処理など)。
- WebAssembly(Wasm)との親和性
- RustでWebAssemblyを記述し、フロントエンド(Webブラウザ)でも高性能なアプリが動作可能。
- 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で機械学習モデルを実装
PyO3やmaturinを使って、RustでPythonの拡張モジュールを作成
私の自動取引botでも、Rustでデータストリーミングや価格計算を高速化し、Pythonで取引ロジックを実装するという形が活かせそうです。
📌 PythonとRustの棲み分け
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パース
- 高速WebSocket接続(
- Python:
ccxtを使ってスポットデータ取得(低頻度)- 過去データのロード・解析
2️⃣ データ処理・特徴量エンジニアリング
- Rust:
polarsやndarrayを使って超高速データ処理- 並列処理でリアルタイムフィルタリング
- 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) - 超低レイテンシな発注
- API通信の最適化 (
- 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から ctypes や cffi で呼び出す。
メリット:さらに低レベルな最適化が可能。
3. maturin でPythonパッケージを作る
RustをPythonのネイティブ拡張としてビルドし、PyPIパッケージとして配布可能。
📌 まとめ
| 役割 | Rust | Python |
|---|---|---|
| データ取得 | WebSocketでリアルタイム処理 | API経由でデータ取得 |
| データ処理 | 高速処理 (polars) | 柔軟な解析 (pandas) |
| 価格予測 | 数値計算 (ndarray) | MLモデル (PyTorch) |
| 戦略実装 | 低レイテンシ戦略 | 高度なロジック |
| 注文処理 | 高速APIリクエスト (reqwest) | ccxt を活用 |
| ログ管理 | tracing で軽量記録 | matplotlib で視覚化 |
Pythonの強み
- 柔軟な開発
- 豊富な機械学習ライブラリ
Rustの強み
- 高速処理
- 低レイテンシな通信
- 安全な並行処理
→ Rustでリアルタイム処理 & 取引API、PythonでMLとデータ解析 という棲み分けが最適!
この構成で開発を進めると、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(¶ms)
.send()
.await?;
println!("Order response: {:?}", response.text().await?);
Ok(())
}
🔹 Rustの取引処理のポイント
reqwestを使ってBybit APIにHTTPリクエストを送信- APIキーとシークレットキー を使って認証
- 予測価格に基づき注文を発注(売買ロジックは後で追加可能)
🔗 RustとPythonの統合フロー
- Rust (
tokio) でリアルタイム価格データを取得 - Rust (
PyO3) で Python の ML モデル (predict_price) を呼び出し、予測価格を取得 - 予測価格をもとに、Rust (
reqwest) で取引注文を実行 - 結果をログに記録し、次の取引機会を待つ
🚀 まとめ
| 機能 | Rust | Python |
|---|---|---|
| リアルタイムデータ取得 | tokio + serde_json | ccxt(バックアップ用) |
| データ処理 | polars | pandas |
| 価格予測 | なし(Rustでも可能) | PyTorch の LSTM モデル |
| 取引実行 | reqwest でBybit APIへ発注 | なし |
| ログ管理 | tracing | matplotlib で可視化 |
この設計で、Rustの超高速処理とPythonの柔軟な機械学習 を最大限に活かすことができます。
しかし、これだけで終わらせるのは面白くないので、もう一工夫してみましょう。
より堅牢なコード
ロジックをさらに改善し、効率的な取引戦略 と パフォーマンスの向上 を図ります。
🛠 改善ポイント
- リアルタイムデータ処理の効率化
- RustでWebSocketを非同期ストリーミング処理
- データのバッファリング で過去データを一定期間保持(時間足データの作成)
- 価格変動率やボラティリティを計算
- 価格予測モデルの最適化
- 過去N件の価格をベクトルとして入力し、LSTMで予測
- 価格予測の信頼度スコアを導入し、エントリー判断を最適化
- 取引戦略の強化
- 注文執行ロジックを改善(指値注文 + 成行注文の組み合わせ)
- ポジションサイズの動的調整(資金管理)
- 取引履歴を記録し、トレード評価を自動化
🚀 改善後のコード
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(¶ms)
.send()
.await?;
println!("Order response: {:?}", response.text().await?);
Ok(())
}
🔹 改善点
- 信頼度スコアに応じて注文量を調整
- リスク管理を強化(不確実な相場では取引量を減らす)
🚀 改善後の全体フロー
- Rust (
tokio) でリアルタイムデータ取得(過去データをバッファリング) - 価格変動率(ボラティリティ)を計算
- Rust (
PyO3) で Python の ML モデルを呼び出し、価格予測 + 信頼度スコアを取得 - Rust (
reqwest) で APIを呼び出し、信頼度に応じたポジションサイズで注文 - 取引結果を記録し、次の取引戦略にフィードバック
🎯 最適化のメリット
| 改善点 | 効果 |
|---|---|
| データのバッファリング | 過去の価格変動を分析しやすくなる |
| ボラティリティ計算 | 相場の安定性を評価可能 |
| 信頼度スコア導入 | 取引の精度向上 |
| リスク管理の強化 | 価格変動が大きいときはポジションを減らす |
この改良で、より堅牢な自動取引ボット になります! 🚀
まとめ
今回、初めてRustを使ったbot開発にチャレンジしてみました。
高速実行が可能なプログラミング言語として採用したRustでしたが、実際に調べて使ってみると、多くの使用方法があることにも気がつきました。
今後の仮想通貨bot開発をより広く展開していくためにも、Rustの習得を進めていきます。
参考にしている本
メインの開発とは別だが、これは今月の宿題。余力があるうちに次のことを進めておく。 pic.twitter.com/JyT01C7n24
— よだか(夜鷹/yodaka) (@yodakablog) February 10, 2025