bitbank-lab-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +388 -0
- package/assets/lightweight-charts.standalone.js +7 -0
- package/bin/bitbank-lab-mcp.js +20 -0
- package/lib/cache.ts +70 -0
- package/lib/candle-utils.ts +48 -0
- package/lib/candle-validate.ts +434 -0
- package/lib/conversions.ts +25 -0
- package/lib/datetime.ts +157 -0
- package/lib/depth-analysis.ts +51 -0
- package/lib/error.ts +15 -0
- package/lib/formatter.ts +296 -0
- package/lib/get-depth.ts +111 -0
- package/lib/http.ts +132 -0
- package/lib/indicator-config.ts +39 -0
- package/lib/indicator_buffer.ts +41 -0
- package/lib/indicators.ts +579 -0
- package/lib/logger.ts +120 -0
- package/lib/ma-snapshot-utils.ts +277 -0
- package/lib/math.ts +89 -0
- package/lib/pattern-diagrams.ts +562 -0
- package/lib/result.ts +104 -0
- package/lib/validate.ts +154 -0
- package/lib/volatility.ts +132 -0
- package/package.json +79 -0
- package/src/env.ts +4 -0
- package/src/handlers/analyzeCandlePatternsHandler.ts +383 -0
- package/src/handlers/analyzeFibonacciHandler.ts +54 -0
- package/src/handlers/analyzeIndicatorsHandler.ts +682 -0
- package/src/handlers/analyzeMarketSignalHandler.ts +272 -0
- package/src/handlers/analyzeMyPortfolioHandler.ts +800 -0
- package/src/handlers/detectPatternsHandler.ts +77 -0
- package/src/handlers/detectPatternsViewsHandler.ts +518 -0
- package/src/handlers/getTickersJpyHandler.ts +145 -0
- package/src/handlers/getVolatilityMetricsHandler.ts +234 -0
- package/src/handlers/portfolio/calc.ts +549 -0
- package/src/handlers/portfolio/fetch.ts +318 -0
- package/src/handlers/portfolio/types.ts +170 -0
- package/src/handlers/renderChartSvgHandler.ts +69 -0
- package/src/handlers/runBacktestHandler.ts +70 -0
- package/src/http.ts +107 -0
- package/src/private/auth.ts +104 -0
- package/src/private/client.ts +298 -0
- package/src/private/config.ts +25 -0
- package/src/private/confirmation.ts +185 -0
- package/src/private/schemas.ts +866 -0
- package/src/prompts.ts +2296 -0
- package/src/resources/app-resources.ts +79 -0
- package/src/schema/analysis.ts +942 -0
- package/src/schema/backtest.ts +100 -0
- package/src/schema/base.ts +88 -0
- package/src/schema/candle-validate.ts +135 -0
- package/src/schema/chart.ts +399 -0
- package/src/schema/index.ts +11 -0
- package/src/schema/indicators.ts +125 -0
- package/src/schema/market-data.ts +298 -0
- package/src/schema/patterns.ts +382 -0
- package/src/schema/types.ts +97 -0
- package/src/schemas.d.ts +37 -0
- package/src/schemas.ts +7 -0
- package/src/server.ts +405 -0
- package/src/tool-definition.ts +44 -0
- package/src/tool-registry.ts +174 -0
- package/src/types/express-shim.d.ts +9 -0
- package/src/types/schemas.generated.d.ts +23 -0
- package/tools/analyze_bb_snapshot.ts +385 -0
- package/tools/analyze_candle_patterns.ts +810 -0
- package/tools/analyze_currency_strength.ts +273 -0
- package/tools/analyze_ema_snapshot.ts +183 -0
- package/tools/analyze_fibonacci.ts +530 -0
- package/tools/analyze_ichimoku_snapshot.ts +606 -0
- package/tools/analyze_indicators.ts +691 -0
- package/tools/analyze_market_signal.ts +665 -0
- package/tools/analyze_mtf_fibonacci.ts +273 -0
- package/tools/analyze_mtf_sma.ts +175 -0
- package/tools/analyze_sma_snapshot.ts +146 -0
- package/tools/analyze_stoch_snapshot.ts +276 -0
- package/tools/analyze_support_resistance.ts +817 -0
- package/tools/analyze_volume_profile.ts +546 -0
- package/tools/chart/ichimoku-cloud.ts +113 -0
- package/tools/chart/render-depth.ts +139 -0
- package/tools/chart/render-sub-panels.ts +208 -0
- package/tools/chart/svg-utils.ts +102 -0
- package/tools/detect_macd_cross.ts +691 -0
- package/tools/detect_patterns.ts +424 -0
- package/tools/detect_whale_events.ts +181 -0
- package/tools/get_candles.ts +487 -0
- package/tools/get_flow_metrics.ts +596 -0
- package/tools/get_orderbook.ts +540 -0
- package/tools/get_ticker.ts +132 -0
- package/tools/get_tickers_jpy.ts +240 -0
- package/tools/get_transactions.ts +209 -0
- package/tools/get_volatility_metrics.ts +302 -0
- package/tools/patterns/aftermath.ts +212 -0
- package/tools/patterns/config.ts +151 -0
- package/tools/patterns/detect_doubles.ts +650 -0
- package/tools/patterns/detect_hs.ts +635 -0
- package/tools/patterns/detect_pennants.ts +373 -0
- package/tools/patterns/detect_triangles.ts +820 -0
- package/tools/patterns/detect_triples.ts +633 -0
- package/tools/patterns/detect_wedges.ts +1072 -0
- package/tools/patterns/helpers.ts +517 -0
- package/tools/patterns/index.ts +40 -0
- package/tools/patterns/regression.ts +153 -0
- package/tools/patterns/smoothing.ts +168 -0
- package/tools/patterns/swing.ts +91 -0
- package/tools/patterns/types.ts +193 -0
- package/tools/prepare_chart_data.ts +294 -0
- package/tools/prepare_depth_data.ts +189 -0
- package/tools/private/analyze_my_portfolio.ts +21 -0
- package/tools/private/cancel_order.ts +127 -0
- package/tools/private/cancel_orders.ts +121 -0
- package/tools/private/create_order.ts +236 -0
- package/tools/private/get_margin_positions.ts +134 -0
- package/tools/private/get_margin_status.ts +155 -0
- package/tools/private/get_margin_trade_history.ts +156 -0
- package/tools/private/get_my_assets.ts +207 -0
- package/tools/private/get_my_deposit_withdrawal.ts +500 -0
- package/tools/private/get_my_orders.ts +157 -0
- package/tools/private/get_my_trade_history.ts +229 -0
- package/tools/private/get_order.ts +95 -0
- package/tools/private/get_orders_info.ts +90 -0
- package/tools/private/preview_cancel_order.ts +172 -0
- package/tools/private/preview_cancel_orders.ts +137 -0
- package/tools/private/preview_order.ts +292 -0
- package/tools/render_candle_pattern_diagram.ts +389 -0
- package/tools/render_chart_svg.ts +799 -0
- package/tools/render_depth_svg.ts +274 -0
- package/tools/trading_process/index.ts +7 -0
- package/tools/trading_process/lib/backtest_engine.ts +252 -0
- package/tools/trading_process/lib/equity.ts +131 -0
- package/tools/trading_process/lib/fetch_candles.ts +181 -0
- package/tools/trading_process/lib/sma.ts +62 -0
- package/tools/trading_process/lib/strategies/bb_breakout.ts +141 -0
- package/tools/trading_process/lib/strategies/index.ts +52 -0
- package/tools/trading_process/lib/strategies/macd_cross.ts +256 -0
- package/tools/trading_process/lib/strategies/rsi.ts +133 -0
- package/tools/trading_process/lib/strategies/sma_cross.ts +214 -0
- package/tools/trading_process/lib/strategies/types.ts +118 -0
- package/tools/trading_process/lib/svg_to_png.ts +64 -0
- package/tools/trading_process/render_backtest_chart_generic.ts +729 -0
- package/tools/trading_process/run_backtest.ts +243 -0
- package/tools/trading_process/types.ts +85 -0
- package/tools/validate_candle_data.ts +260 -0
- package/tsconfig.json +17 -0
- package/ui/cancel-confirm/dist/cancel-confirm.html +99 -0
- package/ui/order-confirm/dist/order-confirm.html +99 -0
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import { formatSummary } from '../lib/formatter.js';
|
|
3
|
+
import { avg } from '../lib/math.js';
|
|
4
|
+
import { fail, failFromError, failFromValidation, ok } from '../lib/result.js';
|
|
5
|
+
import { createMeta, ensurePair } from '../lib/validate.js';
|
|
6
|
+
import {
|
|
7
|
+
type AnalyzeIchimokuSnapshotDataSchemaOut,
|
|
8
|
+
AnalyzeIchimokuSnapshotInputSchema,
|
|
9
|
+
type AnalyzeIchimokuSnapshotMetaSchemaOut,
|
|
10
|
+
AnalyzeIchimokuSnapshotOutputSchema,
|
|
11
|
+
} from '../src/schemas.js';
|
|
12
|
+
import type { ToolDefinition } from '../src/tool-definition.js';
|
|
13
|
+
import analyzeIndicators from './analyze_indicators.js';
|
|
14
|
+
|
|
15
|
+
export interface BuildIchimokuSnapshotTextInput {
|
|
16
|
+
pair: string;
|
|
17
|
+
type: string;
|
|
18
|
+
close: number | null;
|
|
19
|
+
pricePosition: 'above_cloud' | 'in_cloud' | 'below_cloud' | 'unknown';
|
|
20
|
+
tenkan: number | null;
|
|
21
|
+
kijun: number | null;
|
|
22
|
+
tenkanKijun: 'bullish' | 'bearish' | 'neutral' | 'unknown';
|
|
23
|
+
tkDist: number | null;
|
|
24
|
+
cloudTop: number | null;
|
|
25
|
+
cloudBottom: number | null;
|
|
26
|
+
direction: string | null;
|
|
27
|
+
thickness: number | null;
|
|
28
|
+
thicknessPct: number | null;
|
|
29
|
+
strength: string | null;
|
|
30
|
+
futureCloudTop: number | null;
|
|
31
|
+
futureCloudBottom: number | null;
|
|
32
|
+
chikouSpan: { position: 'above' | 'below' | null; distance: number | null; clearance: number | null };
|
|
33
|
+
sanpuku: {
|
|
34
|
+
kouten: boolean;
|
|
35
|
+
gyakuten: boolean;
|
|
36
|
+
conditions: { priceAboveCloud: boolean; tenkanAboveKijun: boolean; chikouAbovePrice: boolean };
|
|
37
|
+
};
|
|
38
|
+
recentCrosses: Array<{ type: 'golden_cross' | 'death_cross'; barsAgo: number; description: string }>;
|
|
39
|
+
kumoTwist: { detected: boolean; barsAgo?: number; direction?: 'bullish' | 'bearish' };
|
|
40
|
+
overallSignal: string;
|
|
41
|
+
overallConfidence: string;
|
|
42
|
+
scenarios: {
|
|
43
|
+
scenarios: {
|
|
44
|
+
bullish: { condition: string; target: number; probability: string };
|
|
45
|
+
bearish: { condition: string; target: number; probability: string };
|
|
46
|
+
};
|
|
47
|
+
keyLevels: { support: number[]; resistance: number[] };
|
|
48
|
+
watchPoints: string[];
|
|
49
|
+
};
|
|
50
|
+
trend: {
|
|
51
|
+
trendStrength: { shortTerm: number; mediumTerm: number };
|
|
52
|
+
momentum: string;
|
|
53
|
+
};
|
|
54
|
+
cloudHistory: Array<{ barsAgo: number; position: string }>;
|
|
55
|
+
// Raw numeric data for 数値データ section
|
|
56
|
+
currentSpanA: number | null;
|
|
57
|
+
currentSpanB: number | null;
|
|
58
|
+
futureSpanA: number | null;
|
|
59
|
+
futureSpanB: number | null;
|
|
60
|
+
tkDistPct: number | null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** テキスト組み立て(一目均衡表スナップショット表示)— テスト可能な純粋関数 */
|
|
64
|
+
export function buildIchimokuSnapshotText(input: BuildIchimokuSnapshotTextInput): string {
|
|
65
|
+
const {
|
|
66
|
+
pair,
|
|
67
|
+
type,
|
|
68
|
+
close,
|
|
69
|
+
pricePosition,
|
|
70
|
+
tenkan,
|
|
71
|
+
kijun,
|
|
72
|
+
tenkanKijun,
|
|
73
|
+
tkDist,
|
|
74
|
+
cloudTop,
|
|
75
|
+
cloudBottom,
|
|
76
|
+
direction,
|
|
77
|
+
thickness,
|
|
78
|
+
thicknessPct,
|
|
79
|
+
strength,
|
|
80
|
+
futureCloudTop,
|
|
81
|
+
futureCloudBottom,
|
|
82
|
+
chikouSpan,
|
|
83
|
+
sanpuku,
|
|
84
|
+
recentCrosses,
|
|
85
|
+
kumoTwist,
|
|
86
|
+
overallSignal,
|
|
87
|
+
overallConfidence,
|
|
88
|
+
scenarios,
|
|
89
|
+
trend,
|
|
90
|
+
cloudHistory,
|
|
91
|
+
currentSpanA,
|
|
92
|
+
currentSpanB,
|
|
93
|
+
futureSpanA,
|
|
94
|
+
futureSpanB,
|
|
95
|
+
tkDistPct,
|
|
96
|
+
} = input;
|
|
97
|
+
const lines: string[] = [];
|
|
98
|
+
lines.push(`${String(pair).toUpperCase()} ${String(type)} 一目均衡表分析`);
|
|
99
|
+
if (close != null) lines.push(`価格: ${Number(close).toLocaleString('ja-JP')}円`);
|
|
100
|
+
lines.push('');
|
|
101
|
+
lines.push('【基本配置】');
|
|
102
|
+
if (pricePosition !== 'unknown') {
|
|
103
|
+
const clr =
|
|
104
|
+
close != null && cloudTop != null && cloudBottom != null
|
|
105
|
+
? pricePosition === 'above_cloud'
|
|
106
|
+
? close - cloudTop
|
|
107
|
+
: pricePosition === 'below_cloud'
|
|
108
|
+
? cloudBottom - close
|
|
109
|
+
: 0
|
|
110
|
+
: null;
|
|
111
|
+
const clrPct = clr != null && close != null && close !== 0 ? Number(((clr / close) * 100).toFixed(2)) : null;
|
|
112
|
+
lines.push(
|
|
113
|
+
`・価格位置: ${pricePosition.replace('_', ' ')}${clr != null ? ` (クリアランス: ${clr >= 0 ? '+' : ''}${clr.toLocaleString('ja-JP')}円${clrPct != null ? `, ${clrPct}%` : ''})` : ''}`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
if (tenkan != null)
|
|
117
|
+
lines.push(
|
|
118
|
+
`・転換線: ${Number(tenkan).toLocaleString('ja-JP')}円${close != null ? ` (価格比 ${Number(((tenkan - close) / close) * 100).toFixed(1)}%)` : ''}`,
|
|
119
|
+
);
|
|
120
|
+
if (kijun != null) lines.push(`・基準線: ${Number(kijun).toLocaleString('ja-JP')}円`);
|
|
121
|
+
if (tenkan != null && kijun != null)
|
|
122
|
+
lines.push(
|
|
123
|
+
`・転換線と基準線: ${tenkanKijun === 'bullish' ? '強気' : tenkanKijun === 'bearish' ? '弱気' : '中立'}配置${tkDist != null ? ` (転換線が${Math.abs(tkDist).toLocaleString('ja-JP')}円${tenkan > (kijun as number) ? '上' : '下'})` : ''}`,
|
|
124
|
+
);
|
|
125
|
+
lines.push('');
|
|
126
|
+
lines.push('【雲の状態(今日の雲)】');
|
|
127
|
+
lines.push(`・雲の方向: ${direction}`);
|
|
128
|
+
if (thickness != null)
|
|
129
|
+
lines.push(
|
|
130
|
+
`・雲の厚み: ${thickness.toLocaleString('ja-JP')}円${thicknessPct != null ? ` (${thicknessPct}%)` : ''} - ${strength ?? 'n/a'}の強度`,
|
|
131
|
+
);
|
|
132
|
+
if (cloudTop != null && cloudBottom != null)
|
|
133
|
+
lines.push(
|
|
134
|
+
`・雲の範囲: ${Number(cloudBottom).toLocaleString('ja-JP')}円 ~ ${Number(cloudTop).toLocaleString('ja-JP')}円`,
|
|
135
|
+
);
|
|
136
|
+
// 26日後の雲(将来の参考情報)
|
|
137
|
+
if (futureCloudTop != null && futureCloudBottom != null) {
|
|
138
|
+
lines.push('');
|
|
139
|
+
lines.push('【26日後の雲(先行スパン)】');
|
|
140
|
+
lines.push(
|
|
141
|
+
`・雲の範囲: ${Number(futureCloudBottom).toLocaleString('ja-JP')}円 ~ ${Number(futureCloudTop).toLocaleString('ja-JP')}円`,
|
|
142
|
+
);
|
|
143
|
+
if (close != null) {
|
|
144
|
+
const futurePos = close > futureCloudTop ? '雲の上' : close < futureCloudBottom ? '雲の下' : '雲の中';
|
|
145
|
+
lines.push(`・現在価格との比較: ${futurePos}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
lines.push('');
|
|
149
|
+
lines.push('【遅行スパン】');
|
|
150
|
+
if (chikouSpan.position)
|
|
151
|
+
lines.push(
|
|
152
|
+
`・位置: 26本前の価格より${chikouSpan.position === 'above' ? '上' : '下'}${chikouSpan.distance != null ? ` (${chikouSpan.distance >= 0 ? '+' : ''}${chikouSpan.distance.toLocaleString('ja-JP')}円)` : ''}`,
|
|
153
|
+
);
|
|
154
|
+
lines.push('');
|
|
155
|
+
lines.push('【シグナル分析】');
|
|
156
|
+
const achieved = [
|
|
157
|
+
sanpuku.conditions.priceAboveCloud,
|
|
158
|
+
sanpuku.conditions.tenkanAboveKijun,
|
|
159
|
+
sanpuku.conditions.chikouAbovePrice,
|
|
160
|
+
].filter(Boolean).length;
|
|
161
|
+
lines.push(`・三役判定: ${sanpuku.kouten ? '好転' : sanpuku.gyakuten ? '逆転' : `好転条件 ${achieved}/3 達成`}`);
|
|
162
|
+
lines.push(` ${sanpuku.conditions.priceAboveCloud ? '✓' : '✗'} 価格が雲の上`);
|
|
163
|
+
lines.push(` ${sanpuku.conditions.tenkanAboveKijun ? '✓' : '✗'} 転換線が基準線の上`);
|
|
164
|
+
lines.push(` ${sanpuku.conditions.chikouAbovePrice ? '✓' : '✗'} 遅行スパンが好転中`);
|
|
165
|
+
if (recentCrosses.length) lines.push('・直近のイベント:');
|
|
166
|
+
for (const ev of recentCrosses)
|
|
167
|
+
lines.push(` - ${ev.barsAgo}本前: ${ev.type === 'golden_cross' ? 'ゴールデンクロス' : 'デッドクロス'}`);
|
|
168
|
+
if (kumoTwist.detected)
|
|
169
|
+
lines.push(
|
|
170
|
+
`・雲のねじれ: ${kumoTwist.barsAgo}本前に${kumoTwist.direction === 'bullish' ? '強気' : '弱気'}のねじれ発生`,
|
|
171
|
+
);
|
|
172
|
+
lines.push(`・総合評価: ${overallSignal.replace('_', ' ')} (信頼度: ${overallConfidence})`);
|
|
173
|
+
|
|
174
|
+
// Phase 3 content additions
|
|
175
|
+
lines.push('');
|
|
176
|
+
lines.push('【今後の注目ポイント】');
|
|
177
|
+
if (scenarios?.scenarios) {
|
|
178
|
+
const bull = scenarios.scenarios.bullish;
|
|
179
|
+
const bear = scenarios.scenarios.bearish;
|
|
180
|
+
if (bull)
|
|
181
|
+
lines.push(
|
|
182
|
+
`・上昇シナリオ: ${bull.condition} → ${Number(bull.target).toLocaleString('ja-JP')}円 (可能性: ${bull.probability})`,
|
|
183
|
+
);
|
|
184
|
+
if (bear)
|
|
185
|
+
lines.push(
|
|
186
|
+
`・下落シナリオ: ${bear.condition} → ${Number(bear.target).toLocaleString('ja-JP')}円 (可能性: ${bear.probability})`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
lines.push('');
|
|
190
|
+
lines.push('・重要価格:');
|
|
191
|
+
if (scenarios?.keyLevels?.support?.length) {
|
|
192
|
+
lines.push(
|
|
193
|
+
` - サポート: ${scenarios.keyLevels.support.map((x: number) => `${Number(x).toLocaleString('ja-JP')}円`).join('、')}`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
if (scenarios?.keyLevels?.resistance?.length) {
|
|
197
|
+
lines.push(
|
|
198
|
+
` - レジスタンス: ${scenarios.keyLevels.resistance.map((x: number) => `${Number(x).toLocaleString('ja-JP')}円`).join('、')}`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
if (Array.isArray(scenarios?.watchPoints)) {
|
|
202
|
+
lines.push('');
|
|
203
|
+
lines.push('・ウォッチリスト:');
|
|
204
|
+
for (const wp of scenarios.watchPoints) lines.push(` - ${wp}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Phase 4 trend content (optional)
|
|
208
|
+
if (trend) {
|
|
209
|
+
lines.push('');
|
|
210
|
+
lines.push('【トレンド分析】');
|
|
211
|
+
lines.push(`・短期強度: ${trend.trendStrength.shortTerm}`);
|
|
212
|
+
lines.push(`・中期強度: ${trend.trendStrength.mediumTerm}`);
|
|
213
|
+
const m = trend.momentum;
|
|
214
|
+
lines.push(`・モメンタム: ${m === 'accelerating' ? '加速中' : m === 'decelerating' ? '減速中' : '安定'}`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// structuredContent.data の数値詳細もテキストに含める(LLM が読めない対策)
|
|
218
|
+
lines.push('');
|
|
219
|
+
lines.push('【数値データ】');
|
|
220
|
+
lines.push(`転換線: ${tenkan} / 基準線: ${kijun}`);
|
|
221
|
+
lines.push(
|
|
222
|
+
`雲(今日): spanA=${currentSpanA} spanB=${currentSpanB} top=${cloudTop} bottom=${cloudBottom} 厚み=${thickness}`,
|
|
223
|
+
);
|
|
224
|
+
lines.push(`雲(26日後): spanA=${futureSpanA} spanB=${futureSpanB} top=${futureCloudTop} bottom=${futureCloudBottom}`);
|
|
225
|
+
lines.push(`転換-基準: 距離=${tkDist}円 (${tkDistPct}%)`);
|
|
226
|
+
if (chikouSpan.distance != null) lines.push(`遅行スパン距離: ${chikouSpan.distance}円`);
|
|
227
|
+
if (cloudHistory.length > 0) {
|
|
228
|
+
lines.push(
|
|
229
|
+
`雲の履歴(直近${cloudHistory.length}本): ${cloudHistory.map((h) => `${h.barsAgo}=${h.position}`).join(' ')}`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
lines.join('\n') +
|
|
235
|
+
`\n\n---\n📌 含まれるもの: 一目均衡表の全要素(転換線・基準線・雲・遅行スパン)、三役判定、シグナル` +
|
|
236
|
+
`\n📌 含まれないもの: 他のテクニカル指標(RSI・MACD・BB)、出来高フロー、板情報` +
|
|
237
|
+
`\n📌 補完ツール: analyze_indicators(他指標), analyze_bb_snapshot(BB), get_flow_metrics(出来高), get_orderbook(板情報)`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export default async function analyzeIchimokuSnapshot(
|
|
242
|
+
pair: string = 'btc_jpy',
|
|
243
|
+
type: string = '1day',
|
|
244
|
+
limit: number = 120,
|
|
245
|
+
lookback: number = 10,
|
|
246
|
+
) {
|
|
247
|
+
const chk = ensurePair(pair);
|
|
248
|
+
if (!chk.ok) return failFromValidation(chk, AnalyzeIchimokuSnapshotOutputSchema);
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
const indRes = await analyzeIndicators(chk.pair, type, Math.max(100, limit));
|
|
252
|
+
if (!indRes?.ok)
|
|
253
|
+
return AnalyzeIchimokuSnapshotOutputSchema.parse(
|
|
254
|
+
fail(indRes?.summary || 'indicators failed', (indRes?.meta as { errorType?: string })?.errorType || 'internal'),
|
|
255
|
+
) as ReturnType<typeof fail>;
|
|
256
|
+
|
|
257
|
+
const latest = indRes.data.indicators;
|
|
258
|
+
const close = indRes.data.normalized.at(-1)?.close ?? null;
|
|
259
|
+
const tenkan = latest.ICHIMOKU_conversion ?? null;
|
|
260
|
+
const kijun = latest.ICHIMOKU_base ?? null;
|
|
261
|
+
// 🚨 CRITICAL: 先行スパンの理解
|
|
262
|
+
// - spanA/spanB(latest.ICHIMOKU_spanA/B): 「今日計算された先行スパン」→ 26日後に表示される雲
|
|
263
|
+
// - 「今日の雲」を判定するには、26本前に計算された先行スパンの値を使う必要がある
|
|
264
|
+
const futureSpanA = latest.ICHIMOKU_spanA ?? null; // 26日後の雲用
|
|
265
|
+
const futureSpanB = latest.ICHIMOKU_spanB ?? null; // 26日後の雲用
|
|
266
|
+
|
|
267
|
+
// 時系列データから「今日の雲」の位置を取得
|
|
268
|
+
// ichi_series.spanA/spanB は時系列データで、最新の値が「今日計算された値」
|
|
269
|
+
// 「今日の雲」は26本前に計算された値なので、配列の末尾から26本前を参照
|
|
270
|
+
const series = indRes.data.indicators.ichi_series;
|
|
271
|
+
let currentSpanA: number | null = null;
|
|
272
|
+
let currentSpanB: number | null = null;
|
|
273
|
+
if (series && Array.isArray(series.spanA) && Array.isArray(series.spanB)) {
|
|
274
|
+
// 配列の長さが26以上あれば、26本前(今日の雲)の値を取得
|
|
275
|
+
// 先行スパンは26期間先にプロットされるため、今日の雲 = 26期間前に計算された値
|
|
276
|
+
const len = Math.min(series.spanA.length, series.spanB.length);
|
|
277
|
+
if (len >= 26) {
|
|
278
|
+
currentSpanA = series.spanA[len - 26] ?? null;
|
|
279
|
+
currentSpanB = series.spanB[len - 26] ?? null;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const chikou = Array.isArray(indRes?.data?.indicators?.ichi_series?.chikou)
|
|
284
|
+
? (indRes.data.indicators.ichi_series.chikou.at(-1) ?? null)
|
|
285
|
+
: null;
|
|
286
|
+
|
|
287
|
+
// 🚨 「今日の雲」(現在価格と比較する用)
|
|
288
|
+
const cloudTop = currentSpanA != null && currentSpanB != null ? Math.max(currentSpanA, currentSpanB) : null;
|
|
289
|
+
const cloudBottom = currentSpanA != null && currentSpanB != null ? Math.min(currentSpanA, currentSpanB) : null;
|
|
290
|
+
|
|
291
|
+
// 「26日後の雲」(将来の参考情報)
|
|
292
|
+
const futureCloudTop = futureSpanA != null && futureSpanB != null ? Math.max(futureSpanA, futureSpanB) : null;
|
|
293
|
+
const futureCloudBottom = futureSpanA != null && futureSpanB != null ? Math.min(futureSpanA, futureSpanB) : null;
|
|
294
|
+
|
|
295
|
+
// Assessments without visual claims - 「今日の雲」を使って判定
|
|
296
|
+
let pricePosition: 'above_cloud' | 'in_cloud' | 'below_cloud' | 'unknown' = 'unknown';
|
|
297
|
+
if (close != null && cloudTop != null && cloudBottom != null) {
|
|
298
|
+
if (close > cloudTop) pricePosition = 'above_cloud';
|
|
299
|
+
else if (close < cloudBottom) pricePosition = 'below_cloud';
|
|
300
|
+
else pricePosition = 'in_cloud';
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let tenkanKijun: 'bullish' | 'bearish' | 'neutral' | 'unknown' = 'unknown';
|
|
304
|
+
if (tenkan != null && kijun != null) {
|
|
305
|
+
if (tenkan > kijun) tenkanKijun = 'bullish';
|
|
306
|
+
else if (tenkan < kijun) tenkanKijun = 'bearish';
|
|
307
|
+
else tenkanKijun = 'neutral';
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Slope of cloud via last two spanA/spanB points when available
|
|
311
|
+
let cloudSlope: 'rising' | 'falling' | 'flat' | 'unknown' = 'unknown';
|
|
312
|
+
// series は上で既に定義済み
|
|
313
|
+
if (series && Array.isArray(series.spanA) && Array.isArray(series.spanB)) {
|
|
314
|
+
const a1 = series.spanA.at(-1),
|
|
315
|
+
a2 = series.spanA.at(-2);
|
|
316
|
+
const b1 = series.spanB.at(-1),
|
|
317
|
+
b2 = series.spanB.at(-2);
|
|
318
|
+
if (a1 != null && a2 != null && b1 != null && b2 != null) {
|
|
319
|
+
const d = (a1 as number) - (a2 as number) + ((b1 as number) - (b2 as number));
|
|
320
|
+
if (Math.abs(d) < 1e-6) cloudSlope = 'flat';
|
|
321
|
+
else cloudSlope = d > 0 ? 'rising' : 'falling';
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Cloud metrics - 「今日の雲」の厚みを使用
|
|
326
|
+
const thickness =
|
|
327
|
+
currentSpanA != null && currentSpanB != null
|
|
328
|
+
? Math.abs((currentSpanA as number) - (currentSpanB as number))
|
|
329
|
+
: null;
|
|
330
|
+
const thicknessPct =
|
|
331
|
+
thickness != null && close != null && close !== 0 ? Number(((thickness / close) * 100).toFixed(2)) : null;
|
|
332
|
+
const direction =
|
|
333
|
+
cloudSlope === 'rising' ? 'rising' : cloudSlope === 'falling' ? 'falling' : cloudSlope === 'flat' ? 'flat' : null;
|
|
334
|
+
const strength =
|
|
335
|
+
thicknessPct == null ? null : thicknessPct >= 2 ? 'strong' : thicknessPct >= 0.8 ? 'moderate' : 'weak';
|
|
336
|
+
|
|
337
|
+
// Tenkan-Kijun detail
|
|
338
|
+
const tkRel = tenkan != null && kijun != null ? (tenkan > kijun ? 'bullish' : 'bearish') : null;
|
|
339
|
+
const tkDist = tenkan != null && kijun != null ? Number(((tenkan as number) - (kijun as number)).toFixed(0)) : null;
|
|
340
|
+
const tkDistPct =
|
|
341
|
+
tkDist != null && close != null && close !== 0 ? Number(((tkDist / close) * 100).toFixed(2)) : null;
|
|
342
|
+
|
|
343
|
+
// Chikou span detail: compare to price 26 bars ago
|
|
344
|
+
let chikouSpan: { position: 'above' | 'below' | null; distance: number | null; clearance: number | null } = {
|
|
345
|
+
position: null,
|
|
346
|
+
distance: null,
|
|
347
|
+
clearance: null,
|
|
348
|
+
};
|
|
349
|
+
const candles = indRes.data.normalized as Array<{ close: number }>;
|
|
350
|
+
if (Array.isArray(candles) && candles.length >= 27 && close != null) {
|
|
351
|
+
const ref = candles.at(-27)?.close ?? null;
|
|
352
|
+
if (ref != null) {
|
|
353
|
+
const dist = Number((close - ref).toFixed(0));
|
|
354
|
+
chikouSpan = { position: close >= ref ? 'above' : 'below', distance: dist, clearance: dist };
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const tags: string[] = [];
|
|
359
|
+
if (pricePosition === 'above_cloud') tags.push('price_above_cloud');
|
|
360
|
+
if (pricePosition === 'below_cloud') tags.push('price_below_cloud');
|
|
361
|
+
if (tenkanKijun === 'bullish') tags.push('tk_bullish');
|
|
362
|
+
if (tenkanKijun === 'bearish') tags.push('tk_bearish');
|
|
363
|
+
if (cloudSlope === 'rising') tags.push('cloud_rising');
|
|
364
|
+
if (cloudSlope === 'falling') tags.push('cloud_falling');
|
|
365
|
+
|
|
366
|
+
const _summary = formatSummary({
|
|
367
|
+
pair: chk.pair,
|
|
368
|
+
latest: close ?? undefined,
|
|
369
|
+
extra: `pos=${pricePosition} tk=${tenkanKijun} cloud=${cloudSlope}`,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Signals (Phase 2)
|
|
373
|
+
// 三役: 価格>雲上、転換>基準、遅行>当時価格(近似: 現在価格>26本前)
|
|
374
|
+
const sanpukuConditions = {
|
|
375
|
+
priceAboveCloud: pricePosition === 'above_cloud',
|
|
376
|
+
tenkanAboveKijun: tenkan != null && kijun != null ? tenkan > (kijun as number) : false,
|
|
377
|
+
chikouAbovePrice:
|
|
378
|
+
Array.isArray(candles) && candles.length >= 27 && close != null
|
|
379
|
+
? close > (candles.at(-27)?.close ?? Infinity)
|
|
380
|
+
: false,
|
|
381
|
+
};
|
|
382
|
+
const sanpuku = {
|
|
383
|
+
kouten:
|
|
384
|
+
sanpukuConditions.priceAboveCloud && sanpukuConditions.tenkanAboveKijun && sanpukuConditions.chikouAbovePrice,
|
|
385
|
+
gyakuten:
|
|
386
|
+
pricePosition === 'below_cloud' &&
|
|
387
|
+
(tenkan != null && kijun != null ? tenkan < (kijun as number) : false) &&
|
|
388
|
+
(Array.isArray(candles) && candles.length >= 27 && close != null
|
|
389
|
+
? close < (candles.at(-27)?.close ?? -Infinity)
|
|
390
|
+
: false),
|
|
391
|
+
conditions: sanpukuConditions,
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// 直近クロス検出(転換線と基準線のクロスを簡易に)
|
|
395
|
+
const recentCrosses: Array<{ type: 'golden_cross' | 'death_cross'; barsAgo: number; description: string }> = [];
|
|
396
|
+
const spanTenkan = indRes?.data?.indicators?.ichi_series?.tenkan as number[] | undefined;
|
|
397
|
+
const spanKijun = indRes?.data?.indicators?.ichi_series?.kijun as number[] | undefined;
|
|
398
|
+
if (Array.isArray(spanTenkan) && Array.isArray(spanKijun) && spanTenkan.length >= 5 && spanKijun.length >= 5) {
|
|
399
|
+
const L = Math.min(spanTenkan.length, spanKijun.length);
|
|
400
|
+
for (let i = 1; i < Math.min(15, L - 1); i++) {
|
|
401
|
+
const a1 = spanTenkan[L - 1 - (i - 1)] - spanKijun[L - 1 - (i - 1)];
|
|
402
|
+
const a2 = spanTenkan[L - 1 - i] - spanKijun[L - 1 - i];
|
|
403
|
+
if (a1 <= 0 && a2 > 0)
|
|
404
|
+
recentCrosses.push({ type: 'golden_cross', barsAgo: i, description: `${i}本前: 転換線が基準線を上抜け` });
|
|
405
|
+
if (a1 >= 0 && a2 < 0)
|
|
406
|
+
recentCrosses.push({ type: 'death_cross', barsAgo: i, description: `${i}本前: 転換線が基準線を下抜け` });
|
|
407
|
+
if (recentCrosses.length >= 3) break;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// 雲のねじれ(spanAとspanBの順位が入れ替わる)
|
|
412
|
+
let kumoTwist = {
|
|
413
|
+
detected: false as boolean,
|
|
414
|
+
barsAgo: undefined as number | undefined,
|
|
415
|
+
direction: undefined as 'bullish' | 'bearish' | undefined,
|
|
416
|
+
};
|
|
417
|
+
if (Array.isArray(series?.spanA) && Array.isArray(series?.spanB)) {
|
|
418
|
+
const L = Math.min(series.spanA.length, series.spanB.length);
|
|
419
|
+
for (let i = 1; i < Math.min(30, L - 1); i++) {
|
|
420
|
+
const aPrev = series.spanA[L - 1 - i];
|
|
421
|
+
const bPrev = series.spanB[L - 1 - i];
|
|
422
|
+
const aNow = series.spanA[L - 1 - (i - 1)];
|
|
423
|
+
const bNow = series.spanB[L - 1 - (i - 1)];
|
|
424
|
+
if (aPrev != null && bPrev != null && aNow != null && bNow != null) {
|
|
425
|
+
if (aPrev <= bPrev && aNow > bNow) {
|
|
426
|
+
kumoTwist = { detected: true, barsAgo: i, direction: 'bullish' };
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
if (aPrev >= bPrev && aNow < bNow) {
|
|
430
|
+
kumoTwist = { detected: true, barsAgo: i, direction: 'bearish' };
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// 総合評価(簡易)
|
|
438
|
+
const bullishScore = Number(
|
|
439
|
+
(sanpuku.kouten ? 1 : 0) +
|
|
440
|
+
(pricePosition === 'above_cloud' ? 0.5 : 0) +
|
|
441
|
+
(tenkanKijun === 'bullish' ? 0.5 : 0) +
|
|
442
|
+
(cloudSlope === 'rising' ? 0.3 : 0),
|
|
443
|
+
).toFixed(2);
|
|
444
|
+
let overallSignal: 'strong_bullish' | 'bullish' | 'neutral' | 'bearish' | 'strong_bearish' = 'neutral';
|
|
445
|
+
if (Number(bullishScore) >= 1.5) overallSignal = 'strong_bullish';
|
|
446
|
+
else if (Number(bullishScore) >= 0.8) overallSignal = 'bullish';
|
|
447
|
+
else if (pricePosition === 'below_cloud' && tenkanKijun === 'bearish' && cloudSlope === 'falling')
|
|
448
|
+
overallSignal = 'strong_bearish';
|
|
449
|
+
else if (pricePosition === 'below_cloud' && tenkanKijun === 'bearish') overallSignal = 'bearish';
|
|
450
|
+
const overallConfidence: 'high' | 'medium' | 'low' =
|
|
451
|
+
sanpuku.kouten || sanpuku.gyakuten ? 'high' : recentCrosses.length ? 'medium' : 'low';
|
|
452
|
+
|
|
453
|
+
// Phase 4: 時系列(雲位置の履歴とトレンド強度)
|
|
454
|
+
const cloudHistory: Array<{ barsAgo: number; position: 'above' | 'in' | 'below' }> = [];
|
|
455
|
+
if (Array.isArray(candles) && cloudTop != null && cloudBottom != null) {
|
|
456
|
+
for (let i = 0; i < Math.min(lookback, candles.length); i++) {
|
|
457
|
+
const idx = candles.length - 1 - i;
|
|
458
|
+
const c = candles[idx]?.close;
|
|
459
|
+
if (c != null) {
|
|
460
|
+
const pos = c > (cloudTop as number) ? 'above' : c < (cloudBottom as number) ? 'below' : 'in';
|
|
461
|
+
cloudHistory.push({ barsAgo: i, position: pos });
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
// 簡易トレンド強度: 直近/中期での雲クリアランス平均
|
|
466
|
+
const avgOrZero = (arr: number[]) => avg(arr) ?? 0;
|
|
467
|
+
let shortTerm = 0,
|
|
468
|
+
mediumTerm = 0;
|
|
469
|
+
if (Array.isArray(candles) && cloudTop != null && cloudBottom != null) {
|
|
470
|
+
const st = candles.slice(-Math.min(lookback, candles.length));
|
|
471
|
+
const mt = candles.slice(-Math.min(lookback * 2, candles.length));
|
|
472
|
+
const clearanceSt = st.map((x) =>
|
|
473
|
+
x.close > (cloudTop as number)
|
|
474
|
+
? x.close - (cloudTop as number)
|
|
475
|
+
: x.close < (cloudBottom as number)
|
|
476
|
+
? ((cloudBottom as number) - x.close) * -1
|
|
477
|
+
: 0,
|
|
478
|
+
);
|
|
479
|
+
const clearanceMt = mt.map((x) =>
|
|
480
|
+
x.close > (cloudTop as number)
|
|
481
|
+
? x.close - (cloudTop as number)
|
|
482
|
+
: x.close < (cloudBottom as number)
|
|
483
|
+
? ((cloudBottom as number) - x.close) * -1
|
|
484
|
+
: 0,
|
|
485
|
+
);
|
|
486
|
+
const norm = (v: number) => Math.max(-100, Math.min(100, Math.round((v / (close || 1)) * 10000)));
|
|
487
|
+
shortTerm = norm(avgOrZero(clearanceSt));
|
|
488
|
+
mediumTerm = norm(avgOrZero(clearanceMt));
|
|
489
|
+
}
|
|
490
|
+
const momentumTrend: 'accelerating' | 'steady' | 'decelerating' =
|
|
491
|
+
shortTerm > mediumTerm + 10 ? 'accelerating' : shortTerm < mediumTerm - 10 ? 'decelerating' : 'steady';
|
|
492
|
+
|
|
493
|
+
const data = {
|
|
494
|
+
latest: {
|
|
495
|
+
close,
|
|
496
|
+
tenkan,
|
|
497
|
+
kijun,
|
|
498
|
+
// 「今日の雲」(現在価格と比較する用)
|
|
499
|
+
spanA: currentSpanA,
|
|
500
|
+
spanB: currentSpanB,
|
|
501
|
+
cloudTop,
|
|
502
|
+
cloudBottom,
|
|
503
|
+
// 「26日後の雲」(将来の参考情報)
|
|
504
|
+
futureSpanA,
|
|
505
|
+
futureSpanB,
|
|
506
|
+
futureCloudTop,
|
|
507
|
+
futureCloudBottom,
|
|
508
|
+
chikou,
|
|
509
|
+
},
|
|
510
|
+
assessment: { pricePosition, tenkanKijun, cloudSlope },
|
|
511
|
+
cloud: { thickness, thicknessPct, direction, strength, upperBound: cloudTop, lowerBound: cloudBottom },
|
|
512
|
+
tenkanKijunDetail: { relationship: tkRel, distance: tkDist, distancePct: tkDistPct },
|
|
513
|
+
chikouSpan,
|
|
514
|
+
trend: { cloudHistory, trendStrength: { shortTerm, mediumTerm }, momentum: momentumTrend },
|
|
515
|
+
signals: { sanpuku, recentCrosses, kumoTwist, overallSignal, confidence: overallConfidence },
|
|
516
|
+
scenarios: {
|
|
517
|
+
keyLevels: {
|
|
518
|
+
resistance: [cloudTop ?? 0].filter(Boolean) as number[],
|
|
519
|
+
support: [cloudBottom ?? 0].filter(Boolean) as number[],
|
|
520
|
+
cloudEntry: cloudTop ?? 0,
|
|
521
|
+
cloudExit: cloudBottom ?? 0,
|
|
522
|
+
},
|
|
523
|
+
scenarios: {
|
|
524
|
+
bullish: {
|
|
525
|
+
condition: '転換線が基準線を上抜け',
|
|
526
|
+
target: close != null ? Math.round((close as number) * 1.07) : 0,
|
|
527
|
+
probability: 'medium',
|
|
528
|
+
},
|
|
529
|
+
bearish: {
|
|
530
|
+
condition: '雲突入(雲上限割れ)',
|
|
531
|
+
target: cloudBottom != null ? Math.round((cloudBottom as number) * 0.97) : 0,
|
|
532
|
+
probability: 'low',
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
watchPoints: ['転換線と基準線のクロス', '雲の厚みの推移(薄い箇所)'],
|
|
536
|
+
},
|
|
537
|
+
tags,
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
const meta = createMeta(chk.pair, { type, count: indRes.data.normalized.length });
|
|
541
|
+
const text = buildIchimokuSnapshotText({
|
|
542
|
+
pair: chk.pair,
|
|
543
|
+
type,
|
|
544
|
+
close,
|
|
545
|
+
pricePosition,
|
|
546
|
+
tenkan,
|
|
547
|
+
kijun,
|
|
548
|
+
tenkanKijun,
|
|
549
|
+
tkDist,
|
|
550
|
+
cloudTop,
|
|
551
|
+
cloudBottom,
|
|
552
|
+
direction,
|
|
553
|
+
thickness,
|
|
554
|
+
thicknessPct,
|
|
555
|
+
strength,
|
|
556
|
+
futureCloudTop,
|
|
557
|
+
futureCloudBottom,
|
|
558
|
+
chikouSpan,
|
|
559
|
+
sanpuku,
|
|
560
|
+
recentCrosses,
|
|
561
|
+
kumoTwist,
|
|
562
|
+
overallSignal,
|
|
563
|
+
overallConfidence,
|
|
564
|
+
scenarios: {
|
|
565
|
+
scenarios: data.scenarios.scenarios,
|
|
566
|
+
keyLevels: { support: data.scenarios.keyLevels.support, resistance: data.scenarios.keyLevels.resistance },
|
|
567
|
+
watchPoints: data.scenarios.watchPoints,
|
|
568
|
+
},
|
|
569
|
+
trend: { trendStrength: data.trend.trendStrength, momentum: data.trend.momentum },
|
|
570
|
+
cloudHistory,
|
|
571
|
+
currentSpanA,
|
|
572
|
+
currentSpanB,
|
|
573
|
+
futureSpanA,
|
|
574
|
+
futureSpanB,
|
|
575
|
+
tkDistPct,
|
|
576
|
+
});
|
|
577
|
+
return AnalyzeIchimokuSnapshotOutputSchema.parse(
|
|
578
|
+
ok(
|
|
579
|
+
text,
|
|
580
|
+
data as z.infer<typeof AnalyzeIchimokuSnapshotDataSchemaOut>,
|
|
581
|
+
meta as z.infer<typeof AnalyzeIchimokuSnapshotMetaSchemaOut>,
|
|
582
|
+
),
|
|
583
|
+
);
|
|
584
|
+
} catch (e: unknown) {
|
|
585
|
+
return failFromError(e, { schema: AnalyzeIchimokuSnapshotOutputSchema });
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// ── MCP ツール定義(tool-registry から自動収集) ──
|
|
590
|
+
export const toolDef: ToolDefinition = {
|
|
591
|
+
name: 'analyze_ichimoku_snapshot',
|
|
592
|
+
description:
|
|
593
|
+
'[Ichimoku Cloud] 一目均衡表(Ichimoku / cloud / kumo / tenkan-kijun)の数値スナップショット。価格と雲の位置関係・転換線/基準線クロス・雲の傾きを数値で返す。\n\n⚠️ 最新値のみ。時系列チャート描画 → prepare_chart_data(indicators: ["ICHIMOKU"])。',
|
|
594
|
+
inputSchema: AnalyzeIchimokuSnapshotInputSchema,
|
|
595
|
+
handler: async ({
|
|
596
|
+
pair,
|
|
597
|
+
type,
|
|
598
|
+
limit,
|
|
599
|
+
lookback,
|
|
600
|
+
}: {
|
|
601
|
+
pair?: string;
|
|
602
|
+
type?: string;
|
|
603
|
+
limit?: number;
|
|
604
|
+
lookback?: number;
|
|
605
|
+
}) => analyzeIchimokuSnapshot(pair, type, limit, lookback),
|
|
606
|
+
};
|