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.
Files changed (147) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +388 -0
  3. package/assets/lightweight-charts.standalone.js +7 -0
  4. package/bin/bitbank-lab-mcp.js +20 -0
  5. package/lib/cache.ts +70 -0
  6. package/lib/candle-utils.ts +48 -0
  7. package/lib/candle-validate.ts +434 -0
  8. package/lib/conversions.ts +25 -0
  9. package/lib/datetime.ts +157 -0
  10. package/lib/depth-analysis.ts +51 -0
  11. package/lib/error.ts +15 -0
  12. package/lib/formatter.ts +296 -0
  13. package/lib/get-depth.ts +111 -0
  14. package/lib/http.ts +132 -0
  15. package/lib/indicator-config.ts +39 -0
  16. package/lib/indicator_buffer.ts +41 -0
  17. package/lib/indicators.ts +579 -0
  18. package/lib/logger.ts +120 -0
  19. package/lib/ma-snapshot-utils.ts +277 -0
  20. package/lib/math.ts +89 -0
  21. package/lib/pattern-diagrams.ts +562 -0
  22. package/lib/result.ts +104 -0
  23. package/lib/validate.ts +154 -0
  24. package/lib/volatility.ts +132 -0
  25. package/package.json +79 -0
  26. package/src/env.ts +4 -0
  27. package/src/handlers/analyzeCandlePatternsHandler.ts +383 -0
  28. package/src/handlers/analyzeFibonacciHandler.ts +54 -0
  29. package/src/handlers/analyzeIndicatorsHandler.ts +682 -0
  30. package/src/handlers/analyzeMarketSignalHandler.ts +272 -0
  31. package/src/handlers/analyzeMyPortfolioHandler.ts +800 -0
  32. package/src/handlers/detectPatternsHandler.ts +77 -0
  33. package/src/handlers/detectPatternsViewsHandler.ts +518 -0
  34. package/src/handlers/getTickersJpyHandler.ts +145 -0
  35. package/src/handlers/getVolatilityMetricsHandler.ts +234 -0
  36. package/src/handlers/portfolio/calc.ts +549 -0
  37. package/src/handlers/portfolio/fetch.ts +318 -0
  38. package/src/handlers/portfolio/types.ts +170 -0
  39. package/src/handlers/renderChartSvgHandler.ts +69 -0
  40. package/src/handlers/runBacktestHandler.ts +70 -0
  41. package/src/http.ts +107 -0
  42. package/src/private/auth.ts +104 -0
  43. package/src/private/client.ts +298 -0
  44. package/src/private/config.ts +25 -0
  45. package/src/private/confirmation.ts +185 -0
  46. package/src/private/schemas.ts +866 -0
  47. package/src/prompts.ts +2296 -0
  48. package/src/resources/app-resources.ts +79 -0
  49. package/src/schema/analysis.ts +942 -0
  50. package/src/schema/backtest.ts +100 -0
  51. package/src/schema/base.ts +88 -0
  52. package/src/schema/candle-validate.ts +135 -0
  53. package/src/schema/chart.ts +399 -0
  54. package/src/schema/index.ts +11 -0
  55. package/src/schema/indicators.ts +125 -0
  56. package/src/schema/market-data.ts +298 -0
  57. package/src/schema/patterns.ts +382 -0
  58. package/src/schema/types.ts +97 -0
  59. package/src/schemas.d.ts +37 -0
  60. package/src/schemas.ts +7 -0
  61. package/src/server.ts +405 -0
  62. package/src/tool-definition.ts +44 -0
  63. package/src/tool-registry.ts +174 -0
  64. package/src/types/express-shim.d.ts +9 -0
  65. package/src/types/schemas.generated.d.ts +23 -0
  66. package/tools/analyze_bb_snapshot.ts +385 -0
  67. package/tools/analyze_candle_patterns.ts +810 -0
  68. package/tools/analyze_currency_strength.ts +273 -0
  69. package/tools/analyze_ema_snapshot.ts +183 -0
  70. package/tools/analyze_fibonacci.ts +530 -0
  71. package/tools/analyze_ichimoku_snapshot.ts +606 -0
  72. package/tools/analyze_indicators.ts +691 -0
  73. package/tools/analyze_market_signal.ts +665 -0
  74. package/tools/analyze_mtf_fibonacci.ts +273 -0
  75. package/tools/analyze_mtf_sma.ts +175 -0
  76. package/tools/analyze_sma_snapshot.ts +146 -0
  77. package/tools/analyze_stoch_snapshot.ts +276 -0
  78. package/tools/analyze_support_resistance.ts +817 -0
  79. package/tools/analyze_volume_profile.ts +546 -0
  80. package/tools/chart/ichimoku-cloud.ts +113 -0
  81. package/tools/chart/render-depth.ts +139 -0
  82. package/tools/chart/render-sub-panels.ts +208 -0
  83. package/tools/chart/svg-utils.ts +102 -0
  84. package/tools/detect_macd_cross.ts +691 -0
  85. package/tools/detect_patterns.ts +424 -0
  86. package/tools/detect_whale_events.ts +181 -0
  87. package/tools/get_candles.ts +487 -0
  88. package/tools/get_flow_metrics.ts +596 -0
  89. package/tools/get_orderbook.ts +540 -0
  90. package/tools/get_ticker.ts +132 -0
  91. package/tools/get_tickers_jpy.ts +240 -0
  92. package/tools/get_transactions.ts +209 -0
  93. package/tools/get_volatility_metrics.ts +302 -0
  94. package/tools/patterns/aftermath.ts +212 -0
  95. package/tools/patterns/config.ts +151 -0
  96. package/tools/patterns/detect_doubles.ts +650 -0
  97. package/tools/patterns/detect_hs.ts +635 -0
  98. package/tools/patterns/detect_pennants.ts +373 -0
  99. package/tools/patterns/detect_triangles.ts +820 -0
  100. package/tools/patterns/detect_triples.ts +633 -0
  101. package/tools/patterns/detect_wedges.ts +1072 -0
  102. package/tools/patterns/helpers.ts +517 -0
  103. package/tools/patterns/index.ts +40 -0
  104. package/tools/patterns/regression.ts +153 -0
  105. package/tools/patterns/smoothing.ts +168 -0
  106. package/tools/patterns/swing.ts +91 -0
  107. package/tools/patterns/types.ts +193 -0
  108. package/tools/prepare_chart_data.ts +294 -0
  109. package/tools/prepare_depth_data.ts +189 -0
  110. package/tools/private/analyze_my_portfolio.ts +21 -0
  111. package/tools/private/cancel_order.ts +127 -0
  112. package/tools/private/cancel_orders.ts +121 -0
  113. package/tools/private/create_order.ts +236 -0
  114. package/tools/private/get_margin_positions.ts +134 -0
  115. package/tools/private/get_margin_status.ts +155 -0
  116. package/tools/private/get_margin_trade_history.ts +156 -0
  117. package/tools/private/get_my_assets.ts +207 -0
  118. package/tools/private/get_my_deposit_withdrawal.ts +500 -0
  119. package/tools/private/get_my_orders.ts +157 -0
  120. package/tools/private/get_my_trade_history.ts +229 -0
  121. package/tools/private/get_order.ts +95 -0
  122. package/tools/private/get_orders_info.ts +90 -0
  123. package/tools/private/preview_cancel_order.ts +172 -0
  124. package/tools/private/preview_cancel_orders.ts +137 -0
  125. package/tools/private/preview_order.ts +292 -0
  126. package/tools/render_candle_pattern_diagram.ts +389 -0
  127. package/tools/render_chart_svg.ts +799 -0
  128. package/tools/render_depth_svg.ts +274 -0
  129. package/tools/trading_process/index.ts +7 -0
  130. package/tools/trading_process/lib/backtest_engine.ts +252 -0
  131. package/tools/trading_process/lib/equity.ts +131 -0
  132. package/tools/trading_process/lib/fetch_candles.ts +181 -0
  133. package/tools/trading_process/lib/sma.ts +62 -0
  134. package/tools/trading_process/lib/strategies/bb_breakout.ts +141 -0
  135. package/tools/trading_process/lib/strategies/index.ts +52 -0
  136. package/tools/trading_process/lib/strategies/macd_cross.ts +256 -0
  137. package/tools/trading_process/lib/strategies/rsi.ts +133 -0
  138. package/tools/trading_process/lib/strategies/sma_cross.ts +214 -0
  139. package/tools/trading_process/lib/strategies/types.ts +118 -0
  140. package/tools/trading_process/lib/svg_to_png.ts +64 -0
  141. package/tools/trading_process/render_backtest_chart_generic.ts +729 -0
  142. package/tools/trading_process/run_backtest.ts +243 -0
  143. package/tools/trading_process/types.ts +85 -0
  144. package/tools/validate_candle_data.ts +260 -0
  145. package/tsconfig.json +17 -0
  146. package/ui/cancel-confirm/dist/cancel-confirm.html +99 -0
  147. package/ui/order-confirm/dist/order-confirm.html +99 -0
@@ -0,0 +1,272 @@
1
+ import type { z } from 'zod';
2
+ import analyzeMarketSignal from '../../tools/analyze_market_signal.js';
3
+ import { AnalyzeMarketSignalInputSchema, AnalyzeMarketSignalOutputSchema } from '../schemas.js';
4
+ import type { ToolDefinition } from '../tool-definition.js';
5
+
6
+ // ── buildMarketSignalHandlerText ───────────────────────────────
7
+
8
+ export type BuildMarketSignalHandlerTextInput = {
9
+ pair: string;
10
+ type: string;
11
+ score: number;
12
+ recommendation: string;
13
+ confidence: string;
14
+ confidenceReason: string;
15
+ scoreRange: { displayMin?: number; displayMax?: number; neutralBandDisplay?: { min: number; max: number } } | null;
16
+ topContributors: string[];
17
+ sma: {
18
+ current: number | null;
19
+ values: { sma25: number | null; sma75: number | null; sma200: number | null };
20
+ deviations: { vs25: number | null; vs75: number | null; vs200: number | null };
21
+ arrangement: string;
22
+ recentCross: { type: string; pair: string; barsAgo: number } | null;
23
+ } | null;
24
+ supplementary: {
25
+ rsi: number | null;
26
+ ichimokuSpanA: number | null;
27
+ ichimokuSpanB: number | null;
28
+ macdHist: number | null;
29
+ };
30
+ breakdownArray: Array<{
31
+ factor: string;
32
+ weight: number;
33
+ rawScore: number;
34
+ contribution: number;
35
+ interpretation: string;
36
+ }>;
37
+ contributions: Record<string, number> | null;
38
+ weights: Record<string, number> | null;
39
+ nextActions: Array<{ priority: string; tool: string; reason: string }>;
40
+ };
41
+
42
+ export function buildMarketSignalHandlerText(input: BuildMarketSignalHandlerTextInput): string {
43
+ const {
44
+ pair,
45
+ type,
46
+ score,
47
+ recommendation,
48
+ confidence,
49
+ confidenceReason,
50
+ scoreRange,
51
+ topContributors,
52
+ sma,
53
+ supplementary,
54
+ breakdownArray,
55
+ contributions,
56
+ weights,
57
+ nextActions,
58
+ } = input;
59
+
60
+ const score100 = Math.round(score * 100);
61
+ const range = scoreRange?.displayMin != null ? `${scoreRange.displayMin}〜${scoreRange.displayMax}` : '-100〜+100';
62
+ const neutralLine = scoreRange?.neutralBandDisplay
63
+ ? `${scoreRange.neutralBandDisplay.min}〜${scoreRange.neutralBandDisplay.max}`
64
+ : '-10〜+10';
65
+
66
+ const lines: string[] = [];
67
+ lines.push(`${String(pair).toUpperCase()} [${String(type || '1day')}]`);
68
+ lines.push(
69
+ `総合スコア: ${score100}(範囲: ${range}、中立域: ${neutralLine}) → 判定: ${recommendation}(信頼度: ${confidence}${confidenceReason ? `: ${confidenceReason}` : ''})`,
70
+ );
71
+ if (topContributors.length) lines.push(`主要因: ${topContributors.join(', ')}`);
72
+
73
+ // SMA詳細
74
+ if (sma) {
75
+ const curPx = Number.isFinite(sma.current) ? Math.round(sma.current!).toLocaleString('ja-JP') : null;
76
+ const v = sma.values;
77
+ const dev = sma.deviations;
78
+ const arr = sma.arrangement;
79
+ if (curPx || v.sma25 != null || v.sma75 != null || v.sma200 != null) {
80
+ lines.push('');
81
+ lines.push('【SMA(移動平均線)詳細】');
82
+ if (curPx) lines.push(`現在価格: ${curPx}円`);
83
+ const fmtVs = (x: number | null) => (x == null ? 'n/a' : `${x >= 0 ? '+' : ''}${x.toFixed(2)}%`);
84
+ const dir = (x: number | null) => (x == null ? '' : x >= 0 ? '上' : '下');
85
+ const s25 = Number.isFinite(v.sma25) ? Math.round(v.sma25!).toLocaleString('ja-JP') : 'n/a';
86
+ const s75 = Number.isFinite(v.sma75) ? Math.round(v.sma75!).toLocaleString('ja-JP') : 'n/a';
87
+ const s200 = Number.isFinite(v.sma200) ? Math.round(v.sma200!).toLocaleString('ja-JP') : 'n/a';
88
+ lines.push(`- 短期(25日): ${s25}円(今の価格より ${fmtVs(dev.vs25)} ${dir(dev.vs25)}に位置)`);
89
+ lines.push(`- 中期(75日): ${s75}円(今の価格より ${fmtVs(dev.vs75)} ${dir(dev.vs75)}に位置)`);
90
+ lines.push(`- 長期(200日): ${s200}円(今の価格より ${fmtVs(dev.vs200)} ${dir(dev.vs200)}に位置)`);
91
+ // 配置
92
+ const curVal = Number.isFinite(sma.current) ? Number(sma.current) : null;
93
+ const v25 = Number.isFinite(v.sma25) ? Number(v.sma25) : null;
94
+ const v75 = Number.isFinite(v.sma75) ? Number(v.sma75) : null;
95
+ const v200 = Number.isFinite(v.sma200) ? Number(v.sma200) : null;
96
+ const pts: Array<{ label: string; value: number }> = [];
97
+ if (curVal != null) pts.push({ label: '価格', value: curVal });
98
+ if (v25 != null) pts.push({ label: '25日', value: v25 });
99
+ if (v75 != null) pts.push({ label: '75日', value: v75 });
100
+ if (v200 != null) pts.push({ label: '200日', value: v200 });
101
+ if (pts.length >= 3) {
102
+ const order = [...pts]
103
+ .sort((a, b) => b.value - a.value)
104
+ .map((p) => p.label)
105
+ .join(' > ');
106
+ const arrLabel = arr === 'bullish' ? '上昇順' : arr === 'bearish' ? '下降順' : '混在';
107
+ const struct = arr === 'bullish' ? '上昇トレンド構造' : arr === 'bearish' ? '下落トレンド構造' : '方向感が弱い';
108
+ lines.push(`配置: ${order}(${arrLabel} → ${struct})`);
109
+ } else {
110
+ const arrLabel = arr === 'bullish' ? '上昇順' : arr === 'bearish' ? '下降順' : '混在';
111
+ lines.push(`配置: ${arrLabel}`);
112
+ }
113
+ // 直近クロス
114
+ if (sma.recentCross?.pair === '25/75') {
115
+ const crossJp = sma.recentCross.type === 'golden_cross' ? 'ゴールデンクロス' : 'デッドクロス';
116
+ const ago = Number(sma.recentCross.barsAgo ?? 0);
117
+ const isDaily = String(type || '').includes('day');
118
+ const unit = isDaily ? '日前' : '本前';
119
+ const verb = sma.recentCross.type === 'golden_cross' ? '上抜け' : '下抜け';
120
+ lines.push(`直近クロス: ${ago}${unit} 25日線が75日線を${verb}(${crossJp})`);
121
+ }
122
+ }
123
+ }
124
+
125
+ // 補足指標
126
+ const { rsi: rsiVal, ichimokuSpanA: spanA, ichimokuSpanB: spanB, macdHist } = supplementary;
127
+ const hasSupplementary = rsiVal != null || (spanA != null && spanB != null) || macdHist != null;
128
+ if (hasSupplementary) {
129
+ lines.push('');
130
+ lines.push('【補足指標】');
131
+ if (rsiVal != null && Number.isFinite(rsiVal)) {
132
+ const rsiRounded = Number(rsiVal).toFixed(2);
133
+ const rsiLabel = rsiVal < 30 ? '売られすぎ' : rsiVal > 70 ? '買われすぎ' : '中立圏';
134
+ lines.push(`RSI(14): ${rsiRounded}(${rsiLabel})`);
135
+ }
136
+ const curPx = sma?.current;
137
+ if (spanA != null && spanB != null && curPx != null && Number.isFinite(spanA) && Number.isFinite(spanB)) {
138
+ const cloudTop = Math.max(Number(spanA), Number(spanB));
139
+ const cloudBottom = Math.min(Number(spanA), Number(spanB));
140
+ const cloudThickness = Math.abs(cloudTop - cloudBottom);
141
+ const cloudThicknessPct = curPx > 0 ? ((cloudThickness / curPx) * 100).toFixed(1) : 'n/a';
142
+ let positionLabel = '雲の中';
143
+ let distancePct = 'n/a';
144
+ if (curPx > cloudTop) {
145
+ positionLabel = '雲の上';
146
+ distancePct = `+${(((curPx - cloudTop) / curPx) * 100).toFixed(1)}%`;
147
+ } else if (curPx < cloudBottom) {
148
+ positionLabel = '雲の下';
149
+ distancePct = `+${(((cloudBottom - curPx) / curPx) * 100).toFixed(1)}%`;
150
+ } else {
151
+ distancePct = '0%';
152
+ }
153
+ lines.push(`一目均衡表: ${positionLabel}(距離 ${distancePct}、雲の厚さ ${cloudThicknessPct}%)`);
154
+ }
155
+ if (macdHist != null && Number.isFinite(macdHist)) {
156
+ const histRounded = Math.round(macdHist).toLocaleString('ja-JP');
157
+ const macdLabel = macdHist > 0 ? '強気' : '弱気';
158
+ lines.push(`MACD: ヒストグラム ${histRounded}(${macdLabel})`);
159
+ }
160
+ }
161
+
162
+ // 内訳
163
+ if (breakdownArray.length) {
164
+ lines.push('');
165
+ lines.push('【内訳(raw×weight=寄与)】');
166
+ for (const b of breakdownArray) {
167
+ const w = `${(Number(b.weight || 0) * 100).toFixed(0)}%`;
168
+ const raw = Number(b.rawScore || 0).toFixed(2);
169
+ const contrib = Number(b.contribution || 0).toFixed(2);
170
+ const interp = String(b.interpretation || 'neutral');
171
+ lines.push(`- ${b.factor}: ${raw}×${w}=${contrib} (${interp})`);
172
+ }
173
+ } else if (contributions && weights) {
174
+ lines.push('');
175
+ lines.push('【内訳(contribution)】');
176
+ for (const k of Object.keys(contributions)) {
177
+ const c = Number(contributions[k]).toFixed(2);
178
+ const w = weights[k] != null ? `${Math.round(weights[k] * 100)}%` : '';
179
+ lines.push(`- ${k}: ${c}${w ? `(weight ${w})` : ''}`);
180
+ }
181
+ }
182
+
183
+ // 次の確認候補
184
+ if (nextActions.length) {
185
+ lines.push('');
186
+ lines.push('【次の確認候補】');
187
+ for (const a of nextActions.slice(0, 3)) {
188
+ const pri = a.priority === 'high' ? '高' : a.priority === 'medium' ? '中' : '低';
189
+ const reason = a.reason ? ` - ${a.reason}` : '';
190
+ lines.push(`- (${pri}) ${a.tool}${reason}`);
191
+ }
192
+ }
193
+
194
+ return lines.join('\n');
195
+ }
196
+
197
+ // ── toolDef ────────────────────────────────────────────────────
198
+
199
+ export const toolDef: ToolDefinition = {
200
+ name: 'analyze_market_signal',
201
+ description:
202
+ '[Market Signal / Score / Triage] 市場の総合シグナル(market signal / composite score / bull-bear / triage)。5要素(板圧力・CVD・モメンタム・ボラティリティ・SMAトレンド)を-100〜+100の単一スコアで瞬時評価。分析の起点・スクリーニングに最適。\n\n⚠️ 最新値スナップショットのみ。時系列チャート描画 → prepare_chart_data(indicators 指定)。\n\n詳細分析には専門ツールを併用: get_flow_metrics / get_volatility_metrics / analyze_indicators / get_orderbook / detect_patterns。',
203
+ inputSchema: AnalyzeMarketSignalInputSchema,
204
+ handler: async ({ pair, type, flowLimit, bucketMs, windows }: z.infer<typeof AnalyzeMarketSignalInputSchema>) => {
205
+ const res = await analyzeMarketSignal(pair, { type, flowLimit, bucketMs, windows });
206
+ try {
207
+ if (!res?.ok) return AnalyzeMarketSignalOutputSchema.parse(res);
208
+ const d = ((res?.data as Record<string, unknown>) || {}) as Record<string, unknown> & {
209
+ score?: number;
210
+ recommendation?: string;
211
+ confidence?: string;
212
+ confidenceReason?: string;
213
+ scoreRange?: { displayMin?: number; displayMax?: number; neutralBandDisplay?: { min: number; max: number } };
214
+ topContributors?: string[];
215
+ sma?: {
216
+ current: number;
217
+ values: { sma25: number | null; sma75: number | null; sma200: number | null };
218
+ deviations: { vs25: number | null; vs75: number | null; vs200: number | null };
219
+ arrangement: string;
220
+ recentCross: { type: string; pair: string; barsAgo: number } | null;
221
+ };
222
+ breakdownArray?: Array<{
223
+ factor: string;
224
+ weight: number;
225
+ rawScore: number;
226
+ contribution: number;
227
+ interpretation: string;
228
+ }>;
229
+ contributions?: Record<string, number>;
230
+ weights?: Record<string, number>;
231
+ nextActions?: Array<{ priority: string; tool: string; reason: string }>;
232
+ refs?: { indicators?: { latest?: Record<string, number> } };
233
+ };
234
+ const refs = d?.refs?.indicators?.latest || {};
235
+ const text = buildMarketSignalHandlerText({
236
+ pair: String(pair || ''),
237
+ type: String(type || '1day'),
238
+ score: d?.score ?? 0,
239
+ recommendation: String(d?.recommendation || 'neutral'),
240
+ confidence: String(d?.confidence || 'unknown'),
241
+ confidenceReason: String(d?.confidenceReason || ''),
242
+ scoreRange: d?.scoreRange || null,
243
+ topContributors: Array.isArray(d?.topContributors) ? d.topContributors.slice(0, 2) : [],
244
+ sma: d?.sma
245
+ ? {
246
+ current: Number.isFinite(d.sma.current) ? d.sma.current : null,
247
+ values: d.sma.values || { sma25: null, sma75: null, sma200: null },
248
+ deviations: d.sma.deviations || { vs25: null, vs75: null, vs200: null },
249
+ arrangement: String(d.sma.arrangement || ''),
250
+ recentCross: d.sma.recentCross || null,
251
+ }
252
+ : null,
253
+ supplementary: {
254
+ rsi: refs?.RSI_14 ?? null,
255
+ ichimokuSpanA: refs?.ICHIMOKU_spanA ?? null,
256
+ ichimokuSpanB: refs?.ICHIMOKU_spanB ?? null,
257
+ macdHist: refs?.MACD_hist ?? null,
258
+ },
259
+ breakdownArray: Array.isArray(d?.breakdownArray) ? d.breakdownArray : [],
260
+ contributions: d?.contributions || null,
261
+ weights: d?.weights || null,
262
+ nextActions: Array.isArray(d?.nextActions) ? d.nextActions : [],
263
+ });
264
+ return {
265
+ content: [{ type: 'text', text }],
266
+ structuredContent: AnalyzeMarketSignalOutputSchema.parse(res) as Record<string, unknown>,
267
+ };
268
+ } catch {
269
+ return AnalyzeMarketSignalOutputSchema.parse(res);
270
+ }
271
+ },
272
+ };