はじめに
お久しぶりです〜
今回はちょっと毛色の違う記事です。楽天WalletのETH/JPY CFDをGeminiが自動でトレードするボットを作ったので、その話をしたいと思います。
AI駆動開発(AI-DLC)で開発し、インフラはAWS CDKで全部コード管理してます。
↓こちらから購入できます↓
Geminiがテクニカル指標を読んでEntry/Exitを判断し、楽天WalletのCFDを自動で取引するボットアプリ
値段交渉もあればぜひ、、笑
作ったもの、一言で言うと
「Geminiがテクニカル指標を読んでエントリー・エグジットを判断し、楽天WalletのCFDを自動で取引するボット」
毎分マーケットデータを収集して、条件が揃ったときだけGeminiに判断を投げます。
返ってきたJSON(ENTER_LONG / SHORT / HOLD / EXIT)をパースして、ガードレールを通過したら発注する、という流れです。
正直に言うと
トレードのことはそんなに詳しくない笑
プロンプトはそれっぽく書いたつもりですが、「これで本当に勝てるのか?」と言われると自信がないです。(現状1ヵ月動かして勝率55%くらい笑)
MAクロス・RSI・ATRあたりを指標に使って、こんな感じの条件でエントリーさせています。
=== A+ MANDATORY CONDITIONS ===
For ENTER_LONG (ALL required):
[L1] MA5 > MA20 > MA60 (強気アライメント)
[L2] MA20_slope > 0 AND MA60_slope >= 0
[L3] market_structure_bias = BULLISH
[L4] RSI < 65 (高値掴み禁止)
[L5] spread < 0.30%
ベースconfidence 0.72スタートで、ブースター条件(+0.03×5)やペナルティ(-0.06)を積み上げて0.70以上にならないとHOLDする、みたいな設計です。
トレードに詳しい人がこのプロンプトを「ここのパラメータをこう変えたら勝率上がるよ」ってチューニングしてくれたら、もっと良くなるんじゃないかなと思っています。
なのでこれ、売ること(or チューニングしてもらうこと)を考えています。
インフラ・コード部分はそれなりにちゃんと作ってあるつもりなので、トレード戦略部分を磨いてもらえると面白いものになるかもしれません。興味ある人はご連絡ください笑
設定値の一部
現在の主要パラメータはこんな感じです。
| 項目 | 値 |
|---|---|
| 通貨ペア | ETH/JPY |
| 運用資金 | 30,000 JPY |
| 1R(リスク単位) | 資金の1.0%(=約300円) |
| 日次損失上限 | -4R |
| 稼働時間 | 平日21:00〜03:00 JST(深夜帯) |
| 最低Confidence | 0.70(Geminiが0.70未満と判断したらHOLD強制) |
ETH/JPYは深夜帯がそこそこ動くので、21時〜翌3時の時間帯を狙っています。EC2は20:50に起動して03:10に止まるスケジュールです。
機能説明
1. システム全体の構成
[楽天Wallet]
↕ REST API(注文・残高・約定)
↕ WebSocket(リアルタイム価格)
[EC2(Docker)]
├── TradingBot(1分サイクル)
│ ├── 市場データ収集 → テクニカル指標計算
│ ├── AITriggerEvaluator → 「今AIを呼ぶべきか?」判断
│ ├── Gemini API呼び出し → JSON応答
│ ├── GuardrailChain(3層フィルタ)
│ └── 発注 → state.json更新
└── RuleEngine(WebSocket Tick毎)
└── SL/TP/TrailingStop をリアルタイム評価
[AWS]
├── EventBridge → EC2の自動起動/停止
├── SSM Parameter Store → APIキー管理
├── CloudWatch → ログ・アラーム
└── SNS → メール通知
AWS CDK(TypeScript)でインフラを全部コード管理しています。
cdk deploy 一発でEC2・IAM・EventBridge・CloudWatch・SNSが全部立ち上がります。
2. AI呼び出しのタイミング制御
「毎分Geminiを呼ぶ」のではなく、呼ぶ必要があるときだけ呼ぶ設計にしています。
Geminiを呼ぶトリガーは2種類あります。
イベントトリガー(即時)
以下のどれかが起きたら即AI呼び出し。
| トリガー | 条件 |
|---|---|
| 価格変動 | 前回評価時から±0.10%以上動いた |
| MAクロス | MA5とMA20の大小関係が逆転した |
| ATRバケット変化 | LOW/MID/HIGHの区分が変わった |
| レジーム変化 | NORMAL↔TREND↔BREAKOUTの切り替わり |
保険ポーリング(定期)
イベントが何も起きなくても、2分に1回は必ずAIを呼びます(「AI盲目化防止」と呼んでいます)。NORMAL相場のときは10分に1回に間引いてAPIコストを節約。
この2段構えにすることで、「チャンスを逃さない」と「無駄なAPI呼び出しを減らす」を両立しています。
3. AIに渡すデータの中身
Geminiに渡す市場データはこんな構成です。
=== MARKET DATA ===
Price: 441500 JPY | Bid: 441480 | Ask: 441520
Spread: 0.009%
=== TECHNICAL INDICATORS ===
MA5: 441200 | MA20: 440800 | MA60: 439500
RSI(14): 58.3 | ATR(14): 3200
MA5 Slope: +0.08% | MA20 Slope: +0.03%
=== MARKET STRUCTURE ===
Structure Bias: BULLISH (MA5>MA20>MA60)
=== VOLATILITY ===
Regime: TREND | ATR%: 0.72% | Spread/ATR: 0.014
=== RISK STATE ===
Capital: 30000 JPY | Usable Margin: 12500 JPY
Daily PnL: -150 JPY | Consecutive Losses: 1/10
=== TIME ===
2025-01-15 23:30:00 JST | Trading Hours: True
これを見て、GeminiがJSON形式で返してきます。
{
"action": "ENTER_LONG",
"confidence": 0.78,
"size": 0.1,
"stop_loss_price": 437600,
"take_profit_price": 451100,
"exit_rules": [
{"type": "stop_loss", "price": 437600},
{"type": "take_profit", "price": 451100},
{"type": "trailing_stop", "trail_pct": 1.5},
{"type": "timeout", "max_minutes": 120}
],
"rationale": "MA5>MA20>MA60の強気アライメント、RSI58で最適ゾーン...",
"key_features": ["MA triple alignment", "RSI optimal zone", "BULLISH structure"]
}
ポイントはJSON形式にしていること。プログラムで直接パースできるので、「AIの返答をコードが受け取ってそのまま動く」ループが完成します。テキストで返ってきたらパースが面倒だし、ハルシネーションで変な値が混じったときにもコードでちゃんと弾けます。
4. 3層ガードレール
AIが「ENTER_LONG」と言っても、ガードレールを3つ通過しないと発注しません。
Gemini応答
↓
① FormatGuard ← JSONが正しいか?SL/TP方向は正しいか?confidence足りてるか?
↓
② MarketGuard ← スプレッドは広すぎないか?板は薄すぎないか?急変動中じゃないか?
↓
③ FundGuard ← 日次損失上限に達していないか?連敗制限に引っかかっていないか?
↓
発注 or HOLD強制
どれか1つでも落ちたらHOLD強制です。AIが何と言おうと関係ない笑
主なチェック内容はこんな感じ。
| レイヤー | チェック内容 |
|---|---|
| FormatGuard | confidence ≥ 0.60、SL < entry < TP(LONG)、サイズ範囲チェック |
| MarketGuard | スプレッド ≤ 0.25%、板厚 ≥ 0.1 ETH、急変動(ATR ≥ 6.0%)でない |
| FundGuard | 日次損失 ≤ -4R、連敗 ≤ 10回、証拠金余裕あり |
5. RuleEngine(WebSocket Tick毎のリアルタイム監視)
「1分サイクルのAI判断ループ」と別に、WebSocketのTickが届くたびにSL/TPを評価する独立タスクを動かしています。
1分待っていたら急落時に大きく損切りポイントを超えてしまう可能性があるので、Tick単位で監視するようにしています。
class RuleType(StrEnum):
STOP_LOSS = "stop_loss"
TAKE_PROFIT = "take_profit"
TRAILING_STOP = "trailing_stop"
TIMEOUT = "timeout"
BREAKEVEN_STOP = "breakeven_stop" # 含み益が出たらSLをentry価格に移動
VOLATILITY_EXIT = "volatility_exit"
TrailingStopは _highest_since_entry(LONG)や _lowest_since_entry(SHORT)をTickごとに更新することで実現しています。
ちなみに、楽天WalletのIFD_OCO注文(サーバーサイドSL/TP)も同時に入れているので、二重安全網の構成です。RuleEngineはネットワーク断やAPI遅延時のバックアップとして機能します。
6. マーケットレジーム
市場の状態を4種類に分類して、それぞれ違う戦略を取ります。
| レジーム | 定義 | AIの行動 |
|---|---|---|
| TREND | MA傾斜あり、トレンド強度 ≥ 0.35% | トレンドフォローのエントリー検討(R:R = 1:3) |
| BREAKOUT | ATR% ≥ 1.5%(ボラティリティ拡大) | モメンタムエントリー検討(R:R = 1:4) |
| NORMAL | レンジ・静かな相場 | AIを呼んでもHOLD強制(ノイズ抑制) |
| PANIC | ATR% ≥ 3.0%(異常急変動) | 新規エントリー禁止、ポジションがあれば緊急EXIT検討 |
NORMAL相場のときはAI自体の呼び出し頻度を下げる(保険ポーリングを10分に延ばす)ことでAPIコストも節約しています。
7. リスク管理の仕組み
ポジションサイズはリスクベースで計算しています。
1R = 資金の1.0% = 300円
ポジションサイズ = min(
リスク予算 / (エントリー価格 - SL価格), # リスクベース
証拠金余裕 × 0.7 × レバレッジ2倍 / 価格, # 証拠金ベース
最大0.2 ETH # 上限キャップ
)
日次損失 -4R に達したら当日の取引を自動停止、連敗10回で同じく停止します。
8. インフラ(AWS CDK)
インフラは全部TypeScriptのCDKで書いています。ざっくりこんな構成。
InfraStack
├── ComputeConstruct → EC2 t3.micro(Amazon Linux 2023)
├── StorageConstruct → EBS 20GB(state.json、ログ保存)
├── SchedulerConstruct → EventBridge(20:50起動 / 03:10停止)
├── IamConstruct → EC2インスタンスプロファイル(最小権限)
└── ObservabilityConstruct
├── CloudWatch Logs → /trading-bot/app
├── CloudWatch Alarms → CRITICAL/ERROR/WebSocket切断
└── SNS Topic → メール通知
EC2のスケジュールは EventBridge Scheduler で制御。平日の深夜帯だけ稼働させることで、24時間起動と比較して約74%のEC2コスト削減ができています(月々数百円レベル)。
APIキーはSSM Parameter Store(SecureString)で管理。EC2のインスタンスプロファイルからIAMで取得するので、コードにシークレットが一切入りません。
9. データ管理
EBSにJSON/JSONLで管理しています。シンプルな構成ですが、小規模運用ではRDSは完全にオーバースペックなのでこれで十分です。
/app/data/
├── state.json # ポジション状態・日次P&L(単一SSOT)
├── trades/
│ └── 2025-01-15.jsonl # 約定記録
└── events/
└── 2025-01-15.jsonl # SL発動・AI判断等のイベントログ
state.json が唯一の真実(SSOT)として機能します。起動時に取引所APIと突合して整合性確認(Reconciliation)もしています。
CI/CD
GitHub ActionsでECRへ自動ビルド・プッシュし、SSM経由でEC2のコンテナを更新する構成です。GitHub OIDC認証を使っているのでシークレット登録不要です。
push to main
→ pytest(テスト)
→ Docker build & push to ECR
→ EC2で docker pull & docker restart(SSM経由)
おわり
今回は自動売買ボットを作った話でした。
インフラとコードの仕組みはそれなりに面白い設計になったと思いますが、肝心のトレード戦略(プロンプトとパラメータ)のチューニングが課題です笑
「FXやCFDが分かる人が触れば化けるかも」と思っているので、売ることや一緒に改善することを考えています。興味ある方はご連絡ください。
何か質問・フィードバックあればコメントやDMで!