はじめに
以前、LangGraphとQlib Alpha158、Google TimesFMで作る「自律型AIヘッジファンド」をOSS公開した話 という記事を公開したところ、世界最大のクオンツ投資リポジトリ集 awesome-quant (⭐️15,000+) に正式採択されるなど、予想以上の大きな反響をいただきました。
しかし、Pythonでマルチエージェントを動かし、リアルタイムな市場データと突合させていく中で、「本番運用の壁」 に直面しました。
-
Pythonのレイテンシ限界:
急落・フラッシュクラッシュ発生時、PythonのGIL(グローバルインタプリタロック)や動的型評価、GC(ガベージコレクション)のせいで、ストップロス判定に数ミリ秒〜数十ミリ秒の遅延が生じるリスク。 -
LLMの「ファクター極性・単位の誤読(ハルシネーション)」:
海外クオンツコミュニティ(Redditのr/algotradingやr/LocalLLaMA等)で議論されている通り、LLMに生の特徴量数値をそのまま渡すと、「PERが高いから割安」「ボラティリティ急増を業績拡大の好材料」といった致命的な解釈ミスを平気で起こす。 -
エージェント反省記憶(Reflexion)の肥大化とEmbedding遅延:
過去の負けトレードの教訓を検索するために外部ベクトルDB(Embedding API)を叩くと、判定ごとに数百msのレイテンシとAPI課金が発生する。 -
日米市場・非同期データにおける相関の歪み(Epps効果):
東証とNY市場、あるいは板情報ごとのミリ秒ラグにより、単純なPearson相関係数では相関が著しく過小評価される。
これらを根本から解決するため、今回 Lumina Capital Engine v6.4.5 への大幅なアーキテクチャ刷新を行いました。
本記事では、
#![forbid(unsafe_code)]Pure Rust による L1/L2キャッシュ常駐型 SoA 番犬エンジン(1ポジション 3.63ナノ秒判定)- LLMの誤読を物理封鎖する「数理ファクター・オントロジー辞書」
- ベクトルDBを捨てて
0.3msで教訓を掘り出す「SQLite FTS5 (BM25) 反省エンジン」 - Epps効果を打破する「林・吉田(Hayashi-Yoshida)非同期共分散推定」
の実装とベンチマーク実測値を詳しく解説します。
1. 🦀 Pure Rust 製 L1/L2キャッシュ常駐 SoA 番犬センチネル (lumina-sentinel-rs)
課題:Pythonの動的オブジェクトとキャッシュミス
Pythonで保有ポジション(ティッカー、取得単価、現在値、高値、損切りラインなど)をループで回してストップロス判定を行う場合、オブジェクトのポインタ参照が散らばり、CPUのL1/L2データキャッシュミスが頻発します。
さらにGILの影響で、マルチスレッドでの高速ポーリングにも制約があります。
設計:Struct-of-Arrays (SoA) による連続メモリ配置
そこで、急落検知・ストップロス監視・トレイリングストップ計算を担う超低遅延エンジン lumina-sentinel-rs を Rust でゼロから実装しました。
メモリ配置には、従来の構造体配列(AoS: Array of Structures)ではなく、SoA(Structure of Arrays) を採用しています。
// AoS(従来):データが混ざり、SIMDやキャッシュラインに無駄が生じる
// struct Position { id: u64, price: f64, stop: f64, ... }
// SoA(採用):同種データがメモリ上で完全にフラット・連続に並ぶ
#[derive(Debug, Clone, Default)]
pub struct PositionBatchSoA {
pub position_ids: Vec<u64>,
pub current_prices: Vec<f64>,
pub entry_prices: Vec<f64>,
pub stop_loss_prices: Vec<f64>,
pub trailing_thresholds: Vec<f64>,
pub highest_prices: Vec<f64>,
pub quantities: Vec<f64>,
pub is_half_profits: Vec<u8>,
}
判定ロジックは以下の優先順位で連続配列をイテレートします:
-
Rule A: 緊急損切り(Stop Loss Trigger) (
current <= stop_loss) -
Rule B: 利確目標到達(Half-Profit Trigger) (
current >= entry * 1.05) -
Rule D: 建値撤退ストップ(利確後に建値
entryを割り込んだら強制撤退) - Rule C: 動的トレイリングストップ(最高値からのドローダウン監視)
安全性:#![forbid(unsafe_code)]
金融システムにおいてメモリ破壊やセグメンテーション違反は許されません。unsafe を完全に禁止し、Rustのコンパイラによる借用検査・メモリ安全性を100%保証しています。
#![forbid(unsafe_code)]
実測ベンチマーク(Linux VPS本番環境)
VPS(Ubuntu / AMD EPYC環境)で実施したベンチマーク結果がこちらです:
| 測定項目 | 処理内容 | 実測タイム |
|---|---|---|
| 単一ポジション判定 | 損切り・利確・トレイリング判定 | 3.63 ナノ秒 |
| 1,000ポジション一括評価 | 全SoAスライスの一括スキャン | 3.62 マイクロ秒 |
| Volume Clock変換 | 1,000本バーの出来高均等化+情報流速計算 | 3.90 マイクロ秒 |
| Python ⇄ Rust IPC | 標準入出力パイプ経由の往復RTT | 2.88 マイクロ秒 |
Pythonのメインプロセス(AutoTrader)からは常駐サブプロセス(パイプIPC)として呼び出すため、通信オーバーヘッドを含めてもわずか 2.88マイクロ秒 で急落の安全弁が作動します。
2. 🧠 数理ファクター・オントロジー辞書(LLMハルシネーション物理封鎖)
LLMに「生の数値」だけを渡してはいけない
マルチエージェント合議(強気派エージェント、弱気派リスク管理者、ファンダメンタルズ分析官など)にLLMを用いる際、最も危険なのが**「数値の解釈ハルシネーション」**です。
例えば、
-
PER: 85.0➔ LLM「PERが高いので成長期待が高く、極めて割安です!(大嘘)」 -
Historical Volatility: 72%➔ LLM「ボラティリティが高いので事業が活発化しており買い推奨です!」
LLMは文脈を流暢に作文するあまり、「その数値が上がることが強気なのか弱気なのか、あるいは極端値がリスクなのか」 というクオンツ的定義を勝手に捏造します。
解決策:Factor Ontology Schema(型安全なオントロジー契約)
これを防ぐため、すべての指標に対して FactorDefinition を定義し、単位・極性・確定シグナルをオントロジー辞書として標準化しました。
from dataclasses import dataclass
from enum import Enum
class InterpretationPolarity(str, Enum):
HIGHER_IS_BULLISH = "HIGHER_IS_BULLISH" # 高いほど強気(例: ROE, OFI)
LOWER_IS_BULLISH = "LOWER_IS_BULLISH" # 低いほど強気(例: PER, 負債比率)
EXTREME_IS_RISK = "EXTREME_IS_RISK" # 極端値はリスク(例: ヒストリカルボラティリティ)
CONTEXTUAL = "CONTEXTUAL" # 複合判断
@dataclass(frozen=True)
class FactorDefinition:
name: str
category: str
unit: str
description: str
polarity: InterpretationPolarity
typical_range: tuple
formula: str
bullish_rule: str
bearish_rule: str
neutral_rule: str
合議前段で、Alpha158 Zスコアや深層OFI、PER、林吉田相関などの生数値をオントロジー契約に基づいて機械的にバリデーションし、「指標の定義・単位・確定判定(BULLISH / BEARISH / RISK)」をプロンプト内に強制埋め込み します。
これにより、LLMが数値の正負や極性を勝手に曲解する余地を物理的にゼロにしました。
3. ⚡ ベクトルDBを捨てて 0.3ms を実現した SQLite FTS5 (BM25) 反省記憶
なぜVector DBをやめたのか?
LLMエージェントが過去の失敗トレードから学ぶ仕組み(Reflexion)では、一般的に text-embedding-3-small などのEmbedding APIとChroma/PineconeといったベクトルDBを使いがちです。
しかし、実運用では以下の問題がありました:
- レイテンシ: Embedding APIの呼び出しに毎回 150ms〜400ms かかる。
- 外部依存: APIのレートリミットや通信エラーで合議がブロックされる。
- 過剰な意味検索: トレードの教訓は「半導体」「決算ギャンブル」「出来高急増」「トレイリング狩り」といった明確なクオンツキーワードの一致(語彙的検索) の方が遥かに精度が高い。
実装:SQLite標準の FTS5 (BM25) 全文検索
そこで、外部DBやAPIを一切使わず、SQLite 3の標準拡張である FTS5 (BM25) 仮想テーブルを採用しました。
CREATE VIRTUAL TABLE IF NOT EXISTS agent_reflexions_fts USING fts5(
reflection_id UNINDEXED,
symbol,
agent_name,
market_regime,
lesson,
tags,
content='agent_reflexions',
content_rowid='id',
tokenize='unicode61 remove_diacritics 2'
);
エージェントが意思決定を行う直前、現在の相場レジームや銘柄ティッカー、テクニカル条件をクエリとして BM25 スコアリング検索を実行します。
# 0.3ms 〜 0.5ms で過去の関連する教訓上位3件を即座に抽出
cursor.execute("""
SELECT r.symbol, r.agent_name, r.lesson, bm25(agent_reflexions_fts) as rank
FROM agent_reflexions_fts f
JOIN agent_reflexions r ON f.reflection_id = r.id
WHERE agent_reflexions_fts MATCH ?
ORDER BY rank ASC
LIMIT 3
""", (search_query,))
これにより、APIコスト0円、外部依存ゼロ、検索レイテンシ 0.3ms(約1,000倍高速化) で、エージェントが過去と同じ罠に引っかかるのを防ぐ機構を実現しました。
4. 📈 林・吉田 (Hayashi-Yoshida 2005) 非同期共分散推定
Epps効果:非同期サンプリングの罠
マルチアセット取引(例: 日経225先物とS&P500、または取引頻度が異なる2銘柄)において、単純に等間隔(1分足など)でリサンプリングしてPearson相関係数を計算すると、サンプリング間隔が短くなるほど見かけ上の相関がゼロに近づく現象(Epps効果) が発生します。
林・吉田推定量による観測インターバル積分
日本の計量経済学者である林高樹氏と吉田朋広氏が2005年に提唱した「林・吉田推定量(Hayashi-Yoshida Covariance Estimator)」は、データのリサンプリングを行わず、観測された時間区間が重なり合っている(Overlapしている)刻み同士のリターン積を累積加算 します。
$$\hat{\Sigma}{XY}^{HY} = \sum{i} \sum_{j} \Delta X_i \Delta Y_j \cdot \mathbb{I}{{ (t{i-1}, t_i] \cap (s_{j-1}, s_j] \neq \emptyset }}$$
def compute_hayashi_yoshida_corr(times_x, prices_x, times_y, prices_y) -> float:
# 観測時間インターバルの重なりを二重ループ(または二分探索)で判定し共分散を推計
# 非同期な2銘柄間の真の相関係数を正確に算出
...
本システムでは、この林・吉田推定量をリードラグ分析器(lead_lag_analyzer.py)および階層型リスクパリティ(HRP)の相関スパイク検知に統合しました。
市場間の時間差や板の更新タイミングのズレに惑わされず、真の波及関係(どちらが先行指標か)を正確に捉えることが可能になりました。
5. まとめとアーキテクチャ全体像
今回実装したアーキテクチャの全体像は以下の通りです:
OSSとして公開中
すべてのコードは GitHub 上で公開しており、Docker 1行ですぐにお手元のPCで動かすことができます(GPU不要、CPUのみで動作します)。
🔗 GitHub Repository:
https://github.com/ramuza0113-sketch/lumina-ai-hedge-fund
もしアーキテクチャや設計思想に興味を持っていただけましたら、GitHubの ⭐️ Star で応援していただけると非常に励みになります!
IssueやDiscussionsでのフィードバックやアイデアも大歓迎です。