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,942 @@
1
+ import { z } from 'zod';
2
+ import {
3
+ BaseMetaSchema,
4
+ BasePairInputSchema,
5
+ CandleTypeEnum,
6
+ FailResultSchema,
7
+ TrendLabelEnum,
8
+ toolResultSchema,
9
+ } from './base.js';
10
+
11
+ // === Volatility Metrics ===
12
+ export const GetVolMetricsInputSchema = z.object({
13
+ pair: z.string(),
14
+ type: CandleTypeEnum,
15
+ limit: z.number().int().min(20).max(500).optional().default(200),
16
+ windows: z.array(z.number().int().min(2)).optional().default([14, 20, 30]),
17
+ useLogReturns: z.boolean().optional().default(true),
18
+ annualize: z.boolean().optional().default(true),
19
+ tz: z.string().optional().default('Asia/Tokyo'),
20
+ cacheTtlMs: z.number().int().optional().default(60_000),
21
+ view: z.enum(['summary', 'detailed', 'full', 'beginner']).optional().default('summary'),
22
+ });
23
+
24
+ export const GetVolMetricsDataSchemaOut = z.object({
25
+ meta: z.object({
26
+ pair: z.string(),
27
+ type: z.string(),
28
+ fetchedAt: z.string(),
29
+ baseIntervalMs: z.number(),
30
+ sampleSize: z.number(),
31
+ windows: z.array(z.number()),
32
+ annualize: z.boolean(),
33
+ useLogReturns: z.boolean(),
34
+ source: z.literal('bitbank:candlestick'),
35
+ }),
36
+ aggregates: z.object({
37
+ rv_std: z.number(),
38
+ rv_std_ann: z.number().optional(),
39
+ parkinson: z.number(),
40
+ garmanKlass: z.number(),
41
+ rogersSatchell: z.number(),
42
+ atr: z.number(),
43
+ skewness: z.number().optional(),
44
+ kurtosis: z.number().optional(),
45
+ gap_ratio: z.number().optional(),
46
+ }),
47
+ rolling: z.array(
48
+ z.object({
49
+ window: z.number(),
50
+ rv_std: z.number(),
51
+ rv_std_ann: z.number().optional(),
52
+ atr: z.number().optional(),
53
+ parkinson: z.number().optional(),
54
+ garmanKlass: z.number().optional(),
55
+ rogersSatchell: z.number().optional(),
56
+ }),
57
+ ),
58
+ series: z.object({
59
+ ts: z.array(z.number()),
60
+ close: z.array(z.number()),
61
+ ret: z.array(z.number()),
62
+ rv_inst: z.array(z.number()).optional(),
63
+ }),
64
+ tags: z.array(z.string()),
65
+ });
66
+
67
+ export const GetVolMetricsMetaSchemaOut = BaseMetaSchema.extend({
68
+ type: CandleTypeEnum,
69
+ count: z.number().int(),
70
+ });
71
+
72
+ export const GetVolMetricsOutputSchema = toolResultSchema(GetVolMetricsDataSchemaOut, GetVolMetricsMetaSchemaOut);
73
+
74
+ // === Analyze Market Signal ===
75
+ export const AnalyzeMarketSignalDataSchemaOut = z.object({
76
+ score: z.number(),
77
+ recommendation: z.enum(['bullish', 'bearish', 'neutral']),
78
+ tags: z.array(z.string()),
79
+ confidence: z.enum(['high', 'medium', 'low']),
80
+ confidenceReason: z.string(),
81
+ nextActions: z.array(
82
+ z.object({
83
+ priority: z.enum(['high', 'medium', 'low']),
84
+ tool: z.string(),
85
+ reason: z.string(),
86
+ suggestedParams: z.record(z.string(), z.any()).optional(),
87
+ }),
88
+ ),
89
+ alerts: z.array(z.object({ level: z.enum(['info', 'warning', 'critical']), message: z.string() })).optional(),
90
+ formula: z.string(),
91
+ weights: z.object({
92
+ buyPressure: z.number(),
93
+ cvdTrend: z.number(),
94
+ momentum: z.number(),
95
+ volatility: z.number(),
96
+ smaTrend: z.number(),
97
+ }),
98
+ contributions: z.object({
99
+ buyPressure: z.number(),
100
+ cvdTrend: z.number(),
101
+ momentum: z.number(),
102
+ volatility: z.number(),
103
+ smaTrend: z.number(),
104
+ }),
105
+ breakdown: z.object({
106
+ buyPressure: z.object({
107
+ rawValue: z.number(),
108
+ weight: z.number(),
109
+ contribution: z.number(),
110
+ interpretation: z.enum(['weak', 'moderate', 'strong', 'neutral']),
111
+ }),
112
+ cvdTrend: z.object({
113
+ rawValue: z.number(),
114
+ weight: z.number(),
115
+ contribution: z.number(),
116
+ interpretation: z.enum(['weak', 'moderate', 'strong', 'neutral']),
117
+ }),
118
+ momentum: z.object({
119
+ rawValue: z.number(),
120
+ weight: z.number(),
121
+ contribution: z.number(),
122
+ interpretation: z.enum(['weak', 'moderate', 'strong', 'neutral']),
123
+ }),
124
+ volatility: z.object({
125
+ rawValue: z.number(),
126
+ weight: z.number(),
127
+ contribution: z.number(),
128
+ interpretation: z.enum(['weak', 'moderate', 'strong', 'neutral']),
129
+ }),
130
+ smaTrend: z.object({
131
+ rawValue: z.number(),
132
+ weight: z.number(),
133
+ contribution: z.number(),
134
+ interpretation: z.enum(['weak', 'moderate', 'strong', 'neutral']),
135
+ }),
136
+ }),
137
+ topContributors: z.array(z.enum(['buyPressure', 'cvdTrend', 'momentum', 'volatility', 'smaTrend'])).min(1),
138
+ thresholds: z.object({ bullish: z.number(), bearish: z.number() }),
139
+ metrics: z.object({
140
+ buyPressure: z.number(),
141
+ cvdTrend: z.number(),
142
+ momentumFactor: z.number(),
143
+ volatilityFactor: z.number(),
144
+ smaTrendFactor: z.number(),
145
+ rsi: z.number().nullable(),
146
+ rv_std_ann: z.number(),
147
+ aggressorRatio: z.number(),
148
+ cvdSlope: z.number(),
149
+ horizon: z.number().int(),
150
+ }),
151
+ // Enriched SMA block for LLM-friendly grounding
152
+ sma: z
153
+ .object({
154
+ current: z.number().nullable(),
155
+ values: z.object({
156
+ sma25: z.number().nullable(),
157
+ sma75: z.number().nullable(),
158
+ sma200: z.number().nullable(),
159
+ }),
160
+ deviations: z.object({
161
+ vs25: z.number().nullable(),
162
+ vs75: z.number().nullable(),
163
+ vs200: z.number().nullable(),
164
+ }),
165
+ arrangement: z.enum(['bullish', 'bearish', 'mixed']),
166
+ position: z.enum(['above_all', 'below_all', 'mixed']),
167
+ distanceFromSma25Pct: z.number().nullable().optional(),
168
+ recentCross: z
169
+ .object({
170
+ type: z.enum(['golden_cross', 'death_cross']),
171
+ pair: z.literal('25/75'),
172
+ barsAgo: z.number().int(),
173
+ })
174
+ .nullable()
175
+ .optional(),
176
+ })
177
+ .optional(),
178
+ // Optional helper fields
179
+ recommendedTimeframes: z.array(z.string()).optional(),
180
+ refs: z.object({
181
+ flow: z.object({ aggregates: z.unknown(), lastBuckets: z.array(z.unknown()) }),
182
+ volatility: z.object({ aggregates: z.unknown() }),
183
+ indicators: z.object({ latest: z.unknown(), trend: TrendLabelEnum }),
184
+ }),
185
+ });
186
+ export const AnalyzeMarketSignalMetaSchemaOut = BaseMetaSchema.extend({
187
+ type: CandleTypeEnum,
188
+ windows: z.array(z.number()),
189
+ bucketMs: z.number().int(),
190
+ flowLimit: z.number().int(),
191
+ });
192
+ export const AnalyzeMarketSignalOutputSchema = toolResultSchema(
193
+ AnalyzeMarketSignalDataSchemaOut,
194
+ AnalyzeMarketSignalMetaSchemaOut,
195
+ );
196
+ export const AnalyzeMarketSignalInputSchema = BasePairInputSchema.extend({
197
+ type: CandleTypeEnum.optional().default('1day'),
198
+ flowLimit: z.number().int().optional().default(300),
199
+ bucketMs: z.number().int().optional().default(60_000),
200
+ windows: z.array(z.number().int()).optional().default([14, 20, 30]),
201
+ });
202
+
203
+ // === Ichimoku numeric snapshot (no visual assumptions) ===
204
+ export const AnalyzeIchimokuSnapshotInputSchema = BasePairInputSchema.extend({
205
+ type: CandleTypeEnum.optional().default('1day'),
206
+ limit: z.number().int().min(60).max(365).optional().default(120),
207
+ lookback: z.number().int().min(2).max(120).optional().default(10),
208
+ });
209
+
210
+ export const AnalyzeIchimokuSnapshotDataSchemaOut = z.object({
211
+ latest: z.object({
212
+ close: z.number().nullable(),
213
+ tenkan: z.number().nullable(),
214
+ kijun: z.number().nullable(),
215
+ spanA: z.number().nullable(),
216
+ spanB: z.number().nullable(),
217
+ chikou: z.number().nullable().optional(),
218
+ cloudTop: z.number().nullable(),
219
+ cloudBottom: z.number().nullable(),
220
+ }),
221
+ assessment: z.object({
222
+ pricePosition: z.enum(['above_cloud', 'in_cloud', 'below_cloud', 'unknown']),
223
+ tenkanKijun: z.enum(['bullish', 'bearish', 'neutral', 'unknown']),
224
+ cloudSlope: z.enum(['rising', 'falling', 'flat', 'unknown']),
225
+ }),
226
+ cloud: z
227
+ .object({
228
+ thickness: z.number().nullable(),
229
+ thicknessPct: z.number().nullable(),
230
+ direction: z.enum(['rising', 'falling', 'flat']).nullable(),
231
+ strength: z.enum(['strong', 'moderate', 'weak']).nullable(),
232
+ upperBound: z.number().nullable(),
233
+ lowerBound: z.number().nullable(),
234
+ })
235
+ .optional(),
236
+ tenkanKijunDetail: z
237
+ .object({
238
+ relationship: z.enum(['bullish', 'bearish']).nullable(),
239
+ distance: z.number().nullable(),
240
+ distancePct: z.number().nullable(),
241
+ })
242
+ .optional(),
243
+ chikouSpan: z
244
+ .object({
245
+ position: z.enum(['above', 'below']).nullable(),
246
+ distance: z.number().nullable(),
247
+ clearance: z.number().nullable(),
248
+ })
249
+ .optional(),
250
+ trend: z
251
+ .object({
252
+ cloudHistory: z.array(z.object({ barsAgo: z.number().int(), position: z.enum(['above', 'in', 'below']) })),
253
+ trendStrength: z.object({ shortTerm: z.number(), mediumTerm: z.number() }),
254
+ momentum: z.enum(['accelerating', 'steady', 'decelerating']),
255
+ })
256
+ .optional(),
257
+ signals: z
258
+ .object({
259
+ sanpuku: z.object({
260
+ kouten: z.boolean(),
261
+ gyakuten: z.boolean(),
262
+ conditions: z.object({
263
+ priceAboveCloud: z.boolean(),
264
+ tenkanAboveKijun: z.boolean(),
265
+ chikouAbovePrice: z.boolean(),
266
+ }),
267
+ }),
268
+ recentCrosses: z.array(
269
+ z.object({ type: z.enum(['golden_cross', 'death_cross']), barsAgo: z.number().int(), description: z.string() }),
270
+ ),
271
+ kumoTwist: z.object({
272
+ detected: z.boolean(),
273
+ barsAgo: z.number().int().optional(),
274
+ direction: z.enum(['bullish', 'bearish']).optional(),
275
+ }),
276
+ overallSignal: z.enum(['strong_bullish', 'bullish', 'neutral', 'bearish', 'strong_bearish']),
277
+ confidence: z.enum(['high', 'medium', 'low']),
278
+ })
279
+ .optional(),
280
+ scenarios: z
281
+ .object({
282
+ keyLevels: z.object({
283
+ resistance: z.array(z.number()),
284
+ support: z.array(z.number()),
285
+ cloudEntry: z.number(),
286
+ cloudExit: z.number(),
287
+ }),
288
+ scenarios: z.object({
289
+ bullish: z.object({
290
+ condition: z.string(),
291
+ target: z.number(),
292
+ probability: z.enum(['high', 'medium', 'low']),
293
+ }),
294
+ bearish: z.object({
295
+ condition: z.string(),
296
+ target: z.number(),
297
+ probability: z.enum(['high', 'medium', 'low']),
298
+ }),
299
+ }),
300
+ watchPoints: z.array(z.string()),
301
+ })
302
+ .optional(),
303
+ tags: z.array(z.string()),
304
+ });
305
+
306
+ export const AnalyzeIchimokuSnapshotMetaSchemaOut = BaseMetaSchema.extend({
307
+ type: CandleTypeEnum,
308
+ count: z.number().int(),
309
+ });
310
+
311
+ export const AnalyzeIchimokuSnapshotOutputSchema = toolResultSchema(
312
+ AnalyzeIchimokuSnapshotDataSchemaOut,
313
+ AnalyzeIchimokuSnapshotMetaSchemaOut,
314
+ );
315
+
316
+ // === BB snapshot ===
317
+ export const AnalyzeBbSnapshotInputSchema = BasePairInputSchema.extend({
318
+ type: CandleTypeEnum.optional().default('1day'),
319
+ limit: z.number().int().min(40).max(365).optional().default(120),
320
+ mode: z.enum(['default', 'extended']).optional().default('default'),
321
+ });
322
+
323
+ // analyze_bb_snapshot: support legacy (flat) and new (structured) data shapes
324
+ const AnalyzeBbSnapshotDataSchemaLegacy = z.object({
325
+ latest: z.object({
326
+ close: z.number().nullable(),
327
+ middle: z.number().nullable(),
328
+ upper: z.number().nullable(),
329
+ lower: z.number().nullable(),
330
+ }),
331
+ zScore: z.number().nullable(),
332
+ bandWidthPct: z.number().nullable(),
333
+ tags: z.array(z.string()),
334
+ });
335
+
336
+ const AnalyzeBbSnapshotDataSchemaStructured = z.object({
337
+ mode: z.enum(['default', 'extended']),
338
+ price: z.number().nullable(),
339
+ bb: z.union([
340
+ // default: middle/upper/lower
341
+ z.object({
342
+ middle: z.number().nullable(),
343
+ upper: z.number().nullable(),
344
+ lower: z.number().nullable(),
345
+ zScore: z.number().nullable(),
346
+ bandWidthPct: z.number().nullable(),
347
+ }),
348
+ // extended: bands map and bandWidthPct per band
349
+ z.object({
350
+ middle: z.number().nullable(),
351
+ bands: z.record(z.string(), z.number().nullable()).optional(),
352
+ zScore: z.number().nullable(),
353
+ bandWidthPct: z.union([z.number().nullable(), z.record(z.string(), z.number().nullable())]),
354
+ }),
355
+ ]),
356
+ interpretation: z.unknown().optional(),
357
+ position_analysis: z.unknown().optional(),
358
+ extreme_events: z.unknown().optional(),
359
+ context: z.unknown().optional(),
360
+ signals: z.array(z.string()).optional(),
361
+ next_steps: z.record(z.string(), z.any()).optional(),
362
+ tags: z.array(z.string()).optional(),
363
+ });
364
+
365
+ export const AnalyzeBbSnapshotDataSchemaOut = z.union([
366
+ AnalyzeBbSnapshotDataSchemaLegacy,
367
+ AnalyzeBbSnapshotDataSchemaStructured,
368
+ ]);
369
+
370
+ export const AnalyzeBbSnapshotMetaSchemaOut = BaseMetaSchema.extend({
371
+ type: CandleTypeEnum,
372
+ count: z.number().int(),
373
+ mode: z.enum(['default', 'extended']),
374
+ // allow additional meta injected by implementation
375
+ extra: z.object({}).passthrough().optional(),
376
+ });
377
+
378
+ export const AnalyzeBbSnapshotOutputSchema = toolResultSchema(
379
+ AnalyzeBbSnapshotDataSchemaOut,
380
+ AnalyzeBbSnapshotMetaSchemaOut,
381
+ );
382
+
383
+ // === SMA snapshot ===
384
+ export const AnalyzeSmaSnapshotInputSchema = BasePairInputSchema.extend({
385
+ type: CandleTypeEnum.optional().default('1day'),
386
+ limit: z.number().int().min(200).max(365).optional().default(220),
387
+ periods: z.array(z.number().int()).optional().default([25, 75, 200]),
388
+ });
389
+
390
+ export const AnalyzeSmaSnapshotDataSchemaOut = z
391
+ .object({
392
+ latest: z.object({ close: z.number().nullable() }),
393
+ sma: z.record(z.string(), z.number().nullable()),
394
+ crosses: z.array(z.object({ a: z.string(), b: z.string(), type: z.enum(['golden', 'dead']), delta: z.number() })),
395
+ alignment: z.enum(['bullish', 'bearish', 'mixed', 'unknown']),
396
+ tags: z.array(z.string()),
397
+ // Extended (optional): enriched summary and SMA analytics
398
+ summary: z
399
+ .object({
400
+ close: z.number().nullable(),
401
+ align: z.enum(['bullish', 'bearish', 'mixed', 'unknown']),
402
+ position: z.enum(['above_all', 'below_all', 'between', 'unknown']),
403
+ })
404
+ .optional(),
405
+ smas: z
406
+ .record(
407
+ z.string(),
408
+ z.object({
409
+ value: z.number().nullable(),
410
+ distancePct: z.number().nullable(),
411
+ distanceAbs: z.number().nullable(),
412
+ slope: z.enum(['rising', 'falling', 'flat']),
413
+ slopePctPerBar: z.number().nullable(),
414
+ slopePctTotal: z.number().nullable(),
415
+ barsWindow: z.number().nullable(),
416
+ slopePctPerDay: z.number().nullable().optional(),
417
+ }),
418
+ )
419
+ .optional(),
420
+ recentCrosses: z
421
+ .array(
422
+ z.object({
423
+ type: z.enum(['golden_cross', 'dead_cross']),
424
+ pair: z.tuple([z.number(), z.number()]),
425
+ barsAgo: z.number().int(),
426
+ date: z.string(),
427
+ }),
428
+ )
429
+ .optional(),
430
+ })
431
+ .passthrough();
432
+
433
+ export const AnalyzeSmaSnapshotMetaSchemaOut = BaseMetaSchema.extend({
434
+ type: CandleTypeEnum,
435
+ count: z.number().int(),
436
+ periods: z.array(z.number().int()),
437
+ });
438
+
439
+ export const AnalyzeSmaSnapshotOutputSchema = toolResultSchema(
440
+ AnalyzeSmaSnapshotDataSchemaOut,
441
+ AnalyzeSmaSnapshotMetaSchemaOut,
442
+ );
443
+
444
+ // === EMA snapshot ===
445
+ export const AnalyzeEmaSnapshotInputSchema = BasePairInputSchema.extend({
446
+ type: CandleTypeEnum.optional().default('1day'),
447
+ limit: z.number().int().min(200).max(365).optional().default(220),
448
+ periods: z.array(z.number().int()).optional().default([12, 26, 50, 200]),
449
+ });
450
+
451
+ export const AnalyzeEmaSnapshotDataSchemaOut = z
452
+ .object({
453
+ latest: z.object({ close: z.number().nullable() }),
454
+ ema: z.record(z.string(), z.number().nullable()),
455
+ crosses: z.array(z.object({ a: z.string(), b: z.string(), type: z.enum(['golden', 'dead']), delta: z.number() })),
456
+ alignment: z.enum(['bullish', 'bearish', 'mixed', 'unknown']),
457
+ tags: z.array(z.string()),
458
+ summary: z
459
+ .object({
460
+ close: z.number().nullable(),
461
+ align: z.enum(['bullish', 'bearish', 'mixed', 'unknown']),
462
+ position: z.enum(['above_all', 'below_all', 'between', 'unknown']),
463
+ })
464
+ .optional(),
465
+ emas: z
466
+ .record(
467
+ z.string(),
468
+ z.object({
469
+ value: z.number().nullable(),
470
+ distancePct: z.number().nullable(),
471
+ distanceAbs: z.number().nullable(),
472
+ slope: z.enum(['rising', 'falling', 'flat']),
473
+ slopePctPerBar: z.number().nullable(),
474
+ slopePctTotal: z.number().nullable(),
475
+ barsWindow: z.number().nullable(),
476
+ slopePctPerDay: z.number().nullable().optional(),
477
+ }),
478
+ )
479
+ .optional(),
480
+ recentCrosses: z
481
+ .array(
482
+ z.object({
483
+ type: z.enum(['golden_cross', 'dead_cross']),
484
+ pair: z.tuple([z.number(), z.number()]),
485
+ barsAgo: z.number().int(),
486
+ date: z.string(),
487
+ }),
488
+ )
489
+ .optional(),
490
+ })
491
+ .passthrough();
492
+
493
+ export const AnalyzeEmaSnapshotMetaSchemaOut = BaseMetaSchema.extend({
494
+ type: CandleTypeEnum,
495
+ count: z.number().int(),
496
+ periods: z.array(z.number().int()),
497
+ });
498
+
499
+ export const AnalyzeEmaSnapshotOutputSchema = toolResultSchema(
500
+ AnalyzeEmaSnapshotDataSchemaOut,
501
+ AnalyzeEmaSnapshotMetaSchemaOut,
502
+ );
503
+
504
+ // === Stochastic Oscillator snapshot ===
505
+ export const AnalyzeStochSnapshotInputSchema = BasePairInputSchema.extend({
506
+ type: CandleTypeEnum.optional().default('1day'),
507
+ limit: z.number().int().min(40).max(365).optional().default(120),
508
+ kPeriod: z.number().int().min(2).max(50).optional().default(14),
509
+ smoothK: z.number().int().min(1).max(10).optional().default(3),
510
+ smoothD: z.number().int().min(1).max(10).optional().default(3),
511
+ });
512
+
513
+ export const AnalyzeStochSnapshotDataSchemaOut = z
514
+ .object({
515
+ latest: z.object({ close: z.number().nullable() }),
516
+ stoch: z.object({
517
+ k: z.number().nullable(),
518
+ d: z.number().nullable(),
519
+ prevK: z.number().nullable(),
520
+ prevD: z.number().nullable(),
521
+ }),
522
+ zone: z.enum(['overbought', 'oversold', 'neutral']),
523
+ crossover: z.object({
524
+ type: z.enum(['bullish_cross', 'bearish_cross', 'none']),
525
+ description: z.string(),
526
+ }),
527
+ recentCrosses: z.array(
528
+ z.object({
529
+ type: z.enum(['bullish_cross', 'bearish_cross']),
530
+ barsAgo: z.number().int(),
531
+ date: z.string(),
532
+ zone: z.enum(['overbought', 'oversold', 'neutral']),
533
+ }),
534
+ ),
535
+ divergence: z.object({
536
+ type: z.enum(['bullish', 'bearish', 'none']),
537
+ description: z.string(),
538
+ }),
539
+ tags: z.array(z.string()),
540
+ })
541
+ .passthrough();
542
+
543
+ export const AnalyzeStochSnapshotMetaSchemaOut = BaseMetaSchema.extend({
544
+ type: CandleTypeEnum,
545
+ count: z.number().int(),
546
+ params: z.object({ kPeriod: z.number().int(), smoothK: z.number().int(), smoothD: z.number().int() }),
547
+ });
548
+
549
+ export const AnalyzeStochSnapshotOutputSchema = toolResultSchema(
550
+ AnalyzeStochSnapshotDataSchemaOut,
551
+ AnalyzeStochSnapshotMetaSchemaOut,
552
+ );
553
+
554
+ // === MTF SMA (Multi-Timeframe SMA Snapshot) ===
555
+ export const AnalyzeMtfSmaInputSchema = BasePairInputSchema.extend({
556
+ timeframes: z.array(CandleTypeEnum).min(1).optional().default(['1hour', '4hour', '1day']),
557
+ periods: z.array(z.number().int()).optional().default([25, 75, 200]),
558
+ });
559
+
560
+ const MtfSmaPerTimeframeSchema = z
561
+ .object({
562
+ alignment: z.enum(['bullish', 'bearish', 'mixed', 'unknown']),
563
+ position: z.enum(['above_all', 'below_all', 'between', 'unknown']).optional(),
564
+ latest: z.object({ close: z.number().nullable() }),
565
+ sma: z.record(z.string(), z.number().nullable()).optional(),
566
+ smas: z
567
+ .record(
568
+ z.string(),
569
+ z.object({
570
+ value: z.number().nullable(),
571
+ distancePct: z.number().nullable(),
572
+ distanceAbs: z.number().nullable().optional(),
573
+ slope: z.enum(['rising', 'falling', 'flat']),
574
+ slopePctPerBar: z.number().nullable().optional(),
575
+ slopePctTotal: z.number().nullable().optional(),
576
+ barsWindow: z.number().int().nullable().optional(),
577
+ slopePctPerDay: z.number().nullable().optional(),
578
+ pricePosition: z.enum(['above', 'below', 'equal']).optional(),
579
+ }),
580
+ )
581
+ .optional(),
582
+ crosses: z
583
+ .array(
584
+ z.object({
585
+ a: z.string(),
586
+ b: z.string(),
587
+ type: z.enum(['golden', 'dead']),
588
+ delta: z.number(),
589
+ }),
590
+ )
591
+ .optional(),
592
+ recentCrosses: z
593
+ .array(
594
+ z.object({
595
+ type: z.enum(['golden_cross', 'dead_cross']),
596
+ pair: z.tuple([z.number(), z.number()]),
597
+ barsAgo: z.number().int(),
598
+ date: z.string(),
599
+ }),
600
+ )
601
+ .optional(),
602
+ tags: z.array(z.string()).optional(),
603
+ })
604
+ .passthrough();
605
+
606
+ export const AnalyzeMtfSmaDataSchemaOut = z
607
+ .object({
608
+ timeframes: z.record(z.string(), MtfSmaPerTimeframeSchema),
609
+ confluence: z.object({
610
+ aligned: z.boolean(),
611
+ direction: z.enum(['bullish', 'bearish', 'mixed', 'unknown']),
612
+ summary: z.string(),
613
+ }),
614
+ })
615
+ .passthrough();
616
+
617
+ export const AnalyzeMtfSmaMetaSchemaOut = BaseMetaSchema.extend({
618
+ timeframes: z.array(z.string()),
619
+ periods: z.array(z.number().int()),
620
+ });
621
+
622
+ export const AnalyzeMtfSmaOutputSchema = toolResultSchema(AnalyzeMtfSmaDataSchemaOut, AnalyzeMtfSmaMetaSchemaOut);
623
+
624
+ // === Support Resistance Analysis ===
625
+ export const AnalyzeSupportResistanceInputSchema = BasePairInputSchema.extend({
626
+ lookbackDays: z.number().int().min(30).max(200).optional().default(90),
627
+ topN: z.number().int().min(1).max(5).optional().default(3),
628
+ tolerance: z.number().min(0.001).max(0.05).optional().default(0.015),
629
+ });
630
+
631
+ const TouchEventSchema = z.object({
632
+ date: z.string(),
633
+ price: z.number(),
634
+ bounceStrength: z.number(),
635
+ type: z.enum(['support', 'resistance']),
636
+ });
637
+
638
+ const SupportResistanceLevelSchema = z.object({
639
+ price: z.number(),
640
+ pctFromCurrent: z.number(),
641
+ strength: z.number().int().min(1).max(3),
642
+ label: z.string(),
643
+ touchCount: z.number().int(),
644
+ touches: z.array(TouchEventSchema),
645
+ recentBreak: z
646
+ .object({
647
+ date: z.string(),
648
+ price: z.number(),
649
+ breakPct: z.number(),
650
+ })
651
+ .optional(),
652
+ });
653
+
654
+ export const AnalyzeSupportResistanceDataSchemaOut = z
655
+ .object({
656
+ currentPrice: z.number(),
657
+ analysisDate: z.string(),
658
+ lookbackDays: z.number().int(),
659
+ supports: z.array(SupportResistanceLevelSchema),
660
+ resistances: z.array(SupportResistanceLevelSchema),
661
+ detectionCriteria: z.object({
662
+ swingDepth: z.number().int(),
663
+ recentBreakWindow: z.number().int(),
664
+ tolerance: z.number(),
665
+ }),
666
+ })
667
+ .passthrough();
668
+
669
+ export const AnalyzeSupportResistanceMetaSchemaOut = BaseMetaSchema.extend({
670
+ lookbackDays: z.number().int(),
671
+ topN: z.number().int(),
672
+ supportCount: z.number().int(),
673
+ resistanceCount: z.number().int(),
674
+ }).passthrough();
675
+
676
+ export const AnalyzeSupportResistanceOutputSchema = z.union([
677
+ z.object({
678
+ ok: z.literal(true),
679
+ summary: z.string(),
680
+ content: z.array(z.object({ type: z.literal('text'), text: z.string() })).optional(),
681
+ data: AnalyzeSupportResistanceDataSchemaOut,
682
+ meta: AnalyzeSupportResistanceMetaSchemaOut,
683
+ }),
684
+ FailResultSchema,
685
+ ]);
686
+
687
+ // === Fibonacci Retracement/Extension Analysis ===
688
+
689
+ export const AnalyzeFibonacciInputSchema = BasePairInputSchema.extend({
690
+ type: CandleTypeEnum.optional().default('1day'),
691
+ lookbackDays: z.number().int().min(14).max(365).optional().default(90),
692
+ mode: z.enum(['retracement', 'extension', 'both']).optional().default('both'),
693
+ historyLookbackDays: z.number().int().min(30).max(365).optional().default(180),
694
+ });
695
+
696
+ const FibonacciLevelSchema = z.object({
697
+ ratio: z.number(),
698
+ price: z.number(),
699
+ distancePct: z.number(),
700
+ isNearest: z.boolean(),
701
+ });
702
+
703
+ const FibonacciLevelStatSchema = z.object({
704
+ ratio: z.number(),
705
+ samplesCount: z.number().int(),
706
+ bounceRate: z.number(),
707
+ avgBounceReturnPct: z.number(),
708
+ avgBreakthroughReturnPct: z.number(),
709
+ medianDwellBars: z.number().int(),
710
+ confidence: z.enum(['high', 'medium', 'low']),
711
+ });
712
+
713
+ export const AnalyzeFibonacciDataSchemaOut = z
714
+ .object({
715
+ pair: z.string(),
716
+ timeframe: z.string(),
717
+ currentPrice: z.number(),
718
+ trend: z.enum(['up', 'down']),
719
+ swingHigh: z.object({ price: z.number(), date: z.string(), index: z.number().int() }),
720
+ swingLow: z.object({ price: z.number(), date: z.string(), index: z.number().int() }),
721
+ range: z.number(),
722
+ levels: z.array(FibonacciLevelSchema),
723
+ extensions: z.array(FibonacciLevelSchema),
724
+ position: z.object({
725
+ aboveLevel: FibonacciLevelSchema.nullable(),
726
+ belowLevel: FibonacciLevelSchema.nullable(),
727
+ nearestLevel: FibonacciLevelSchema.nullable(),
728
+ }),
729
+ levelStats: z.array(FibonacciLevelStatSchema).optional(),
730
+ })
731
+ .passthrough();
732
+
733
+ export const AnalyzeFibonacciMetaSchemaOut = BaseMetaSchema.extend({
734
+ timeframe: z.string(),
735
+ lookbackDays: z.number().int(),
736
+ mode: z.string(),
737
+ historyLookbackDays: z.number().int().optional(),
738
+ }).passthrough();
739
+
740
+ export const AnalyzeFibonacciOutputSchema = z.union([
741
+ z.object({
742
+ ok: z.literal(true),
743
+ summary: z.string(),
744
+ content: z.array(z.object({ type: z.literal('text'), text: z.string() })).optional(),
745
+ data: AnalyzeFibonacciDataSchemaOut,
746
+ meta: AnalyzeFibonacciMetaSchemaOut,
747
+ }),
748
+ FailResultSchema,
749
+ ]);
750
+
751
+ // === Multi-Timeframe Fibonacci Analysis ===
752
+
753
+ export const AnalyzeMtfFibonacciInputSchema = BasePairInputSchema.extend({
754
+ lookbackDays: z.array(z.number().int().min(14).max(365)).optional().default([30, 90, 180]),
755
+ });
756
+
757
+ const MtfFibonacciPerPeriodSchema = z
758
+ .object({
759
+ lookbackDays: z.number().int(),
760
+ trend: z.enum(['up', 'down']),
761
+ swingHigh: z.object({ price: z.number(), date: z.string() }),
762
+ swingLow: z.object({ price: z.number(), date: z.string() }),
763
+ levels: z.array(FibonacciLevelSchema),
764
+ })
765
+ .passthrough();
766
+
767
+ const ConfluenceZoneSchema = z.object({
768
+ priceZone: z.tuple([z.number(), z.number()]),
769
+ matchedLevels: z.array(
770
+ z.object({
771
+ lookbackDays: z.number().int(),
772
+ ratio: z.number(),
773
+ price: z.number(),
774
+ }),
775
+ ),
776
+ strength: z.enum(['strong', 'moderate', 'weak']),
777
+ distancePct: z.number(),
778
+ });
779
+
780
+ export const AnalyzeMtfFibonacciDataSchemaOut = z
781
+ .object({
782
+ pair: z.string(),
783
+ currentPrice: z.number(),
784
+ periods: z.record(z.string(), MtfFibonacciPerPeriodSchema),
785
+ confluence: z.array(ConfluenceZoneSchema),
786
+ })
787
+ .passthrough();
788
+
789
+ export const AnalyzeMtfFibonacciMetaSchemaOut = BaseMetaSchema.extend({
790
+ lookbackDays: z.array(z.number().int()),
791
+ }).passthrough();
792
+
793
+ export const AnalyzeMtfFibonacciOutputSchema = z.union([
794
+ z.object({
795
+ ok: z.literal(true),
796
+ summary: z.string(),
797
+ content: z.array(z.object({ type: z.literal('text'), text: z.string() })).optional(),
798
+ data: AnalyzeMtfFibonacciDataSchemaOut,
799
+ meta: AnalyzeMtfFibonacciMetaSchemaOut,
800
+ }),
801
+ FailResultSchema,
802
+ ]);
803
+
804
+ // === Analyze Volume Profile (VWAP + Volume Profile + Trade Size Distribution) ===
805
+ export const AnalyzeVolumeProfileInputSchema = BasePairInputSchema.extend({
806
+ hours: z
807
+ .number()
808
+ .min(0.5)
809
+ .max(24)
810
+ .optional()
811
+ .default(4)
812
+ .describe('直近N時間分の約定を取得(デフォルト4h)。limit より優先'),
813
+ limit: z.number().int().min(50).max(2000).optional().default(500).describe('取得する約定件数。hours 指定時は無視'),
814
+ bins: z.number().int().min(5).max(100).optional().default(20).describe('Volume Profile の価格帯分割数'),
815
+ valueAreaPct: z
816
+ .number()
817
+ .min(0.5)
818
+ .max(0.95)
819
+ .optional()
820
+ .default(0.7)
821
+ .describe('Value Area のカバー率(デフォルト70%)'),
822
+ tz: z.string().optional().default('Asia/Tokyo'),
823
+ });
824
+
825
+ const VwapBandSchema = z.object({
826
+ upper2sigma: z.number(),
827
+ upper1sigma: z.number(),
828
+ lower1sigma: z.number(),
829
+ lower2sigma: z.number(),
830
+ });
831
+
832
+ const VolumeProfileBinSchema = z.object({
833
+ low: z.number(),
834
+ high: z.number(),
835
+ label: z.string(),
836
+ buyVolume: z.number(),
837
+ sellVolume: z.number(),
838
+ totalVolume: z.number(),
839
+ pct: z.number(),
840
+ dominant: z.enum(['buy', 'sell', 'balanced']),
841
+ });
842
+
843
+ const TradeSizeCategorySchema = z.object({
844
+ label: z.string(),
845
+ minSize: z.number(),
846
+ maxSize: z.number().nullable(),
847
+ count: z.number().int(),
848
+ volume: z.number(),
849
+ pct: z.number(),
850
+ buyVolume: z.number(),
851
+ sellVolume: z.number(),
852
+ });
853
+
854
+ export const AnalyzeVolumeProfileDataSchemaOut = z.object({
855
+ vwap: z.object({
856
+ price: z.number(),
857
+ stdDev: z.number(),
858
+ bands: VwapBandSchema,
859
+ currentPrice: z.number(),
860
+ deviationPct: z.number(),
861
+ position: z.enum(['above_2sigma', 'above_1sigma', 'at_vwap', 'below_1sigma', 'below_2sigma']),
862
+ interpretation: z.string(),
863
+ }),
864
+ profile: z.object({
865
+ bins: z.array(VolumeProfileBinSchema),
866
+ poc: z.object({ price: z.number(), volume: z.number(), binIndex: z.number().int() }),
867
+ valueArea: z.object({ high: z.number(), low: z.number(), volume: z.number(), pct: z.number() }),
868
+ }),
869
+ tradeSizes: z.object({
870
+ categories: z.array(TradeSizeCategorySchema),
871
+ thresholds: z.object({ p25: z.number(), p75: z.number(), p95: z.number() }),
872
+ largeTradeBias: z.object({
873
+ buyVolume: z.number(),
874
+ sellVolume: z.number(),
875
+ ratio: z.number().nullable(),
876
+ interpretation: z.string(),
877
+ }),
878
+ }),
879
+ params: z.object({
880
+ totalTrades: z.number().int(),
881
+ totalVolume: z.number(),
882
+ priceRange: z.object({ high: z.number(), low: z.number() }),
883
+ timeRange: z.object({ start: z.string(), end: z.string(), durationMin: z.number() }),
884
+ bins: z.number().int(),
885
+ valueAreaPct: z.number(),
886
+ }),
887
+ });
888
+
889
+ export const AnalyzeVolumeProfileMetaSchemaOut = BaseMetaSchema.extend({
890
+ count: z.number().int(),
891
+ });
892
+
893
+ export const AnalyzeVolumeProfileOutputSchema = toolResultSchema(
894
+ AnalyzeVolumeProfileDataSchemaOut,
895
+ AnalyzeVolumeProfileMetaSchemaOut,
896
+ );
897
+
898
+ // ── analyze_currency_strength ──
899
+
900
+ export const AnalyzeCurrencyStrengthInputSchema = z.object({
901
+ topN: z.number().int().min(3).max(30).optional().default(10).describe('分析対象の上位ペア数(出来高順で選出)'),
902
+ type: CandleTypeEnum.optional().default('1day').describe('RSI/SMA 算出に使うローソク足の種類'),
903
+ });
904
+
905
+ const CurrencyStrengthItemSchema = z.object({
906
+ pair: z.string(),
907
+ currency: z.string().describe('通貨コード(例: BTC)'),
908
+ score: z.number().describe('総合強弱スコア(-100〜+100)'),
909
+ rank: z.number().int(),
910
+ components: z.object({
911
+ change24h: z.number().nullable().describe('24h変化率 %'),
912
+ rsi: z.number().nullable().describe('RSI(14)'),
913
+ smaDeviation: z.number().nullable().describe('現在価格のSMA25からの乖離率 %'),
914
+ volumeRank: z.number().int().describe('出来高順位(1=最大)'),
915
+ }),
916
+ price: z.number().nullable(),
917
+ volumeJPY: z.number().nullable(),
918
+ interpretation: z.enum(['strong_bullish', 'bullish', 'neutral', 'bearish', 'strong_bearish']),
919
+ });
920
+
921
+ export const AnalyzeCurrencyStrengthDataSchemaOut = z.object({
922
+ rankings: z.array(CurrencyStrengthItemSchema),
923
+ summary: z.object({
924
+ totalPairs: z.number().int(),
925
+ analyzedPairs: z.number().int(),
926
+ strongBullish: z.array(z.string()).describe('強気トップ銘柄'),
927
+ strongBearish: z.array(z.string()).describe('弱気ボトム銘柄'),
928
+ marketBias: z.enum(['bullish', 'bearish', 'neutral']).describe('市場全体のバイアス'),
929
+ avgScore: z.number(),
930
+ }),
931
+ });
932
+
933
+ export const AnalyzeCurrencyStrengthMetaSchemaOut = z.object({
934
+ fetchedAt: z.string(),
935
+ type: z.string(),
936
+ topN: z.number().int(),
937
+ });
938
+
939
+ export const AnalyzeCurrencyStrengthOutputSchema = toolResultSchema(
940
+ AnalyzeCurrencyStrengthDataSchemaOut,
941
+ AnalyzeCurrencyStrengthMetaSchemaOut,
942
+ );