技術解説2026-03-26 · 約8分
Next.js 16 + Anthropic Claude APIでトレード日記AI分析ツールを作った話
はじめに
個人トレーダーとして数年間FXと株を続ける中で、ずっと感じていた課題がありました。「なぜ同じ失敗を繰り返すのか」という問いへの答えが、スプレッドシートや手書き日記では得られなかったのです。
その課題を解決するために作ったのが TradeJournal(tradejournal.company)です。トレード記録を蓄積し、週次でAIがプロのコーチのような分析レポートを自動生成するWebアプリです。
1. なぜ作ったか
トレードにおける最大の敵は「感情」と「自分のルール違反」です。一般的なトレーダーのジャーナル管理は以下のような問題を抱えています。
- Excelで記録しても「分析」は自分でやらなければならない
- 手書き日記は書くのが面倒で続かない
- 勝率・損益の集計はできても「パターンの発見」には繋がらない
- 「ルール違反」を記録していても、その損益インパクトを計算していない
「ルール違反をしたらどれだけ損をしているか」を数値で把握していないトレーダーがほとんどです。この数値を可視化し、さらにAIが毎週フィードバックをくれる仕組みがあれば、トレードは劇的に改善できると考えました。
2. 技術スタック
- Next.js 16(App Router / React Server Components)
- Supabase(Auth + PostgreSQL + RLS)
- Anthropic Claude API(claude-haiku-4-5 — 週次AI分析)
- Tailwind CSS + Resend(週次メール)
- Vercel(ホスティング + Cron)
Next.js 16 (App Router) を選んだ理由
React Server Components(RSC)によってデータフェッチのアーキテクチャが大きく変わりました。トレードジャーナルのようなデータ集計が多いアプリでは、サーバー側でSupabaseクエリを実行してHTMLとして返せるRSCは非常に相性が良いです。
Anthropic Claude API を選んだ理由
GPT-4と比較検討しましたが、以下の理由でClaudeを選択しました。
- 長いコンテキスト: 数十件のトレードデータ+統計数値をまとめてプロンプトに注入しても処理できる
- 日本語品質: 日本語ユーザー向けの自然な文章生成品質が高い
- コスト: claude-haiku-4-5 は推論速度と価格のバランスが優秀
あなたのトレード、データで見直しませんか?
月30件まで無料・クレカ不要・30秒で登録
無料で始める →3. 実装のポイント
3-1. App Router でのサーバーコンポーネント活用
ダッシュボードページは完全なServer Componentとして実装し、Supabaseからのデータ取得をサーバー側で完結させています。
typescript
// src/app/(app)/dashboard/page.tsx
export default async function DashboardPage() {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
const [
{ isPro, isStandard },
{ data: trades },
{ count: weeklyReviewCount },
] = await Promise.all([
getUserPlan(),
supabase.from('trades').select('*').eq('user_id', user!.id)
.order('trade_date', { ascending: false }).limit(200),
supabase.from('weekly_reviews').select('id', { count: 'exact', head: true })
.eq('user_id', user!.id).gte('created_at', weekStart),
])
return <DashboardClient trades={trades ?? []} ... />
}
クライアントコンポーネントはインタラクションが必要な部分のみに限定し、初期表示は全てSSRで完結させています。
3-2. Supabase Auth + RLS によるマルチテナント設計
RLSポリシーをテーブル単位で設定し、アプリ層を一切通さずにデータアクセスを制御しています。マルチテナントSaaSを個人で構築する際に非常に強力です。
sql
-- supabase/schema.sql
ALTER TABLE trades ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can only access their own trades"
ON trades FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
サーバーサイドのSupabaseクライアントは、リクエストのCookieからセッションを復元して動作します。Next.js 16では cookies() がPromiseを返す非同期APIに変更されているため注意が必要です。
typescript
// src/lib/supabase/server.ts
export async function createClient() {
const cookieStore = await cookies() // Next.js 16では await が必要
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return cookieStore.getAll() },
setAll(cookiesToSet: { name: string; value: string; options: object }[]) {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
)
},
},
}
)
}
3-3. Claude API プロンプトエンジニアリング
週次AIレビューの品質は、プロンプトにどれだけ具体的な数値を注入できるかで決まります。単純に「トレード記録を分析してください」と渡すだけでは汎用的な回答しか返ってきません。
プロフィットファクター・期待値・チルト検知などの指標を事前に計算し、構造化して注入することで、AIが具体的な数値根拠を持ったコーチングができるようになります。
typescript
// src/lib/ai/generate-review.ts
const prompt = `
あなたはプロのトレードコーチです。以下の週次データを分析し、
具体的な数値根拠を持ったフィードバックを日本語で提供してください。
## 今週の基本統計
- 総トレード数: ${stats.totalTrades}件
- 勝率: ${(stats.winRate * 100).toFixed(1)}%
- プロフィットファクター: ${stats.profitFactor.toFixed(2)}
- 期待値: ${stats.expectancy.toFixed(0)}円/トレード
## ルール違反分析
- 違反トレード数: ${violationTrades.length}件
- 違反時の平均損益: ${violationAvgPnl.toFixed(0)}円
- 違反による推定損失: ${estimatedViolationCost.toFixed(0)}円
## チルト指標
- 連勝後翌日勝率: ${(postStreakWinRate * 100).toFixed(1)}%(通常比 ${diff}%)
`
3-4. ルール違反自動検知エンジン
トレーダーが「損切りは-2%以上」「エントリーは9時〜15時のみ」のようなルールを設定すると、記録時に自動で違反チェックが走ります。
typescript
// src/lib/rule-engine/check-violations.ts
type Operator = 'gt' | 'gte' | 'lt' | 'lte' | 'eq' | 'neq'
const OPERATORS: Record<Operator, (a: number, b: number) => boolean> = {
gt: (a, b) => a > b,
gte: (a, b) => a >= b,
lt: (a, b) => a < b,
lte: (a, b) => a <= b,
eq: (a, b) => a === b,
neq: (a, b) => a !== b,
}
export function checkViolations(trade: Partial<Trade>, rules: Rule[]): Rule[] {
return rules.filter(rule => {
const tradeValue = getTradeField(trade, rule.field)
if (tradeValue === undefined || tradeValue === null) return false
const op = OPERATORS[rule.operator as Operator]
return !op(Number(tradeValue), Number(rule.threshold))
})
}
5フィールド(損益率・RR・ポジションサイズ・エントリー時間・保有時間)×6演算子で、ほとんどのトレードルールを表現できます。
4. 詰まったポイントと解決法
Next.js 16 の破壊的変更
cookies()が非同期APIになったため、await cookies()が必要。また setAll の型に明示的なアノテーションが必要になりました。
Claude API のレスポンス型エラー
message.content[0]は TextBlock | ImageBlock のユニオン型のため、.text に直接アクセスするとTypeScriptエラーになります。
typescript
// NG
const text = message.content[0].text
// OK
const block = message.content[0]
const text = block.type === 'text' ? block.text : ''
まとめ
Next.js 16 App Router + Supabase + Claude APIの組み合わせは個人開発SaaSに非常に適したスタックです。RSCでサーバー側集計、RLSでデータ分離、Claude APIで高品質な日本語分析——この3つを組み合わせることで、数ヶ月で実用的なAI SaaSを一人で構築できました。
TradeJournal は tradejournal.company で公開中。フリープランで月30件まで無料で試せます。フィードバックお待ちしています。
あなたのトレードデータを分析してみませんか?
TradeJournalなら、感情別・セッション別・pips帯別の勝率を自動で可視化。月30件まで無料。
TradeJournal を無料で試す
月30件まで無料。クレジットカード不要。
無料で始める →