opentool 0.20.1 → 0.21.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.
@@ -0,0 +1,1103 @@
1
+ import { z } from 'zod';
2
+
3
+ // src/quant/schemas.ts
4
+ var quantStrategyFamilySchema = z.enum([
5
+ "benchmark",
6
+ "signal_rule",
7
+ "trend",
8
+ "mean_reversion",
9
+ "stat_arb",
10
+ "carry",
11
+ "hedge",
12
+ "execution",
13
+ "market_making",
14
+ "options",
15
+ "regime_ml",
16
+ "prediction_market",
17
+ "defi_lp"
18
+ ]);
19
+ var quantTestKindSchema = z.enum([
20
+ "prompt_plan_check",
21
+ "data_availability",
22
+ "signal_study",
23
+ "idea_rule_simulation",
24
+ "variant_sensitivity",
25
+ "leakage_check",
26
+ "cost_stress"
27
+ ]);
28
+ var quantResolutionSchema = z.enum(["1", "5", "15", "30", "60", "240", "1D", "1W"]);
29
+ var quantBarSchema = z.object({
30
+ time: z.number().int().nonnegative(),
31
+ open: z.number().positive(),
32
+ high: z.number().positive(),
33
+ low: z.number().positive(),
34
+ close: z.number().positive(),
35
+ volume: z.number().nonnegative().optional(),
36
+ fundingRate: z.number().optional()
37
+ }).strict();
38
+ var quantFeatureSpecSchema = z.object({
39
+ id: z.string().min(1),
40
+ kind: z.string().min(1),
41
+ params: z.record(z.string(), z.unknown()).default({})
42
+ }).strict();
43
+ var quantRuleSpecSchema = z.object({
44
+ kind: z.string().min(1),
45
+ params: z.record(z.string(), z.unknown()).default({})
46
+ }).strict();
47
+ var quantIdeaSpecV1Schema = z.object({
48
+ version: z.literal("1"),
49
+ family: quantStrategyFamilySchema,
50
+ thesis: z.object({
51
+ title: z.string().min(1),
52
+ belief: z.string().min(1),
53
+ expectedDirection: z.enum([
54
+ "up",
55
+ "down",
56
+ "relative",
57
+ "mean_revert",
58
+ "volatility",
59
+ "carry",
60
+ "hedge",
61
+ "unknown"
62
+ ]),
63
+ horizon: z.array(z.string().min(1)).min(1)
64
+ }).strict(),
65
+ market: z.object({
66
+ venue: z.enum(["hyperliquid", "polymarket", "derive", "external_fixture"]),
67
+ symbol: z.string().min(1).optional(),
68
+ universe: z.array(z.string().min(1)).optional()
69
+ }).strict(),
70
+ requiredSources: z.array(z.string().min(1)).default([]),
71
+ features: z.array(quantFeatureSpecSchema).default([]),
72
+ rule: quantRuleSpecSchema.optional(),
73
+ risk: z.object({
74
+ maxPositionUsd: z.number().positive().optional(),
75
+ maxLeverage: z.number().positive().optional(),
76
+ stopLossPct: z.number().positive().optional(),
77
+ takeProfitPct: z.number().positive().optional(),
78
+ maxDrawdownPct: z.number().positive().optional()
79
+ }).strict().optional()
80
+ }).strict();
81
+ var quantTestRequestV1Schema = z.object({
82
+ version: z.literal("1"),
83
+ idea: quantIdeaSpecV1Schema,
84
+ testKinds: z.array(quantTestKindSchema).min(1),
85
+ window: z.object({
86
+ resolution: quantResolutionSchema,
87
+ timeframeStart: z.string().min(1),
88
+ timeframeEnd: z.string().min(1),
89
+ warmupBars: z.number().int().nonnegative().optional()
90
+ }).strict(),
91
+ assumptions: z.object({
92
+ initialEquityUsd: z.number().positive().optional(),
93
+ makerFeeBps: z.number().nonnegative().optional(),
94
+ takerFeeBps: z.number().nonnegative().optional(),
95
+ slippageBps: z.number().nonnegative().optional(),
96
+ fundingModel: z.enum(["ignore", "historical", "estimated"]).optional()
97
+ }).strict().default({}),
98
+ variantSpace: z.record(z.string(), z.unknown()).optional()
99
+ }).strict();
100
+ var quantDecisionActionSchema = z.enum(["hold", "flat", "long", "short", "exit"]);
101
+ var quantDecisionSchema = z.object({
102
+ time: z.number().int().nonnegative(),
103
+ symbol: z.string().min(1),
104
+ action: quantDecisionActionSchema,
105
+ targetPosition: z.number(),
106
+ reason: z.string().min(1),
107
+ price: z.number().positive().optional()
108
+ }).strict();
109
+ var quantDecisionArtifactV1Schema = z.object({
110
+ version: z.literal("1"),
111
+ family: quantStrategyFamilySchema,
112
+ symbol: z.string().min(1),
113
+ resolution: quantResolutionSchema,
114
+ decisions: z.array(quantDecisionSchema),
115
+ warnings: z.array(z.string()).default([])
116
+ }).strict();
117
+ var quantTesterReportV1Schema = z.object({
118
+ ok: z.boolean(),
119
+ testRunKind: z.enum([
120
+ "prompt_plan_check",
121
+ "data_availability",
122
+ "signal_study",
123
+ "idea_rule_simulation",
124
+ "variant_sensitivity"
125
+ ]),
126
+ supported: z.boolean(),
127
+ unsupportedReason: z.string().optional(),
128
+ summary: z.string(),
129
+ dataLineage: z.object({
130
+ venue: z.string(),
131
+ symbols: z.array(z.string()),
132
+ resolution: quantResolutionSchema,
133
+ timeframeStart: z.string(),
134
+ timeframeEnd: z.string(),
135
+ sourceIds: z.array(z.string()),
136
+ warnings: z.array(z.string())
137
+ }).strict(),
138
+ signalStudy: z.record(z.string(), z.unknown()).optional(),
139
+ ideaSimulation: z.record(z.string(), z.unknown()).optional(),
140
+ decisionArtifact: quantDecisionArtifactV1Schema.optional(),
141
+ warnings: z.array(z.string()).default([])
142
+ }).strict();
143
+
144
+ // src/quant/bars.ts
145
+ function normalizeQuantBars(input) {
146
+ const bars = quantBarSchema.array().parse(input).slice();
147
+ bars.sort((a, b) => a.time - b.time);
148
+ for (let index = 0; index < bars.length; index += 1) {
149
+ const bar = bars[index];
150
+ if (bar.high < Math.max(bar.open, bar.close) || bar.low > Math.min(bar.open, bar.close)) {
151
+ throw new Error(`Invalid OHLC relationship at bar ${index}`);
152
+ }
153
+ if (index > 0 && bar.time <= bars[index - 1].time) {
154
+ throw new Error(`Duplicate or non-increasing bar time at index ${index}`);
155
+ }
156
+ }
157
+ return bars;
158
+ }
159
+ function closePrices(bars) {
160
+ return bars.map((bar) => bar.close);
161
+ }
162
+ function typicalPrices(bars) {
163
+ return bars.map((bar) => (bar.high + bar.low + bar.close) / 3);
164
+ }
165
+ function sliceBarsToWindow(params) {
166
+ const startIndex = params.bars.findIndex((bar) => bar.time >= params.startSeconds);
167
+ const fromIndex = startIndex < 0 ? params.bars.length : Math.max(0, startIndex - (params.warmupBars ?? 0));
168
+ return params.bars.slice(fromIndex).filter((bar) => bar.time <= params.endSeconds);
169
+ }
170
+
171
+ // src/quant/decision-series.ts
172
+ function validateDecisionArtifact(value) {
173
+ const artifact = quantDecisionArtifactV1Schema.parse(value);
174
+ for (let index = 1; index < artifact.decisions.length; index += 1) {
175
+ if (artifact.decisions[index].time < artifact.decisions[index - 1].time) {
176
+ throw new Error(`Decision artifact times must be sorted at index ${index}`);
177
+ }
178
+ }
179
+ return artifact;
180
+ }
181
+ function compactDecisionChanges(decisions) {
182
+ const compact = [];
183
+ let previousTarget = null;
184
+ for (const decision of decisions) {
185
+ if (previousTarget === null || decision.targetPosition !== previousTarget) {
186
+ compact.push(decision);
187
+ previousTarget = decision.targetPosition;
188
+ }
189
+ }
190
+ return compact;
191
+ }
192
+
193
+ // src/quant/features/beta.ts
194
+ function beta(assetReturns, benchmarkReturns) {
195
+ const length = Math.min(assetReturns.length, benchmarkReturns.length);
196
+ if (length < 2) return null;
197
+ const asset = assetReturns.slice(assetReturns.length - length);
198
+ const benchmark = benchmarkReturns.slice(benchmarkReturns.length - length);
199
+ const meanAsset = asset.reduce((total, value) => total + value, 0) / length;
200
+ const meanBenchmark = benchmark.reduce((total, value) => total + value, 0) / length;
201
+ let covariance = 0;
202
+ let benchmarkVariance = 0;
203
+ for (let index = 0; index < length; index += 1) {
204
+ covariance += (asset[index] - meanAsset) * (benchmark[index] - meanBenchmark);
205
+ benchmarkVariance += (benchmark[index] - meanBenchmark) ** 2;
206
+ }
207
+ return benchmarkVariance === 0 ? null : covariance / benchmarkVariance;
208
+ }
209
+
210
+ // src/quant/features/correlation.ts
211
+ function correlation(a, b) {
212
+ const length = Math.min(a.length, b.length);
213
+ if (length < 2) return null;
214
+ const left = a.slice(a.length - length);
215
+ const right = b.slice(b.length - length);
216
+ const meanA = left.reduce((total, value) => total + value, 0) / length;
217
+ const meanB = right.reduce((total, value) => total + value, 0) / length;
218
+ let covariance = 0;
219
+ let varianceA = 0;
220
+ let varianceB = 0;
221
+ for (let index = 0; index < length; index += 1) {
222
+ const da = left[index] - meanA;
223
+ const db = right[index] - meanB;
224
+ covariance += da * db;
225
+ varianceA += da * da;
226
+ varianceB += db * db;
227
+ }
228
+ if (varianceA === 0 || varianceB === 0) return null;
229
+ return covariance / Math.sqrt(varianceA * varianceB);
230
+ }
231
+ function rollingCorrelation(a, b, period = 20) {
232
+ return a.map((_, index) => {
233
+ if (index < period - 1) return null;
234
+ return correlation(
235
+ a.slice(index - period + 1, index + 1),
236
+ b.slice(index - period + 1, index + 1)
237
+ );
238
+ });
239
+ }
240
+
241
+ // src/quant/features/funding.ts
242
+ function fundingRates(bars) {
243
+ return bars.map((bar) => bar.fundingRate ?? 0);
244
+ }
245
+ function cumulativeFunding(bars) {
246
+ let running = 0;
247
+ return fundingRates(bars).map((rate) => {
248
+ running += rate;
249
+ return running;
250
+ });
251
+ }
252
+
253
+ // src/quant/features/momentum.ts
254
+ function momentum(values, lookbackBars) {
255
+ if (!Number.isInteger(lookbackBars) || lookbackBars <= 0) {
256
+ throw new Error("Momentum lookback must be a positive integer");
257
+ }
258
+ return values.map((value, index) => {
259
+ const prior = values[index - lookbackBars];
260
+ return prior == null || prior === 0 ? null : value / prior - 1;
261
+ });
262
+ }
263
+
264
+ // src/quant/features/relative-strength.ts
265
+ function relativeStrength(assetPrices, benchmarkPrices, lookbackBars) {
266
+ const assetMomentum = momentum(assetPrices, lookbackBars);
267
+ const benchmarkMomentum = momentum(benchmarkPrices, lookbackBars);
268
+ return assetMomentum.map(
269
+ (value, index) => value == null || benchmarkMomentum[index] == null ? null : value - benchmarkMomentum[index]
270
+ );
271
+ }
272
+
273
+ // src/quant/features/returns.ts
274
+ function simpleReturns(values) {
275
+ return values.map((value, index) => {
276
+ if (index === 0) return 0;
277
+ const previous = values[index - 1];
278
+ return previous === 0 ? 0 : value / previous - 1;
279
+ });
280
+ }
281
+ function logReturns(values) {
282
+ return values.map((value, index) => {
283
+ if (index === 0) return 0;
284
+ const previous = values[index - 1];
285
+ return previous <= 0 || value <= 0 ? 0 : Math.log(value / previous);
286
+ });
287
+ }
288
+ function forwardReturns(values, horizonBars2) {
289
+ if (!Number.isInteger(horizonBars2) || horizonBars2 <= 0) {
290
+ throw new Error("Forward return horizon must be a positive integer");
291
+ }
292
+ return values.map((value, index) => {
293
+ const future = values[index + horizonBars2];
294
+ return future == null || value === 0 ? null : future / value - 1;
295
+ });
296
+ }
297
+
298
+ // src/quant/indicators/sma.ts
299
+ function sma(values, period) {
300
+ if (!Number.isInteger(period) || period <= 0) {
301
+ throw new Error("SMA period must be a positive integer");
302
+ }
303
+ const output = [];
304
+ let sum = 0;
305
+ for (let index = 0; index < values.length; index += 1) {
306
+ sum += values[index];
307
+ if (index >= period) sum -= values[index - period];
308
+ output.push(index >= period - 1 ? sum / period : null);
309
+ }
310
+ return output;
311
+ }
312
+
313
+ // src/quant/features/volume.ts
314
+ function volumes(bars) {
315
+ return bars.map((bar) => bar.volume ?? 0);
316
+ }
317
+ function relativeVolume(bars, period = 20) {
318
+ const raw = volumes(bars);
319
+ const average = sma(raw, period);
320
+ return raw.map((value, index) => {
321
+ const baseline = average[index];
322
+ return baseline == null || baseline === 0 ? null : value / baseline;
323
+ });
324
+ }
325
+
326
+ // src/quant/indicators/atr.ts
327
+ function trueRanges(bars) {
328
+ return bars.map((bar, index) => {
329
+ const previousClose = index > 0 ? bars[index - 1].close : bar.close;
330
+ return Math.max(
331
+ bar.high - bar.low,
332
+ Math.abs(bar.high - previousClose),
333
+ Math.abs(bar.low - previousClose)
334
+ );
335
+ });
336
+ }
337
+ function atr(bars, period = 14) {
338
+ return sma(trueRanges(bars), period);
339
+ }
340
+
341
+ // src/quant/indicators/bollinger.ts
342
+ function bollinger(values, period = 20, standardDeviations = 2) {
343
+ const middle = sma(values, period);
344
+ return values.map((_, index) => {
345
+ const average = middle[index];
346
+ if (average == null || index < period - 1) {
347
+ return { lower: null, middle: null, upper: null, width: null };
348
+ }
349
+ const window = values.slice(index - period + 1, index + 1);
350
+ const variance = window.reduce((total, value) => total + (value - average) ** 2, 0) / period;
351
+ const deviation = Math.sqrt(variance);
352
+ const lower = average - deviation * standardDeviations;
353
+ const upper = average + deviation * standardDeviations;
354
+ return {
355
+ lower,
356
+ middle: average,
357
+ upper,
358
+ width: average === 0 ? null : (upper - lower) / average
359
+ };
360
+ });
361
+ }
362
+
363
+ // src/quant/indicators/donchian.ts
364
+ function donchian(highs, lows, period = 20) {
365
+ if (highs.length !== lows.length) {
366
+ throw new Error("Donchian high/low arrays must have the same length");
367
+ }
368
+ if (!Number.isInteger(period) || period <= 0) {
369
+ throw new Error("Donchian period must be a positive integer");
370
+ }
371
+ return highs.map((_, index) => {
372
+ if (index < period - 1) return { lower: null, upper: null };
373
+ const highWindow = highs.slice(index - period + 1, index + 1);
374
+ const lowWindow = lows.slice(index - period + 1, index + 1);
375
+ return {
376
+ lower: Math.min(...lowWindow),
377
+ upper: Math.max(...highWindow)
378
+ };
379
+ });
380
+ }
381
+
382
+ // src/quant/indicators/ema.ts
383
+ function ema(values, period) {
384
+ if (!Number.isInteger(period) || period <= 0) {
385
+ throw new Error("EMA period must be a positive integer");
386
+ }
387
+ const output = [];
388
+ const multiplier = 2 / (period + 1);
389
+ let previous = null;
390
+ let seedSum = 0;
391
+ for (let index = 0; index < values.length; index += 1) {
392
+ const value = values[index];
393
+ if (index < period) {
394
+ seedSum += value;
395
+ if (index === period - 1) {
396
+ previous = seedSum / period;
397
+ output.push(previous);
398
+ } else {
399
+ output.push(null);
400
+ }
401
+ continue;
402
+ }
403
+ previous = previous == null ? value : (value - previous) * multiplier + previous;
404
+ output.push(previous);
405
+ }
406
+ return output;
407
+ }
408
+
409
+ // src/quant/indicators/macd.ts
410
+ function macd(values, fastPeriod = 12, slowPeriod = 26, signalPeriod = 9) {
411
+ if (fastPeriod >= slowPeriod) {
412
+ throw new Error("MACD fast period must be less than slow period");
413
+ }
414
+ const fast = ema(values, fastPeriod);
415
+ const slow = ema(values, slowPeriod);
416
+ const macdLine = values.map(
417
+ (_, index) => fast[index] == null || slow[index] == null ? null : fast[index] - slow[index]
418
+ );
419
+ const signalInput = macdLine.map((value) => value ?? 0);
420
+ const signalLine = ema(signalInput, signalPeriod);
421
+ return values.map((_, index) => {
422
+ const macdValue = macdLine[index];
423
+ const signalValue = macdValue == null ? null : signalLine[index];
424
+ return {
425
+ macd: macdValue,
426
+ signal: signalValue,
427
+ histogram: macdValue == null || signalValue == null ? null : macdValue - signalValue
428
+ };
429
+ });
430
+ }
431
+
432
+ // src/quant/indicators/rsi.ts
433
+ function rsi(values, period = 14) {
434
+ if (!Number.isInteger(period) || period <= 0) {
435
+ throw new Error("RSI period must be a positive integer");
436
+ }
437
+ const output = Array.from({ length: values.length }, () => null);
438
+ if (values.length <= period) return output;
439
+ let gainSum = 0;
440
+ let lossSum = 0;
441
+ for (let index = 1; index <= period; index += 1) {
442
+ const delta = values[index] - values[index - 1];
443
+ if (delta >= 0) gainSum += delta;
444
+ else lossSum += Math.abs(delta);
445
+ }
446
+ let averageGain = gainSum / period;
447
+ let averageLoss = lossSum / period;
448
+ output[period] = averageLoss === 0 ? 100 : 100 - 100 / (1 + averageGain / averageLoss);
449
+ for (let index = period + 1; index < values.length; index += 1) {
450
+ const delta = values[index] - values[index - 1];
451
+ const gain = delta > 0 ? delta : 0;
452
+ const loss = delta < 0 ? Math.abs(delta) : 0;
453
+ averageGain = (averageGain * (period - 1) + gain) / period;
454
+ averageLoss = (averageLoss * (period - 1) + loss) / period;
455
+ output[index] = averageLoss === 0 ? 100 : 100 - 100 / (1 + averageGain / averageLoss);
456
+ }
457
+ return output;
458
+ }
459
+
460
+ // src/quant/indicators/volatility.ts
461
+ function rollingVolatility(returns, period = 20, annualization = 365) {
462
+ if (!Number.isInteger(period) || period <= 1) {
463
+ throw new Error("Volatility period must be an integer greater than 1");
464
+ }
465
+ return returns.map((_, index) => {
466
+ if (index < period - 1) return null;
467
+ const window = returns.slice(index - period + 1, index + 1);
468
+ const mean = window.reduce((total, value) => total + value, 0) / period;
469
+ const variance = window.reduce((total, value) => total + (value - mean) ** 2, 0) / (period - 1);
470
+ return Math.sqrt(variance) * Math.sqrt(annualization);
471
+ });
472
+ }
473
+
474
+ // src/quant/indicators/zscore.ts
475
+ function rollingZScore(values, period = 20) {
476
+ if (!Number.isInteger(period) || period <= 1) {
477
+ throw new Error("Z-score period must be an integer greater than 1");
478
+ }
479
+ return values.map((value, index) => {
480
+ if (index < period - 1) return null;
481
+ const window = values.slice(index - period + 1, index + 1);
482
+ const mean = window.reduce((total, current) => total + current, 0) / period;
483
+ const variance = window.reduce((total, current) => total + (current - mean) ** 2, 0) / period;
484
+ const deviation = Math.sqrt(variance);
485
+ return deviation === 0 ? 0 : (value - mean) / deviation;
486
+ });
487
+ }
488
+
489
+ // src/quant/lineage.ts
490
+ function buildQuantDataLineage(params) {
491
+ const symbol = params.request.idea.market.symbol ?? params.request.idea.market.universe?.[0] ?? "UNKNOWN";
492
+ return {
493
+ venue: params.request.idea.market.venue,
494
+ symbols: params.request.idea.market.universe ?? [symbol],
495
+ resolution: params.request.window.resolution,
496
+ timeframeStart: params.request.window.timeframeStart,
497
+ timeframeEnd: params.request.window.timeframeEnd,
498
+ sourceIds: params.request.idea.requiredSources,
499
+ warnings: params.warnings ?? []
500
+ };
501
+ }
502
+
503
+ // src/quant/result.ts
504
+ function summarizeNumbers(values) {
505
+ const finite = values.filter((value) => Number.isFinite(value));
506
+ if (finite.length === 0) {
507
+ return {
508
+ count: 0,
509
+ max: null,
510
+ mean: null,
511
+ median: null,
512
+ min: null,
513
+ positiveRate: null,
514
+ standardDeviation: null
515
+ };
516
+ }
517
+ const sorted = finite.slice().sort((a, b) => a - b);
518
+ const mean = finite.reduce((total, value) => total + value, 0) / finite.length;
519
+ const variance = finite.reduce((total, value) => total + (value - mean) ** 2, 0) / finite.length;
520
+ const middle = Math.floor(sorted.length / 2);
521
+ const median = sorted.length % 2 === 0 ? (sorted[middle - 1] + sorted[middle]) / 2 : sorted[middle];
522
+ return {
523
+ count: finite.length,
524
+ max: sorted[sorted.length - 1],
525
+ mean,
526
+ median,
527
+ min: sorted[0],
528
+ positiveRate: finite.filter((value) => value > 0).length / Math.max(1, finite.length),
529
+ standardDeviation: Math.sqrt(variance)
530
+ };
531
+ }
532
+
533
+ // src/quant/signal-study/adverse-excursion.ts
534
+ function summarizeExcursions(params) {
535
+ const adverse = [];
536
+ const favorable = [];
537
+ for (let index = 0; index < params.bars.length; index += 1) {
538
+ if (!params.condition[index]) continue;
539
+ const entry = params.bars[index].close;
540
+ const window = params.bars.slice(index + 1, index + params.horizonBars + 1);
541
+ if (window.length === 0) continue;
542
+ adverse.push(Math.min(...window.map((bar) => bar.low / entry - 1)));
543
+ favorable.push(Math.max(...window.map((bar) => bar.high / entry - 1)));
544
+ }
545
+ return {
546
+ averageAdverse: adverse.length === 0 ? null : adverse.reduce((total, value) => total + value, 0) / adverse.length,
547
+ averageFavorable: favorable.length === 0 ? null : favorable.reduce((total, value) => total + value, 0) / favorable.length,
548
+ count: adverse.length
549
+ };
550
+ }
551
+
552
+ // src/quant/signal-study/event-windows.ts
553
+ function buildEventWindows(params) {
554
+ const windows = [];
555
+ for (let index = 0; index < params.bars.length; index += 1) {
556
+ if (!params.condition[index]) continue;
557
+ const start = params.bars[Math.max(0, index - params.preBars)];
558
+ const end = params.bars[Math.min(params.bars.length - 1, index + params.postBars)];
559
+ windows.push({
560
+ endTime: end.time,
561
+ eventTime: params.bars[index].time,
562
+ startTime: start.time
563
+ });
564
+ }
565
+ return windows;
566
+ }
567
+
568
+ // src/quant/signal-study/forward-returns.ts
569
+ function studyForwardReturns(params) {
570
+ const forward = forwardReturns(params.prices, params.horizonBars);
571
+ const all = forward.filter((value) => value != null);
572
+ const conditioned = forward.filter(
573
+ (value, index) => value != null && params.condition[index] === true
574
+ );
575
+ return {
576
+ conditioned: summarizeNumbers(conditioned),
577
+ horizonBars: params.horizonBars,
578
+ unconditional: summarizeNumbers(all)
579
+ };
580
+ }
581
+
582
+ // src/quant/signal-study/hit-rate.ts
583
+ function hitRate(values) {
584
+ const finite = values.filter((value) => Number.isFinite(value));
585
+ if (finite.length === 0) return null;
586
+ return finite.filter((value) => value > 0).length / finite.length;
587
+ }
588
+
589
+ // src/quant/signal-study/information-coefficient.ts
590
+ function informationCoefficient(params) {
591
+ const pairs = [];
592
+ const length = Math.min(params.signal.length, params.forwardReturns.length);
593
+ for (let index = 0; index < length; index += 1) {
594
+ const x = params.signal[index];
595
+ const y = params.forwardReturns[index];
596
+ if (x != null && y != null && Number.isFinite(x) && Number.isFinite(y)) {
597
+ pairs.push({ x, y });
598
+ }
599
+ }
600
+ if (pairs.length < 3) return null;
601
+ const meanX = pairs.reduce((total, pair) => total + pair.x, 0) / pairs.length;
602
+ const meanY = pairs.reduce((total, pair) => total + pair.y, 0) / pairs.length;
603
+ let covariance = 0;
604
+ let varianceX = 0;
605
+ let varianceY = 0;
606
+ for (const pair of pairs) {
607
+ const dx = pair.x - meanX;
608
+ const dy = pair.y - meanY;
609
+ covariance += dx * dy;
610
+ varianceX += dx * dx;
611
+ varianceY += dy * dy;
612
+ }
613
+ if (varianceX === 0 || varianceY === 0) return null;
614
+ return covariance / Math.sqrt(varianceX * varianceY);
615
+ }
616
+
617
+ // src/quant/signal-study/summary.ts
618
+ function signalStudySummary(params) {
619
+ if (params.conditionedCount === 0) {
620
+ return "No signal events were available in the supplied window.";
621
+ }
622
+ const conditioned = params.conditionedMean == null ? "n/a" : `${(params.conditionedMean * 100).toFixed(2)}%`;
623
+ const unconditional = params.unconditionalMean == null ? "n/a" : `${(params.unconditionalMean * 100).toFixed(2)}%`;
624
+ return `Signal events=${params.conditionedCount}; conditioned forward return=${conditioned}; unconditional=${unconditional}.`;
625
+ }
626
+
627
+ // src/quant/strategies/breakout.ts
628
+ var DONCHIAN_BREAKOUT_RULE_KIND = "donchian_breakout";
629
+
630
+ // src/quant/strategies/funding-carry.ts
631
+ var FUNDING_CARRY_RULE_KIND = "funding_carry";
632
+
633
+ // src/quant/strategies/macd-trend.ts
634
+ var MACD_CROSSOVER_RULE_KIND = "macd_crossover";
635
+
636
+ // src/quant/strategies/ma-cross.ts
637
+ var MA_CROSS_RULE_KIND = "ma_cross";
638
+
639
+ // src/quant/strategies/momentum.ts
640
+ var MOMENTUM_RULE_KIND = "momentum";
641
+
642
+ // src/quant/strategies/registry.ts
643
+ var BASE_TESTS = [
644
+ "prompt_plan_check",
645
+ "data_availability",
646
+ "signal_study",
647
+ "idea_rule_simulation",
648
+ "variant_sensitivity",
649
+ "leakage_check",
650
+ "cost_stress"
651
+ ];
652
+ var QUANT_FAMILY_CAPABILITIES = [
653
+ {
654
+ aliases: ["benchmark", "buy-and-hold", "buy_and_hold", "hold"],
655
+ family: "benchmark",
656
+ supportLevel: "v1_replayable",
657
+ supportedTestKinds: BASE_TESTS
658
+ },
659
+ {
660
+ aliases: ["signal_rule", "moving_average", "ma_cross", "rsi", "macd", "bollinger", "donchian"],
661
+ family: "signal_rule",
662
+ supportLevel: "v1_replayable",
663
+ supportedTestKinds: BASE_TESTS
664
+ },
665
+ {
666
+ aliases: ["trend", "momentum", "breakout", "time_series_momentum"],
667
+ family: "trend",
668
+ supportLevel: "v1_replayable",
669
+ supportedTestKinds: BASE_TESTS
670
+ },
671
+ {
672
+ aliases: ["mean_reversion", "zscore", "rsi_mean_reversion", "bollinger_mean_reversion"],
673
+ family: "mean_reversion",
674
+ supportLevel: "v1_replayable",
675
+ supportedTestKinds: BASE_TESTS
676
+ },
677
+ {
678
+ aliases: ["stat_arb", "pairs", "pair_spread", "cointegration"],
679
+ family: "stat_arb",
680
+ supportLevel: "v1_signal_or_idea_test",
681
+ supportedTestKinds: ["prompt_plan_check", "data_availability", "signal_study", "leakage_check"]
682
+ },
683
+ {
684
+ aliases: ["carry", "funding", "basis", "funding_carry"],
685
+ family: "carry",
686
+ supportLevel: "v1_signal_or_idea_test",
687
+ supportedTestKinds: BASE_TESTS
688
+ },
689
+ {
690
+ aliases: ["hedge", "overlay", "tail_hedge"],
691
+ family: "hedge",
692
+ supportLevel: "v1_signal_or_idea_test",
693
+ supportedTestKinds: ["prompt_plan_check", "data_availability", "signal_study", "cost_stress"]
694
+ },
695
+ {
696
+ aliases: ["execution", "twap", "vwap", "participation", "iceberg"],
697
+ family: "execution",
698
+ supportLevel: "advanced_research",
699
+ supportedTestKinds: ["prompt_plan_check", "data_availability", "cost_stress"]
700
+ },
701
+ {
702
+ aliases: ["market_making", "maker", "avellaneda", "inventory_skew"],
703
+ family: "market_making",
704
+ supportLevel: "advanced_research",
705
+ supportedTestKinds: ["prompt_plan_check", "data_availability", "cost_stress"]
706
+ },
707
+ {
708
+ aliases: ["options", "vol", "black_scholes", "put_call_parity"],
709
+ family: "options",
710
+ supportLevel: "unsupported_until_data",
711
+ supportedTestKinds: ["prompt_plan_check", "data_availability"]
712
+ },
713
+ {
714
+ aliases: ["regime_ml", "ml", "regime", "markov", "hmm", "rl"],
715
+ family: "regime_ml",
716
+ supportLevel: "advanced_research",
717
+ supportedTestKinds: ["prompt_plan_check", "data_availability", "leakage_check"]
718
+ },
719
+ {
720
+ aliases: ["prediction_market", "outcome", "hip4", "binary"],
721
+ family: "prediction_market",
722
+ supportLevel: "v1_signal_or_idea_test",
723
+ supportedTestKinds: ["prompt_plan_check", "data_availability", "signal_study", "cost_stress"]
724
+ },
725
+ {
726
+ aliases: ["defi_lp", "amm", "lp", "concentrated_liquidity"],
727
+ family: "defi_lp",
728
+ supportLevel: "unsupported_until_data",
729
+ supportedTestKinds: ["prompt_plan_check", "data_availability"]
730
+ }
731
+ ];
732
+ function resolveQuantFamilyCapability(value) {
733
+ const normalized = value.trim().toLowerCase().replace(/[\s-]+/g, "_");
734
+ return QUANT_FAMILY_CAPABILITIES.find(
735
+ (capability) => capability.family === normalized || capability.aliases.some((alias) => alias === normalized)
736
+ ) ?? null;
737
+ }
738
+
739
+ // src/quant/strategies/rsi-mean-reversion.ts
740
+ var RSI_MEAN_REVERSION_RULE_KIND = "rsi_mean_reversion";
741
+
742
+ // src/quant/strategies/rule-evaluator.ts
743
+ function numberParam(params, key, fallback) {
744
+ const value = params[key];
745
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
746
+ }
747
+ function stringParam(params, key, fallback) {
748
+ const value = params[key];
749
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : fallback;
750
+ }
751
+ function ma(values, type, period) {
752
+ return type.toLowerCase() === "sma" ? sma(values, period) : ema(values, period);
753
+ }
754
+ function shiftedBreakoutCondition(bars, period) {
755
+ const channels = donchian(
756
+ bars.map((bar) => bar.high),
757
+ bars.map((bar) => bar.low),
758
+ period
759
+ );
760
+ return bars.map((bar, index) => {
761
+ const prior = channels[index - 1];
762
+ return prior?.upper != null && bar.close > prior.upper;
763
+ });
764
+ }
765
+ function evaluateQuantRule(params) {
766
+ const prices = closePrices(params.bars);
767
+ const rule = params.idea.rule;
768
+ const kind = rule?.kind ?? (params.idea.family === "benchmark" ? "buy_hold" : "momentum");
769
+ const ruleParams = rule?.params ?? {};
770
+ const warnings = [];
771
+ let condition = [];
772
+ let positions = [];
773
+ let signal = [];
774
+ if (kind === "buy_hold") {
775
+ condition = prices.map(() => true);
776
+ positions = prices.map(() => 1);
777
+ signal = prices.map(() => 1);
778
+ } else if (kind === "ma_cross" || kind === "moving_average_crossover") {
779
+ const fastPeriod = Math.max(1, Math.trunc(numberParam(ruleParams, "fastPeriod", 20)));
780
+ const slowPeriod = Math.max(fastPeriod + 1, Math.trunc(numberParam(ruleParams, "slowPeriod", 100)));
781
+ const averageType = stringParam(ruleParams, "averageType", "ema");
782
+ const fast = ma(prices, averageType, fastPeriod);
783
+ const slow = ma(prices, averageType, slowPeriod);
784
+ signal = prices.map(
785
+ (_, index) => fast[index] == null || slow[index] == null ? null : fast[index] - slow[index]
786
+ );
787
+ condition = signal.map((value) => value != null && value > 0);
788
+ positions = condition.map((active) => active ? 1 : 0);
789
+ } else if (kind === "rsi_mean_reversion") {
790
+ const period = Math.max(2, Math.trunc(numberParam(ruleParams, "period", 14)));
791
+ const oversold = numberParam(ruleParams, "oversold", 30);
792
+ const exit = numberParam(ruleParams, "exit", 50);
793
+ signal = rsi(prices, period);
794
+ let active = false;
795
+ positions = signal.map((value) => {
796
+ if (value == null) return 0;
797
+ if (value <= oversold) active = true;
798
+ if (value >= exit) active = false;
799
+ return active ? 1 : 0;
800
+ });
801
+ condition = signal.map((value) => value != null && value <= oversold);
802
+ } else if (kind === "macd_crossover") {
803
+ const points = macd(prices);
804
+ signal = points.map(
805
+ (point) => point.macd == null || point.signal == null ? null : point.macd - point.signal
806
+ );
807
+ condition = signal.map((value) => value != null && value > 0);
808
+ positions = condition.map((active) => active ? 1 : 0);
809
+ } else if (kind === "donchian_breakout") {
810
+ const period = Math.max(2, Math.trunc(numberParam(ruleParams, "period", 20)));
811
+ condition = shiftedBreakoutCondition(params.bars, period);
812
+ signal = condition.map((active) => active ? 1 : 0);
813
+ positions = condition.map((active) => active ? 1 : 0);
814
+ } else if (kind === "zscore_mean_reversion") {
815
+ const period = Math.max(2, Math.trunc(numberParam(ruleParams, "period", 20)));
816
+ const entry = Math.abs(numberParam(ruleParams, "entry", 2));
817
+ const exit = Math.abs(numberParam(ruleParams, "exit", 0.25));
818
+ signal = rollingZScore(prices, period);
819
+ let active = false;
820
+ positions = signal.map((value) => {
821
+ if (value == null) return 0;
822
+ if (value <= -entry) active = true;
823
+ if (Math.abs(value) <= exit || value >= entry) active = false;
824
+ return active ? 1 : 0;
825
+ });
826
+ condition = signal.map((value) => value != null && value <= -entry);
827
+ } else if (kind === "momentum") {
828
+ const lookbackBars = Math.max(1, Math.trunc(numberParam(ruleParams, "lookbackBars", 30)));
829
+ const threshold = numberParam(ruleParams, "threshold", 0);
830
+ signal = momentum(prices, lookbackBars);
831
+ condition = signal.map((value) => value != null && value > threshold);
832
+ positions = condition.map((active) => active ? 1 : 0);
833
+ } else if (kind === "funding_carry") {
834
+ const threshold = numberParam(ruleParams, "maxFundingRate", 0);
835
+ signal = fundingRates(params.bars);
836
+ condition = signal.map((value) => value != null && value <= threshold);
837
+ positions = condition.map((active) => active ? 1 : 0);
838
+ } else {
839
+ warnings.push(`Rule kind ${kind} is not supported by the V1 evaluator; using signal-only flat positions.`);
840
+ condition = prices.map(() => false);
841
+ positions = prices.map(() => 0);
842
+ signal = prices.map(() => null);
843
+ }
844
+ return { condition, positions, signal, warnings };
845
+ }
846
+
847
+ // src/quant/strategies/zscore-mean-reversion.ts
848
+ var ZSCORE_MEAN_REVERSION_RULE_KIND = "zscore_mean_reversion";
849
+
850
+ // src/quant/tester/plan.ts
851
+ function buildQuantTestPlan(idea) {
852
+ const capability = resolveQuantFamilyCapability(idea.family);
853
+ if (!capability) {
854
+ return {
855
+ supportedTestKinds: ["prompt_plan_check", "data_availability"],
856
+ supportLevel: "unsupported_until_data",
857
+ warnings: [`No V1 quant capability found for family ${idea.family}.`]
858
+ };
859
+ }
860
+ return {
861
+ supportedTestKinds: capability.supportedTestKinds,
862
+ supportLevel: capability.supportLevel,
863
+ warnings: capability.supportLevel === "advanced_research" || capability.supportLevel === "unsupported_until_data" ? [`Family ${idea.family} is ${capability.supportLevel}; do not mark it strict-backtest-ready.`] : []
864
+ };
865
+ }
866
+
867
+ // src/quant/tester/report.ts
868
+ function finalizeQuantTesterReport(report) {
869
+ return quantTesterReportV1Schema.parse(report);
870
+ }
871
+
872
+ // src/quant/tester/warnings.ts
873
+ function quantDataWarnings(params) {
874
+ const warnings = [];
875
+ if (params.bars.length < 50) {
876
+ warnings.push("Sample has fewer than 50 bars; treat metrics as unstable.");
877
+ }
878
+ if (params.bars.length > 1) {
879
+ const gaps = [];
880
+ for (let index = 1; index < params.bars.length; index += 1) {
881
+ gaps.push(params.bars[index].time - params.bars[index - 1].time);
882
+ }
883
+ const medianGap = gaps.slice().sort((a, b) => a - b)[Math.floor(gaps.length / 2)];
884
+ if (gaps.some((gap) => gap > medianGap * 2)) {
885
+ warnings.push("Bars contain time gaps larger than twice the median interval.");
886
+ }
887
+ }
888
+ if (params.request.variantSpace && Object.keys(params.request.variantSpace).length > 20) {
889
+ warnings.push("Variant space is broad; use multiple-comparison controls before promotion.");
890
+ }
891
+ return warnings;
892
+ }
893
+ function quantCostBps(request) {
894
+ return (request.assumptions.takerFeeBps ?? request.assumptions.makerFeeBps ?? 0) + (request.assumptions.slippageBps ?? 0);
895
+ }
896
+
897
+ // src/quant/tester/run-idea-test.ts
898
+ function symbolForRequest(request) {
899
+ return request.idea.market.symbol ?? request.idea.market.universe?.[0] ?? "UNKNOWN";
900
+ }
901
+ function actionForPosition(target) {
902
+ if (target > 0) return "long";
903
+ if (target < 0) return "short";
904
+ return "flat";
905
+ }
906
+ function simulate(params) {
907
+ const prices = closePrices(params.bars);
908
+ const initialEquity = params.request.assumptions.initialEquityUsd ?? 1e4;
909
+ const symbol = symbolForRequest(params.request);
910
+ let equity = initialEquity;
911
+ let peak = initialEquity;
912
+ let maxDrawdown = 0;
913
+ let previousPosition = 0;
914
+ let turnover = 0;
915
+ let trades = 0;
916
+ const decisions = [];
917
+ for (let index = 0; index < params.bars.length; index += 1) {
918
+ const targetPosition = params.positions[index] ?? 0;
919
+ const deltaPosition = Math.abs(targetPosition - previousPosition);
920
+ if (deltaPosition > 0) {
921
+ turnover += deltaPosition;
922
+ trades += 1;
923
+ equity -= equity * deltaPosition * (params.costBps / 1e4);
924
+ }
925
+ if (index > 0) {
926
+ const priceReturn = prices[index - 1] === 0 ? 0 : prices[index] / prices[index - 1] - 1;
927
+ equity *= 1 + previousPosition * priceReturn;
928
+ }
929
+ peak = Math.max(peak, equity);
930
+ maxDrawdown = Math.max(maxDrawdown, peak === 0 ? 0 : (peak - equity) / peak);
931
+ decisions.push({
932
+ time: params.bars[index].time,
933
+ symbol,
934
+ action: actionForPosition(targetPosition),
935
+ targetPosition,
936
+ reason: targetPosition === previousPosition ? "rule maintained target position" : "rule changed target position",
937
+ price: params.bars[index].close
938
+ });
939
+ previousPosition = targetPosition;
940
+ }
941
+ return {
942
+ decisions: compactDecisionChanges(decisions),
943
+ metrics: {
944
+ endingEquityUsd: equity,
945
+ maxDrawdownPct: maxDrawdown * 100,
946
+ netReturnPct: (equity / initialEquity - 1) * 100,
947
+ turnover,
948
+ trades
949
+ }
950
+ };
951
+ }
952
+ function runQuantIdeaTest(params) {
953
+ const request = quantTestRequestV1Schema.parse(params.request);
954
+ const bars = normalizeQuantBars(params.bars);
955
+ const plan = buildQuantTestPlan(request.idea);
956
+ const warnings = [...plan.warnings, ...quantDataWarnings({ bars, request })];
957
+ const supported = plan.supportedTestKinds.includes("idea_rule_simulation");
958
+ if (!supported) {
959
+ return finalizeQuantTesterReport({
960
+ ok: false,
961
+ testRunKind: "idea_rule_simulation",
962
+ supported: false,
963
+ unsupportedReason: `Family ${request.idea.family} does not support deterministic idea simulation in V1.`,
964
+ summary: "Idea simulation is not supported for this family.",
965
+ dataLineage: buildQuantDataLineage({ request, warnings }),
966
+ warnings
967
+ });
968
+ }
969
+ const evaluated = evaluateQuantRule({ bars, idea: request.idea });
970
+ warnings.push(...evaluated.warnings);
971
+ const simulation = simulate({
972
+ bars,
973
+ costBps: quantCostBps(request),
974
+ positions: evaluated.positions,
975
+ request
976
+ });
977
+ const decisionArtifact = validateDecisionArtifact({
978
+ version: "1",
979
+ family: request.idea.family,
980
+ symbol: symbolForRequest(request),
981
+ resolution: request.window.resolution,
982
+ decisions: simulation.decisions,
983
+ warnings
984
+ });
985
+ return finalizeQuantTesterReport({
986
+ ok: true,
987
+ testRunKind: "idea_rule_simulation",
988
+ supported: true,
989
+ summary: `Idea simulation completed: net return ${simulation.metrics.netReturnPct.toFixed(2)}%, trades ${simulation.metrics.trades}.`,
990
+ dataLineage: buildQuantDataLineage({ request, warnings }),
991
+ ideaSimulation: simulation.metrics,
992
+ decisionArtifact,
993
+ warnings
994
+ });
995
+ }
996
+
997
+ // src/quant/tester/run-signal-study.ts
998
+ function horizonBars(request) {
999
+ const value = request.idea.rule?.params.horizonBars;
1000
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : 1;
1001
+ }
1002
+ function runSignalStudy(params) {
1003
+ const request = quantTestRequestV1Schema.parse(params.request);
1004
+ const bars = normalizeQuantBars(params.bars);
1005
+ const plan = buildQuantTestPlan(request.idea);
1006
+ const warnings = [...plan.warnings, ...quantDataWarnings({ bars, request })];
1007
+ const supported = plan.supportedTestKinds.includes("signal_study");
1008
+ if (!supported) {
1009
+ return finalizeQuantTesterReport({
1010
+ ok: false,
1011
+ testRunKind: "signal_study",
1012
+ supported: false,
1013
+ unsupportedReason: `Family ${request.idea.family} does not support signal studies in V1.`,
1014
+ summary: "Signal study is not supported for this family.",
1015
+ dataLineage: buildQuantDataLineage({ request, warnings }),
1016
+ warnings
1017
+ });
1018
+ }
1019
+ const evaluated = evaluateQuantRule({ bars, idea: request.idea });
1020
+ warnings.push(...evaluated.warnings);
1021
+ const prices = closePrices(bars);
1022
+ const horizon = horizonBars(request);
1023
+ const study = studyForwardReturns({
1024
+ condition: evaluated.condition,
1025
+ horizonBars: horizon,
1026
+ prices
1027
+ });
1028
+ const forward = forwardReturns(prices, horizon);
1029
+ const excursions = summarizeExcursions({
1030
+ bars,
1031
+ condition: evaluated.condition,
1032
+ horizonBars: horizon
1033
+ });
1034
+ const ic = informationCoefficient({
1035
+ forwardReturns: forward,
1036
+ signal: evaluated.signal
1037
+ });
1038
+ const summary = signalStudySummary({
1039
+ conditionedCount: study.conditioned.count,
1040
+ conditionedMean: study.conditioned.mean,
1041
+ unconditionalMean: study.unconditional.mean
1042
+ });
1043
+ return finalizeQuantTesterReport({
1044
+ ok: true,
1045
+ testRunKind: "signal_study",
1046
+ supported: true,
1047
+ summary,
1048
+ dataLineage: buildQuantDataLineage({ request, warnings }),
1049
+ signalStudy: {
1050
+ forwardReturns: study,
1051
+ informationCoefficient: ic,
1052
+ excursions,
1053
+ signalEventCount: evaluated.condition.filter(Boolean).length
1054
+ },
1055
+ warnings
1056
+ });
1057
+ }
1058
+
1059
+ // src/quant/timeframes.ts
1060
+ var QUANT_RESOLUTION_SECONDS = {
1061
+ "1": 60,
1062
+ "5": 300,
1063
+ "15": 900,
1064
+ "30": 1800,
1065
+ "60": 3600,
1066
+ "240": 14400,
1067
+ "1D": 86400,
1068
+ "1W": 604800
1069
+ };
1070
+ function quantResolutionToSeconds(resolution) {
1071
+ return QUANT_RESOLUTION_SECONDS[resolution];
1072
+ }
1073
+ function parseQuantTimeToSeconds(value) {
1074
+ if (typeof value === "number" && Number.isFinite(value)) {
1075
+ return Math.max(0, Math.trunc(value));
1076
+ }
1077
+ if (typeof value === "string" && value.trim().length > 0) {
1078
+ const trimmed = value.trim();
1079
+ if (/^-?(?:\d+\.?\d*|\.\d+)$/.test(trimmed)) {
1080
+ return Math.max(0, Math.trunc(Number.parseFloat(trimmed)));
1081
+ }
1082
+ const parsed = new Date(trimmed);
1083
+ if (!Number.isNaN(parsed.getTime())) {
1084
+ return Math.max(0, Math.trunc(parsed.getTime() / 1e3));
1085
+ }
1086
+ }
1087
+ return null;
1088
+ }
1089
+ function assertQuantWindow(params) {
1090
+ const startSeconds = parseQuantTimeToSeconds(params.timeframeStart);
1091
+ const endSeconds = parseQuantTimeToSeconds(params.timeframeEnd);
1092
+ if (startSeconds == null || endSeconds == null) {
1093
+ throw new Error("Quant test window must use parseable start and end times");
1094
+ }
1095
+ if (endSeconds <= startSeconds) {
1096
+ throw new Error("Quant test window end must be after start");
1097
+ }
1098
+ return { endSeconds, startSeconds };
1099
+ }
1100
+
1101
+ export { DONCHIAN_BREAKOUT_RULE_KIND, FUNDING_CARRY_RULE_KIND, MACD_CROSSOVER_RULE_KIND, MA_CROSS_RULE_KIND, MOMENTUM_RULE_KIND, QUANT_FAMILY_CAPABILITIES, QUANT_RESOLUTION_SECONDS, RSI_MEAN_REVERSION_RULE_KIND, ZSCORE_MEAN_REVERSION_RULE_KIND, assertQuantWindow, atr, beta, bollinger, buildEventWindows, buildQuantDataLineage, buildQuantTestPlan, closePrices, compactDecisionChanges, correlation, cumulativeFunding, donchian, ema, evaluateQuantRule, finalizeQuantTesterReport, forwardReturns, fundingRates, hitRate, informationCoefficient, logReturns, macd, momentum, normalizeQuantBars, parseQuantTimeToSeconds, quantBarSchema, quantCostBps, quantDataWarnings, quantDecisionActionSchema, quantDecisionArtifactV1Schema, quantDecisionSchema, quantFeatureSpecSchema, quantIdeaSpecV1Schema, quantResolutionSchema, quantResolutionToSeconds, quantRuleSpecSchema, quantStrategyFamilySchema, quantTestKindSchema, quantTestRequestV1Schema, quantTesterReportV1Schema, relativeStrength, relativeVolume, resolveQuantFamilyCapability, rollingCorrelation, rollingVolatility, rollingZScore, rsi, runQuantIdeaTest, runSignalStudy, signalStudySummary, simpleReturns, sliceBarsToWindow, sma, studyForwardReturns, summarizeExcursions, summarizeNumbers, trueRanges, typicalPrices, validateDecisionArtifact, volumes };
1102
+ //# sourceMappingURL=index.js.map
1103
+ //# sourceMappingURL=index.js.map