fixparser-plugin-mcp 9.1.7-38cee007 → 9.1.7-3c6fd297

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.
@@ -129,7 +129,7 @@ var toolSchemas = {
129
129
  }
130
130
  },
131
131
  executeOrder: {
132
- description: "Executes a verified order. verifyOrder must be called before executeOrder.",
132
+ description: "Executes a verified order. verifyOrder must be called before executeOrder. user has to explicitly allow executeOrder.",
133
133
  schema: {
134
134
  type: "object",
135
135
  properties: {
@@ -273,395 +273,19 @@ 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
- }
289
276
  }
290
277
  };
291
278
 
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
-
602
279
  // src/tools/marketData.ts
603
280
  import { Field, Fields, MDEntryType, Messages } from "fixparser";
604
281
  import QuickChart from "quickchart-js";
605
282
  var createMarketDataRequestHandler = (parser, pendingRequests) => {
606
283
  return async (args) => {
607
284
  try {
608
- parser.logger.log({
609
- level: "info",
610
- message: `Sending market data request for symbols: ${args.symbols.join(", ")}`
611
- });
612
285
  const response = new Promise((resolve) => {
613
286
  pendingRequests.set(args.mdReqID, resolve);
614
- parser.logger.log({
615
- level: "info",
616
- message: `Registered callback for market data request ID: ${args.mdReqID}`
617
- });
618
287
  });
619
- const entryTypes = args.mdEntryTypes || [
620
- MDEntryType.Bid,
621
- MDEntryType.Offer,
622
- MDEntryType.Trade,
623
- MDEntryType.IndexValue,
624
- MDEntryType.OpeningPrice,
625
- MDEntryType.ClosingPrice,
626
- MDEntryType.SettlementPrice,
627
- MDEntryType.TradingSessionHighPrice,
628
- MDEntryType.TradingSessionLowPrice,
629
- MDEntryType.VWAP,
630
- MDEntryType.Imbalance,
631
- MDEntryType.TradeVolume,
632
- MDEntryType.OpenInterest,
633
- MDEntryType.CompositeUnderlyingPrice,
634
- MDEntryType.SimulatedSellPrice,
635
- MDEntryType.SimulatedBuyPrice,
636
- MDEntryType.MarginRate,
637
- MDEntryType.MidPrice,
638
- MDEntryType.EmptyBook,
639
- MDEntryType.SettleHighPrice,
640
- MDEntryType.SettleLowPrice,
641
- MDEntryType.PriorSettlePrice,
642
- MDEntryType.SessionHighBid,
643
- MDEntryType.SessionLowOffer,
644
- MDEntryType.EarlyPrices,
645
- MDEntryType.AuctionClearingPrice,
646
- MDEntryType.SwapValueFactor,
647
- MDEntryType.DailyValueAdjustmentForLongPositions,
648
- MDEntryType.CumulativeValueAdjustmentForLongPositions,
649
- MDEntryType.DailyValueAdjustmentForShortPositions,
650
- MDEntryType.CumulativeValueAdjustmentForShortPositions,
651
- MDEntryType.FixingPrice,
652
- MDEntryType.CashRate,
653
- MDEntryType.RecoveryRate,
654
- MDEntryType.RecoveryRateForLong,
655
- MDEntryType.RecoveryRateForShort,
656
- MDEntryType.MarketBid,
657
- MDEntryType.MarketOffer,
658
- MDEntryType.ShortSaleMinPrice,
659
- MDEntryType.PreviousClosingPrice,
660
- MDEntryType.ThresholdLimitPriceBanding,
661
- MDEntryType.DailyFinancingValue,
662
- MDEntryType.AccruedFinancingValue,
663
- MDEntryType.TWAP
664
- ];
288
+ const entryTypes = args.mdEntryTypes || [MDEntryType.Bid, MDEntryType.Offer, MDEntryType.TradeVolume];
665
289
  const messageFields = [
666
290
  new Field(Fields.MsgType, Messages.MarketDataRequest),
667
291
  new Field(Fields.SenderCompID, parser.sender),
@@ -683,10 +307,6 @@ var createMarketDataRequestHandler = (parser, pendingRequests) => {
683
307
  });
684
308
  const mdr = parser.createMessage(...messageFields);
685
309
  if (!parser.connected) {
686
- parser.logger.log({
687
- level: "error",
688
- message: "Not connected. Cannot send market data request."
689
- });
690
310
  return {
691
311
  content: [
692
312
  {
@@ -698,16 +318,8 @@ var createMarketDataRequestHandler = (parser, pendingRequests) => {
698
318
  isError: true
699
319
  };
700
320
  }
701
- parser.logger.log({
702
- level: "info",
703
- message: `Sending market data request message: ${JSON.stringify(mdr?.toFIXJSON())}`
704
- });
705
321
  parser.send(mdr);
706
322
  const fixData = await response;
707
- parser.logger.log({
708
- level: "info",
709
- message: `Received market data response for request ID: ${args.mdReqID}`
710
- });
711
323
  return {
712
324
  content: [
713
325
  {
@@ -756,9 +368,6 @@ var createGetStockGraphHandler = (marketDataPrices) => {
756
368
  const offerData = priceHistory.map((point) => point.offer);
757
369
  const spreadData = priceHistory.map((point) => point.spread);
758
370
  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);
762
371
  const config = {
763
372
  type: "line",
764
373
  data: {
@@ -788,30 +397,6 @@ var createGetStockGraphHandler = (marketDataPrices) => {
788
397
  fill: false,
789
398
  tension: 0.4
790
399
  },
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
- },
815
400
  {
816
401
  label: "Volume",
817
402
  data: volumeData,
@@ -857,7 +442,7 @@ var createGetStockGraphHandler = (marketDataPrices) => {
857
442
  content: [
858
443
  {
859
444
  type: "text",
860
- text: `Error: ${error instanceof Error ? error.message : "Failed to generate graph"}`,
445
+ text: `Error: ${error instanceof Error ? error.message : "Failed to generate chart"}`,
861
446
  uri: "getStockGraph"
862
447
  }
863
448
  ],
@@ -895,48 +480,7 @@ var createGetStockPriceHistoryHandler = (marketDataPrices) => {
895
480
  bid: point.bid,
896
481
  offer: point.offer,
897
482
  spread: point.spread,
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
483
+ volume: point.volume
940
484
  }))
941
485
  },
942
486
  null,
@@ -951,7 +495,7 @@ var createGetStockPriceHistoryHandler = (marketDataPrices) => {
951
495
  content: [
952
496
  {
953
497
  type: "text",
954
- text: `Error: ${error instanceof Error ? error.message : "Failed to get price history"}`,
498
+ text: `Error: ${error instanceof Error ? error.message : "Failed to get stock price history"}`,
955
499
  uri: "getStockPriceHistory"
956
500
  }
957
501
  ],
@@ -1059,7 +603,7 @@ Parameters verified:
1059
603
  - Symbol: ${args.symbol}
1060
604
  - TimeInForce: ${args.timeInForce} (${timeInForceNames[args.timeInForce]})
1061
605
 
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!`,
606
+ To execute this order, call the executeOrder tool with these exact same parameters.`,
1063
607
  uri: "verifyOrder"
1064
608
  }
1065
609
  ]
@@ -1255,210 +799,48 @@ var createToolHandlers = (parser, verifiedOrders, pendingRequests, marketDataPri
1255
799
  executeOrder: createExecuteOrderHandler(parser, verifiedOrders, pendingRequests),
1256
800
  marketDataRequest: createMarketDataRequestHandler(parser, pendingRequests),
1257
801
  getStockGraph: createGetStockGraphHandler(marketDataPrices),
1258
- getStockPriceHistory: createGetStockPriceHistoryHandler(marketDataPrices),
1259
- technicalAnalysis: createTechnicalAnalysisHandler(marketDataPrices)
802
+ getStockPriceHistory: createGetStockPriceHistoryHandler(marketDataPrices)
1260
803
  });
1261
804
 
1262
805
  // src/utils/messageHandler.ts
1263
806
  import { Fields as Fields3, MDEntryType as MDEntryType2, Messages as Messages3 } from "fixparser";
1264
- function getEnumValue(enumObj, name) {
1265
- return enumObj[name] || name;
1266
- }
1267
807
  function handleMessage(message, parser, pendingRequests, marketDataPrices, maxPriceHistory, onPriceUpdate) {
808
+ parser.logger.log({
809
+ level: "info",
810
+ message: `MCP Server received message: ${message.messageType}: ${message.description}`
811
+ });
1268
812
  const msgType = message.messageType;
1269
- if (msgType === Messages3.MarketDataSnapshotFullRefresh || msgType === Messages3.MarketDataIncrementalRefresh) {
813
+ if (msgType === Messages3.MarketDataSnapshotFullRefresh) {
1270
814
  const symbol = message.getField(Fields3.Symbol)?.value;
1271
- const fixJson = message.toFIXJSON();
1272
- const entries = fixJson.Body?.NoMDEntries || [];
1273
- const data = {
1274
- timestamp: Date.now(),
1275
- bid: 0,
1276
- offer: 0,
1277
- spread: 0,
1278
- volume: 0,
1279
- trade: 0,
1280
- indexValue: 0,
1281
- openingPrice: 0,
1282
- closingPrice: 0,
1283
- settlementPrice: 0,
1284
- tradingSessionHighPrice: 0,
1285
- tradingSessionLowPrice: 0,
1286
- vwap: 0,
1287
- imbalance: 0,
1288
- openInterest: 0,
1289
- compositeUnderlyingPrice: 0,
1290
- simulatedSellPrice: 0,
1291
- simulatedBuyPrice: 0,
1292
- marginRate: 0,
1293
- midPrice: 0,
1294
- emptyBook: 0,
1295
- settleHighPrice: 0,
1296
- settleLowPrice: 0,
1297
- priorSettlePrice: 0,
1298
- sessionHighBid: 0,
1299
- sessionLowOffer: 0,
1300
- earlyPrices: 0,
1301
- auctionClearingPrice: 0,
1302
- swapValueFactor: 0,
1303
- dailyValueAdjustmentForLongPositions: 0,
1304
- cumulativeValueAdjustmentForLongPositions: 0,
1305
- dailyValueAdjustmentForShortPositions: 0,
1306
- cumulativeValueAdjustmentForShortPositions: 0,
1307
- fixingPrice: 0,
1308
- cashRate: 0,
1309
- recoveryRate: 0,
1310
- recoveryRateForLong: 0,
1311
- recoveryRateForShort: 0,
1312
- marketBid: 0,
1313
- marketOffer: 0,
1314
- shortSaleMinPrice: 0,
1315
- previousClosingPrice: 0,
1316
- thresholdLimitPriceBanding: 0,
1317
- dailyFinancingValue: 0,
1318
- accruedFinancingValue: 0,
1319
- twap: 0
1320
- };
1321
- for (const entry of entries) {
1322
- const entryType = entry.MDEntryType;
1323
- const price = entry.MDEntryPx ? Number.parseFloat(entry.MDEntryPx) : 0;
1324
- const size = entry.MDEntrySize ? Number.parseFloat(entry.MDEntrySize) : 0;
1325
- const enumValue = getEnumValue(MDEntryType2, entryType);
1326
- switch (enumValue) {
1327
- case MDEntryType2.Bid:
1328
- data.bid = price;
1329
- break;
1330
- case MDEntryType2.Offer:
1331
- data.offer = price;
1332
- break;
1333
- case MDEntryType2.Trade:
1334
- data.trade = price;
1335
- break;
1336
- case MDEntryType2.IndexValue:
1337
- data.indexValue = price;
1338
- break;
1339
- case MDEntryType2.OpeningPrice:
1340
- data.openingPrice = price;
1341
- break;
1342
- case MDEntryType2.ClosingPrice:
1343
- data.closingPrice = price;
1344
- break;
1345
- case MDEntryType2.SettlementPrice:
1346
- data.settlementPrice = price;
1347
- break;
1348
- case MDEntryType2.TradingSessionHighPrice:
1349
- data.tradingSessionHighPrice = price;
1350
- break;
1351
- case MDEntryType2.TradingSessionLowPrice:
1352
- data.tradingSessionLowPrice = price;
1353
- break;
1354
- case MDEntryType2.VWAP:
1355
- data.vwap = price;
1356
- break;
1357
- case MDEntryType2.Imbalance:
1358
- data.imbalance = size;
1359
- break;
1360
- case MDEntryType2.TradeVolume:
1361
- data.volume = size;
1362
- break;
1363
- case MDEntryType2.OpenInterest:
1364
- data.openInterest = size;
1365
- break;
1366
- case MDEntryType2.CompositeUnderlyingPrice:
1367
- data.compositeUnderlyingPrice = price;
1368
- break;
1369
- case MDEntryType2.SimulatedSellPrice:
1370
- data.simulatedSellPrice = price;
1371
- break;
1372
- case MDEntryType2.SimulatedBuyPrice:
1373
- data.simulatedBuyPrice = price;
1374
- break;
1375
- case MDEntryType2.MarginRate:
1376
- data.marginRate = price;
1377
- break;
1378
- case MDEntryType2.MidPrice:
1379
- data.midPrice = price;
1380
- break;
1381
- case MDEntryType2.EmptyBook:
1382
- data.emptyBook = 1;
1383
- break;
1384
- case MDEntryType2.SettleHighPrice:
1385
- data.settleHighPrice = price;
1386
- break;
1387
- case MDEntryType2.SettleLowPrice:
1388
- data.settleLowPrice = price;
1389
- break;
1390
- case MDEntryType2.PriorSettlePrice:
1391
- data.priorSettlePrice = price;
1392
- break;
1393
- case MDEntryType2.SessionHighBid:
1394
- data.sessionHighBid = price;
1395
- break;
1396
- case MDEntryType2.SessionLowOffer:
1397
- data.sessionLowOffer = price;
1398
- break;
1399
- case MDEntryType2.EarlyPrices:
1400
- data.earlyPrices = price;
1401
- break;
1402
- case MDEntryType2.AuctionClearingPrice:
1403
- data.auctionClearingPrice = price;
1404
- break;
1405
- case MDEntryType2.SwapValueFactor:
1406
- data.swapValueFactor = price;
1407
- break;
1408
- case MDEntryType2.DailyValueAdjustmentForLongPositions:
1409
- data.dailyValueAdjustmentForLongPositions = price;
1410
- break;
1411
- case MDEntryType2.CumulativeValueAdjustmentForLongPositions:
1412
- data.cumulativeValueAdjustmentForLongPositions = price;
1413
- break;
1414
- case MDEntryType2.DailyValueAdjustmentForShortPositions:
1415
- data.dailyValueAdjustmentForShortPositions = price;
1416
- break;
1417
- case MDEntryType2.CumulativeValueAdjustmentForShortPositions:
1418
- data.cumulativeValueAdjustmentForShortPositions = price;
1419
- break;
1420
- case MDEntryType2.FixingPrice:
1421
- data.fixingPrice = price;
1422
- break;
1423
- case MDEntryType2.CashRate:
1424
- data.cashRate = price;
1425
- break;
1426
- case MDEntryType2.RecoveryRate:
1427
- data.recoveryRate = price;
1428
- break;
1429
- case MDEntryType2.RecoveryRateForLong:
1430
- data.recoveryRateForLong = price;
1431
- break;
1432
- case MDEntryType2.RecoveryRateForShort:
1433
- data.recoveryRateForShort = price;
1434
- break;
1435
- case MDEntryType2.MarketBid:
1436
- data.marketBid = price;
1437
- break;
1438
- case MDEntryType2.MarketOffer:
1439
- data.marketOffer = price;
1440
- break;
1441
- case MDEntryType2.ShortSaleMinPrice:
1442
- data.shortSaleMinPrice = price;
1443
- break;
1444
- case MDEntryType2.PreviousClosingPrice:
1445
- data.previousClosingPrice = price;
1446
- break;
1447
- case MDEntryType2.ThresholdLimitPriceBanding:
1448
- data.thresholdLimitPriceBanding = price;
1449
- break;
1450
- case MDEntryType2.DailyFinancingValue:
1451
- data.dailyFinancingValue = price;
1452
- break;
1453
- case MDEntryType2.AccruedFinancingValue:
1454
- data.accruedFinancingValue = price;
1455
- break;
1456
- case MDEntryType2.TWAP:
1457
- data.twap = price;
1458
- break;
815
+ const entries = message.getField(Fields3.NoMDEntries)?.value;
816
+ let bid = 0;
817
+ let offer = 0;
818
+ let volume = 0;
819
+ const entryTypes = message.getFields(Fields3.MDEntryType);
820
+ const entryPrices = message.getFields(Fields3.MDEntryPx);
821
+ const entrySizes = message.getFields(Fields3.MDEntrySize);
822
+ if (entryTypes && entryPrices && entrySizes) {
823
+ for (let i = 0; i < entries; i++) {
824
+ const entryType = entryTypes[i]?.value;
825
+ const entryPrice = Number.parseFloat(entryPrices[i]?.value);
826
+ const entrySize = Number.parseFloat(entrySizes[i]?.value);
827
+ if (entryType === MDEntryType2.Bid) {
828
+ bid = entryPrice;
829
+ } else if (entryType === MDEntryType2.Offer) {
830
+ offer = entryPrice;
831
+ }
832
+ volume += entrySize;
1459
833
  }
1460
834
  }
1461
- data.spread = data.offer - data.bid;
835
+ const spread = offer - bid;
836
+ const timestamp = Date.now();
837
+ const data = {
838
+ timestamp,
839
+ bid,
840
+ offer,
841
+ spread,
842
+ volume
843
+ };
1462
844
  if (!marketDataPrices.has(symbol)) {
1463
845
  marketDataPrices.set(symbol, []);
1464
846
  }
@@ -1468,14 +850,6 @@ function handleMessage(message, parser, pendingRequests, marketDataPrices, maxPr
1468
850
  prices.splice(0, prices.length - maxPriceHistory);
1469
851
  }
1470
852
  onPriceUpdate?.(symbol, data);
1471
- const mdReqID = message.getField(Fields3.MDReqID)?.value;
1472
- if (mdReqID) {
1473
- const callback = pendingRequests.get(mdReqID);
1474
- if (callback) {
1475
- callback(message);
1476
- pendingRequests.delete(mdReqID);
1477
- }
1478
- }
1479
853
  } else if (msgType === Messages3.ExecutionReport) {
1480
854
  const reqId = message.getField(Fields3.ClOrdID)?.value;
1481
855
  const callback = pendingRequests.get(reqId);
@@ -1504,7 +878,7 @@ var MCPLocal = class extends MCPBase {
1504
878
  */
1505
879
  marketDataPrices = /* @__PURE__ */ new Map();
1506
880
  /**
1507
- * Maximum number of price history entries to keep per symbol
881
+ * Maximum number of price points to store per symbol
1508
882
  * @private
1509
883
  */
1510
884
  MAX_PRICE_HISTORY = 1e5;
@@ -1613,39 +987,6 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
1613
987
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
1614
988
  import { z as z2 } from "zod";
1615
989
  var transports = {};
1616
- function jsonSchemaToZod(schema) {
1617
- if (schema.type === "object") {
1618
- const shape = {};
1619
- for (const [key, prop] of Object.entries(schema.properties || {})) {
1620
- const propSchema = prop;
1621
- if (propSchema.type === "string") {
1622
- if (propSchema.enum) {
1623
- shape[key] = z2.enum(propSchema.enum);
1624
- } else {
1625
- shape[key] = z2.string();
1626
- }
1627
- } else if (propSchema.type === "number") {
1628
- shape[key] = z2.number();
1629
- } else if (propSchema.type === "boolean") {
1630
- shape[key] = z2.boolean();
1631
- } else if (propSchema.type === "array") {
1632
- if (propSchema.items.type === "string") {
1633
- shape[key] = z2.array(z2.string());
1634
- } else if (propSchema.items.type === "number") {
1635
- shape[key] = z2.array(z2.number());
1636
- } else if (propSchema.items.type === "boolean") {
1637
- shape[key] = z2.array(z2.boolean());
1638
- } else {
1639
- shape[key] = z2.array(z2.any());
1640
- }
1641
- } else {
1642
- shape[key] = z2.any();
1643
- }
1644
- }
1645
- return shape;
1646
- }
1647
- return {};
1648
- }
1649
990
  var MCPRemote = class extends MCPBase {
1650
991
  /**
1651
992
  * Port number the server will listen on.
@@ -1688,7 +1029,7 @@ var MCPRemote = class extends MCPBase {
1688
1029
  */
1689
1030
  marketDataPrices = /* @__PURE__ */ new Map();
1690
1031
  /**
1691
- * Maximum number of price history entries to keep per symbol
1032
+ * Maximum number of price points to store per symbol
1692
1033
  * @private
1693
1034
  */
1694
1035
  MAX_PRICE_HISTORY = 1e5;
@@ -1710,7 +1051,31 @@ var MCPRemote = class extends MCPBase {
1710
1051
  this.parser,
1711
1052
  this.pendingRequests,
1712
1053
  this.marketDataPrices,
1713
- this.MAX_PRICE_HISTORY
1054
+ this.MAX_PRICE_HISTORY,
1055
+ (symbol, data) => {
1056
+ this.mcpServer?.tool(
1057
+ "priceUpdate",
1058
+ {
1059
+ description: "Price update notification",
1060
+ schema: z2.object({
1061
+ symbol: z2.string(),
1062
+ timestamp: z2.number(),
1063
+ bid: z2.number(),
1064
+ offer: z2.number(),
1065
+ spread: z2.number(),
1066
+ volume: z2.number()
1067
+ })
1068
+ },
1069
+ () => ({
1070
+ content: [
1071
+ {
1072
+ type: "text",
1073
+ text: JSON.stringify({ symbol, ...data })
1074
+ }
1075
+ ]
1076
+ })
1077
+ );
1078
+ }
1714
1079
  );
1715
1080
  }
1716
1081
  });
@@ -1772,15 +1137,7 @@ var MCPRemote = class extends MCPBase {
1772
1137
  );
1773
1138
  return;
1774
1139
  }
1775
- try {
1776
- await transport.handleRequest(req, res, parsed);
1777
- } catch (error) {
1778
- this.logger?.log({
1779
- level: "error",
1780
- message: `Error handling request: ${error}`
1781
- });
1782
- throw error;
1783
- }
1140
+ await transport.handleRequest(req, res, parsed);
1784
1141
  });
1785
1142
  } else if (req.method === "GET" || req.method === "DELETE") {
1786
1143
  if (!sessionId || !transports[sessionId]) {
@@ -1789,20 +1146,8 @@ var MCPRemote = class extends MCPBase {
1789
1146
  return;
1790
1147
  }
1791
1148
  const transport = transports[sessionId];
1792
- try {
1793
- await transport.handleRequest(req, res);
1794
- } catch (error) {
1795
- this.logger?.log({
1796
- level: "error",
1797
- message: `Error handling ${req.method} request: ${error}`
1798
- });
1799
- throw error;
1800
- }
1149
+ await transport.handleRequest(req, res);
1801
1150
  } else {
1802
- this.logger?.log({
1803
- level: "error",
1804
- message: `Method not allowed: ${req.method}`
1805
- });
1806
1151
  res.writeHead(405);
1807
1152
  res.end("Method Not Allowed");
1808
1153
  }
@@ -1836,40 +1181,69 @@ var MCPRemote = class extends MCPBase {
1836
1181
  });
1837
1182
  return;
1838
1183
  }
1839
- const toolHandlers = createToolHandlers(
1840
- this.parser,
1841
- this.verifiedOrders,
1842
- this.pendingRequests,
1843
- this.marketDataPrices
1184
+ this.mcpServer.tool(
1185
+ "tools/list",
1186
+ {
1187
+ description: "List available tools",
1188
+ schema: z2.object({})
1189
+ },
1190
+ async () => {
1191
+ return {
1192
+ content: [
1193
+ {
1194
+ type: "text",
1195
+ text: JSON.stringify(
1196
+ Object.entries(toolSchemas).map(([name, { description, schema }]) => ({
1197
+ name,
1198
+ description,
1199
+ inputSchema: schema
1200
+ }))
1201
+ )
1202
+ }
1203
+ ]
1204
+ };
1205
+ }
1844
1206
  );
1845
- Object.entries(toolSchemas).forEach(([name, { description, schema }]) => {
1846
- this.mcpServer?.registerTool(
1847
- name,
1848
- {
1849
- description,
1850
- inputSchema: jsonSchemaToZod(schema)
1851
- },
1852
- async (args) => {
1853
- const handler = toolHandlers[name];
1854
- if (!handler) {
1855
- return {
1856
- content: [
1857
- {
1858
- type: "text",
1859
- text: `Tool not found: ${name}`
1860
- }
1861
- ],
1862
- isError: true
1863
- };
1864
- }
1865
- const result = await handler(args);
1207
+ this.mcpServer.tool(
1208
+ "tools/call",
1209
+ {
1210
+ description: "Call a tool",
1211
+ schema: z2.object({
1212
+ name: z2.string(),
1213
+ arguments: z2.any(),
1214
+ _meta: z2.object({
1215
+ progressToken: z2.number()
1216
+ }).optional()
1217
+ })
1218
+ },
1219
+ async (request) => {
1220
+ const { name, arguments: args } = request;
1221
+ const toolHandlers = createToolHandlers(
1222
+ this.parser,
1223
+ this.verifiedOrders,
1224
+ this.pendingRequests,
1225
+ this.marketDataPrices
1226
+ );
1227
+ const handler = toolHandlers[name];
1228
+ if (!handler) {
1866
1229
  return {
1867
- content: result.content,
1868
- isError: result.isError
1230
+ content: [
1231
+ {
1232
+ type: "text",
1233
+ text: `Tool not found: ${name}`,
1234
+ uri: name
1235
+ }
1236
+ ],
1237
+ isError: true
1869
1238
  };
1870
1239
  }
1871
- );
1872
- });
1240
+ const result = await handler(args);
1241
+ return {
1242
+ content: result.content,
1243
+ isError: result.isError
1244
+ };
1245
+ }
1246
+ );
1873
1247
  }
1874
1248
  };
1875
1249
  export {