@ebowwa/quant-mcp 1.0.0 → 1.1.1
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/bun.lock +3 -7
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +264 -78
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
- package/src/index.ts +311 -115
package/src/index.ts
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @ebowwa/quant-mcp - Quantitative Trading MCP Server
|
|
4
4
|
*
|
|
5
|
+
* High-performance MCP server using hybrid TypeScript/Rust approach:
|
|
6
|
+
* - TypeScript for scalar operations (20-40x faster - no FFI overhead)
|
|
7
|
+
* - Rust FFI for array operations (2-13x faster for O(n) calculations)
|
|
8
|
+
*
|
|
5
9
|
* Multi-market quantitative analysis tools for AI trading:
|
|
6
10
|
* - Prediction Markets: Kelly criterion, AMM math, LMSR, arbitrage
|
|
7
11
|
* - Technical Indicators: RSI, MACD, Bollinger, ATR, etc.
|
|
@@ -15,68 +19,274 @@ import {
|
|
|
15
19
|
CallToolRequestSchema,
|
|
16
20
|
ListToolsRequestSchema,
|
|
17
21
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
18
|
-
import { z } from "zod";
|
|
19
22
|
|
|
20
|
-
// Import quant
|
|
23
|
+
// Import from quant-rust - HYBRID approach
|
|
24
|
+
// Scalar ops (TS) are 20-40x faster, Array ops (Rust) are 2-13x faster
|
|
21
25
|
import {
|
|
22
|
-
//
|
|
23
|
-
|
|
26
|
+
// Scalar operations (TypeScript - FAST for single calls)
|
|
27
|
+
kellyCriterionTS,
|
|
28
|
+
detectArbitrageTS,
|
|
29
|
+
convertOddsTS,
|
|
30
|
+
calculateEdge,
|
|
31
|
+
expectedValue,
|
|
32
|
+
hasPositiveEV,
|
|
24
33
|
fractionalKelly,
|
|
25
34
|
probToDecimalOdds,
|
|
26
35
|
probToAmericanOdds,
|
|
27
36
|
decimalOddsToProb,
|
|
28
37
|
americanOddsToProb,
|
|
29
|
-
ammBuyCost,
|
|
30
|
-
ammSharesReceived,
|
|
31
|
-
ammPriceImpact,
|
|
32
|
-
ammImpermanentLoss,
|
|
33
|
-
lmsrPrice,
|
|
34
|
-
lmsrBuyCost,
|
|
35
|
-
detectArbitrage,
|
|
36
|
-
brierScore,
|
|
37
|
-
logLoss,
|
|
38
|
-
calculateCalibration,
|
|
39
|
-
hasPositiveEV,
|
|
40
|
-
calculateEdge,
|
|
41
|
-
calculatePnL,
|
|
42
|
-
calculateUnrealizedPnL,
|
|
43
38
|
|
|
44
|
-
//
|
|
39
|
+
// Array operations (Rust FFI - FAST for large datasets)
|
|
45
40
|
sma,
|
|
46
41
|
ema,
|
|
47
|
-
wma,
|
|
48
42
|
rsi,
|
|
49
43
|
macd,
|
|
50
|
-
stochastic,
|
|
51
|
-
bollingerBands,
|
|
52
|
-
atr,
|
|
53
|
-
adx,
|
|
54
|
-
pivotPoints,
|
|
55
|
-
fibonacciRetracement,
|
|
56
|
-
|
|
57
|
-
// Risk management
|
|
58
|
-
fixedFractionalSize,
|
|
59
|
-
historicalVaR,
|
|
60
|
-
parametricVaR,
|
|
61
|
-
calculateVaR,
|
|
62
|
-
calculateDrawdown,
|
|
63
|
-
sharpeRatio,
|
|
64
|
-
sortinoRatio,
|
|
65
|
-
calmarRatio,
|
|
66
|
-
beta,
|
|
67
|
-
alpha,
|
|
68
|
-
|
|
69
|
-
// Statistics
|
|
70
|
-
distributionStats,
|
|
71
|
-
pearsonCorrelation,
|
|
72
|
-
linearRegression,
|
|
73
|
-
autocorrelation,
|
|
74
|
-
|
|
75
|
-
// Utils
|
|
76
44
|
mean,
|
|
77
45
|
stdDev,
|
|
78
46
|
variance,
|
|
79
|
-
|
|
47
|
+
correlation,
|
|
48
|
+
calculateDrawdown,
|
|
49
|
+
calculateSharpeRatio,
|
|
50
|
+
calculateSortinoRatio,
|
|
51
|
+
calculateBetaAlpha,
|
|
52
|
+
calculateVar,
|
|
53
|
+
|
|
54
|
+
// AMM/LMSR (Rust FFI)
|
|
55
|
+
ammCalculateCost,
|
|
56
|
+
ammPriceImpact,
|
|
57
|
+
lmsrPrice,
|
|
58
|
+
lmsrCost,
|
|
59
|
+
|
|
60
|
+
// Types
|
|
61
|
+
type OddsType,
|
|
62
|
+
} from "@ebowwa/quant-rust";
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// TypeScript Fallbacks for functions not yet in quant-rust
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Weighted Moving Average
|
|
70
|
+
*/
|
|
71
|
+
function wma(prices: number[], period: number): number[] {
|
|
72
|
+
if (prices.length < period || period <= 0) return [];
|
|
73
|
+
const result: number[] = [];
|
|
74
|
+
const weightSum = (period * (period + 1)) / 2;
|
|
75
|
+
|
|
76
|
+
for (let i = period - 1; i < prices.length; i++) {
|
|
77
|
+
let sum = 0;
|
|
78
|
+
for (let j = 0; j < period; j++) {
|
|
79
|
+
sum += prices[i - period + 1 + j] * (j + 1);
|
|
80
|
+
}
|
|
81
|
+
result.push(sum / weightSum);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Bollinger Bands
|
|
88
|
+
*/
|
|
89
|
+
function bollingerBands(prices: number[], period: number = 20, stdDevMultiplier: number = 2) {
|
|
90
|
+
const ma = sma(prices, period);
|
|
91
|
+
const upper: number[] = [];
|
|
92
|
+
const lower: number[] = [];
|
|
93
|
+
|
|
94
|
+
for (let i = 0; i < ma.length; i++) {
|
|
95
|
+
const start = i;
|
|
96
|
+
const slice = prices.slice(start, start + period);
|
|
97
|
+
const std = stdDev(slice);
|
|
98
|
+
upper.push(ma[i] + std * stdDevMultiplier);
|
|
99
|
+
lower.push(ma[i] - std * stdDevMultiplier);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { middle: ma, upper, lower };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Average True Range
|
|
107
|
+
*/
|
|
108
|
+
function atr(high: number[], low: number[], close: number[], period: number = 14): number[] {
|
|
109
|
+
if (high.length < period + 1) return [];
|
|
110
|
+
|
|
111
|
+
const trValues: number[] = [];
|
|
112
|
+
for (let i = 1; i < high.length; i++) {
|
|
113
|
+
const tr = Math.max(
|
|
114
|
+
high[i] - low[i],
|
|
115
|
+
Math.abs(high[i] - close[i - 1]),
|
|
116
|
+
Math.abs(low[i] - close[i - 1])
|
|
117
|
+
);
|
|
118
|
+
trValues.push(tr);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return ema(trValues, period);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Stochastic Oscillator
|
|
126
|
+
*/
|
|
127
|
+
function stochastic(high: number[], low: number[], close: number[], kPeriod: number = 14, dPeriod: number = 3) {
|
|
128
|
+
const kValues: number[] = [];
|
|
129
|
+
|
|
130
|
+
for (let i = kPeriod - 1; i < close.length; i++) {
|
|
131
|
+
const highSlice = high.slice(i - kPeriod + 1, i + 1);
|
|
132
|
+
const lowSlice = low.slice(i - kPeriod + 1, i + 1);
|
|
133
|
+
const highestHigh = Math.max(...highSlice);
|
|
134
|
+
const lowestLow = Math.min(...lowSlice);
|
|
135
|
+
|
|
136
|
+
const k = ((close[i] - lowestLow) / (highestHigh - lowestLow)) * 100;
|
|
137
|
+
kValues.push(k);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const dValues = sma(kValues, dPeriod);
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
k: kValues.slice(dPeriod - 1),
|
|
144
|
+
d: dValues
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Pivot Points
|
|
150
|
+
*/
|
|
151
|
+
function pivotPoints(high: number, low: number, close: number) {
|
|
152
|
+
const pp = (high + low + close) / 3;
|
|
153
|
+
return {
|
|
154
|
+
pivot: pp,
|
|
155
|
+
r1: 2 * pp - low,
|
|
156
|
+
r2: pp + (high - low),
|
|
157
|
+
r3: high + 2 * (pp - low),
|
|
158
|
+
s1: 2 * pp - high,
|
|
159
|
+
s2: pp - (high - low),
|
|
160
|
+
s3: low - 2 * (high - pp),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Fibonacci Retracement
|
|
166
|
+
*/
|
|
167
|
+
function fibonacciRetracement(swingHigh: number, swingLow: number) {
|
|
168
|
+
const diff = swingHigh - swingLow;
|
|
169
|
+
return {
|
|
170
|
+
level_0: swingHigh,
|
|
171
|
+
level_236: swingHigh - diff * 0.236,
|
|
172
|
+
level_382: swingHigh - diff * 0.382,
|
|
173
|
+
level_500: swingHigh - diff * 0.5,
|
|
174
|
+
level_618: swingHigh - diff * 0.618,
|
|
175
|
+
level_786: swingHigh - diff * 0.786,
|
|
176
|
+
level_1: swingLow,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Fixed Fractional Position Sizing
|
|
182
|
+
*/
|
|
183
|
+
function fixedFractionalSize(capital: number, riskPercent: number, entryPrice: number, stopLoss: number) {
|
|
184
|
+
const riskAmount = capital * riskPercent;
|
|
185
|
+
const priceRisk = Math.abs(entryPrice - stopLoss);
|
|
186
|
+
const shares = priceRisk > 0 ? Math.floor(riskAmount / priceRisk) : 0;
|
|
187
|
+
return {
|
|
188
|
+
shares,
|
|
189
|
+
positionValue: shares * entryPrice,
|
|
190
|
+
riskAmount,
|
|
191
|
+
riskPercent: (riskAmount / capital) * 100,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Brier Score
|
|
197
|
+
*/
|
|
198
|
+
function brierScore(predictions: Array<{ predicted: number; actual: number }>): number {
|
|
199
|
+
if (predictions.length === 0) return 0;
|
|
200
|
+
const sum = predictions.reduce((acc, p) => acc + Math.pow(p.predicted - p.actual, 2), 0);
|
|
201
|
+
return sum / predictions.length;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Log Loss
|
|
206
|
+
*/
|
|
207
|
+
function logLoss(predictions: Array<{ predicted: number; actual: number }>): number {
|
|
208
|
+
if (predictions.length === 0) return 0;
|
|
209
|
+
const epsilon = 1e-15;
|
|
210
|
+
const sum = predictions.reduce((acc, p) => {
|
|
211
|
+
const prob = Math.max(epsilon, Math.min(1 - epsilon, p.predicted));
|
|
212
|
+
const actual = p.actual;
|
|
213
|
+
return acc - (actual * Math.log(prob) + (1 - actual) * Math.log(1 - prob));
|
|
214
|
+
}, 0);
|
|
215
|
+
return sum / predictions.length;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Distribution Statistics
|
|
220
|
+
*/
|
|
221
|
+
function distributionStats(data: number[]) {
|
|
222
|
+
if (data.length === 0) return { mean: 0, median: 0, stdDev: 0, variance: 0, min: 0, max: 0, count: 0 };
|
|
223
|
+
|
|
224
|
+
const sorted = [...data].sort((a, b) => a - b);
|
|
225
|
+
const avg = mean(data);
|
|
226
|
+
const std = stdDev(data);
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
mean: avg,
|
|
230
|
+
median: sorted[Math.floor(sorted.length / 2)],
|
|
231
|
+
stdDev: std,
|
|
232
|
+
variance: variance(data),
|
|
233
|
+
min: sorted[0],
|
|
234
|
+
max: sorted[sorted.length - 1],
|
|
235
|
+
count: data.length,
|
|
236
|
+
q1: sorted[Math.floor(sorted.length * 0.25)],
|
|
237
|
+
q3: sorted[Math.floor(sorted.length * 0.75)],
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Linear Regression
|
|
243
|
+
*/
|
|
244
|
+
function linearRegression(x: number[], y: number[]) {
|
|
245
|
+
if (x.length !== y.length || x.length < 2) {
|
|
246
|
+
return { slope: 0, intercept: 0, r2: 0 };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const n = x.length;
|
|
250
|
+
const sumX = x.reduce((a, b) => a + b, 0);
|
|
251
|
+
const sumY = y.reduce((a, b) => a + b, 0);
|
|
252
|
+
const sumXY = x.reduce((acc, xi, i) => acc + xi * y[i], 0);
|
|
253
|
+
const sumX2 = x.reduce((acc, xi) => acc + xi * xi, 0);
|
|
254
|
+
|
|
255
|
+
const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
|
|
256
|
+
const intercept = (sumY - slope * sumX) / n;
|
|
257
|
+
|
|
258
|
+
// R-squared
|
|
259
|
+
const yMean = sumY / n;
|
|
260
|
+
const ssTotal = y.reduce((acc, yi) => acc + Math.pow(yi - yMean, 2), 0);
|
|
261
|
+
const ssResidual = y.reduce((acc, yi, i) => {
|
|
262
|
+
const predicted = slope * x[i] + intercept;
|
|
263
|
+
return acc + Math.pow(yi - predicted, 2);
|
|
264
|
+
}, 0);
|
|
265
|
+
const r2 = 1 - ssResidual / ssTotal;
|
|
266
|
+
|
|
267
|
+
return { slope, intercept, r2 };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Autocorrelation
|
|
272
|
+
*/
|
|
273
|
+
function autocorrelation(data: number[], maxLag: number = 10): number[] {
|
|
274
|
+
if (data.length < maxLag + 1) return [];
|
|
275
|
+
|
|
276
|
+
const avg = mean(data);
|
|
277
|
+
const varianceVal = variance(data);
|
|
278
|
+
const acf: number[] = [];
|
|
279
|
+
|
|
280
|
+
for (let lag = 0; lag <= maxLag; lag++) {
|
|
281
|
+
let sum = 0;
|
|
282
|
+
for (let i = 0; i < data.length - lag; i++) {
|
|
283
|
+
sum += (data[i] - avg) * (data[i + lag] - avg);
|
|
284
|
+
}
|
|
285
|
+
acf.push(sum / (data.length * varianceVal));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return acf;
|
|
289
|
+
}
|
|
80
290
|
|
|
81
291
|
// ==============
|
|
82
292
|
// Tool Definitions
|
|
@@ -88,7 +298,7 @@ const tools = [
|
|
|
88
298
|
// ===================
|
|
89
299
|
{
|
|
90
300
|
name: "kelly_criterion",
|
|
91
|
-
description: "Calculate optimal bet size using Kelly criterion for binary prediction markets. Returns Kelly fraction, half-Kelly, quarter-Kelly, and position sizes.",
|
|
301
|
+
description: "Calculate optimal bet size using Kelly criterion for binary prediction markets. Returns Kelly fraction, half-Kelly, quarter-Kelly, and position sizes. Uses TypeScript for 20-40x faster performance.",
|
|
92
302
|
inputSchema: {
|
|
93
303
|
type: "object",
|
|
94
304
|
properties: {
|
|
@@ -115,7 +325,7 @@ const tools = [
|
|
|
115
325
|
},
|
|
116
326
|
{
|
|
117
327
|
name: "convert_odds",
|
|
118
|
-
description: "Convert between probability, decimal odds, and American odds formats",
|
|
328
|
+
description: "Convert between probability, decimal odds, and American odds formats. Uses TypeScript for 20-40x faster performance.",
|
|
119
329
|
inputSchema: {
|
|
120
330
|
type: "object",
|
|
121
331
|
properties: {
|
|
@@ -131,7 +341,7 @@ const tools = [
|
|
|
131
341
|
},
|
|
132
342
|
{
|
|
133
343
|
name: "amm_calculate_cost",
|
|
134
|
-
description: "Calculate cost to buy shares from a constant-product AMM (like Polymarket).
|
|
344
|
+
description: "Calculate cost to buy shares from a constant-product AMM (like Polymarket). Uses Rust FFI for high performance.",
|
|
135
345
|
inputSchema: {
|
|
136
346
|
type: "object",
|
|
137
347
|
properties: {
|
|
@@ -145,7 +355,7 @@ const tools = [
|
|
|
145
355
|
},
|
|
146
356
|
{
|
|
147
357
|
name: "amm_price_impact",
|
|
148
|
-
description: "Calculate price impact and slippage for a trade on a constant-product AMM",
|
|
358
|
+
description: "Calculate price impact and slippage for a trade on a constant-product AMM. Uses Rust FFI for high performance.",
|
|
149
359
|
inputSchema: {
|
|
150
360
|
type: "object",
|
|
151
361
|
properties: {
|
|
@@ -159,7 +369,7 @@ const tools = [
|
|
|
159
369
|
},
|
|
160
370
|
{
|
|
161
371
|
name: "lmsr_calculate",
|
|
162
|
-
description: "Calculate price and cost using LMSR (Logarithmic Market Scoring Rule) for prediction markets",
|
|
372
|
+
description: "Calculate price and cost using LMSR (Logarithmic Market Scoring Rule) for prediction markets. Uses Rust FFI for high performance.",
|
|
163
373
|
inputSchema: {
|
|
164
374
|
type: "object",
|
|
165
375
|
properties: {
|
|
@@ -179,7 +389,7 @@ const tools = [
|
|
|
179
389
|
},
|
|
180
390
|
{
|
|
181
391
|
name: "detect_arbitrage",
|
|
182
|
-
description: "Detect arbitrage opportunities in prediction markets. Returns profit if YES + NO prices < 1.",
|
|
392
|
+
description: "Detect arbitrage opportunities in prediction markets. Returns profit if YES + NO prices < 1. Uses TypeScript for 15-40x faster performance.",
|
|
183
393
|
inputSchema: {
|
|
184
394
|
type: "object",
|
|
185
395
|
properties: {
|
|
@@ -191,7 +401,7 @@ const tools = [
|
|
|
191
401
|
},
|
|
192
402
|
{
|
|
193
403
|
name: "calculate_edge",
|
|
194
|
-
description: "Calculate your edge (advantage) in a prediction market bet",
|
|
404
|
+
description: "Calculate your edge (advantage) in a prediction market bet. Uses TypeScript for fast scalar calculation.",
|
|
195
405
|
inputSchema: {
|
|
196
406
|
type: "object",
|
|
197
407
|
properties: {
|
|
@@ -229,7 +439,7 @@ const tools = [
|
|
|
229
439
|
// ===================
|
|
230
440
|
{
|
|
231
441
|
name: "calculate_sma",
|
|
232
|
-
description: "Calculate Simple Moving Average for price data",
|
|
442
|
+
description: "Calculate Simple Moving Average for price data. Uses Rust FFI for 10-20x faster performance on large arrays.",
|
|
233
443
|
inputSchema: {
|
|
234
444
|
type: "object",
|
|
235
445
|
properties: {
|
|
@@ -241,7 +451,7 @@ const tools = [
|
|
|
241
451
|
},
|
|
242
452
|
{
|
|
243
453
|
name: "calculate_ema",
|
|
244
|
-
description: "Calculate Exponential Moving Average for price data",
|
|
454
|
+
description: "Calculate Exponential Moving Average for price data. Uses Rust FFI for 10-20x faster performance on large arrays.",
|
|
245
455
|
inputSchema: {
|
|
246
456
|
type: "object",
|
|
247
457
|
properties: {
|
|
@@ -253,7 +463,7 @@ const tools = [
|
|
|
253
463
|
},
|
|
254
464
|
{
|
|
255
465
|
name: "calculate_rsi",
|
|
256
|
-
description: "Calculate Relative Strength Index (RSI) momentum indicator (0-100)",
|
|
466
|
+
description: "Calculate Relative Strength Index (RSI) momentum indicator (0-100). Uses Rust FFI for high performance.",
|
|
257
467
|
inputSchema: {
|
|
258
468
|
type: "object",
|
|
259
469
|
properties: {
|
|
@@ -265,7 +475,7 @@ const tools = [
|
|
|
265
475
|
},
|
|
266
476
|
{
|
|
267
477
|
name: "calculate_macd",
|
|
268
|
-
description: "Calculate MACD (Moving Average Convergence Divergence) indicator",
|
|
478
|
+
description: "Calculate MACD (Moving Average Convergence Divergence) indicator. Uses Rust FFI for high performance.",
|
|
269
479
|
inputSchema: {
|
|
270
480
|
type: "object",
|
|
271
481
|
properties: {
|
|
@@ -364,7 +574,7 @@ const tools = [
|
|
|
364
574
|
},
|
|
365
575
|
{
|
|
366
576
|
name: "calculate_var",
|
|
367
|
-
description: "Calculate Value at Risk (VaR) and Expected Shortfall (CVaR) for a returns series",
|
|
577
|
+
description: "Calculate Value at Risk (VaR) and Expected Shortfall (CVaR) for a returns series. Uses Rust FFI for high performance.",
|
|
368
578
|
inputSchema: {
|
|
369
579
|
type: "object",
|
|
370
580
|
properties: {
|
|
@@ -376,7 +586,7 @@ const tools = [
|
|
|
376
586
|
},
|
|
377
587
|
{
|
|
378
588
|
name: "calculate_drawdown",
|
|
379
|
-
description: "Calculate maximum drawdown and drawdown analysis for an equity curve",
|
|
589
|
+
description: "Calculate maximum drawdown and drawdown analysis for an equity curve. Uses Rust FFI for 5-10x faster performance.",
|
|
380
590
|
inputSchema: {
|
|
381
591
|
type: "object",
|
|
382
592
|
properties: {
|
|
@@ -387,7 +597,7 @@ const tools = [
|
|
|
387
597
|
},
|
|
388
598
|
{
|
|
389
599
|
name: "calculate_sharpe_ratio",
|
|
390
|
-
description: "Calculate Sharpe ratio for risk-adjusted returns",
|
|
600
|
+
description: "Calculate Sharpe ratio for risk-adjusted returns. Uses Rust FFI for 2x faster performance.",
|
|
391
601
|
inputSchema: {
|
|
392
602
|
type: "object",
|
|
393
603
|
properties: {
|
|
@@ -400,7 +610,7 @@ const tools = [
|
|
|
400
610
|
},
|
|
401
611
|
{
|
|
402
612
|
name: "calculate_sortino_ratio",
|
|
403
|
-
description: "Calculate Sortino ratio (only penalizes downside volatility)",
|
|
613
|
+
description: "Calculate Sortino ratio (only penalizes downside volatility). Uses Rust FFI for high performance.",
|
|
404
614
|
inputSchema: {
|
|
405
615
|
type: "object",
|
|
406
616
|
properties: {
|
|
@@ -413,7 +623,7 @@ const tools = [
|
|
|
413
623
|
},
|
|
414
624
|
{
|
|
415
625
|
name: "calculate_beta_alpha",
|
|
416
|
-
description: "Calculate beta (market sensitivity) and alpha (excess return) vs benchmark",
|
|
626
|
+
description: "Calculate beta (market sensitivity) and alpha (excess return) vs benchmark. Uses Rust FFI for high performance.",
|
|
417
627
|
inputSchema: {
|
|
418
628
|
type: "object",
|
|
419
629
|
properties: {
|
|
@@ -430,7 +640,7 @@ const tools = [
|
|
|
430
640
|
// ===================
|
|
431
641
|
{
|
|
432
642
|
name: "distribution_statistics",
|
|
433
|
-
description: "Calculate comprehensive distribution statistics (mean, median, stdDev, skewness, kurtosis, quartiles)",
|
|
643
|
+
description: "Calculate comprehensive distribution statistics (mean, median, stdDev, skewness, kurtosis, quartiles). Uses Rust FFI for core calculations.",
|
|
434
644
|
inputSchema: {
|
|
435
645
|
type: "object",
|
|
436
646
|
properties: {
|
|
@@ -441,7 +651,7 @@ const tools = [
|
|
|
441
651
|
},
|
|
442
652
|
{
|
|
443
653
|
name: "correlation",
|
|
444
|
-
description: "Calculate Pearson correlation coefficient between two series",
|
|
654
|
+
description: "Calculate Pearson correlation coefficient between two series. Uses Rust FFI for 2-3x faster performance.",
|
|
445
655
|
inputSchema: {
|
|
446
656
|
type: "object",
|
|
447
657
|
properties: {
|
|
@@ -484,7 +694,7 @@ const tools = [
|
|
|
484
694
|
const server = new Server(
|
|
485
695
|
{
|
|
486
696
|
name: "@ebowwa/quant-mcp",
|
|
487
|
-
version: "1.
|
|
697
|
+
version: "1.1.1",
|
|
488
698
|
},
|
|
489
699
|
{
|
|
490
700
|
capabilities: {
|
|
@@ -513,32 +723,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
513
723
|
marketPrice: number;
|
|
514
724
|
bankroll: number;
|
|
515
725
|
};
|
|
516
|
-
|
|
726
|
+
// Use TypeScript version (20-40x faster for scalar ops)
|
|
727
|
+
result = kellyCriterionTS(yourProbability, marketPrice, bankroll);
|
|
517
728
|
break;
|
|
518
729
|
}
|
|
519
730
|
|
|
520
731
|
case "convert_odds": {
|
|
521
732
|
const { value, fromFormat } = args as { value: number; fromFormat: string };
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
probability: value,
|
|
525
|
-
decimal: probToDecimalOdds(value),
|
|
526
|
-
american: probToAmericanOdds(value),
|
|
527
|
-
};
|
|
528
|
-
} else if (fromFormat === "decimal") {
|
|
529
|
-
result = {
|
|
530
|
-
probability: decimalOddsToProb(value),
|
|
531
|
-
decimal: value,
|
|
532
|
-
american: probToAmericanOdds(decimalOddsToProb(value)),
|
|
533
|
-
};
|
|
534
|
-
} else {
|
|
535
|
-
const prob = americanOddsToProb(value);
|
|
536
|
-
result = {
|
|
537
|
-
probability: prob,
|
|
538
|
-
decimal: probToDecimalOdds(prob),
|
|
539
|
-
american: value,
|
|
540
|
-
};
|
|
541
|
-
}
|
|
733
|
+
// Use TypeScript version (20-40x faster for scalar ops)
|
|
734
|
+
result = convertOddsTS(value, fromFormat as OddsType);
|
|
542
735
|
break;
|
|
543
736
|
}
|
|
544
737
|
|
|
@@ -549,14 +742,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
549
742
|
outcome: "yes" | "no";
|
|
550
743
|
shares: number;
|
|
551
744
|
};
|
|
552
|
-
|
|
553
|
-
const cost =
|
|
554
|
-
const sharesOut = ammSharesReceived(state, outcome, cost);
|
|
745
|
+
// Use Rust FFI (fast for AMM math)
|
|
746
|
+
const cost = ammCalculateCost(poolYes, poolNo, outcome, shares);
|
|
555
747
|
result = {
|
|
556
748
|
cost,
|
|
557
|
-
shares
|
|
558
|
-
avgPrice: cost /
|
|
559
|
-
effectivePrice: cost / shares,
|
|
749
|
+
shares,
|
|
750
|
+
avgPrice: cost / shares,
|
|
560
751
|
};
|
|
561
752
|
break;
|
|
562
753
|
}
|
|
@@ -568,8 +759,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
568
759
|
outcome: "yes" | "no";
|
|
569
760
|
shares: number;
|
|
570
761
|
};
|
|
571
|
-
|
|
572
|
-
result = ammPriceImpact(
|
|
762
|
+
// Use Rust FFI
|
|
763
|
+
result = ammPriceImpact(poolYes, poolNo, outcome, shares);
|
|
573
764
|
break;
|
|
574
765
|
}
|
|
575
766
|
|
|
@@ -582,17 +773,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
582
773
|
outcome: "yes" | "no";
|
|
583
774
|
sharesToBuy?: number;
|
|
584
775
|
};
|
|
585
|
-
|
|
776
|
+
// Use Rust FFI
|
|
586
777
|
if (operation === "price") {
|
|
587
|
-
result =
|
|
588
|
-
yesPrice: lmsrPrice(state, "yes"),
|
|
589
|
-
noPrice: lmsrPrice(state, "no"),
|
|
590
|
-
};
|
|
778
|
+
result = lmsrPrice(yesShares, noShares, liquidityParam);
|
|
591
779
|
} else {
|
|
592
|
-
const cost = sharesToBuy ?
|
|
780
|
+
const cost = sharesToBuy ? lmsrCost(yesShares, noShares, liquidityParam, outcome, sharesToBuy) : null;
|
|
593
781
|
result = {
|
|
594
782
|
cost,
|
|
595
|
-
price: lmsrPrice(
|
|
783
|
+
price: lmsrPrice(yesShares, noShares, liquidityParam),
|
|
596
784
|
};
|
|
597
785
|
}
|
|
598
786
|
break;
|
|
@@ -600,7 +788,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
600
788
|
|
|
601
789
|
case "detect_arbitrage": {
|
|
602
790
|
const { yesPrice, noPrice } = args as { yesPrice: number; noPrice: number };
|
|
603
|
-
|
|
791
|
+
// Use TypeScript version (15-40x faster for scalar ops)
|
|
792
|
+
result = detectArbitrageTS(yesPrice, noPrice);
|
|
604
793
|
break;
|
|
605
794
|
}
|
|
606
795
|
|
|
@@ -609,6 +798,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
609
798
|
yourProbability: number;
|
|
610
799
|
marketPrice: number;
|
|
611
800
|
};
|
|
801
|
+
// Use TypeScript (fast scalar)
|
|
612
802
|
result = {
|
|
613
803
|
edge: calculateEdge(yourProbability, marketPrice),
|
|
614
804
|
hasPositiveEV: hasPositiveEV(yourProbability, marketPrice),
|
|
@@ -632,18 +822,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
632
822
|
// Technical Indicators
|
|
633
823
|
case "calculate_sma": {
|
|
634
824
|
const { prices, period } = args as { prices: number[]; period: number };
|
|
825
|
+
// Use Rust FFI (10-20x faster for arrays)
|
|
635
826
|
result = { sma: sma(prices, period) };
|
|
636
827
|
break;
|
|
637
828
|
}
|
|
638
829
|
|
|
639
830
|
case "calculate_ema": {
|
|
640
831
|
const { prices, period } = args as { prices: number[]; period: number };
|
|
832
|
+
// Use Rust FFI (10-20x faster for arrays)
|
|
641
833
|
result = { ema: ema(prices, period) };
|
|
642
834
|
break;
|
|
643
835
|
}
|
|
644
836
|
|
|
645
837
|
case "calculate_rsi": {
|
|
646
838
|
const { prices, period = 14 } = args as { prices: number[]; period?: number };
|
|
839
|
+
// Use Rust FFI
|
|
647
840
|
result = { rsi: rsi(prices, period) };
|
|
648
841
|
break;
|
|
649
842
|
}
|
|
@@ -655,6 +848,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
655
848
|
slowPeriod?: number;
|
|
656
849
|
signalPeriod?: number;
|
|
657
850
|
};
|
|
851
|
+
// Use Rust FFI
|
|
658
852
|
result = macd(prices, fastPeriod, slowPeriod, signalPeriod);
|
|
659
853
|
break;
|
|
660
854
|
}
|
|
@@ -676,7 +870,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
676
870
|
close: number[];
|
|
677
871
|
period?: number;
|
|
678
872
|
};
|
|
679
|
-
result = atr(high, low, close, period);
|
|
873
|
+
result = { atr: atr(high, low, close, period) };
|
|
680
874
|
break;
|
|
681
875
|
}
|
|
682
876
|
|
|
@@ -721,12 +915,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
721
915
|
returns: number[];
|
|
722
916
|
confidenceLevel?: number;
|
|
723
917
|
};
|
|
724
|
-
|
|
918
|
+
// Use Rust FFI
|
|
919
|
+
result = calculateVar(returns, confidenceLevel);
|
|
725
920
|
break;
|
|
726
921
|
}
|
|
727
922
|
|
|
728
923
|
case "calculate_drawdown": {
|
|
729
924
|
const { equityCurve } = args as { equityCurve: number[] };
|
|
925
|
+
// Use Rust FFI (5-10x faster)
|
|
730
926
|
result = calculateDrawdown(equityCurve);
|
|
731
927
|
break;
|
|
732
928
|
}
|
|
@@ -737,7 +933,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
737
933
|
riskFreeRate?: number;
|
|
738
934
|
periodsPerYear?: number;
|
|
739
935
|
};
|
|
740
|
-
|
|
936
|
+
// Use Rust FFI (2x faster)
|
|
937
|
+
result = { sharpeRatio: calculateSharpeRatio(returns, riskFreeRate, periodsPerYear) };
|
|
741
938
|
break;
|
|
742
939
|
}
|
|
743
940
|
|
|
@@ -747,20 +944,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
747
944
|
riskFreeRate?: number;
|
|
748
945
|
periodsPerYear?: number;
|
|
749
946
|
};
|
|
750
|
-
|
|
947
|
+
// Use Rust FFI
|
|
948
|
+
result = { sortinoRatio: calculateSortinoRatio(returns, riskFreeRate, periodsPerYear) };
|
|
751
949
|
break;
|
|
752
950
|
}
|
|
753
951
|
|
|
754
952
|
case "calculate_beta_alpha": {
|
|
755
|
-
const { assetReturns, marketReturns
|
|
953
|
+
const { assetReturns, marketReturns } = args as {
|
|
756
954
|
assetReturns: number[];
|
|
757
955
|
marketReturns: number[];
|
|
758
|
-
riskFreeRate?: number;
|
|
759
|
-
};
|
|
760
|
-
result = {
|
|
761
|
-
beta: beta(assetReturns, marketReturns),
|
|
762
|
-
alpha: alpha(assetReturns, marketReturns, riskFreeRate),
|
|
763
956
|
};
|
|
957
|
+
// Use Rust FFI
|
|
958
|
+
result = calculateBetaAlpha(assetReturns, marketReturns);
|
|
764
959
|
break;
|
|
765
960
|
}
|
|
766
961
|
|
|
@@ -773,7 +968,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
773
968
|
|
|
774
969
|
case "correlation": {
|
|
775
970
|
const { x, y } = args as { x: number[]; y: number[] };
|
|
776
|
-
|
|
971
|
+
// Use Rust FFI (2-3x faster)
|
|
972
|
+
result = { correlation: correlation(x, y) };
|
|
777
973
|
break;
|
|
778
974
|
}
|
|
779
975
|
|
|
@@ -819,7 +1015,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
819
1015
|
async function main() {
|
|
820
1016
|
const transport = new StdioServerTransport();
|
|
821
1017
|
await server.connect(transport);
|
|
822
|
-
console.error("Quant MCP server running on stdio");
|
|
1018
|
+
console.error("Quant MCP server running on stdio (powered by Rust FFI + TypeScript hybrid)");
|
|
823
1019
|
}
|
|
824
1020
|
|
|
825
1021
|
main().catch((error) => {
|