fixparser-plugin-mcp 9.1.7-c415bb75 → 9.1.7-c6228661
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/build/cjs/MCPLocal.js +422 -10
- package/build/cjs/MCPLocal.js.map +4 -4
- package/build/cjs/MCPRemote.js +422 -66
- package/build/cjs/MCPRemote.js.map +4 -4
- package/build/cjs/index.js +422 -66
- package/build/cjs/index.js.map +4 -4
- package/build/esm/MCPLocal.mjs +422 -10
- package/build/esm/MCPLocal.mjs.map +4 -4
- package/build/esm/MCPRemote.mjs +422 -66
- package/build/esm/MCPRemote.mjs.map +4 -4
- package/build/esm/index.mjs +422 -66
- package/build/esm/index.mjs.map +4 -4
- package/build-examples/cjs/example_mcp_local.js +9 -7
- package/build-examples/cjs/example_mcp_local.js.map +4 -4
- package/build-examples/cjs/example_mcp_remote.js +9 -7
- package/build-examples/cjs/example_mcp_remote.js.map +4 -4
- package/build-examples/esm/example_mcp_local.mjs +9 -7
- package/build-examples/esm/example_mcp_local.mjs.map +4 -4
- package/build-examples/esm/example_mcp_remote.mjs +9 -7
- package/build-examples/esm/example_mcp_remote.mjs.map +4 -4
- package/package.json +2 -2
- package/types/MCPBase.d.ts +0 -49
- package/types/MCPLocal.d.ts +0 -32
- package/types/MCPRemote.d.ts +0 -58
- package/types/PluginOptions.d.ts +0 -6
- package/types/index.d.ts +0 -3
- package/types/schemas/index.d.ts +0 -15
- package/types/schemas/marketData.d.ts +0 -48
- package/types/schemas/schemas.d.ts +0 -168
- package/types/tools/index.d.ts +0 -17
- package/types/tools/marketData.d.ts +0 -40
- package/types/tools/order.d.ts +0 -12
- package/types/tools/parse.d.ts +0 -5
- package/types/tools/parseToJSON.d.ts +0 -5
- package/types/utils/messageHandler.d.ts +0 -12
package/build/esm/index.mjs
CHANGED
|
@@ -273,17 +273,348 @@ var toolSchemas = {
|
|
|
273
273
|
},
|
|
274
274
|
required: ["symbol"]
|
|
275
275
|
}
|
|
276
|
+
},
|
|
277
|
+
technicalAnalysis: {
|
|
278
|
+
description: "Performs comprehensive technical analysis on market data for a given symbol, including indicators like SMA, EMA, RSI, Bollinger Bands, and trading signals",
|
|
279
|
+
schema: {
|
|
280
|
+
type: "object",
|
|
281
|
+
properties: {
|
|
282
|
+
symbol: {
|
|
283
|
+
type: "string",
|
|
284
|
+
description: "The trading symbol to analyze (e.g., AAPL, MSFT, EURUSD)"
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
required: ["symbol"]
|
|
288
|
+
}
|
|
276
289
|
}
|
|
277
290
|
};
|
|
278
291
|
|
|
292
|
+
// src/tools/analytics.ts
|
|
293
|
+
function sum(numbers) {
|
|
294
|
+
return numbers.reduce((acc, val) => acc + val, 0);
|
|
295
|
+
}
|
|
296
|
+
var TechnicalAnalyzer = class {
|
|
297
|
+
prices;
|
|
298
|
+
volumes;
|
|
299
|
+
highs;
|
|
300
|
+
lows;
|
|
301
|
+
constructor(data) {
|
|
302
|
+
this.prices = data.map((d) => d.trade > 0 ? d.trade : d.midPrice);
|
|
303
|
+
this.volumes = data.map((d) => d.volume);
|
|
304
|
+
this.highs = data.map((d) => d.tradingSessionHighPrice > 0 ? d.tradingSessionHighPrice : d.trade);
|
|
305
|
+
this.lows = data.map((d) => d.tradingSessionLowPrice > 0 ? d.tradingSessionLowPrice : d.trade);
|
|
306
|
+
}
|
|
307
|
+
// Calculate Simple Moving Average
|
|
308
|
+
calculateSMA(data, period) {
|
|
309
|
+
const sma = [];
|
|
310
|
+
for (let i = period - 1; i < data.length; i++) {
|
|
311
|
+
const sum2 = data.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0);
|
|
312
|
+
sma.push(sum2 / period);
|
|
313
|
+
}
|
|
314
|
+
return sma;
|
|
315
|
+
}
|
|
316
|
+
// Calculate Exponential Moving Average
|
|
317
|
+
calculateEMA(data, period) {
|
|
318
|
+
const multiplier = 2 / (period + 1);
|
|
319
|
+
const ema = [data[0]];
|
|
320
|
+
for (let i = 1; i < data.length; i++) {
|
|
321
|
+
ema.push(data[i] * multiplier + ema[i - 1] * (1 - multiplier));
|
|
322
|
+
}
|
|
323
|
+
return ema;
|
|
324
|
+
}
|
|
325
|
+
// Calculate RSI
|
|
326
|
+
calculateRSI(data, period = 14) {
|
|
327
|
+
if (data.length < period + 1) return [];
|
|
328
|
+
const changes = [];
|
|
329
|
+
for (let i = 1; i < data.length; i++) {
|
|
330
|
+
changes.push(data[i] - data[i - 1]);
|
|
331
|
+
}
|
|
332
|
+
const gains = changes.map((change) => change > 0 ? change : 0);
|
|
333
|
+
const losses = changes.map((change) => change < 0 ? Math.abs(change) : 0);
|
|
334
|
+
let avgGain = gains.slice(0, period).reduce((a, b) => a + b, 0) / period;
|
|
335
|
+
let avgLoss = losses.slice(0, period).reduce((a, b) => a + b, 0) / period;
|
|
336
|
+
const rsi = [];
|
|
337
|
+
for (let i = period; i < changes.length; i++) {
|
|
338
|
+
const rs = avgGain / avgLoss;
|
|
339
|
+
rsi.push(100 - 100 / (1 + rs));
|
|
340
|
+
avgGain = (avgGain * (period - 1) + gains[i]) / period;
|
|
341
|
+
avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
|
|
342
|
+
}
|
|
343
|
+
return rsi;
|
|
344
|
+
}
|
|
345
|
+
// Calculate Bollinger Bands
|
|
346
|
+
calculateBollingerBands(data, period = 20, stdDev = 2) {
|
|
347
|
+
if (data.length < period) return [];
|
|
348
|
+
const sma = this.calculateSMA(data, period);
|
|
349
|
+
const bands = [];
|
|
350
|
+
for (let i = 0; i < sma.length; i++) {
|
|
351
|
+
const dataSlice = data.slice(i, i + period);
|
|
352
|
+
const mean = sma[i];
|
|
353
|
+
const variance = dataSlice.reduce((sum2, price) => sum2 + (price - mean) ** 2, 0) / period;
|
|
354
|
+
const standardDeviation = Math.sqrt(variance);
|
|
355
|
+
bands.push({
|
|
356
|
+
upper: mean + standardDeviation * stdDev,
|
|
357
|
+
middle: mean,
|
|
358
|
+
lower: mean - standardDeviation * stdDev
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
return bands;
|
|
362
|
+
}
|
|
363
|
+
// Calculate maximum drawdown
|
|
364
|
+
calculateMaxDrawdown(prices) {
|
|
365
|
+
let maxPrice = prices[0];
|
|
366
|
+
let maxDrawdown = 0;
|
|
367
|
+
for (let i = 1; i < prices.length; i++) {
|
|
368
|
+
if (prices[i] > maxPrice) {
|
|
369
|
+
maxPrice = prices[i];
|
|
370
|
+
}
|
|
371
|
+
const drawdown = (maxPrice - prices[i]) / maxPrice;
|
|
372
|
+
if (drawdown > maxDrawdown) {
|
|
373
|
+
maxDrawdown = drawdown;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return maxDrawdown;
|
|
377
|
+
}
|
|
378
|
+
// Calculate price changes for volatility
|
|
379
|
+
calculatePriceChanges() {
|
|
380
|
+
const changes = [];
|
|
381
|
+
for (let i = 1; i < this.prices.length; i++) {
|
|
382
|
+
changes.push((this.prices[i] - this.prices[i - 1]) / this.prices[i - 1]);
|
|
383
|
+
}
|
|
384
|
+
return changes;
|
|
385
|
+
}
|
|
386
|
+
// Generate comprehensive market analysis
|
|
387
|
+
analyze() {
|
|
388
|
+
const currentPrice = this.prices[this.prices.length - 1];
|
|
389
|
+
const startPrice = this.prices[0];
|
|
390
|
+
const sessionHigh = Math.max(...this.highs);
|
|
391
|
+
const sessionLow = Math.min(...this.lows);
|
|
392
|
+
const totalVolume = sum(this.volumes);
|
|
393
|
+
const avgVolume = totalVolume / this.volumes.length;
|
|
394
|
+
const priceChanges = this.calculatePriceChanges();
|
|
395
|
+
const volatility = priceChanges.length > 0 ? Math.sqrt(
|
|
396
|
+
priceChanges.reduce((sum2, change) => sum2 + change ** 2, 0) / priceChanges.length
|
|
397
|
+
) * Math.sqrt(252) * 100 : 0;
|
|
398
|
+
const sessionReturn = (currentPrice - startPrice) / startPrice * 100;
|
|
399
|
+
const pricePosition = (currentPrice - sessionLow) / (sessionHigh - sessionLow) * 100;
|
|
400
|
+
const trueVWAP = this.prices.reduce((sum2, price, i) => sum2 + price * this.volumes[i], 0) / totalVolume;
|
|
401
|
+
const momentum5 = this.prices.length > 5 ? (currentPrice - this.prices[Math.max(0, this.prices.length - 6)]) / this.prices[Math.max(0, this.prices.length - 6)] * 100 : 0;
|
|
402
|
+
const momentum10 = this.prices.length > 10 ? (currentPrice - this.prices[Math.max(0, this.prices.length - 11)]) / this.prices[Math.max(0, this.prices.length - 11)] * 100 : 0;
|
|
403
|
+
const maxDrawdown = this.calculateMaxDrawdown(this.prices);
|
|
404
|
+
return {
|
|
405
|
+
currentPrice,
|
|
406
|
+
startPrice,
|
|
407
|
+
sessionHigh,
|
|
408
|
+
sessionLow,
|
|
409
|
+
totalVolume,
|
|
410
|
+
avgVolume,
|
|
411
|
+
volatility,
|
|
412
|
+
sessionReturn,
|
|
413
|
+
pricePosition,
|
|
414
|
+
trueVWAP,
|
|
415
|
+
momentum5,
|
|
416
|
+
momentum10,
|
|
417
|
+
maxDrawdown
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
// Generate technical indicators
|
|
421
|
+
getTechnicalIndicators() {
|
|
422
|
+
return {
|
|
423
|
+
sma5: this.calculateSMA(this.prices, 5),
|
|
424
|
+
sma10: this.calculateSMA(this.prices, 10),
|
|
425
|
+
ema8: this.calculateEMA(this.prices, 8),
|
|
426
|
+
ema21: this.calculateEMA(this.prices, 21),
|
|
427
|
+
rsi: this.calculateRSI(this.prices, 14),
|
|
428
|
+
bollinger: this.calculateBollingerBands(this.prices, 20, 2)
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
// Generate trading signals
|
|
432
|
+
generateSignals() {
|
|
433
|
+
const analysis = this.analyze();
|
|
434
|
+
let bullishSignals = 0;
|
|
435
|
+
let bearishSignals = 0;
|
|
436
|
+
const signals = [];
|
|
437
|
+
if (analysis.currentPrice > analysis.trueVWAP) {
|
|
438
|
+
signals.push(
|
|
439
|
+
`\u2713 BULLISH: Price above VWAP (+${((analysis.currentPrice - analysis.trueVWAP) / analysis.trueVWAP * 100).toFixed(2)}%)`
|
|
440
|
+
);
|
|
441
|
+
bullishSignals++;
|
|
442
|
+
} else {
|
|
443
|
+
signals.push(
|
|
444
|
+
`\u2717 BEARISH: Price below VWAP (${((analysis.currentPrice - analysis.trueVWAP) / analysis.trueVWAP * 100).toFixed(2)}%)`
|
|
445
|
+
);
|
|
446
|
+
bearishSignals++;
|
|
447
|
+
}
|
|
448
|
+
if (analysis.momentum5 > 0 && analysis.momentum10 > 0) {
|
|
449
|
+
signals.push("\u2713 BULLISH: Positive momentum on both timeframes");
|
|
450
|
+
bullishSignals++;
|
|
451
|
+
} else if (analysis.momentum5 < 0 && analysis.momentum10 < 0) {
|
|
452
|
+
signals.push("\u2717 BEARISH: Negative momentum on both timeframes");
|
|
453
|
+
bearishSignals++;
|
|
454
|
+
} else {
|
|
455
|
+
signals.push("\u25D0 MIXED: Conflicting momentum signals");
|
|
456
|
+
}
|
|
457
|
+
const currentVolume = this.volumes[this.volumes.length - 1];
|
|
458
|
+
const volumeRatio = currentVolume / analysis.avgVolume;
|
|
459
|
+
if (volumeRatio > 1.2 && analysis.sessionReturn > 0) {
|
|
460
|
+
signals.push("\u2713 BULLISH: Above-average volume supporting upward move");
|
|
461
|
+
bullishSignals++;
|
|
462
|
+
} else if (volumeRatio > 1.2 && analysis.sessionReturn < 0) {
|
|
463
|
+
signals.push("\u2717 BEARISH: Above-average volume supporting downward move");
|
|
464
|
+
bearishSignals++;
|
|
465
|
+
} else {
|
|
466
|
+
signals.push("\u25D0 NEUTRAL: Volume not providing clear direction");
|
|
467
|
+
}
|
|
468
|
+
if (analysis.pricePosition > 65 && analysis.volatility > 30) {
|
|
469
|
+
signals.push("\u2717 BEARISH: High in range with elevated volatility - reversal risk");
|
|
470
|
+
bearishSignals++;
|
|
471
|
+
} else if (analysis.pricePosition < 35 && analysis.volatility > 30) {
|
|
472
|
+
signals.push("\u2713 BULLISH: Low in range with volatility - potential bounce");
|
|
473
|
+
bullishSignals++;
|
|
474
|
+
} else {
|
|
475
|
+
signals.push("\u25D0 NEUTRAL: Price position and volatility not extreme");
|
|
476
|
+
}
|
|
477
|
+
return { bullishSignals, bearishSignals, signals };
|
|
478
|
+
}
|
|
479
|
+
// Generate comprehensive JSON analysis
|
|
480
|
+
generateJSONAnalysis(symbol) {
|
|
481
|
+
const analysis = this.analyze();
|
|
482
|
+
const indicators = this.getTechnicalIndicators();
|
|
483
|
+
const signals = this.generateSignals();
|
|
484
|
+
const currentSMA5 = indicators.sma5.length > 0 ? indicators.sma5[indicators.sma5.length - 1] : null;
|
|
485
|
+
const currentSMA10 = indicators.sma10.length > 0 ? indicators.sma10[indicators.sma10.length - 1] : null;
|
|
486
|
+
const currentEMA8 = indicators.ema8[indicators.ema8.length - 1];
|
|
487
|
+
const currentEMA21 = indicators.ema21[indicators.ema21.length - 1];
|
|
488
|
+
const currentRSI = indicators.rsi.length > 0 ? indicators.rsi[indicators.rsi.length - 1] : null;
|
|
489
|
+
const currentBB = indicators.bollinger.length > 0 ? indicators.bollinger[indicators.bollinger.length - 1] : null;
|
|
490
|
+
const currentVolume = this.volumes[this.volumes.length - 1];
|
|
491
|
+
const volumeRatio = currentVolume / analysis.avgVolume;
|
|
492
|
+
const currentDrawdown = (analysis.sessionHigh - analysis.currentPrice) / analysis.sessionHigh * 100;
|
|
493
|
+
const rangeWidth = (analysis.sessionHigh - analysis.sessionLow) / analysis.sessionLow * 100;
|
|
494
|
+
const priceVsVWAP = (analysis.currentPrice - analysis.trueVWAP) / analysis.trueVWAP * 100;
|
|
495
|
+
const totalScore = signals.bullishSignals - signals.bearishSignals;
|
|
496
|
+
const overallSignal = totalScore > 0 ? "BULLISH_BIAS" : totalScore < 0 ? "BEARISH_BIAS" : "NEUTRAL";
|
|
497
|
+
const targetEntry = Math.max(analysis.sessionLow * 1.005, analysis.trueVWAP * 0.998);
|
|
498
|
+
const stopLoss = analysis.sessionLow * 0.995;
|
|
499
|
+
const profitTarget = analysis.sessionHigh * 0.995;
|
|
500
|
+
const riskRewardRatio = (profitTarget - analysis.currentPrice) / (analysis.currentPrice - stopLoss);
|
|
501
|
+
return {
|
|
502
|
+
symbol,
|
|
503
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
504
|
+
marketStructure: {
|
|
505
|
+
currentPrice: analysis.currentPrice,
|
|
506
|
+
startPrice: analysis.startPrice,
|
|
507
|
+
sessionHigh: analysis.sessionHigh,
|
|
508
|
+
sessionLow: analysis.sessionLow,
|
|
509
|
+
rangeWidth,
|
|
510
|
+
totalVolume: analysis.totalVolume,
|
|
511
|
+
sessionPerformance: analysis.sessionReturn,
|
|
512
|
+
positionInRange: analysis.pricePosition
|
|
513
|
+
},
|
|
514
|
+
volatility: {
|
|
515
|
+
impliedVolatility: analysis.volatility,
|
|
516
|
+
maxDrawdown: analysis.maxDrawdown * 100,
|
|
517
|
+
currentDrawdown
|
|
518
|
+
},
|
|
519
|
+
technicalIndicators: {
|
|
520
|
+
sma5: currentSMA5,
|
|
521
|
+
sma10: currentSMA10,
|
|
522
|
+
ema8: currentEMA8,
|
|
523
|
+
ema21: currentEMA21,
|
|
524
|
+
rsi: currentRSI,
|
|
525
|
+
bollingerBands: currentBB ? {
|
|
526
|
+
upper: currentBB.upper,
|
|
527
|
+
middle: currentBB.middle,
|
|
528
|
+
lower: currentBB.lower,
|
|
529
|
+
position: (analysis.currentPrice - currentBB.lower) / (currentBB.upper - currentBB.lower) * 100
|
|
530
|
+
} : null
|
|
531
|
+
},
|
|
532
|
+
volumeAnalysis: {
|
|
533
|
+
currentVolume,
|
|
534
|
+
averageVolume: Math.round(analysis.avgVolume),
|
|
535
|
+
volumeRatio,
|
|
536
|
+
trueVWAP: analysis.trueVWAP,
|
|
537
|
+
priceVsVWAP
|
|
538
|
+
},
|
|
539
|
+
momentum: {
|
|
540
|
+
momentum5: analysis.momentum5,
|
|
541
|
+
momentum10: analysis.momentum10,
|
|
542
|
+
sessionROC: analysis.sessionReturn
|
|
543
|
+
},
|
|
544
|
+
tradingSignals: {
|
|
545
|
+
...signals,
|
|
546
|
+
overallSignal,
|
|
547
|
+
signalScore: totalScore
|
|
548
|
+
},
|
|
549
|
+
riskManagement: {
|
|
550
|
+
targetEntry,
|
|
551
|
+
stopLoss,
|
|
552
|
+
profitTarget,
|
|
553
|
+
riskRewardRatio
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
var createTechnicalAnalysisHandler = (marketDataPrices) => {
|
|
559
|
+
return async (args) => {
|
|
560
|
+
try {
|
|
561
|
+
const symbol = args.symbol;
|
|
562
|
+
const priceHistory = marketDataPrices.get(symbol) || [];
|
|
563
|
+
if (priceHistory.length === 0) {
|
|
564
|
+
return {
|
|
565
|
+
content: [
|
|
566
|
+
{
|
|
567
|
+
type: "text",
|
|
568
|
+
text: `No price data available for ${symbol}. Please request market data first.`,
|
|
569
|
+
uri: "technicalAnalysis"
|
|
570
|
+
}
|
|
571
|
+
]
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
const analyzer = new TechnicalAnalyzer(priceHistory);
|
|
575
|
+
const analysis = analyzer.generateJSONAnalysis(symbol);
|
|
576
|
+
return {
|
|
577
|
+
content: [
|
|
578
|
+
{
|
|
579
|
+
type: "text",
|
|
580
|
+
text: `Technical Analysis for ${symbol}:
|
|
581
|
+
|
|
582
|
+
${JSON.stringify(analysis, null, 2)}`,
|
|
583
|
+
uri: "technicalAnalysis"
|
|
584
|
+
}
|
|
585
|
+
]
|
|
586
|
+
};
|
|
587
|
+
} catch (error) {
|
|
588
|
+
return {
|
|
589
|
+
content: [
|
|
590
|
+
{
|
|
591
|
+
type: "text",
|
|
592
|
+
text: `Error performing technical analysis: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
593
|
+
uri: "technicalAnalysis"
|
|
594
|
+
}
|
|
595
|
+
],
|
|
596
|
+
isError: true
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
};
|
|
601
|
+
|
|
279
602
|
// src/tools/marketData.ts
|
|
280
603
|
import { Field, Fields, MDEntryType, Messages } from "fixparser";
|
|
281
604
|
import QuickChart from "quickchart-js";
|
|
282
605
|
var createMarketDataRequestHandler = (parser, pendingRequests) => {
|
|
283
606
|
return async (args) => {
|
|
284
607
|
try {
|
|
608
|
+
parser.logger.log({
|
|
609
|
+
level: "info",
|
|
610
|
+
message: `Sending market data request for symbols: ${args.symbols.join(", ")}`
|
|
611
|
+
});
|
|
285
612
|
const response = new Promise((resolve) => {
|
|
286
613
|
pendingRequests.set(args.mdReqID, resolve);
|
|
614
|
+
parser.logger.log({
|
|
615
|
+
level: "info",
|
|
616
|
+
message: `Registered callback for market data request ID: ${args.mdReqID}`
|
|
617
|
+
});
|
|
287
618
|
});
|
|
288
619
|
const entryTypes = args.mdEntryTypes || [
|
|
289
620
|
MDEntryType.Bid,
|
|
@@ -352,6 +683,10 @@ var createMarketDataRequestHandler = (parser, pendingRequests) => {
|
|
|
352
683
|
});
|
|
353
684
|
const mdr = parser.createMessage(...messageFields);
|
|
354
685
|
if (!parser.connected) {
|
|
686
|
+
parser.logger.log({
|
|
687
|
+
level: "error",
|
|
688
|
+
message: "Not connected. Cannot send market data request."
|
|
689
|
+
});
|
|
355
690
|
return {
|
|
356
691
|
content: [
|
|
357
692
|
{
|
|
@@ -363,8 +698,16 @@ var createMarketDataRequestHandler = (parser, pendingRequests) => {
|
|
|
363
698
|
isError: true
|
|
364
699
|
};
|
|
365
700
|
}
|
|
701
|
+
parser.logger.log({
|
|
702
|
+
level: "info",
|
|
703
|
+
message: `Sending market data request message: ${JSON.stringify(mdr?.toFIXJSON())}`
|
|
704
|
+
});
|
|
366
705
|
parser.send(mdr);
|
|
367
706
|
const fixData = await response;
|
|
707
|
+
parser.logger.log({
|
|
708
|
+
level: "info",
|
|
709
|
+
message: `Received market data response for request ID: ${args.mdReqID}`
|
|
710
|
+
});
|
|
368
711
|
return {
|
|
369
712
|
content: [
|
|
370
713
|
{
|
|
@@ -413,6 +756,9 @@ var createGetStockGraphHandler = (marketDataPrices) => {
|
|
|
413
756
|
const offerData = priceHistory.map((point) => point.offer);
|
|
414
757
|
const spreadData = priceHistory.map((point) => point.spread);
|
|
415
758
|
const volumeData = priceHistory.map((point) => point.volume);
|
|
759
|
+
const tradeData = priceHistory.map((point) => point.trade);
|
|
760
|
+
const vwapData = priceHistory.map((point) => point.vwap);
|
|
761
|
+
const twapData = priceHistory.map((point) => point.twap);
|
|
416
762
|
const config = {
|
|
417
763
|
type: "line",
|
|
418
764
|
data: {
|
|
@@ -442,6 +788,30 @@ var createGetStockGraphHandler = (marketDataPrices) => {
|
|
|
442
788
|
fill: false,
|
|
443
789
|
tension: 0.4
|
|
444
790
|
},
|
|
791
|
+
{
|
|
792
|
+
label: "Trade",
|
|
793
|
+
data: tradeData,
|
|
794
|
+
borderColor: "#ffc107",
|
|
795
|
+
backgroundColor: "rgba(255, 193, 7, 0.1)",
|
|
796
|
+
fill: false,
|
|
797
|
+
tension: 0.4
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
label: "VWAP",
|
|
801
|
+
data: vwapData,
|
|
802
|
+
borderColor: "#17a2b8",
|
|
803
|
+
backgroundColor: "rgba(23, 162, 184, 0.1)",
|
|
804
|
+
fill: false,
|
|
805
|
+
tension: 0.4
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
label: "TWAP",
|
|
809
|
+
data: twapData,
|
|
810
|
+
borderColor: "#6610f2",
|
|
811
|
+
backgroundColor: "rgba(102, 16, 242, 0.1)",
|
|
812
|
+
fill: false,
|
|
813
|
+
tension: 0.4
|
|
814
|
+
},
|
|
445
815
|
{
|
|
446
816
|
label: "Volume",
|
|
447
817
|
data: volumeData,
|
|
@@ -487,7 +857,7 @@ var createGetStockGraphHandler = (marketDataPrices) => {
|
|
|
487
857
|
content: [
|
|
488
858
|
{
|
|
489
859
|
type: "text",
|
|
490
|
-
text: `Error: ${error instanceof Error ? error.message : "Failed to generate
|
|
860
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to generate graph"}`,
|
|
491
861
|
uri: "getStockGraph"
|
|
492
862
|
}
|
|
493
863
|
],
|
|
@@ -525,7 +895,48 @@ var createGetStockPriceHistoryHandler = (marketDataPrices) => {
|
|
|
525
895
|
bid: point.bid,
|
|
526
896
|
offer: point.offer,
|
|
527
897
|
spread: point.spread,
|
|
528
|
-
volume: point.volume
|
|
898
|
+
volume: point.volume,
|
|
899
|
+
trade: point.trade,
|
|
900
|
+
indexValue: point.indexValue,
|
|
901
|
+
openingPrice: point.openingPrice,
|
|
902
|
+
closingPrice: point.closingPrice,
|
|
903
|
+
settlementPrice: point.settlementPrice,
|
|
904
|
+
tradingSessionHighPrice: point.tradingSessionHighPrice,
|
|
905
|
+
tradingSessionLowPrice: point.tradingSessionLowPrice,
|
|
906
|
+
vwap: point.vwap,
|
|
907
|
+
imbalance: point.imbalance,
|
|
908
|
+
openInterest: point.openInterest,
|
|
909
|
+
compositeUnderlyingPrice: point.compositeUnderlyingPrice,
|
|
910
|
+
simulatedSellPrice: point.simulatedSellPrice,
|
|
911
|
+
simulatedBuyPrice: point.simulatedBuyPrice,
|
|
912
|
+
marginRate: point.marginRate,
|
|
913
|
+
midPrice: point.midPrice,
|
|
914
|
+
emptyBook: point.emptyBook,
|
|
915
|
+
settleHighPrice: point.settleHighPrice,
|
|
916
|
+
settleLowPrice: point.settleLowPrice,
|
|
917
|
+
priorSettlePrice: point.priorSettlePrice,
|
|
918
|
+
sessionHighBid: point.sessionHighBid,
|
|
919
|
+
sessionLowOffer: point.sessionLowOffer,
|
|
920
|
+
earlyPrices: point.earlyPrices,
|
|
921
|
+
auctionClearingPrice: point.auctionClearingPrice,
|
|
922
|
+
swapValueFactor: point.swapValueFactor,
|
|
923
|
+
dailyValueAdjustmentForLongPositions: point.dailyValueAdjustmentForLongPositions,
|
|
924
|
+
cumulativeValueAdjustmentForLongPositions: point.cumulativeValueAdjustmentForLongPositions,
|
|
925
|
+
dailyValueAdjustmentForShortPositions: point.dailyValueAdjustmentForShortPositions,
|
|
926
|
+
cumulativeValueAdjustmentForShortPositions: point.cumulativeValueAdjustmentForShortPositions,
|
|
927
|
+
fixingPrice: point.fixingPrice,
|
|
928
|
+
cashRate: point.cashRate,
|
|
929
|
+
recoveryRate: point.recoveryRate,
|
|
930
|
+
recoveryRateForLong: point.recoveryRateForLong,
|
|
931
|
+
recoveryRateForShort: point.recoveryRateForShort,
|
|
932
|
+
marketBid: point.marketBid,
|
|
933
|
+
marketOffer: point.marketOffer,
|
|
934
|
+
shortSaleMinPrice: point.shortSaleMinPrice,
|
|
935
|
+
previousClosingPrice: point.previousClosingPrice,
|
|
936
|
+
thresholdLimitPriceBanding: point.thresholdLimitPriceBanding,
|
|
937
|
+
dailyFinancingValue: point.dailyFinancingValue,
|
|
938
|
+
accruedFinancingValue: point.accruedFinancingValue,
|
|
939
|
+
twap: point.twap
|
|
529
940
|
}))
|
|
530
941
|
},
|
|
531
942
|
null,
|
|
@@ -540,7 +951,7 @@ var createGetStockPriceHistoryHandler = (marketDataPrices) => {
|
|
|
540
951
|
content: [
|
|
541
952
|
{
|
|
542
953
|
type: "text",
|
|
543
|
-
text: `Error: ${error instanceof Error ? error.message : "Failed to get
|
|
954
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to get price history"}`,
|
|
544
955
|
uri: "getStockPriceHistory"
|
|
545
956
|
}
|
|
546
957
|
],
|
|
@@ -648,7 +1059,7 @@ Parameters verified:
|
|
|
648
1059
|
- Symbol: ${args.symbol}
|
|
649
1060
|
- TimeInForce: ${args.timeInForce} (${timeInForceNames[args.timeInForce]})
|
|
650
1061
|
|
|
651
|
-
To execute this order, call the executeOrder tool with these exact same parameters
|
|
1062
|
+
To execute this order, call the executeOrder tool with these exact same parameters. Important: The user has to explicitly confirm before executeOrder is called!`,
|
|
652
1063
|
uri: "verifyOrder"
|
|
653
1064
|
}
|
|
654
1065
|
]
|
|
@@ -844,16 +1255,16 @@ var createToolHandlers = (parser, verifiedOrders, pendingRequests, marketDataPri
|
|
|
844
1255
|
executeOrder: createExecuteOrderHandler(parser, verifiedOrders, pendingRequests),
|
|
845
1256
|
marketDataRequest: createMarketDataRequestHandler(parser, pendingRequests),
|
|
846
1257
|
getStockGraph: createGetStockGraphHandler(marketDataPrices),
|
|
847
|
-
getStockPriceHistory: createGetStockPriceHistoryHandler(marketDataPrices)
|
|
1258
|
+
getStockPriceHistory: createGetStockPriceHistoryHandler(marketDataPrices),
|
|
1259
|
+
technicalAnalysis: createTechnicalAnalysisHandler(marketDataPrices)
|
|
848
1260
|
});
|
|
849
1261
|
|
|
850
1262
|
// src/utils/messageHandler.ts
|
|
851
1263
|
import { Fields as Fields3, MDEntryType as MDEntryType2, Messages as Messages3 } from "fixparser";
|
|
1264
|
+
function getEnumValue(enumObj, name) {
|
|
1265
|
+
return enumObj[name] || name;
|
|
1266
|
+
}
|
|
852
1267
|
function handleMessage(message, parser, pendingRequests, marketDataPrices, maxPriceHistory, onPriceUpdate) {
|
|
853
|
-
parser.logger.log({
|
|
854
|
-
level: "info",
|
|
855
|
-
message: `MCP Server received message: ${message.messageType}: ${message.description}`
|
|
856
|
-
});
|
|
857
1268
|
const msgType = message.messageType;
|
|
858
1269
|
if (msgType === Messages3.MarketDataSnapshotFullRefresh || msgType === Messages3.MarketDataIncrementalRefresh) {
|
|
859
1270
|
const symbol = message.getField(Fields3.Symbol)?.value;
|
|
@@ -911,7 +1322,8 @@ function handleMessage(message, parser, pendingRequests, marketDataPrices, maxPr
|
|
|
911
1322
|
const entryType = entry.MDEntryType;
|
|
912
1323
|
const price = entry.MDEntryPx ? Number.parseFloat(entry.MDEntryPx) : 0;
|
|
913
1324
|
const size = entry.MDEntrySize ? Number.parseFloat(entry.MDEntrySize) : 0;
|
|
914
|
-
|
|
1325
|
+
const enumValue = getEnumValue(MDEntryType2, entryType);
|
|
1326
|
+
switch (enumValue) {
|
|
915
1327
|
case MDEntryType2.Bid:
|
|
916
1328
|
data.bid = price;
|
|
917
1329
|
break;
|
|
@@ -1303,25 +1715,13 @@ var MCPRemote = class extends MCPBase {
|
|
|
1303
1715
|
}
|
|
1304
1716
|
});
|
|
1305
1717
|
this.httpServer = createServer(async (req, res) => {
|
|
1306
|
-
this.logger?.log({
|
|
1307
|
-
level: "info",
|
|
1308
|
-
message: `Incoming request: ${req.method} ${req.url}`
|
|
1309
|
-
});
|
|
1310
1718
|
if (!req.url || !req.method) {
|
|
1311
|
-
this.logger?.log({
|
|
1312
|
-
level: "error",
|
|
1313
|
-
message: "Invalid request: missing URL or method"
|
|
1314
|
-
});
|
|
1315
1719
|
res.writeHead(400);
|
|
1316
1720
|
res.end("Bad Request");
|
|
1317
1721
|
return;
|
|
1318
1722
|
}
|
|
1319
1723
|
if (req.url === "/mcp") {
|
|
1320
1724
|
const sessionId = req.headers["mcp-session-id"];
|
|
1321
|
-
this.logger?.log({
|
|
1322
|
-
level: "info",
|
|
1323
|
-
message: `MCP request received. Session ID: ${sessionId || "none"}, headers: ${req.headers}`
|
|
1324
|
-
});
|
|
1325
1725
|
if (req.method === "POST") {
|
|
1326
1726
|
const bodyChunks = [];
|
|
1327
1727
|
req.on("data", (chunk) => {
|
|
@@ -1332,47 +1732,23 @@ var MCPRemote = class extends MCPBase {
|
|
|
1332
1732
|
const body = Buffer.concat(bodyChunks).toString();
|
|
1333
1733
|
try {
|
|
1334
1734
|
parsed = JSON.parse(body);
|
|
1335
|
-
this.logger?.log({
|
|
1336
|
-
level: "info",
|
|
1337
|
-
message: `Parsed request body: ${JSON.stringify(parsed)}`
|
|
1338
|
-
});
|
|
1339
1735
|
} catch (err) {
|
|
1340
|
-
this.logger?.log({
|
|
1341
|
-
level: "error",
|
|
1342
|
-
message: `Failed to parse JSON body: ${err}`
|
|
1343
|
-
});
|
|
1344
1736
|
res.writeHead(400);
|
|
1345
1737
|
res.end(JSON.stringify({ error: "Invalid JSON" }));
|
|
1346
1738
|
return;
|
|
1347
1739
|
}
|
|
1348
1740
|
let transport;
|
|
1349
1741
|
if (sessionId && transports[sessionId]) {
|
|
1350
|
-
this.logger?.log({
|
|
1351
|
-
level: "info",
|
|
1352
|
-
message: `Using existing transport for session: ${sessionId}`
|
|
1353
|
-
});
|
|
1354
1742
|
transport = transports[sessionId];
|
|
1355
1743
|
} else if (!sessionId && req.method === "POST" && isInitializeRequest(parsed)) {
|
|
1356
|
-
this.logger?.log({
|
|
1357
|
-
level: "info",
|
|
1358
|
-
message: "Creating new transport for initialization request"
|
|
1359
|
-
});
|
|
1360
1744
|
transport = new StreamableHTTPServerTransport({
|
|
1361
1745
|
sessionIdGenerator: () => randomUUID(),
|
|
1362
1746
|
onsessioninitialized: (sessionId2) => {
|
|
1363
|
-
this.logger?.log({
|
|
1364
|
-
level: "info",
|
|
1365
|
-
message: `New session initialized: ${sessionId2}`
|
|
1366
|
-
});
|
|
1367
1747
|
transports[sessionId2] = transport;
|
|
1368
1748
|
}
|
|
1369
1749
|
});
|
|
1370
1750
|
transport.onclose = () => {
|
|
1371
1751
|
if (transport.sessionId) {
|
|
1372
|
-
this.logger?.log({
|
|
1373
|
-
level: "info",
|
|
1374
|
-
message: `Session closed: ${transport.sessionId}`
|
|
1375
|
-
});
|
|
1376
1752
|
delete transports[transport.sessionId];
|
|
1377
1753
|
}
|
|
1378
1754
|
};
|
|
@@ -1383,10 +1759,6 @@ var MCPRemote = class extends MCPBase {
|
|
|
1383
1759
|
this.setupTools();
|
|
1384
1760
|
await this.mcpServer.connect(transport);
|
|
1385
1761
|
} else {
|
|
1386
|
-
this.logger?.log({
|
|
1387
|
-
level: "error",
|
|
1388
|
-
message: "Invalid request: No valid session ID provided"
|
|
1389
|
-
});
|
|
1390
1762
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1391
1763
|
res.end(
|
|
1392
1764
|
JSON.stringify({
|
|
@@ -1402,10 +1774,6 @@ var MCPRemote = class extends MCPBase {
|
|
|
1402
1774
|
}
|
|
1403
1775
|
try {
|
|
1404
1776
|
await transport.handleRequest(req, res, parsed);
|
|
1405
|
-
this.logger?.log({
|
|
1406
|
-
level: "info",
|
|
1407
|
-
message: "Request handled successfully"
|
|
1408
|
-
});
|
|
1409
1777
|
} catch (error) {
|
|
1410
1778
|
this.logger?.log({
|
|
1411
1779
|
level: "error",
|
|
@@ -1416,10 +1784,6 @@ var MCPRemote = class extends MCPBase {
|
|
|
1416
1784
|
});
|
|
1417
1785
|
} else if (req.method === "GET" || req.method === "DELETE") {
|
|
1418
1786
|
if (!sessionId || !transports[sessionId]) {
|
|
1419
|
-
this.logger?.log({
|
|
1420
|
-
level: "error",
|
|
1421
|
-
message: `Invalid session ID for ${req.method} request: ${sessionId}`
|
|
1422
|
-
});
|
|
1423
1787
|
res.writeHead(400);
|
|
1424
1788
|
res.end("Invalid or missing session ID");
|
|
1425
1789
|
return;
|
|
@@ -1427,10 +1791,6 @@ var MCPRemote = class extends MCPBase {
|
|
|
1427
1791
|
const transport = transports[sessionId];
|
|
1428
1792
|
try {
|
|
1429
1793
|
await transport.handleRequest(req, res);
|
|
1430
|
-
this.logger?.log({
|
|
1431
|
-
level: "info",
|
|
1432
|
-
message: `${req.method} request handled successfully for session: ${sessionId}`
|
|
1433
|
-
});
|
|
1434
1794
|
} catch (error) {
|
|
1435
1795
|
this.logger?.log({
|
|
1436
1796
|
level: "error",
|
|
@@ -1447,10 +1807,6 @@ var MCPRemote = class extends MCPBase {
|
|
|
1447
1807
|
res.end("Method Not Allowed");
|
|
1448
1808
|
}
|
|
1449
1809
|
} else {
|
|
1450
|
-
this.logger?.log({
|
|
1451
|
-
level: "error",
|
|
1452
|
-
message: `Not found: ${req.url}`
|
|
1453
|
-
});
|
|
1454
1810
|
res.writeHead(404);
|
|
1455
1811
|
res.end("Not Found");
|
|
1456
1812
|
}
|