fixparser-plugin-mcp 9.1.7-75ded9c1 → 9.1.7-81ea0211

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.
@@ -3,6 +3,100 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { Field, Fields, Messages } from "fixparser";
5
5
  import { z } from "zod";
6
+ var fixStringSchema = z.object({
7
+ fixString: z.string()
8
+ });
9
+ var orderSchema = z.object({
10
+ clOrdID: z.string(),
11
+ handlInst: z.enum(["1", "2", "3"]),
12
+ quantity: z.string(),
13
+ price: z.string(),
14
+ ordType: z.enum([
15
+ "1",
16
+ "2",
17
+ "3",
18
+ "4",
19
+ "5",
20
+ "6",
21
+ "7",
22
+ "8",
23
+ "9",
24
+ "A",
25
+ "B",
26
+ "C",
27
+ "D",
28
+ "E",
29
+ "F",
30
+ "G",
31
+ "H",
32
+ "I",
33
+ "J",
34
+ "K",
35
+ "L",
36
+ "M",
37
+ "P",
38
+ "Q",
39
+ "R",
40
+ "S"
41
+ ]),
42
+ side: z.enum(["1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H"]),
43
+ symbol: z.string(),
44
+ timeInForce: z.enum(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C"])
45
+ });
46
+ var marketDataRequestSchema = z.object({
47
+ mdUpdateType: z.enum(["0", "1"]),
48
+ symbols: z.array(z.string()),
49
+ mdReqID: z.string(),
50
+ subscriptionRequestType: z.enum(["0", "1", "2"]),
51
+ mdEntryTypes: z.array(
52
+ z.enum([
53
+ "0",
54
+ "1",
55
+ "2",
56
+ "3",
57
+ "4",
58
+ "5",
59
+ "6",
60
+ "7",
61
+ "8",
62
+ "9",
63
+ "A",
64
+ "B",
65
+ "C",
66
+ "D",
67
+ "E",
68
+ "F",
69
+ "G",
70
+ "H",
71
+ "J",
72
+ "K",
73
+ "L",
74
+ "M",
75
+ "N",
76
+ "O",
77
+ "P",
78
+ "Q",
79
+ "R",
80
+ "S",
81
+ "T",
82
+ "U",
83
+ "V",
84
+ "W",
85
+ "X",
86
+ "Y",
87
+ "Z",
88
+ "a",
89
+ "b",
90
+ "c",
91
+ "d",
92
+ "e",
93
+ "g",
94
+ "h",
95
+ "i",
96
+ "t"
97
+ ])
98
+ )
99
+ });
6
100
  var MCPLocal = class {
7
101
  parser;
8
102
  server = new Server(
@@ -216,14 +310,6 @@ var MCPLocal = class {
216
310
  level: "info",
217
311
  message: `MCP Server added ${symbol}: ${priceNum}`
218
312
  });
219
- this.server.notification({
220
- method: "priceUpdate",
221
- params: {
222
- symbol: symbolStr,
223
- price: priceNum,
224
- timestamp: Number(timestamp)
225
- }
226
- });
227
313
  }
228
314
  }
229
315
  if (msgType === Messages.MarketDataSnapshotFullRefresh) {
@@ -276,11 +362,11 @@ var MCPLocal = class {
276
362
  z.object({ method: z.literal("resources/templates/list") }),
277
363
  async (request, extra) => {
278
364
  return {
279
- resourceTemplates: [
365
+ templates: [
280
366
  {
281
367
  name: "stockGraph",
282
368
  description: "Generates a price chart for a given symbol",
283
- uriTemplate: "stockGraph/{symbol}",
369
+ uri: "stockGraph/{symbol}",
284
370
  parameters: {
285
371
  type: "object",
286
372
  properties: {
@@ -292,7 +378,7 @@ var MCPLocal = class {
292
378
  {
293
379
  name: "stockPriceHistory",
294
380
  description: "Returns price history for a given symbol",
295
- uriTemplate: "stockPriceHistory/{symbol}",
381
+ uri: "stockPriceHistory/{symbol}",
296
382
  parameters: {
297
383
  type: "object",
298
384
  properties: {
@@ -465,361 +551,6 @@ var MCPLocal = class {
465
551
  };
466
552
  }
467
553
  );
468
- this.server.setRequestHandler(
469
- z.object({
470
- method: z.literal("tools/call"),
471
- params: z.object({
472
- name: z.string(),
473
- arguments: z.any(),
474
- _meta: z.object({
475
- progressToken: z.number()
476
- }).optional()
477
- })
478
- }),
479
- async (request, extra) => {
480
- const { name, arguments: args } = request.params;
481
- switch (name) {
482
- case "parse":
483
- try {
484
- const parsedMessage = this.parser?.parse(args.fixString);
485
- if (!parsedMessage || parsedMessage.length === 0) {
486
- return {
487
- contents: [
488
- {
489
- type: "text",
490
- text: "Error: Failed to parse FIX string",
491
- uri: "parse"
492
- }
493
- ],
494
- isError: true
495
- };
496
- }
497
- return {
498
- contents: [
499
- {
500
- type: "text",
501
- text: `${parsedMessage[0].description}
502
- ${parsedMessage[0].messageTypeDescription}`,
503
- uri: "parse"
504
- }
505
- ]
506
- };
507
- } catch (error) {
508
- return {
509
- contents: [
510
- {
511
- type: "text",
512
- text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`,
513
- uri: "parse"
514
- }
515
- ],
516
- isError: true
517
- };
518
- }
519
- case "parseToJSON":
520
- try {
521
- const parsedMessage = this.parser?.parse(args.fixString);
522
- if (!parsedMessage || parsedMessage.length === 0) {
523
- return {
524
- contents: [
525
- {
526
- type: "text",
527
- text: "Error: Failed to parse FIX string",
528
- uri: "parseToJSON"
529
- }
530
- ],
531
- isError: true
532
- };
533
- }
534
- return {
535
- contents: [
536
- {
537
- type: "text",
538
- text: `${parsedMessage[0].toFIXJSON()}`,
539
- uri: "parseToJSON"
540
- }
541
- ]
542
- };
543
- } catch (error) {
544
- return {
545
- contents: [
546
- {
547
- type: "text",
548
- text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`,
549
- uri: "parseToJSON"
550
- }
551
- ],
552
- isError: true
553
- };
554
- }
555
- case "verifyOrder":
556
- try {
557
- this.verifiedOrders.set(args.clOrdID, {
558
- clOrdID: args.clOrdID,
559
- handlInst: args.handlInst,
560
- quantity: Number.parseFloat(args.quantity),
561
- price: Number.parseFloat(args.price),
562
- ordType: args.ordType,
563
- side: args.side,
564
- symbol: args.symbol,
565
- timeInForce: args.timeInForce
566
- });
567
- const ordTypeNames = {
568
- "1": "Market",
569
- "2": "Limit",
570
- "3": "Stop",
571
- "4": "StopLimit",
572
- "5": "MarketOnClose",
573
- "6": "WithOrWithout",
574
- "7": "LimitOrBetter",
575
- "8": "LimitWithOrWithout",
576
- "9": "OnBasis",
577
- A: "OnClose",
578
- B: "LimitOnClose",
579
- C: "ForexMarket",
580
- D: "PreviouslyQuoted",
581
- E: "PreviouslyIndicated",
582
- F: "ForexLimit",
583
- G: "ForexSwap",
584
- H: "ForexPreviouslyQuoted",
585
- I: "Funari",
586
- J: "MarketIfTouched",
587
- K: "MarketWithLeftOverAsLimit",
588
- L: "PreviousFundValuationPoint",
589
- M: "NextFundValuationPoint",
590
- P: "Pegged",
591
- Q: "CounterOrderSelection",
592
- R: "StopOnBidOrOffer",
593
- S: "StopLimitOnBidOrOffer"
594
- };
595
- const sideNames = {
596
- "1": "Buy",
597
- "2": "Sell",
598
- "3": "BuyMinus",
599
- "4": "SellPlus",
600
- "5": "SellShort",
601
- "6": "SellShortExempt",
602
- "7": "Undisclosed",
603
- "8": "Cross",
604
- "9": "CrossShort",
605
- A: "CrossShortExempt",
606
- B: "AsDefined",
607
- C: "Opposite",
608
- D: "Subscribe",
609
- E: "Redeem",
610
- F: "Lend",
611
- G: "Borrow",
612
- H: "SellUndisclosed"
613
- };
614
- const timeInForceNames = {
615
- "0": "Day",
616
- "1": "GoodTillCancel",
617
- "2": "AtTheOpening",
618
- "3": "ImmediateOrCancel",
619
- "4": "FillOrKill",
620
- "5": "GoodTillCrossing",
621
- "6": "GoodTillDate",
622
- "7": "AtTheClose",
623
- "8": "GoodThroughCrossing",
624
- "9": "AtCrossing",
625
- A: "GoodForTime",
626
- B: "GoodForAuction",
627
- C: "GoodForMonth"
628
- };
629
- const handlInstNames = {
630
- "1": "AutomatedExecutionNoIntervention",
631
- "2": "AutomatedExecutionInterventionOK",
632
- "3": "ManualOrder"
633
- };
634
- return {
635
- contents: [
636
- {
637
- type: "text",
638
- text: `VERIFICATION: All parameters valid. Ready to proceed with order execution.
639
-
640
- Parameters verified:
641
- - ClOrdID: ${args.clOrdID}
642
- - HandlInst: ${args.handlInst} (${handlInstNames[args.handlInst]})
643
- - Quantity: ${args.quantity}
644
- - Price: ${args.price}
645
- - OrdType: ${args.ordType} (${ordTypeNames[args.ordType]})
646
- - Side: ${args.side} (${sideNames[args.side]})
647
- - Symbol: ${args.symbol}
648
- - TimeInForce: ${args.timeInForce} (${timeInForceNames[args.timeInForce]})
649
-
650
- To execute this order, call the executeOrder tool with these exact same parameters.`,
651
- uri: "verifyOrder"
652
- }
653
- ]
654
- };
655
- } catch (error) {
656
- return {
657
- contents: [
658
- {
659
- type: "text",
660
- text: `Error: ${error instanceof Error ? error.message : "Failed to verify order parameters"}`,
661
- uri: "verifyOrder"
662
- }
663
- ],
664
- isError: true
665
- };
666
- }
667
- case "executeOrder":
668
- try {
669
- const verifiedOrder = this.verifiedOrders.get(args.clOrdID);
670
- if (!verifiedOrder) {
671
- return {
672
- contents: [
673
- {
674
- type: "text",
675
- text: `Error: Order ${args.clOrdID} has not been verified. Please call verifyOrder first.`,
676
- uri: "executeOrder"
677
- }
678
- ],
679
- isError: true
680
- };
681
- }
682
- if (verifiedOrder.handlInst !== args.handlInst || verifiedOrder.quantity !== Number.parseFloat(args.quantity) || verifiedOrder.price !== Number.parseFloat(args.price) || verifiedOrder.ordType !== args.ordType || verifiedOrder.side !== args.side || verifiedOrder.symbol !== args.symbol || verifiedOrder.timeInForce !== args.timeInForce) {
683
- return {
684
- contents: [
685
- {
686
- type: "text",
687
- text: "Error: Order parameters do not match the verified order. Please use the exact same parameters that were verified.",
688
- uri: "executeOrder"
689
- }
690
- ],
691
- isError: true
692
- };
693
- }
694
- const response = new Promise((resolve) => {
695
- this.pendingRequests.set(args.clOrdID, resolve);
696
- });
697
- const order = this.parser?.createMessage(
698
- new Field(Fields.MsgType, Messages.NewOrderSingle),
699
- new Field(Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
700
- new Field(Fields.SenderCompID, this.parser?.sender),
701
- new Field(Fields.TargetCompID, this.parser?.target),
702
- new Field(Fields.SendingTime, this.parser?.getTimestamp()),
703
- new Field(Fields.ClOrdID, args.clOrdID),
704
- new Field(Fields.Side, args.side),
705
- new Field(Fields.Symbol, args.symbol),
706
- new Field(Fields.OrderQty, Number.parseFloat(args.quantity)),
707
- new Field(Fields.Price, Number.parseFloat(args.price)),
708
- new Field(Fields.OrdType, args.ordType),
709
- new Field(Fields.HandlInst, args.handlInst),
710
- new Field(Fields.TimeInForce, args.timeInForce),
711
- new Field(Fields.TransactTime, this.parser?.getTimestamp())
712
- );
713
- if (!this.parser?.connected) {
714
- return {
715
- contents: [
716
- {
717
- type: "text",
718
- text: "Error: Not connected. Ignoring message.",
719
- uri: "executeOrder"
720
- }
721
- ],
722
- isError: true
723
- };
724
- }
725
- this.parser?.send(order);
726
- const fixData = await response;
727
- this.verifiedOrders.delete(args.clOrdID);
728
- return {
729
- contents: [
730
- {
731
- type: "text",
732
- text: fixData.messageType === Messages.Reject ? `Reject message for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}` : `Execution Report for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}`,
733
- uri: "executeOrder"
734
- }
735
- ]
736
- };
737
- } catch (error) {
738
- return {
739
- contents: [
740
- {
741
- type: "text",
742
- text: `Error: ${error instanceof Error ? error.message : "Failed to execute order"}`,
743
- uri: "executeOrder"
744
- }
745
- ],
746
- isError: true
747
- };
748
- }
749
- case "marketDataRequest":
750
- try {
751
- const response = new Promise((resolve) => {
752
- this.pendingRequests.set(args.mdReqID, resolve);
753
- });
754
- const messageFields = [
755
- new Field(Fields.MsgType, Messages.MarketDataRequest),
756
- new Field(Fields.SenderCompID, this.parser?.sender),
757
- new Field(Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
758
- new Field(Fields.TargetCompID, this.parser?.target),
759
- new Field(Fields.SendingTime, this.parser?.getTimestamp()),
760
- new Field(Fields.MDReqID, args.mdReqID),
761
- new Field(Fields.SubscriptionRequestType, args.subscriptionRequestType),
762
- new Field(Fields.MarketDepth, 0),
763
- new Field(Fields.MDUpdateType, args.mdUpdateType)
764
- ];
765
- messageFields.push(new Field(Fields.NoRelatedSym, args.symbols.length));
766
- args.symbols.forEach((symbol) => {
767
- messageFields.push(new Field(Fields.Symbol, symbol));
768
- });
769
- messageFields.push(new Field(Fields.NoMDEntryTypes, args.mdEntryTypes.length));
770
- args.mdEntryTypes.forEach((entryType) => {
771
- messageFields.push(new Field(Fields.MDEntryType, entryType));
772
- });
773
- const mdr = this.parser?.createMessage(...messageFields);
774
- if (!this.parser?.connected) {
775
- return {
776
- contents: [
777
- {
778
- type: "text",
779
- text: "Error: Not connected. Ignoring message.",
780
- uri: "marketDataRequest"
781
- }
782
- ],
783
- isError: true
784
- };
785
- }
786
- this.parser?.send(mdr);
787
- const fixData = await response;
788
- return {
789
- contents: [
790
- {
791
- type: "text",
792
- text: `Market data for ${args.symbols.join(", ")}: ${JSON.stringify(fixData.toFIXJSON())}`,
793
- uri: "marketDataRequest"
794
- }
795
- ]
796
- };
797
- } catch (error) {
798
- return {
799
- contents: [
800
- {
801
- type: "text",
802
- text: `Error: ${error instanceof Error ? error.message : "Failed to request market data"}`,
803
- uri: "marketDataRequest"
804
- }
805
- ],
806
- isError: true
807
- };
808
- }
809
- default:
810
- return {
811
- contents: [
812
- {
813
- type: "text",
814
- text: `Tool not found: ${name}`,
815
- uri: name
816
- }
817
- ],
818
- isError: true
819
- };
820
- }
821
- }
822
- );
823
554
  this.server.setRequestHandler(
824
555
  z.object({
825
556
  method: z.literal("resources/read"),
@@ -869,8 +600,7 @@ To execute this order, call the executeOrder tool with these exact same paramete
869
600
  contents: [
870
601
  {
871
602
  type: "text",
872
- text: `No price data available for ${symbol}`,
873
- uri
603
+ text: `No price data available for ${symbol}`
874
604
  }
875
605
  ]
876
606
  };
@@ -939,8 +669,7 @@ To execute this order, call the executeOrder tool with these exact same paramete
939
669
  contents: [
940
670
  {
941
671
  type: "text",
942
- text: svg,
943
- uri
672
+ text: svg
944
673
  }
945
674
  ]
946
675
  };
@@ -953,8 +682,7 @@ To execute this order, call the executeOrder tool with these exact same paramete
953
682
  contents: [
954
683
  {
955
684
  type: "text",
956
- text: `No price data available for ${symbol}`,
957
- uri
685
+ text: `No price data available for ${symbol}`
958
686
  }
959
687
  ]
960
688
  };
@@ -974,8 +702,7 @@ To execute this order, call the executeOrder tool with these exact same paramete
974
702
  },
975
703
  null,
976
704
  2
977
- ),
978
- uri
705
+ )
979
706
  }
980
707
  ]
981
708
  };
@@ -993,6 +720,347 @@ To execute this order, call the executeOrder tool with these exact same paramete
993
720
  }
994
721
  }
995
722
  );
723
+ this.server.setRequestHandler(
724
+ z.object({
725
+ method: z.literal("parse"),
726
+ params: fixStringSchema
727
+ }),
728
+ async (request, extra) => {
729
+ try {
730
+ const args = request.params;
731
+ const parsedMessage = this.parser?.parse(args.fixString);
732
+ if (!parsedMessage || parsedMessage.length === 0) {
733
+ return {
734
+ contents: [{ type: "text", text: "Error: Failed to parse FIX string" }],
735
+ isError: true
736
+ };
737
+ }
738
+ return {
739
+ contents: [
740
+ {
741
+ type: "text",
742
+ text: `${parsedMessage[0].description}
743
+ ${parsedMessage[0].messageTypeDescription}`
744
+ }
745
+ ]
746
+ };
747
+ } catch (error) {
748
+ return {
749
+ contents: [
750
+ {
751
+ type: "text",
752
+ text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
753
+ }
754
+ ],
755
+ isError: true
756
+ };
757
+ }
758
+ }
759
+ );
760
+ this.server.setRequestHandler(
761
+ z.object({
762
+ method: z.literal("parseToJSON"),
763
+ params: fixStringSchema
764
+ }),
765
+ async (request, extra) => {
766
+ try {
767
+ const args = request.params;
768
+ const parsedMessage = this.parser?.parse(args.fixString);
769
+ if (!parsedMessage || parsedMessage.length === 0) {
770
+ return {
771
+ contents: [{ type: "text", text: "Error: Failed to parse FIX string" }],
772
+ isError: true
773
+ };
774
+ }
775
+ return {
776
+ contents: [
777
+ {
778
+ type: "text",
779
+ text: `${parsedMessage[0].toFIXJSON()}`
780
+ }
781
+ ]
782
+ };
783
+ } catch (error) {
784
+ return {
785
+ contents: [
786
+ {
787
+ type: "text",
788
+ text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
789
+ }
790
+ ],
791
+ isError: true
792
+ };
793
+ }
794
+ }
795
+ );
796
+ this.server.setRequestHandler(
797
+ z.object({
798
+ method: z.literal("verifyOrder"),
799
+ params: orderSchema
800
+ }),
801
+ async (request, extra) => {
802
+ try {
803
+ const args = request.params;
804
+ this.verifiedOrders.set(args.clOrdID, {
805
+ clOrdID: args.clOrdID,
806
+ handlInst: args.handlInst,
807
+ quantity: Number.parseFloat(args.quantity),
808
+ price: Number.parseFloat(args.price),
809
+ ordType: args.ordType,
810
+ side: args.side,
811
+ symbol: args.symbol,
812
+ timeInForce: args.timeInForce
813
+ });
814
+ const ordTypeNames = {
815
+ "1": "Market",
816
+ "2": "Limit",
817
+ "3": "Stop",
818
+ "4": "StopLimit",
819
+ "5": "MarketOnClose",
820
+ "6": "WithOrWithout",
821
+ "7": "LimitOrBetter",
822
+ "8": "LimitWithOrWithout",
823
+ "9": "OnBasis",
824
+ A: "OnClose",
825
+ B: "LimitOnClose",
826
+ C: "ForexMarket",
827
+ D: "PreviouslyQuoted",
828
+ E: "PreviouslyIndicated",
829
+ F: "ForexLimit",
830
+ G: "ForexSwap",
831
+ H: "ForexPreviouslyQuoted",
832
+ I: "Funari",
833
+ J: "MarketIfTouched",
834
+ K: "MarketWithLeftOverAsLimit",
835
+ L: "PreviousFundValuationPoint",
836
+ M: "NextFundValuationPoint",
837
+ P: "Pegged",
838
+ Q: "CounterOrderSelection",
839
+ R: "StopOnBidOrOffer",
840
+ S: "StopLimitOnBidOrOffer"
841
+ };
842
+ const sideNames = {
843
+ "1": "Buy",
844
+ "2": "Sell",
845
+ "3": "BuyMinus",
846
+ "4": "SellPlus",
847
+ "5": "SellShort",
848
+ "6": "SellShortExempt",
849
+ "7": "Undisclosed",
850
+ "8": "Cross",
851
+ "9": "CrossShort",
852
+ A: "CrossShortExempt",
853
+ B: "AsDefined",
854
+ C: "Opposite",
855
+ D: "Subscribe",
856
+ E: "Redeem",
857
+ F: "Lend",
858
+ G: "Borrow",
859
+ H: "SellUndisclosed"
860
+ };
861
+ const timeInForceNames = {
862
+ "0": "Day",
863
+ "1": "GoodTillCancel",
864
+ "2": "AtTheOpening",
865
+ "3": "ImmediateOrCancel",
866
+ "4": "FillOrKill",
867
+ "5": "GoodTillCrossing",
868
+ "6": "GoodTillDate",
869
+ "7": "AtTheClose",
870
+ "8": "GoodThroughCrossing",
871
+ "9": "AtCrossing",
872
+ A: "GoodForTime",
873
+ B: "GoodForAuction",
874
+ C: "GoodForMonth"
875
+ };
876
+ const handlInstNames = {
877
+ "1": "AutomatedExecutionNoIntervention",
878
+ "2": "AutomatedExecutionInterventionOK",
879
+ "3": "ManualOrder"
880
+ };
881
+ return {
882
+ contents: [
883
+ {
884
+ type: "text",
885
+ text: `VERIFICATION: All parameters valid. Ready to proceed with order execution.
886
+
887
+ Parameters verified:
888
+ - ClOrdID: ${args.clOrdID}
889
+ - HandlInst: ${args.handlInst} (${handlInstNames[args.handlInst]})
890
+ - Quantity: ${args.quantity}
891
+ - Price: ${args.price}
892
+ - OrdType: ${args.ordType} (${ordTypeNames[args.ordType]})
893
+ - Side: ${args.side} (${sideNames[args.side]})
894
+ - Symbol: ${args.symbol}
895
+ - TimeInForce: ${args.timeInForce} (${timeInForceNames[args.timeInForce]})
896
+
897
+ To execute this order, call the executeOrder tool with these exact same parameters.`
898
+ }
899
+ ]
900
+ };
901
+ } catch (error) {
902
+ return {
903
+ contents: [
904
+ {
905
+ type: "text",
906
+ text: `Error: ${error instanceof Error ? error.message : "Failed to verify order parameters"}`
907
+ }
908
+ ],
909
+ isError: true
910
+ };
911
+ }
912
+ }
913
+ );
914
+ this.server.setRequestHandler(
915
+ z.object({
916
+ method: z.literal("executeOrder"),
917
+ params: orderSchema
918
+ }),
919
+ async (request, extra) => {
920
+ try {
921
+ const args = request.params;
922
+ const verifiedOrder = this.verifiedOrders.get(args.clOrdID);
923
+ if (!verifiedOrder) {
924
+ return {
925
+ contents: [
926
+ {
927
+ type: "text",
928
+ text: `Error: Order ${args.clOrdID} has not been verified. Please call verifyOrder first.`
929
+ }
930
+ ],
931
+ isError: true
932
+ };
933
+ }
934
+ if (verifiedOrder.handlInst !== args.handlInst || verifiedOrder.quantity !== Number.parseFloat(args.quantity) || verifiedOrder.price !== Number.parseFloat(args.price) || verifiedOrder.ordType !== args.ordType || verifiedOrder.side !== args.side || verifiedOrder.symbol !== args.symbol || verifiedOrder.timeInForce !== args.timeInForce) {
935
+ return {
936
+ contents: [
937
+ {
938
+ type: "text",
939
+ text: "Error: Order parameters do not match the verified order. Please use the exact same parameters that were verified."
940
+ }
941
+ ],
942
+ isError: true
943
+ };
944
+ }
945
+ const response = new Promise((resolve) => {
946
+ this.pendingRequests.set(args.clOrdID, resolve);
947
+ });
948
+ const order = this.parser?.createMessage(
949
+ new Field(Fields.MsgType, Messages.NewOrderSingle),
950
+ new Field(Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
951
+ new Field(Fields.SenderCompID, this.parser?.sender),
952
+ new Field(Fields.TargetCompID, this.parser?.target),
953
+ new Field(Fields.SendingTime, this.parser?.getTimestamp()),
954
+ new Field(Fields.ClOrdID, args.clOrdID),
955
+ new Field(Fields.Side, args.side),
956
+ new Field(Fields.Symbol, args.symbol),
957
+ new Field(Fields.OrderQty, Number.parseFloat(args.quantity)),
958
+ new Field(Fields.Price, Number.parseFloat(args.price)),
959
+ new Field(Fields.OrdType, args.ordType),
960
+ new Field(Fields.HandlInst, args.handlInst),
961
+ new Field(Fields.TimeInForce, args.timeInForce),
962
+ new Field(Fields.TransactTime, this.parser?.getTimestamp())
963
+ );
964
+ if (!this.parser?.connected) {
965
+ return {
966
+ contents: [
967
+ {
968
+ type: "text",
969
+ text: "Error: Not connected. Ignoring message."
970
+ }
971
+ ],
972
+ isError: true
973
+ };
974
+ }
975
+ this.parser?.send(order);
976
+ const fixData = await response;
977
+ this.verifiedOrders.delete(args.clOrdID);
978
+ return {
979
+ contents: [
980
+ {
981
+ type: "text",
982
+ text: fixData.messageType === Messages.Reject ? `Reject message for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}` : `Execution Report for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}`
983
+ }
984
+ ]
985
+ };
986
+ } catch (error) {
987
+ return {
988
+ contents: [
989
+ {
990
+ type: "text",
991
+ text: `Error: ${error instanceof Error ? error.message : "Failed to execute order"}`
992
+ }
993
+ ],
994
+ isError: true
995
+ };
996
+ }
997
+ }
998
+ );
999
+ this.server.setRequestHandler(
1000
+ z.object({
1001
+ method: z.literal("marketDataRequest"),
1002
+ params: marketDataRequestSchema
1003
+ }),
1004
+ async (request, extra) => {
1005
+ try {
1006
+ const args = request.params;
1007
+ const response = new Promise((resolve) => {
1008
+ this.pendingRequests.set(args.mdReqID, resolve);
1009
+ });
1010
+ const messageFields = [
1011
+ new Field(Fields.MsgType, Messages.MarketDataRequest),
1012
+ new Field(Fields.SenderCompID, this.parser?.sender),
1013
+ new Field(Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
1014
+ new Field(Fields.TargetCompID, this.parser?.target),
1015
+ new Field(Fields.SendingTime, this.parser?.getTimestamp()),
1016
+ new Field(Fields.MDReqID, args.mdReqID),
1017
+ new Field(Fields.SubscriptionRequestType, args.subscriptionRequestType),
1018
+ new Field(Fields.MarketDepth, 0),
1019
+ new Field(Fields.MDUpdateType, args.mdUpdateType)
1020
+ ];
1021
+ messageFields.push(new Field(Fields.NoRelatedSym, args.symbols.length));
1022
+ args.symbols.forEach((symbol) => {
1023
+ messageFields.push(new Field(Fields.Symbol, symbol));
1024
+ });
1025
+ messageFields.push(new Field(Fields.NoMDEntryTypes, args.mdEntryTypes.length));
1026
+ args.mdEntryTypes.forEach((entryType) => {
1027
+ messageFields.push(new Field(Fields.MDEntryType, entryType));
1028
+ });
1029
+ const mdr = this.parser?.createMessage(...messageFields);
1030
+ if (!this.parser?.connected) {
1031
+ return {
1032
+ contents: [
1033
+ {
1034
+ type: "text",
1035
+ text: "Error: Not connected. Ignoring message."
1036
+ }
1037
+ ],
1038
+ isError: true
1039
+ };
1040
+ }
1041
+ this.parser?.send(mdr);
1042
+ const fixData = await response;
1043
+ return {
1044
+ contents: [
1045
+ {
1046
+ type: "text",
1047
+ text: `Market data for ${args.symbols.join(", ")}: ${JSON.stringify(fixData.toFIXJSON())}`
1048
+ }
1049
+ ]
1050
+ };
1051
+ } catch (error) {
1052
+ return {
1053
+ contents: [
1054
+ {
1055
+ type: "text",
1056
+ text: `Error: ${error instanceof Error ? error.message : "Failed to request market data"}`
1057
+ }
1058
+ ],
1059
+ isError: true
1060
+ };
1061
+ }
1062
+ }
1063
+ );
996
1064
  process.on("SIGINT", async () => {
997
1065
  await this.server.close();
998
1066
  process.exit(0);