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,100 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// === Trading Process: Backtest Schemas ===
|
|
4
|
+
|
|
5
|
+
export const BacktestTimeframeEnum = z.enum(['1D', '4H', '1H']);
|
|
6
|
+
export const BacktestPeriodEnum = z.enum(['1M', '3M', '6M']);
|
|
7
|
+
|
|
8
|
+
const BacktestTradeSchema = z.object({
|
|
9
|
+
entry_time: z.string(),
|
|
10
|
+
entry_price: z.number(),
|
|
11
|
+
exit_time: z.string(),
|
|
12
|
+
exit_price: z.number(),
|
|
13
|
+
pnl_pct: z.number(),
|
|
14
|
+
fee_pct: z.number(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const EquityPointSchema = z.object({
|
|
18
|
+
time: z.string(),
|
|
19
|
+
equity_pct: z.number(),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const DrawdownPointSchema = z.object({
|
|
23
|
+
time: z.string(),
|
|
24
|
+
drawdown_pct: z.number(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// === Generic Backtest Schema ===
|
|
28
|
+
|
|
29
|
+
export const StrategyTypeEnum = z.enum(['sma_cross', 'rsi', 'macd_cross', 'bb_breakout']);
|
|
30
|
+
|
|
31
|
+
export const StrategyConfigSchema = z.object({
|
|
32
|
+
type: StrategyTypeEnum.describe('Strategy type'),
|
|
33
|
+
params: z.record(z.string(), z.number()).optional().default({}).describe('Strategy parameters (overrides defaults)'),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const RunBacktestInputSchema = z.object({
|
|
37
|
+
pair: z.string().optional().default('btc_jpy').describe('Trading pair (e.g., btc_jpy)'),
|
|
38
|
+
timeframe: BacktestTimeframeEnum.optional()
|
|
39
|
+
.default('1D')
|
|
40
|
+
.describe('Candle timeframe: 1D (daily), 4H (4-hour), 1H (hourly)'),
|
|
41
|
+
period: BacktestPeriodEnum.optional().default('3M').describe('Backtest period: 1M, 3M, or 6M'),
|
|
42
|
+
strategy: StrategyConfigSchema.describe('Strategy configuration'),
|
|
43
|
+
fee_bp: z.number().min(0).max(100).optional().default(12).describe('One-way fee in basis points'),
|
|
44
|
+
execution: z.literal('t+1_open').optional().default('t+1_open').describe('Execution timing (fixed: t+1_open)'),
|
|
45
|
+
outputDir: z.string().optional().default('/mnt/user-data/outputs').describe('Output directory for chart files'),
|
|
46
|
+
savePng: z.boolean().optional().default(true).describe('Save chart as PNG file (default: true)'),
|
|
47
|
+
includeSvg: z
|
|
48
|
+
.boolean()
|
|
49
|
+
.optional()
|
|
50
|
+
.default(false)
|
|
51
|
+
.describe('Include SVG string in response (default: false, for token saving)'),
|
|
52
|
+
chartDetail: z
|
|
53
|
+
.enum(['default', 'full'])
|
|
54
|
+
.optional()
|
|
55
|
+
.default('default')
|
|
56
|
+
.describe(
|
|
57
|
+
'Chart detail level: default (equity+DD only) or full (price+indicator+equity+DD+position). Use full ONLY when user explicitly requests price chart or indicator visualization.',
|
|
58
|
+
),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const GenericBacktestSummarySchema = z.object({
|
|
62
|
+
total_pnl_pct: z.number(),
|
|
63
|
+
trade_count: z.number(),
|
|
64
|
+
win_rate: z.number(),
|
|
65
|
+
max_drawdown_pct: z.number(),
|
|
66
|
+
buy_hold_pnl_pct: z.number(),
|
|
67
|
+
excess_return_pct: z.number(),
|
|
68
|
+
profit_factor: z.number().nullable().describe('Profit Factor (gross profit / gross loss). null if no losing trades'),
|
|
69
|
+
sharpe_ratio: z.number().nullable().describe('Annualized Sharpe Ratio (daily returns, sqrt(365))'),
|
|
70
|
+
avg_pnl_pct: z.number().describe('Average P&L per trade [%]'),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export const RunBacktestOutputSchema = z.union([
|
|
74
|
+
z.object({
|
|
75
|
+
ok: z.literal(true),
|
|
76
|
+
summary: z.string(),
|
|
77
|
+
data: z.object({
|
|
78
|
+
input: z.object({
|
|
79
|
+
pair: z.string(),
|
|
80
|
+
timeframe: z.string(),
|
|
81
|
+
period: z.string(),
|
|
82
|
+
strategy: StrategyConfigSchema,
|
|
83
|
+
fee_bp: z.number(),
|
|
84
|
+
execution: z.string(),
|
|
85
|
+
}),
|
|
86
|
+
summary: GenericBacktestSummarySchema,
|
|
87
|
+
trades: z.array(BacktestTradeSchema),
|
|
88
|
+
equity_curve: z.array(EquityPointSchema),
|
|
89
|
+
drawdown_curve: z.array(DrawdownPointSchema),
|
|
90
|
+
overlays: z.array(z.any()),
|
|
91
|
+
}),
|
|
92
|
+
chartPath: z.string().optional().describe('Path to saved PNG chart file'),
|
|
93
|
+
svg: z.string().optional().describe('SVG string (only if includeSvg: true)'),
|
|
94
|
+
}),
|
|
95
|
+
z.object({
|
|
96
|
+
ok: z.literal(false),
|
|
97
|
+
error: z.string(),
|
|
98
|
+
availableStrategies: z.array(StrategyTypeEnum).optional(),
|
|
99
|
+
}),
|
|
100
|
+
]);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const CandleTypeEnum = z.enum([
|
|
4
|
+
'1min',
|
|
5
|
+
'5min',
|
|
6
|
+
'15min',
|
|
7
|
+
'30min',
|
|
8
|
+
'1hour',
|
|
9
|
+
'4hour',
|
|
10
|
+
'8hour',
|
|
11
|
+
'12hour',
|
|
12
|
+
'1day',
|
|
13
|
+
'1week',
|
|
14
|
+
'1month',
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
// ── Shared base schemas ──
|
|
18
|
+
|
|
19
|
+
/** レートリミット情報スキーマ(レスポンスヘッダから抽出、ヘッダ未提供時は省略) */
|
|
20
|
+
export const RateLimitSchema = z
|
|
21
|
+
.object({
|
|
22
|
+
remaining: z.number().describe('残りリクエスト数'),
|
|
23
|
+
limit: z.number().describe('期間あたりの上限数'),
|
|
24
|
+
reset: z.number().describe('リセット時刻(Unix epoch 秒)'),
|
|
25
|
+
})
|
|
26
|
+
.optional();
|
|
27
|
+
|
|
28
|
+
/** pair + fetchedAt: 全 Meta スキーマの共通ベース */
|
|
29
|
+
export const BaseMetaSchema = z.object({
|
|
30
|
+
pair: z.string(),
|
|
31
|
+
fetchedAt: z.string(),
|
|
32
|
+
rateLimit: RateLimitSchema,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/** pair デフォルト入力: z.string().optional().default('btc_jpy') */
|
|
36
|
+
export const BasePairInputSchema = z.object({ pair: z.string().optional().default('btc_jpy') });
|
|
37
|
+
|
|
38
|
+
/** 全ツール共通のエラー分岐 */
|
|
39
|
+
export const FailResultSchema = z.object({
|
|
40
|
+
ok: z.literal(false),
|
|
41
|
+
summary: z.string(),
|
|
42
|
+
data: z.object({}).passthrough(),
|
|
43
|
+
meta: z.object({ errorType: z.string() }).passthrough(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
/** ok/fail Result union を生成するヘルパー */
|
|
47
|
+
export function toolResultSchema<D extends z.ZodTypeAny, M extends z.ZodTypeAny>(data: D, meta: M) {
|
|
48
|
+
return z.union([z.object({ ok: z.literal(true), summary: z.string(), data, meta }), FailResultSchema]);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const TrendLabelEnum = z.enum([
|
|
52
|
+
'strong_uptrend',
|
|
53
|
+
'uptrend',
|
|
54
|
+
'strong_downtrend',
|
|
55
|
+
'downtrend',
|
|
56
|
+
'overbought',
|
|
57
|
+
'oversold',
|
|
58
|
+
'sideways',
|
|
59
|
+
'insufficient_data',
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
// === Shared output schemas (partial) ===
|
|
63
|
+
export const NumericSeriesSchema = z
|
|
64
|
+
.array(z.union([z.number(), z.null()]))
|
|
65
|
+
.transform((arr) => arr.map((v) => (v == null ? null : Number(Number(v).toFixed(2)))));
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* ローソク足スキーマ
|
|
69
|
+
* volume: base 通貨建ての合算取引量(買い+売り区別なし)。
|
|
70
|
+
* 例: btc_jpy → BTC 単位、eth_jpy → ETH 単位。
|
|
71
|
+
* bitbank /candlestick API の OHLCV[4] をそのまま使用。
|
|
72
|
+
* 公式アプリの Volume バー(買い/売り色分け)とは集計方法が異なる。
|
|
73
|
+
*/
|
|
74
|
+
export const CandleSchema = z.object({
|
|
75
|
+
open: z.number(),
|
|
76
|
+
high: z.number(),
|
|
77
|
+
low: z.number(),
|
|
78
|
+
close: z.number(),
|
|
79
|
+
volume: z.number().optional(),
|
|
80
|
+
isoTime: z.string().nullable().optional(),
|
|
81
|
+
isoTimeLocal: z
|
|
82
|
+
.string()
|
|
83
|
+
.nullable()
|
|
84
|
+
.optional()
|
|
85
|
+
.describe('tz パラメータ指定時のローカル時刻(例: 2026-02-20T09:00:00)'),
|
|
86
|
+
time: z.union([z.string(), z.number()]).optional(),
|
|
87
|
+
timestamp: z.number().optional(),
|
|
88
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseMetaSchema, CandleTypeEnum, toolResultSchema } from './base.js';
|
|
3
|
+
|
|
4
|
+
// ── Input ──
|
|
5
|
+
|
|
6
|
+
export const ValidateCandleDataInputSchema = z.object({
|
|
7
|
+
pair: z.string().optional().default('btc_jpy'),
|
|
8
|
+
type: CandleTypeEnum.optional().default('1day'),
|
|
9
|
+
date: z
|
|
10
|
+
.string()
|
|
11
|
+
.regex(/^\d{4}(\d{4})?$/, 'YYYYMMDD or YYYY format')
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('YYYYMMDD or YYYY format. If omitted, uses latest data.'),
|
|
14
|
+
limit: z.number().int().min(10).max(1000).optional().default(200).describe('検証対象のローソク足本数(10〜1000)'),
|
|
15
|
+
price_sigma: z
|
|
16
|
+
.number()
|
|
17
|
+
.min(1)
|
|
18
|
+
.max(10)
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('価格変化率がこの σ を超えたら異常値とみなす。省略時はペアのティア(major/mid/minor)に応じて自動設定'),
|
|
21
|
+
volume_multiplier: z
|
|
22
|
+
.number()
|
|
23
|
+
.min(2)
|
|
24
|
+
.max(100)
|
|
25
|
+
.optional()
|
|
26
|
+
.describe('出来高が全体平均の何倍を超えたらスパイクとみなすか。省略時はペアのティアに応じて自動設定'),
|
|
27
|
+
tz: z.string().optional().default('Asia/Tokyo').describe('タイムゾーン(デフォルト: Asia/Tokyo)'),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// ── Output sub-schemas ──
|
|
31
|
+
|
|
32
|
+
const CompletenessSchema = z.object({
|
|
33
|
+
expected: z.number(),
|
|
34
|
+
actual: z.number(),
|
|
35
|
+
missing: z.number(),
|
|
36
|
+
missingTimestamps: z.array(z.string()),
|
|
37
|
+
ratio: z.number(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const DuplicatesSchema = z.object({
|
|
41
|
+
count: z.number(),
|
|
42
|
+
timestamps: z.array(z.string()),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const IntegrityIssueSchema = z.object({
|
|
46
|
+
index: z.number(),
|
|
47
|
+
isoTime: z.string().nullable(),
|
|
48
|
+
issues: z.array(z.string()),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const IntegritySchema = z.object({
|
|
52
|
+
totalChecked: z.number(),
|
|
53
|
+
invalidCount: z.number(),
|
|
54
|
+
issues: z.array(IntegrityIssueSchema),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const PriceAnomalySchema = z.object({
|
|
58
|
+
index: z.number(),
|
|
59
|
+
isoTime: z.string().nullable(),
|
|
60
|
+
returnPct: z.number(),
|
|
61
|
+
sigma: z.number(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const PriceAnomalyResultSchema = z.object({
|
|
65
|
+
totalBars: z.number(),
|
|
66
|
+
anomalyCount: z.number(),
|
|
67
|
+
anomalies: z.array(PriceAnomalySchema),
|
|
68
|
+
stats: z
|
|
69
|
+
.object({
|
|
70
|
+
mean: z.number(),
|
|
71
|
+
stddev: z.number(),
|
|
72
|
+
threshold: z.number(),
|
|
73
|
+
})
|
|
74
|
+
.nullable(),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const VolumeAnomalySchema = z.object({
|
|
78
|
+
index: z.number(),
|
|
79
|
+
isoTime: z.string().nullable(),
|
|
80
|
+
volume: z.number(),
|
|
81
|
+
reason: z.enum(['zero', 'spike']),
|
|
82
|
+
multiplier: z.number().optional(),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const VolumeAnomalyResultSchema = z.object({
|
|
86
|
+
totalBars: z.number(),
|
|
87
|
+
anomalyCount: z.number(),
|
|
88
|
+
zeroCount: z.number(),
|
|
89
|
+
spikeCount: z.number(),
|
|
90
|
+
anomalies: z.array(VolumeAnomalySchema),
|
|
91
|
+
stats: z
|
|
92
|
+
.object({
|
|
93
|
+
avgVolume: z.number(),
|
|
94
|
+
threshold: z.number(),
|
|
95
|
+
})
|
|
96
|
+
.nullable(),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const QualityScoreSchema = z.object({
|
|
100
|
+
score: z.number().min(0).max(100),
|
|
101
|
+
breakdown: z.object({
|
|
102
|
+
completeness: z.number(),
|
|
103
|
+
integrity: z.number(),
|
|
104
|
+
priceStability: z.number(),
|
|
105
|
+
volumeHealth: z.number(),
|
|
106
|
+
}),
|
|
107
|
+
grade: z.enum(['A', 'B', 'C', 'D', 'F']),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ── Output ──
|
|
111
|
+
|
|
112
|
+
export const ValidateCandleDataDataSchema = z.object({
|
|
113
|
+
completeness: CompletenessSchema,
|
|
114
|
+
duplicates: DuplicatesSchema,
|
|
115
|
+
integrity: IntegritySchema,
|
|
116
|
+
priceAnomalies: PriceAnomalyResultSchema,
|
|
117
|
+
volumeAnomalies: VolumeAnomalyResultSchema,
|
|
118
|
+
qualityScore: QualityScoreSchema,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
export const ValidateCandleDataMetaSchema = BaseMetaSchema.extend({
|
|
122
|
+
type: CandleTypeEnum,
|
|
123
|
+
count: z.number().int(),
|
|
124
|
+
tier: z.enum(['major', 'mid', 'minor']),
|
|
125
|
+
thresholds: z.object({
|
|
126
|
+
priceSigma: z.number(),
|
|
127
|
+
volumeMultiplier: z.number(),
|
|
128
|
+
zeroVolumePenaltyWeight: z.number(),
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
export const ValidateCandleDataOutputSchema = toolResultSchema(
|
|
133
|
+
ValidateCandleDataDataSchema,
|
|
134
|
+
ValidateCandleDataMetaSchema,
|
|
135
|
+
);
|