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.
@@ -166,7 +166,7 @@ var toolSchemas = {
166
166
  }
167
167
  },
168
168
  executeOrder: {
169
- description: "Executes a verified order. verifyOrder must be called before executeOrder.",
169
+ description: "Executes a verified order. verifyOrder must be called before executeOrder. user has to explicitly allow executeOrder.",
170
170
  schema: {
171
171
  type: "object",
172
172
  properties: {
@@ -310,395 +310,19 @@ var toolSchemas = {
310
310
  },
311
311
  required: ["symbol"]
312
312
  }
313
- },
314
- technicalAnalysis: {
315
- description: "Performs comprehensive technical analysis on market data for a given symbol, including indicators like SMA, EMA, RSI, Bollinger Bands, and trading signals",
316
- schema: {
317
- type: "object",
318
- properties: {
319
- symbol: {
320
- type: "string",
321
- description: "The trading symbol to analyze (e.g., AAPL, MSFT, EURUSD)"
322
- }
323
- },
324
- required: ["symbol"]
325
- }
326
313
  }
327
314
  };
328
315
 
329
- // src/tools/analytics.ts
330
- function sum(numbers) {
331
- return numbers.reduce((acc, val) => acc + val, 0);
332
- }
333
- var TechnicalAnalyzer = class {
334
- prices;
335
- volumes;
336
- highs;
337
- lows;
338
- constructor(data) {
339
- this.prices = data.map((d) => d.trade > 0 ? d.trade : d.midPrice);
340
- this.volumes = data.map((d) => d.volume);
341
- this.highs = data.map((d) => d.tradingSessionHighPrice > 0 ? d.tradingSessionHighPrice : d.trade);
342
- this.lows = data.map((d) => d.tradingSessionLowPrice > 0 ? d.tradingSessionLowPrice : d.trade);
343
- }
344
- // Calculate Simple Moving Average
345
- calculateSMA(data, period) {
346
- const sma = [];
347
- for (let i = period - 1; i < data.length; i++) {
348
- const sum2 = data.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0);
349
- sma.push(sum2 / period);
350
- }
351
- return sma;
352
- }
353
- // Calculate Exponential Moving Average
354
- calculateEMA(data, period) {
355
- const multiplier = 2 / (period + 1);
356
- const ema = [data[0]];
357
- for (let i = 1; i < data.length; i++) {
358
- ema.push(data[i] * multiplier + ema[i - 1] * (1 - multiplier));
359
- }
360
- return ema;
361
- }
362
- // Calculate RSI
363
- calculateRSI(data, period = 14) {
364
- if (data.length < period + 1) return [];
365
- const changes = [];
366
- for (let i = 1; i < data.length; i++) {
367
- changes.push(data[i] - data[i - 1]);
368
- }
369
- const gains = changes.map((change) => change > 0 ? change : 0);
370
- const losses = changes.map((change) => change < 0 ? Math.abs(change) : 0);
371
- let avgGain = gains.slice(0, period).reduce((a, b) => a + b, 0) / period;
372
- let avgLoss = losses.slice(0, period).reduce((a, b) => a + b, 0) / period;
373
- const rsi = [];
374
- for (let i = period; i < changes.length; i++) {
375
- const rs = avgGain / avgLoss;
376
- rsi.push(100 - 100 / (1 + rs));
377
- avgGain = (avgGain * (period - 1) + gains[i]) / period;
378
- avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
379
- }
380
- return rsi;
381
- }
382
- // Calculate Bollinger Bands
383
- calculateBollingerBands(data, period = 20, stdDev = 2) {
384
- if (data.length < period) return [];
385
- const sma = this.calculateSMA(data, period);
386
- const bands = [];
387
- for (let i = 0; i < sma.length; i++) {
388
- const dataSlice = data.slice(i, i + period);
389
- const mean = sma[i];
390
- const variance = dataSlice.reduce((sum2, price) => sum2 + (price - mean) ** 2, 0) / period;
391
- const standardDeviation = Math.sqrt(variance);
392
- bands.push({
393
- upper: mean + standardDeviation * stdDev,
394
- middle: mean,
395
- lower: mean - standardDeviation * stdDev
396
- });
397
- }
398
- return bands;
399
- }
400
- // Calculate maximum drawdown
401
- calculateMaxDrawdown(prices) {
402
- let maxPrice = prices[0];
403
- let maxDrawdown = 0;
404
- for (let i = 1; i < prices.length; i++) {
405
- if (prices[i] > maxPrice) {
406
- maxPrice = prices[i];
407
- }
408
- const drawdown = (maxPrice - prices[i]) / maxPrice;
409
- if (drawdown > maxDrawdown) {
410
- maxDrawdown = drawdown;
411
- }
412
- }
413
- return maxDrawdown;
414
- }
415
- // Calculate price changes for volatility
416
- calculatePriceChanges() {
417
- const changes = [];
418
- for (let i = 1; i < this.prices.length; i++) {
419
- changes.push((this.prices[i] - this.prices[i - 1]) / this.prices[i - 1]);
420
- }
421
- return changes;
422
- }
423
- // Generate comprehensive market analysis
424
- analyze() {
425
- const currentPrice = this.prices[this.prices.length - 1];
426
- const startPrice = this.prices[0];
427
- const sessionHigh = Math.max(...this.highs);
428
- const sessionLow = Math.min(...this.lows);
429
- const totalVolume = sum(this.volumes);
430
- const avgVolume = totalVolume / this.volumes.length;
431
- const priceChanges = this.calculatePriceChanges();
432
- const volatility = priceChanges.length > 0 ? Math.sqrt(
433
- priceChanges.reduce((sum2, change) => sum2 + change ** 2, 0) / priceChanges.length
434
- ) * Math.sqrt(252) * 100 : 0;
435
- const sessionReturn = (currentPrice - startPrice) / startPrice * 100;
436
- const pricePosition = (currentPrice - sessionLow) / (sessionHigh - sessionLow) * 100;
437
- const trueVWAP = this.prices.reduce((sum2, price, i) => sum2 + price * this.volumes[i], 0) / totalVolume;
438
- 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;
439
- 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;
440
- const maxDrawdown = this.calculateMaxDrawdown(this.prices);
441
- return {
442
- currentPrice,
443
- startPrice,
444
- sessionHigh,
445
- sessionLow,
446
- totalVolume,
447
- avgVolume,
448
- volatility,
449
- sessionReturn,
450
- pricePosition,
451
- trueVWAP,
452
- momentum5,
453
- momentum10,
454
- maxDrawdown
455
- };
456
- }
457
- // Generate technical indicators
458
- getTechnicalIndicators() {
459
- return {
460
- sma5: this.calculateSMA(this.prices, 5),
461
- sma10: this.calculateSMA(this.prices, 10),
462
- ema8: this.calculateEMA(this.prices, 8),
463
- ema21: this.calculateEMA(this.prices, 21),
464
- rsi: this.calculateRSI(this.prices, 14),
465
- bollinger: this.calculateBollingerBands(this.prices, 20, 2)
466
- };
467
- }
468
- // Generate trading signals
469
- generateSignals() {
470
- const analysis = this.analyze();
471
- let bullishSignals = 0;
472
- let bearishSignals = 0;
473
- const signals = [];
474
- if (analysis.currentPrice > analysis.trueVWAP) {
475
- signals.push(
476
- `\u2713 BULLISH: Price above VWAP (+${((analysis.currentPrice - analysis.trueVWAP) / analysis.trueVWAP * 100).toFixed(2)}%)`
477
- );
478
- bullishSignals++;
479
- } else {
480
- signals.push(
481
- `\u2717 BEARISH: Price below VWAP (${((analysis.currentPrice - analysis.trueVWAP) / analysis.trueVWAP * 100).toFixed(2)}%)`
482
- );
483
- bearishSignals++;
484
- }
485
- if (analysis.momentum5 > 0 && analysis.momentum10 > 0) {
486
- signals.push("\u2713 BULLISH: Positive momentum on both timeframes");
487
- bullishSignals++;
488
- } else if (analysis.momentum5 < 0 && analysis.momentum10 < 0) {
489
- signals.push("\u2717 BEARISH: Negative momentum on both timeframes");
490
- bearishSignals++;
491
- } else {
492
- signals.push("\u25D0 MIXED: Conflicting momentum signals");
493
- }
494
- const currentVolume = this.volumes[this.volumes.length - 1];
495
- const volumeRatio = currentVolume / analysis.avgVolume;
496
- if (volumeRatio > 1.2 && analysis.sessionReturn > 0) {
497
- signals.push("\u2713 BULLISH: Above-average volume supporting upward move");
498
- bullishSignals++;
499
- } else if (volumeRatio > 1.2 && analysis.sessionReturn < 0) {
500
- signals.push("\u2717 BEARISH: Above-average volume supporting downward move");
501
- bearishSignals++;
502
- } else {
503
- signals.push("\u25D0 NEUTRAL: Volume not providing clear direction");
504
- }
505
- if (analysis.pricePosition > 65 && analysis.volatility > 30) {
506
- signals.push("\u2717 BEARISH: High in range with elevated volatility - reversal risk");
507
- bearishSignals++;
508
- } else if (analysis.pricePosition < 35 && analysis.volatility > 30) {
509
- signals.push("\u2713 BULLISH: Low in range with volatility - potential bounce");
510
- bullishSignals++;
511
- } else {
512
- signals.push("\u25D0 NEUTRAL: Price position and volatility not extreme");
513
- }
514
- return { bullishSignals, bearishSignals, signals };
515
- }
516
- // Generate comprehensive JSON analysis
517
- generateJSONAnalysis(symbol) {
518
- const analysis = this.analyze();
519
- const indicators = this.getTechnicalIndicators();
520
- const signals = this.generateSignals();
521
- const currentSMA5 = indicators.sma5.length > 0 ? indicators.sma5[indicators.sma5.length - 1] : null;
522
- const currentSMA10 = indicators.sma10.length > 0 ? indicators.sma10[indicators.sma10.length - 1] : null;
523
- const currentEMA8 = indicators.ema8[indicators.ema8.length - 1];
524
- const currentEMA21 = indicators.ema21[indicators.ema21.length - 1];
525
- const currentRSI = indicators.rsi.length > 0 ? indicators.rsi[indicators.rsi.length - 1] : null;
526
- const currentBB = indicators.bollinger.length > 0 ? indicators.bollinger[indicators.bollinger.length - 1] : null;
527
- const currentVolume = this.volumes[this.volumes.length - 1];
528
- const volumeRatio = currentVolume / analysis.avgVolume;
529
- const currentDrawdown = (analysis.sessionHigh - analysis.currentPrice) / analysis.sessionHigh * 100;
530
- const rangeWidth = (analysis.sessionHigh - analysis.sessionLow) / analysis.sessionLow * 100;
531
- const priceVsVWAP = (analysis.currentPrice - analysis.trueVWAP) / analysis.trueVWAP * 100;
532
- const totalScore = signals.bullishSignals - signals.bearishSignals;
533
- const overallSignal = totalScore > 0 ? "BULLISH_BIAS" : totalScore < 0 ? "BEARISH_BIAS" : "NEUTRAL";
534
- const targetEntry = Math.max(analysis.sessionLow * 1.005, analysis.trueVWAP * 0.998);
535
- const stopLoss = analysis.sessionLow * 0.995;
536
- const profitTarget = analysis.sessionHigh * 0.995;
537
- const riskRewardRatio = (profitTarget - analysis.currentPrice) / (analysis.currentPrice - stopLoss);
538
- return {
539
- symbol,
540
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
541
- marketStructure: {
542
- currentPrice: analysis.currentPrice,
543
- startPrice: analysis.startPrice,
544
- sessionHigh: analysis.sessionHigh,
545
- sessionLow: analysis.sessionLow,
546
- rangeWidth,
547
- totalVolume: analysis.totalVolume,
548
- sessionPerformance: analysis.sessionReturn,
549
- positionInRange: analysis.pricePosition
550
- },
551
- volatility: {
552
- impliedVolatility: analysis.volatility,
553
- maxDrawdown: analysis.maxDrawdown * 100,
554
- currentDrawdown
555
- },
556
- technicalIndicators: {
557
- sma5: currentSMA5,
558
- sma10: currentSMA10,
559
- ema8: currentEMA8,
560
- ema21: currentEMA21,
561
- rsi: currentRSI,
562
- bollingerBands: currentBB ? {
563
- upper: currentBB.upper,
564
- middle: currentBB.middle,
565
- lower: currentBB.lower,
566
- position: (analysis.currentPrice - currentBB.lower) / (currentBB.upper - currentBB.lower) * 100
567
- } : null
568
- },
569
- volumeAnalysis: {
570
- currentVolume,
571
- averageVolume: Math.round(analysis.avgVolume),
572
- volumeRatio,
573
- trueVWAP: analysis.trueVWAP,
574
- priceVsVWAP
575
- },
576
- momentum: {
577
- momentum5: analysis.momentum5,
578
- momentum10: analysis.momentum10,
579
- sessionROC: analysis.sessionReturn
580
- },
581
- tradingSignals: {
582
- ...signals,
583
- overallSignal,
584
- signalScore: totalScore
585
- },
586
- riskManagement: {
587
- targetEntry,
588
- stopLoss,
589
- profitTarget,
590
- riskRewardRatio
591
- }
592
- };
593
- }
594
- };
595
- var createTechnicalAnalysisHandler = (marketDataPrices) => {
596
- return async (args) => {
597
- try {
598
- const symbol = args.symbol;
599
- const priceHistory = marketDataPrices.get(symbol) || [];
600
- if (priceHistory.length === 0) {
601
- return {
602
- content: [
603
- {
604
- type: "text",
605
- text: `No price data available for ${symbol}. Please request market data first.`,
606
- uri: "technicalAnalysis"
607
- }
608
- ]
609
- };
610
- }
611
- const analyzer = new TechnicalAnalyzer(priceHistory);
612
- const analysis = analyzer.generateJSONAnalysis(symbol);
613
- return {
614
- content: [
615
- {
616
- type: "text",
617
- text: `Technical Analysis for ${symbol}:
618
-
619
- ${JSON.stringify(analysis, null, 2)}`,
620
- uri: "technicalAnalysis"
621
- }
622
- ]
623
- };
624
- } catch (error) {
625
- return {
626
- content: [
627
- {
628
- type: "text",
629
- text: `Error performing technical analysis: ${error instanceof Error ? error.message : "Unknown error"}`,
630
- uri: "technicalAnalysis"
631
- }
632
- ],
633
- isError: true
634
- };
635
- }
636
- };
637
- };
638
-
639
316
  // src/tools/marketData.ts
640
317
  var import_fixparser = require("fixparser");
641
318
  var import_quickchart_js = __toESM(require("quickchart-js"), 1);
642
319
  var createMarketDataRequestHandler = (parser, pendingRequests) => {
643
320
  return async (args) => {
644
321
  try {
645
- parser.logger.log({
646
- level: "info",
647
- message: `Sending market data request for symbols: ${args.symbols.join(", ")}`
648
- });
649
322
  const response = new Promise((resolve) => {
650
323
  pendingRequests.set(args.mdReqID, resolve);
651
- parser.logger.log({
652
- level: "info",
653
- message: `Registered callback for market data request ID: ${args.mdReqID}`
654
- });
655
324
  });
656
- const entryTypes = args.mdEntryTypes || [
657
- import_fixparser.MDEntryType.Bid,
658
- import_fixparser.MDEntryType.Offer,
659
- import_fixparser.MDEntryType.Trade,
660
- import_fixparser.MDEntryType.IndexValue,
661
- import_fixparser.MDEntryType.OpeningPrice,
662
- import_fixparser.MDEntryType.ClosingPrice,
663
- import_fixparser.MDEntryType.SettlementPrice,
664
- import_fixparser.MDEntryType.TradingSessionHighPrice,
665
- import_fixparser.MDEntryType.TradingSessionLowPrice,
666
- import_fixparser.MDEntryType.VWAP,
667
- import_fixparser.MDEntryType.Imbalance,
668
- import_fixparser.MDEntryType.TradeVolume,
669
- import_fixparser.MDEntryType.OpenInterest,
670
- import_fixparser.MDEntryType.CompositeUnderlyingPrice,
671
- import_fixparser.MDEntryType.SimulatedSellPrice,
672
- import_fixparser.MDEntryType.SimulatedBuyPrice,
673
- import_fixparser.MDEntryType.MarginRate,
674
- import_fixparser.MDEntryType.MidPrice,
675
- import_fixparser.MDEntryType.EmptyBook,
676
- import_fixparser.MDEntryType.SettleHighPrice,
677
- import_fixparser.MDEntryType.SettleLowPrice,
678
- import_fixparser.MDEntryType.PriorSettlePrice,
679
- import_fixparser.MDEntryType.SessionHighBid,
680
- import_fixparser.MDEntryType.SessionLowOffer,
681
- import_fixparser.MDEntryType.EarlyPrices,
682
- import_fixparser.MDEntryType.AuctionClearingPrice,
683
- import_fixparser.MDEntryType.SwapValueFactor,
684
- import_fixparser.MDEntryType.DailyValueAdjustmentForLongPositions,
685
- import_fixparser.MDEntryType.CumulativeValueAdjustmentForLongPositions,
686
- import_fixparser.MDEntryType.DailyValueAdjustmentForShortPositions,
687
- import_fixparser.MDEntryType.CumulativeValueAdjustmentForShortPositions,
688
- import_fixparser.MDEntryType.FixingPrice,
689
- import_fixparser.MDEntryType.CashRate,
690
- import_fixparser.MDEntryType.RecoveryRate,
691
- import_fixparser.MDEntryType.RecoveryRateForLong,
692
- import_fixparser.MDEntryType.RecoveryRateForShort,
693
- import_fixparser.MDEntryType.MarketBid,
694
- import_fixparser.MDEntryType.MarketOffer,
695
- import_fixparser.MDEntryType.ShortSaleMinPrice,
696
- import_fixparser.MDEntryType.PreviousClosingPrice,
697
- import_fixparser.MDEntryType.ThresholdLimitPriceBanding,
698
- import_fixparser.MDEntryType.DailyFinancingValue,
699
- import_fixparser.MDEntryType.AccruedFinancingValue,
700
- import_fixparser.MDEntryType.TWAP
701
- ];
325
+ const entryTypes = args.mdEntryTypes || [import_fixparser.MDEntryType.Bid, import_fixparser.MDEntryType.Offer, import_fixparser.MDEntryType.TradeVolume];
702
326
  const messageFields = [
703
327
  new import_fixparser.Field(import_fixparser.Fields.MsgType, import_fixparser.Messages.MarketDataRequest),
704
328
  new import_fixparser.Field(import_fixparser.Fields.SenderCompID, parser.sender),
@@ -720,10 +344,6 @@ var createMarketDataRequestHandler = (parser, pendingRequests) => {
720
344
  });
721
345
  const mdr = parser.createMessage(...messageFields);
722
346
  if (!parser.connected) {
723
- parser.logger.log({
724
- level: "error",
725
- message: "Not connected. Cannot send market data request."
726
- });
727
347
  return {
728
348
  content: [
729
349
  {
@@ -735,16 +355,8 @@ var createMarketDataRequestHandler = (parser, pendingRequests) => {
735
355
  isError: true
736
356
  };
737
357
  }
738
- parser.logger.log({
739
- level: "info",
740
- message: `Sending market data request message: ${JSON.stringify(mdr?.toFIXJSON())}`
741
- });
742
358
  parser.send(mdr);
743
359
  const fixData = await response;
744
- parser.logger.log({
745
- level: "info",
746
- message: `Received market data response for request ID: ${args.mdReqID}`
747
- });
748
360
  return {
749
361
  content: [
750
362
  {
@@ -793,9 +405,6 @@ var createGetStockGraphHandler = (marketDataPrices) => {
793
405
  const offerData = priceHistory.map((point) => point.offer);
794
406
  const spreadData = priceHistory.map((point) => point.spread);
795
407
  const volumeData = priceHistory.map((point) => point.volume);
796
- const tradeData = priceHistory.map((point) => point.trade);
797
- const vwapData = priceHistory.map((point) => point.vwap);
798
- const twapData = priceHistory.map((point) => point.twap);
799
408
  const config = {
800
409
  type: "line",
801
410
  data: {
@@ -825,30 +434,6 @@ var createGetStockGraphHandler = (marketDataPrices) => {
825
434
  fill: false,
826
435
  tension: 0.4
827
436
  },
828
- {
829
- label: "Trade",
830
- data: tradeData,
831
- borderColor: "#ffc107",
832
- backgroundColor: "rgba(255, 193, 7, 0.1)",
833
- fill: false,
834
- tension: 0.4
835
- },
836
- {
837
- label: "VWAP",
838
- data: vwapData,
839
- borderColor: "#17a2b8",
840
- backgroundColor: "rgba(23, 162, 184, 0.1)",
841
- fill: false,
842
- tension: 0.4
843
- },
844
- {
845
- label: "TWAP",
846
- data: twapData,
847
- borderColor: "#6610f2",
848
- backgroundColor: "rgba(102, 16, 242, 0.1)",
849
- fill: false,
850
- tension: 0.4
851
- },
852
437
  {
853
438
  label: "Volume",
854
439
  data: volumeData,
@@ -894,7 +479,7 @@ var createGetStockGraphHandler = (marketDataPrices) => {
894
479
  content: [
895
480
  {
896
481
  type: "text",
897
- text: `Error: ${error instanceof Error ? error.message : "Failed to generate graph"}`,
482
+ text: `Error: ${error instanceof Error ? error.message : "Failed to generate chart"}`,
898
483
  uri: "getStockGraph"
899
484
  }
900
485
  ],
@@ -932,48 +517,7 @@ var createGetStockPriceHistoryHandler = (marketDataPrices) => {
932
517
  bid: point.bid,
933
518
  offer: point.offer,
934
519
  spread: point.spread,
935
- volume: point.volume,
936
- trade: point.trade,
937
- indexValue: point.indexValue,
938
- openingPrice: point.openingPrice,
939
- closingPrice: point.closingPrice,
940
- settlementPrice: point.settlementPrice,
941
- tradingSessionHighPrice: point.tradingSessionHighPrice,
942
- tradingSessionLowPrice: point.tradingSessionLowPrice,
943
- vwap: point.vwap,
944
- imbalance: point.imbalance,
945
- openInterest: point.openInterest,
946
- compositeUnderlyingPrice: point.compositeUnderlyingPrice,
947
- simulatedSellPrice: point.simulatedSellPrice,
948
- simulatedBuyPrice: point.simulatedBuyPrice,
949
- marginRate: point.marginRate,
950
- midPrice: point.midPrice,
951
- emptyBook: point.emptyBook,
952
- settleHighPrice: point.settleHighPrice,
953
- settleLowPrice: point.settleLowPrice,
954
- priorSettlePrice: point.priorSettlePrice,
955
- sessionHighBid: point.sessionHighBid,
956
- sessionLowOffer: point.sessionLowOffer,
957
- earlyPrices: point.earlyPrices,
958
- auctionClearingPrice: point.auctionClearingPrice,
959
- swapValueFactor: point.swapValueFactor,
960
- dailyValueAdjustmentForLongPositions: point.dailyValueAdjustmentForLongPositions,
961
- cumulativeValueAdjustmentForLongPositions: point.cumulativeValueAdjustmentForLongPositions,
962
- dailyValueAdjustmentForShortPositions: point.dailyValueAdjustmentForShortPositions,
963
- cumulativeValueAdjustmentForShortPositions: point.cumulativeValueAdjustmentForShortPositions,
964
- fixingPrice: point.fixingPrice,
965
- cashRate: point.cashRate,
966
- recoveryRate: point.recoveryRate,
967
- recoveryRateForLong: point.recoveryRateForLong,
968
- recoveryRateForShort: point.recoveryRateForShort,
969
- marketBid: point.marketBid,
970
- marketOffer: point.marketOffer,
971
- shortSaleMinPrice: point.shortSaleMinPrice,
972
- previousClosingPrice: point.previousClosingPrice,
973
- thresholdLimitPriceBanding: point.thresholdLimitPriceBanding,
974
- dailyFinancingValue: point.dailyFinancingValue,
975
- accruedFinancingValue: point.accruedFinancingValue,
976
- twap: point.twap
520
+ volume: point.volume
977
521
  }))
978
522
  },
979
523
  null,
@@ -988,7 +532,7 @@ var createGetStockPriceHistoryHandler = (marketDataPrices) => {
988
532
  content: [
989
533
  {
990
534
  type: "text",
991
- text: `Error: ${error instanceof Error ? error.message : "Failed to get price history"}`,
535
+ text: `Error: ${error instanceof Error ? error.message : "Failed to get stock price history"}`,
992
536
  uri: "getStockPriceHistory"
993
537
  }
994
538
  ],
@@ -1096,7 +640,7 @@ Parameters verified:
1096
640
  - Symbol: ${args.symbol}
1097
641
  - TimeInForce: ${args.timeInForce} (${timeInForceNames[args.timeInForce]})
1098
642
 
1099
- To execute this order, call the executeOrder tool with these exact same parameters. Important: The user has to explicitly confirm before executeOrder is called!`,
643
+ To execute this order, call the executeOrder tool with these exact same parameters.`,
1100
644
  uri: "verifyOrder"
1101
645
  }
1102
646
  ]
@@ -1292,210 +836,48 @@ var createToolHandlers = (parser, verifiedOrders, pendingRequests, marketDataPri
1292
836
  executeOrder: createExecuteOrderHandler(parser, verifiedOrders, pendingRequests),
1293
837
  marketDataRequest: createMarketDataRequestHandler(parser, pendingRequests),
1294
838
  getStockGraph: createGetStockGraphHandler(marketDataPrices),
1295
- getStockPriceHistory: createGetStockPriceHistoryHandler(marketDataPrices),
1296
- technicalAnalysis: createTechnicalAnalysisHandler(marketDataPrices)
839
+ getStockPriceHistory: createGetStockPriceHistoryHandler(marketDataPrices)
1297
840
  });
1298
841
 
1299
842
  // src/utils/messageHandler.ts
1300
843
  var import_fixparser3 = require("fixparser");
1301
- function getEnumValue(enumObj, name) {
1302
- return enumObj[name] || name;
1303
- }
1304
844
  function handleMessage(message, parser, pendingRequests, marketDataPrices, maxPriceHistory, onPriceUpdate) {
845
+ parser.logger.log({
846
+ level: "info",
847
+ message: `MCP Server received message: ${message.messageType}: ${message.description}`
848
+ });
1305
849
  const msgType = message.messageType;
1306
- if (msgType === import_fixparser3.Messages.MarketDataSnapshotFullRefresh || msgType === import_fixparser3.Messages.MarketDataIncrementalRefresh) {
850
+ if (msgType === import_fixparser3.Messages.MarketDataSnapshotFullRefresh) {
1307
851
  const symbol = message.getField(import_fixparser3.Fields.Symbol)?.value;
1308
- const fixJson = message.toFIXJSON();
1309
- const entries = fixJson.Body?.NoMDEntries || [];
1310
- const data = {
1311
- timestamp: Date.now(),
1312
- bid: 0,
1313
- offer: 0,
1314
- spread: 0,
1315
- volume: 0,
1316
- trade: 0,
1317
- indexValue: 0,
1318
- openingPrice: 0,
1319
- closingPrice: 0,
1320
- settlementPrice: 0,
1321
- tradingSessionHighPrice: 0,
1322
- tradingSessionLowPrice: 0,
1323
- vwap: 0,
1324
- imbalance: 0,
1325
- openInterest: 0,
1326
- compositeUnderlyingPrice: 0,
1327
- simulatedSellPrice: 0,
1328
- simulatedBuyPrice: 0,
1329
- marginRate: 0,
1330
- midPrice: 0,
1331
- emptyBook: 0,
1332
- settleHighPrice: 0,
1333
- settleLowPrice: 0,
1334
- priorSettlePrice: 0,
1335
- sessionHighBid: 0,
1336
- sessionLowOffer: 0,
1337
- earlyPrices: 0,
1338
- auctionClearingPrice: 0,
1339
- swapValueFactor: 0,
1340
- dailyValueAdjustmentForLongPositions: 0,
1341
- cumulativeValueAdjustmentForLongPositions: 0,
1342
- dailyValueAdjustmentForShortPositions: 0,
1343
- cumulativeValueAdjustmentForShortPositions: 0,
1344
- fixingPrice: 0,
1345
- cashRate: 0,
1346
- recoveryRate: 0,
1347
- recoveryRateForLong: 0,
1348
- recoveryRateForShort: 0,
1349
- marketBid: 0,
1350
- marketOffer: 0,
1351
- shortSaleMinPrice: 0,
1352
- previousClosingPrice: 0,
1353
- thresholdLimitPriceBanding: 0,
1354
- dailyFinancingValue: 0,
1355
- accruedFinancingValue: 0,
1356
- twap: 0
1357
- };
1358
- for (const entry of entries) {
1359
- const entryType = entry.MDEntryType;
1360
- const price = entry.MDEntryPx ? Number.parseFloat(entry.MDEntryPx) : 0;
1361
- const size = entry.MDEntrySize ? Number.parseFloat(entry.MDEntrySize) : 0;
1362
- const enumValue = getEnumValue(import_fixparser3.MDEntryType, entryType);
1363
- switch (enumValue) {
1364
- case import_fixparser3.MDEntryType.Bid:
1365
- data.bid = price;
1366
- break;
1367
- case import_fixparser3.MDEntryType.Offer:
1368
- data.offer = price;
1369
- break;
1370
- case import_fixparser3.MDEntryType.Trade:
1371
- data.trade = price;
1372
- break;
1373
- case import_fixparser3.MDEntryType.IndexValue:
1374
- data.indexValue = price;
1375
- break;
1376
- case import_fixparser3.MDEntryType.OpeningPrice:
1377
- data.openingPrice = price;
1378
- break;
1379
- case import_fixparser3.MDEntryType.ClosingPrice:
1380
- data.closingPrice = price;
1381
- break;
1382
- case import_fixparser3.MDEntryType.SettlementPrice:
1383
- data.settlementPrice = price;
1384
- break;
1385
- case import_fixparser3.MDEntryType.TradingSessionHighPrice:
1386
- data.tradingSessionHighPrice = price;
1387
- break;
1388
- case import_fixparser3.MDEntryType.TradingSessionLowPrice:
1389
- data.tradingSessionLowPrice = price;
1390
- break;
1391
- case import_fixparser3.MDEntryType.VWAP:
1392
- data.vwap = price;
1393
- break;
1394
- case import_fixparser3.MDEntryType.Imbalance:
1395
- data.imbalance = size;
1396
- break;
1397
- case import_fixparser3.MDEntryType.TradeVolume:
1398
- data.volume = size;
1399
- break;
1400
- case import_fixparser3.MDEntryType.OpenInterest:
1401
- data.openInterest = size;
1402
- break;
1403
- case import_fixparser3.MDEntryType.CompositeUnderlyingPrice:
1404
- data.compositeUnderlyingPrice = price;
1405
- break;
1406
- case import_fixparser3.MDEntryType.SimulatedSellPrice:
1407
- data.simulatedSellPrice = price;
1408
- break;
1409
- case import_fixparser3.MDEntryType.SimulatedBuyPrice:
1410
- data.simulatedBuyPrice = price;
1411
- break;
1412
- case import_fixparser3.MDEntryType.MarginRate:
1413
- data.marginRate = price;
1414
- break;
1415
- case import_fixparser3.MDEntryType.MidPrice:
1416
- data.midPrice = price;
1417
- break;
1418
- case import_fixparser3.MDEntryType.EmptyBook:
1419
- data.emptyBook = 1;
1420
- break;
1421
- case import_fixparser3.MDEntryType.SettleHighPrice:
1422
- data.settleHighPrice = price;
1423
- break;
1424
- case import_fixparser3.MDEntryType.SettleLowPrice:
1425
- data.settleLowPrice = price;
1426
- break;
1427
- case import_fixparser3.MDEntryType.PriorSettlePrice:
1428
- data.priorSettlePrice = price;
1429
- break;
1430
- case import_fixparser3.MDEntryType.SessionHighBid:
1431
- data.sessionHighBid = price;
1432
- break;
1433
- case import_fixparser3.MDEntryType.SessionLowOffer:
1434
- data.sessionLowOffer = price;
1435
- break;
1436
- case import_fixparser3.MDEntryType.EarlyPrices:
1437
- data.earlyPrices = price;
1438
- break;
1439
- case import_fixparser3.MDEntryType.AuctionClearingPrice:
1440
- data.auctionClearingPrice = price;
1441
- break;
1442
- case import_fixparser3.MDEntryType.SwapValueFactor:
1443
- data.swapValueFactor = price;
1444
- break;
1445
- case import_fixparser3.MDEntryType.DailyValueAdjustmentForLongPositions:
1446
- data.dailyValueAdjustmentForLongPositions = price;
1447
- break;
1448
- case import_fixparser3.MDEntryType.CumulativeValueAdjustmentForLongPositions:
1449
- data.cumulativeValueAdjustmentForLongPositions = price;
1450
- break;
1451
- case import_fixparser3.MDEntryType.DailyValueAdjustmentForShortPositions:
1452
- data.dailyValueAdjustmentForShortPositions = price;
1453
- break;
1454
- case import_fixparser3.MDEntryType.CumulativeValueAdjustmentForShortPositions:
1455
- data.cumulativeValueAdjustmentForShortPositions = price;
1456
- break;
1457
- case import_fixparser3.MDEntryType.FixingPrice:
1458
- data.fixingPrice = price;
1459
- break;
1460
- case import_fixparser3.MDEntryType.CashRate:
1461
- data.cashRate = price;
1462
- break;
1463
- case import_fixparser3.MDEntryType.RecoveryRate:
1464
- data.recoveryRate = price;
1465
- break;
1466
- case import_fixparser3.MDEntryType.RecoveryRateForLong:
1467
- data.recoveryRateForLong = price;
1468
- break;
1469
- case import_fixparser3.MDEntryType.RecoveryRateForShort:
1470
- data.recoveryRateForShort = price;
1471
- break;
1472
- case import_fixparser3.MDEntryType.MarketBid:
1473
- data.marketBid = price;
1474
- break;
1475
- case import_fixparser3.MDEntryType.MarketOffer:
1476
- data.marketOffer = price;
1477
- break;
1478
- case import_fixparser3.MDEntryType.ShortSaleMinPrice:
1479
- data.shortSaleMinPrice = price;
1480
- break;
1481
- case import_fixparser3.MDEntryType.PreviousClosingPrice:
1482
- data.previousClosingPrice = price;
1483
- break;
1484
- case import_fixparser3.MDEntryType.ThresholdLimitPriceBanding:
1485
- data.thresholdLimitPriceBanding = price;
1486
- break;
1487
- case import_fixparser3.MDEntryType.DailyFinancingValue:
1488
- data.dailyFinancingValue = price;
1489
- break;
1490
- case import_fixparser3.MDEntryType.AccruedFinancingValue:
1491
- data.accruedFinancingValue = price;
1492
- break;
1493
- case import_fixparser3.MDEntryType.TWAP:
1494
- data.twap = price;
1495
- break;
852
+ const entries = message.getField(import_fixparser3.Fields.NoMDEntries)?.value;
853
+ let bid = 0;
854
+ let offer = 0;
855
+ let volume = 0;
856
+ const entryTypes = message.getFields(import_fixparser3.Fields.MDEntryType);
857
+ const entryPrices = message.getFields(import_fixparser3.Fields.MDEntryPx);
858
+ const entrySizes = message.getFields(import_fixparser3.Fields.MDEntrySize);
859
+ if (entryTypes && entryPrices && entrySizes) {
860
+ for (let i = 0; i < entries; i++) {
861
+ const entryType = entryTypes[i]?.value;
862
+ const entryPrice = Number.parseFloat(entryPrices[i]?.value);
863
+ const entrySize = Number.parseFloat(entrySizes[i]?.value);
864
+ if (entryType === import_fixparser3.MDEntryType.Bid) {
865
+ bid = entryPrice;
866
+ } else if (entryType === import_fixparser3.MDEntryType.Offer) {
867
+ offer = entryPrice;
868
+ }
869
+ volume += entrySize;
1496
870
  }
1497
871
  }
1498
- data.spread = data.offer - data.bid;
872
+ const spread = offer - bid;
873
+ const timestamp = Date.now();
874
+ const data = {
875
+ timestamp,
876
+ bid,
877
+ offer,
878
+ spread,
879
+ volume
880
+ };
1499
881
  if (!marketDataPrices.has(symbol)) {
1500
882
  marketDataPrices.set(symbol, []);
1501
883
  }
@@ -1505,14 +887,6 @@ function handleMessage(message, parser, pendingRequests, marketDataPrices, maxPr
1505
887
  prices.splice(0, prices.length - maxPriceHistory);
1506
888
  }
1507
889
  onPriceUpdate?.(symbol, data);
1508
- const mdReqID = message.getField(import_fixparser3.Fields.MDReqID)?.value;
1509
- if (mdReqID) {
1510
- const callback = pendingRequests.get(mdReqID);
1511
- if (callback) {
1512
- callback(message);
1513
- pendingRequests.delete(mdReqID);
1514
- }
1515
- }
1516
890
  } else if (msgType === import_fixparser3.Messages.ExecutionReport) {
1517
891
  const reqId = message.getField(import_fixparser3.Fields.ClOrdID)?.value;
1518
892
  const callback = pendingRequests.get(reqId);
@@ -1541,7 +915,7 @@ var MCPLocal = class extends MCPBase {
1541
915
  */
1542
916
  marketDataPrices = /* @__PURE__ */ new Map();
1543
917
  /**
1544
- * Maximum number of price history entries to keep per symbol
918
+ * Maximum number of price points to store per symbol
1545
919
  * @private
1546
920
  */
1547
921
  MAX_PRICE_HISTORY = 1e5;
@@ -1650,39 +1024,6 @@ var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamable
1650
1024
  var import_types = require("@modelcontextprotocol/sdk/types.js");
1651
1025
  var import_zod2 = require("zod");
1652
1026
  var transports = {};
1653
- function jsonSchemaToZod(schema) {
1654
- if (schema.type === "object") {
1655
- const shape = {};
1656
- for (const [key, prop] of Object.entries(schema.properties || {})) {
1657
- const propSchema = prop;
1658
- if (propSchema.type === "string") {
1659
- if (propSchema.enum) {
1660
- shape[key] = import_zod2.z.enum(propSchema.enum);
1661
- } else {
1662
- shape[key] = import_zod2.z.string();
1663
- }
1664
- } else if (propSchema.type === "number") {
1665
- shape[key] = import_zod2.z.number();
1666
- } else if (propSchema.type === "boolean") {
1667
- shape[key] = import_zod2.z.boolean();
1668
- } else if (propSchema.type === "array") {
1669
- if (propSchema.items.type === "string") {
1670
- shape[key] = import_zod2.z.array(import_zod2.z.string());
1671
- } else if (propSchema.items.type === "number") {
1672
- shape[key] = import_zod2.z.array(import_zod2.z.number());
1673
- } else if (propSchema.items.type === "boolean") {
1674
- shape[key] = import_zod2.z.array(import_zod2.z.boolean());
1675
- } else {
1676
- shape[key] = import_zod2.z.array(import_zod2.z.any());
1677
- }
1678
- } else {
1679
- shape[key] = import_zod2.z.any();
1680
- }
1681
- }
1682
- return shape;
1683
- }
1684
- return {};
1685
- }
1686
1027
  var MCPRemote = class extends MCPBase {
1687
1028
  /**
1688
1029
  * Port number the server will listen on.
@@ -1725,7 +1066,7 @@ var MCPRemote = class extends MCPBase {
1725
1066
  */
1726
1067
  marketDataPrices = /* @__PURE__ */ new Map();
1727
1068
  /**
1728
- * Maximum number of price history entries to keep per symbol
1069
+ * Maximum number of price points to store per symbol
1729
1070
  * @private
1730
1071
  */
1731
1072
  MAX_PRICE_HISTORY = 1e5;
@@ -1747,7 +1088,31 @@ var MCPRemote = class extends MCPBase {
1747
1088
  this.parser,
1748
1089
  this.pendingRequests,
1749
1090
  this.marketDataPrices,
1750
- this.MAX_PRICE_HISTORY
1091
+ this.MAX_PRICE_HISTORY,
1092
+ (symbol, data) => {
1093
+ this.mcpServer?.tool(
1094
+ "priceUpdate",
1095
+ {
1096
+ description: "Price update notification",
1097
+ schema: import_zod2.z.object({
1098
+ symbol: import_zod2.z.string(),
1099
+ timestamp: import_zod2.z.number(),
1100
+ bid: import_zod2.z.number(),
1101
+ offer: import_zod2.z.number(),
1102
+ spread: import_zod2.z.number(),
1103
+ volume: import_zod2.z.number()
1104
+ })
1105
+ },
1106
+ () => ({
1107
+ content: [
1108
+ {
1109
+ type: "text",
1110
+ text: JSON.stringify({ symbol, ...data })
1111
+ }
1112
+ ]
1113
+ })
1114
+ );
1115
+ }
1751
1116
  );
1752
1117
  }
1753
1118
  });
@@ -1809,15 +1174,7 @@ var MCPRemote = class extends MCPBase {
1809
1174
  );
1810
1175
  return;
1811
1176
  }
1812
- try {
1813
- await transport.handleRequest(req, res, parsed);
1814
- } catch (error) {
1815
- this.logger?.log({
1816
- level: "error",
1817
- message: `Error handling request: ${error}`
1818
- });
1819
- throw error;
1820
- }
1177
+ await transport.handleRequest(req, res, parsed);
1821
1178
  });
1822
1179
  } else if (req.method === "GET" || req.method === "DELETE") {
1823
1180
  if (!sessionId || !transports[sessionId]) {
@@ -1826,20 +1183,8 @@ var MCPRemote = class extends MCPBase {
1826
1183
  return;
1827
1184
  }
1828
1185
  const transport = transports[sessionId];
1829
- try {
1830
- await transport.handleRequest(req, res);
1831
- } catch (error) {
1832
- this.logger?.log({
1833
- level: "error",
1834
- message: `Error handling ${req.method} request: ${error}`
1835
- });
1836
- throw error;
1837
- }
1186
+ await transport.handleRequest(req, res);
1838
1187
  } else {
1839
- this.logger?.log({
1840
- level: "error",
1841
- message: `Method not allowed: ${req.method}`
1842
- });
1843
1188
  res.writeHead(405);
1844
1189
  res.end("Method Not Allowed");
1845
1190
  }
@@ -1873,40 +1218,69 @@ var MCPRemote = class extends MCPBase {
1873
1218
  });
1874
1219
  return;
1875
1220
  }
1876
- const toolHandlers = createToolHandlers(
1877
- this.parser,
1878
- this.verifiedOrders,
1879
- this.pendingRequests,
1880
- this.marketDataPrices
1221
+ this.mcpServer.tool(
1222
+ "tools/list",
1223
+ {
1224
+ description: "List available tools",
1225
+ schema: import_zod2.z.object({})
1226
+ },
1227
+ async () => {
1228
+ return {
1229
+ content: [
1230
+ {
1231
+ type: "text",
1232
+ text: JSON.stringify(
1233
+ Object.entries(toolSchemas).map(([name, { description, schema }]) => ({
1234
+ name,
1235
+ description,
1236
+ inputSchema: schema
1237
+ }))
1238
+ )
1239
+ }
1240
+ ]
1241
+ };
1242
+ }
1881
1243
  );
1882
- Object.entries(toolSchemas).forEach(([name, { description, schema }]) => {
1883
- this.mcpServer?.registerTool(
1884
- name,
1885
- {
1886
- description,
1887
- inputSchema: jsonSchemaToZod(schema)
1888
- },
1889
- async (args) => {
1890
- const handler = toolHandlers[name];
1891
- if (!handler) {
1892
- return {
1893
- content: [
1894
- {
1895
- type: "text",
1896
- text: `Tool not found: ${name}`
1897
- }
1898
- ],
1899
- isError: true
1900
- };
1901
- }
1902
- const result = await handler(args);
1244
+ this.mcpServer.tool(
1245
+ "tools/call",
1246
+ {
1247
+ description: "Call a tool",
1248
+ schema: import_zod2.z.object({
1249
+ name: import_zod2.z.string(),
1250
+ arguments: import_zod2.z.any(),
1251
+ _meta: import_zod2.z.object({
1252
+ progressToken: import_zod2.z.number()
1253
+ }).optional()
1254
+ })
1255
+ },
1256
+ async (request) => {
1257
+ const { name, arguments: args } = request;
1258
+ const toolHandlers = createToolHandlers(
1259
+ this.parser,
1260
+ this.verifiedOrders,
1261
+ this.pendingRequests,
1262
+ this.marketDataPrices
1263
+ );
1264
+ const handler = toolHandlers[name];
1265
+ if (!handler) {
1903
1266
  return {
1904
- content: result.content,
1905
- isError: result.isError
1267
+ content: [
1268
+ {
1269
+ type: "text",
1270
+ text: `Tool not found: ${name}`,
1271
+ uri: name
1272
+ }
1273
+ ],
1274
+ isError: true
1906
1275
  };
1907
1276
  }
1908
- );
1909
- });
1277
+ const result = await handler(args);
1278
+ return {
1279
+ content: result.content,
1280
+ isError: result.isError
1281
+ };
1282
+ }
1283
+ );
1910
1284
  }
1911
1285
  };
1912
1286
  // Annotate the CommonJS export names for ESM import in node: