jspurefix 5.11.3 → 5.11.4

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.
Files changed (100) hide show
  1. package/.gitattributes +4 -0
  2. package/dist/buffer/index.d.ts +6 -0
  3. package/dist/buffer/index.js +6 -0
  4. package/dist/buffer/index.js.map +1 -1
  5. package/dist/collections/index.d.ts +1 -0
  6. package/dist/collections/index.js +16 -0
  7. package/dist/collections/index.js.map +1 -1
  8. package/dist/dictionary/contained/index.d.ts +3 -0
  9. package/dist/dictionary/contained/index.js +3 -0
  10. package/dist/dictionary/contained/index.js.map +1 -1
  11. package/dist/dictionary/index.d.ts +5 -0
  12. package/dist/dictionary/index.js +5 -0
  13. package/dist/dictionary/index.js.map +1 -1
  14. package/dist/generator/applicability.d.ts +3 -0
  15. package/dist/generator/applicability.js +48 -0
  16. package/dist/generator/applicability.js.map +1 -0
  17. package/dist/generator/case-builder.d.ts +34 -0
  18. package/dist/generator/case-builder.js +111 -0
  19. package/dist/generator/case-builder.js.map +1 -0
  20. package/dist/generator/corpus-store.d.ts +43 -0
  21. package/dist/generator/corpus-store.js +115 -0
  22. package/dist/generator/corpus-store.js.map +1 -0
  23. package/dist/generator/index.d.ts +12 -0
  24. package/dist/generator/index.js +29 -0
  25. package/dist/generator/index.js.map +1 -0
  26. package/dist/generator/instrument-universe.d.ts +54 -0
  27. package/dist/generator/instrument-universe.js +286 -0
  28. package/dist/generator/instrument-universe.js.map +1 -0
  29. package/dist/generator/layout.d.ts +46 -0
  30. package/dist/generator/layout.js +208 -0
  31. package/dist/generator/layout.js.map +1 -0
  32. package/dist/generator/market-context.d.ts +64 -0
  33. package/dist/generator/market-context.js +119 -0
  34. package/dist/generator/market-context.js.map +1 -0
  35. package/dist/generator/outcome.d.ts +16 -0
  36. package/dist/generator/outcome.js +71 -0
  37. package/dist/generator/outcome.js.map +1 -0
  38. package/dist/generator/realistic-generator.d.ts +53 -0
  39. package/dist/generator/realistic-generator.js +275 -0
  40. package/dist/generator/realistic-generator.js.map +1 -0
  41. package/dist/generator/rng.d.ts +12 -0
  42. package/dist/generator/rng.js +60 -0
  43. package/dist/generator/rng.js.map +1 -0
  44. package/dist/generator/scatter.d.ts +50 -0
  45. package/dist/generator/scatter.js +285 -0
  46. package/dist/generator/scatter.js.map +1 -0
  47. package/dist/generator/scenarios.d.ts +13 -0
  48. package/dist/generator/scenarios.js +139 -0
  49. package/dist/generator/scenarios.js.map +1 -0
  50. package/dist/generator/value-conventions.d.ts +15 -0
  51. package/dist/generator/value-conventions.js +217 -0
  52. package/dist/generator/value-conventions.js.map +1 -0
  53. package/dist/index.d.ts +6 -0
  54. package/dist/index.js +4 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/jsfix-cmd.d.ts +5 -0
  57. package/dist/jsfix-cmd.js +161 -0
  58. package/dist/jsfix-cmd.js.map +1 -1
  59. package/dist/store/fix-msg-ascii-store-resend.js +2 -2
  60. package/dist/store/fix-msg-ascii-store-resend.js.map +1 -1
  61. package/dist/store/fix-msg-store-record.d.ts +1 -1
  62. package/dist/store/fix-msg-store-record.js.map +1 -1
  63. package/dist/store/index.d.ts +1 -0
  64. package/dist/store/index.js +1 -0
  65. package/dist/store/index.js.map +1 -1
  66. package/dist/transport/index.d.ts +7 -0
  67. package/dist/transport/index.js +7 -0
  68. package/dist/transport/index.js.map +1 -1
  69. package/dist/transport/session/index.d.ts +1 -0
  70. package/dist/transport/session/index.js +1 -0
  71. package/dist/transport/session/index.js.map +1 -1
  72. package/docs/generated-cases.md +237 -0
  73. package/docs/scattered-components.md +60 -5
  74. package/jsfix.test_client.txt +62 -62
  75. package/jsfix.test_server.txt +72 -72
  76. package/package.json +5 -2
  77. package/src/buffer/index.ts +6 -0
  78. package/src/collections/index.ts +1 -1
  79. package/src/dictionary/contained/index.ts +3 -0
  80. package/src/dictionary/index.ts +5 -0
  81. package/src/generator/applicability.ts +87 -0
  82. package/src/generator/case-builder.ts +167 -0
  83. package/src/generator/corpus-store.ts +166 -0
  84. package/src/generator/index.ts +12 -0
  85. package/src/generator/instrument-universe.ts +347 -0
  86. package/src/generator/layout.ts +279 -0
  87. package/src/generator/market-context.ts +207 -0
  88. package/src/generator/outcome.ts +102 -0
  89. package/src/generator/realistic-generator.ts +404 -0
  90. package/src/generator/rng.ts +92 -0
  91. package/src/generator/scatter.ts +432 -0
  92. package/src/generator/scenarios.ts +167 -0
  93. package/src/generator/value-conventions.ts +295 -0
  94. package/src/index.ts +12 -0
  95. package/src/jsfix-cmd.ts +204 -0
  96. package/src/store/fix-msg-ascii-store-resend.ts +2 -1
  97. package/src/store/fix-msg-store-record.ts +4 -1
  98. package/src/store/index.ts +1 -0
  99. package/src/transport/index.ts +10 -0
  100. package/src/transport/session/index.ts +1 -0
@@ -0,0 +1,64 @@
1
+ import { Rng } from './rng';
2
+ import { AssetClass, IInstrumentSpec, ILegSpec } from './instrument-universe';
3
+ export interface IParty {
4
+ readonly id: string;
5
+ readonly idSource: string;
6
+ readonly role: number;
7
+ readonly desc: string;
8
+ }
9
+ export interface IMarketContextOptions {
10
+ readonly rng: Rng;
11
+ readonly instrument?: IInstrumentSpec;
12
+ readonly assetClass?: AssetClass;
13
+ readonly asOf?: Date;
14
+ readonly senderCompId?: string;
15
+ readonly targetCompId?: string;
16
+ }
17
+ export declare class MarketContext {
18
+ readonly rng: Rng;
19
+ readonly instrument: IInstrumentSpec;
20
+ readonly asOf: Date;
21
+ readonly side: string;
22
+ readonly orderQty: number;
23
+ readonly price: number;
24
+ readonly lastPx: number;
25
+ readonly lastQty: number;
26
+ readonly cumQty: number;
27
+ readonly leavesQty: number;
28
+ readonly avgPx: number;
29
+ readonly tradeDate: Date;
30
+ readonly transactTime: Date;
31
+ readonly settlDate: Date;
32
+ readonly clOrdId: string;
33
+ readonly origClOrdId: string;
34
+ readonly orderId: string;
35
+ readonly execId: string;
36
+ readonly tradeId: string;
37
+ readonly account: string;
38
+ readonly desk: string;
39
+ readonly senderCompId: string;
40
+ readonly targetCompId: string;
41
+ readonly firms: readonly IParty[];
42
+ readonly traders: readonly IParty[];
43
+ leg: ILegSpec | null;
44
+ underlying: IInstrumentSpec | null;
45
+ private sequence;
46
+ constructor(options: IMarketContextOptions);
47
+ current(): {
48
+ symbol: string;
49
+ securityId: string;
50
+ securityIdSource: string;
51
+ securityType: string;
52
+ refPx: number;
53
+ maturityMonthYear?: string;
54
+ };
55
+ nextSeq(): number;
56
+ text(): string;
57
+ party(index: number): IParty;
58
+ quantity(max?: number): number;
59
+ grossAmount(): number;
60
+ priceNear(centre?: number): number;
61
+ static midnightUtc(d: Date): Date;
62
+ static addBusinessDays(from: Date, days: number): Date;
63
+ static yyyymmdd(d: Date): string;
64
+ }
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MarketContext = void 0;
4
+ const instrument_universe_1 = require("./instrument-universe");
5
+ const firms = [
6
+ { id: 'MGSTGB2L', idSource: 'C', role: 1, desc: 'executing firm' },
7
+ { id: 'CHASGB2L', idSource: 'C', role: 4, desc: 'clearing firm' },
8
+ { id: 'BARCGB22', idSource: 'C', role: 17, desc: 'contra firm' },
9
+ { id: 'ABNANL2A', idSource: 'C', role: 13, desc: 'order origination firm' }
10
+ ];
11
+ const traders = [
12
+ { id: 'jhalliday', idSource: 'D', role: 12, desc: 'executing trader' },
13
+ { id: 'rmcdowall', idSource: 'D', role: 11, desc: 'order origination trader' },
14
+ { id: 'kbannerjee', idSource: 'D', role: 12, desc: 'executing trader' }
15
+ ];
16
+ const accounts = [
17
+ 'ACCT-00417', 'ACCT-01925', 'OMNI-4471', 'SEGR-2210'
18
+ ];
19
+ const desks = ['ENERGY', 'MACRO', 'CASH-EQ', 'FICC'];
20
+ const freeText = [
21
+ 'give up to clearer',
22
+ 'client instruction on file',
23
+ 'worked over the close',
24
+ 'cross with house book',
25
+ 'blocked - settle T+2'
26
+ ];
27
+ class MarketContext {
28
+ constructor(options) {
29
+ var _a, _b, _c, _d;
30
+ this.leg = null;
31
+ this.underlying = null;
32
+ this.sequence = 0;
33
+ const rng = this.rng = options.rng;
34
+ this.instrument = (_a = options.instrument) !== null && _a !== void 0 ? _a : instrument_universe_1.InstrumentUniverse.choose(rng, options.assetClass);
35
+ const inst = this.instrument;
36
+ this.asOf = (_b = options.asOf) !== null && _b !== void 0 ? _b : new Date();
37
+ this.side = rng.bool(0.5) ? '1' : '2';
38
+ const lots = rng.int(1, 40);
39
+ this.orderQty = lots * inst.lotSize;
40
+ this.price = rng.around(inst.refPx, 0.004, inst.tick);
41
+ this.lastPx = rng.around(this.price, 0.002, inst.tick);
42
+ const filledLots = rng.bool(0.35) ? lots : rng.int(1, Math.max(1, lots - 1));
43
+ this.cumQty = filledLots * inst.lotSize;
44
+ this.leavesQty = this.orderQty - this.cumQty;
45
+ this.lastQty = this.cumQty;
46
+ this.avgPx = this.lastPx;
47
+ this.tradeDate = MarketContext.midnightUtc(this.asOf);
48
+ this.transactTime = new Date(this.asOf.getTime() - rng.int(0, 90) * 1000);
49
+ this.settlDate = MarketContext.addBusinessDays(this.tradeDate, inst.assetClass === instrument_universe_1.AssetClass.Fx ? 2 : 2);
50
+ const stamp = MarketContext.yyyymmdd(this.tradeDate);
51
+ this.clOrdId = `CLORD-${stamp}-${rng.int(10000, 99999)}`;
52
+ this.origClOrdId = `CLORD-${stamp}-${rng.int(10000, 99999)}`;
53
+ this.orderId = `ORD-${rng.int(1000000, 9999999)}`;
54
+ this.execId = `EXE-${rng.int(1000000, 9999999)}`;
55
+ this.tradeId = `TRD-${stamp}-${rng.int(1000, 9999)}`;
56
+ this.account = rng.pick(accounts);
57
+ this.desk = rng.pick(desks);
58
+ this.senderCompId = (_c = options.senderCompId) !== null && _c !== void 0 ? _c : 'init-comp';
59
+ this.targetCompId = (_d = options.targetCompId) !== null && _d !== void 0 ? _d : 'accept-comp';
60
+ this.firms = rng.shuffled(firms);
61
+ this.traders = rng.shuffled(traders);
62
+ }
63
+ current() {
64
+ const leg = this.leg;
65
+ if (leg)
66
+ return leg;
67
+ const under = this.underlying;
68
+ if (under)
69
+ return under;
70
+ return this.instrument;
71
+ }
72
+ nextSeq() {
73
+ return ++this.sequence;
74
+ }
75
+ text() {
76
+ return this.rng.pick(freeText);
77
+ }
78
+ party(index) {
79
+ const all = [...this.firms, ...this.traders];
80
+ return all[index % all.length];
81
+ }
82
+ quantity(max) {
83
+ const lot = this.instrument.lotSize;
84
+ const cap = max != null ? Math.max(1, Math.floor(max / lot)) : 40;
85
+ return this.rng.int(1, cap) * lot;
86
+ }
87
+ grossAmount() {
88
+ var _a;
89
+ const multiplier = (_a = this.instrument.contractMultiplier) !== null && _a !== void 0 ? _a : 1;
90
+ return Number((this.lastQty * this.lastPx * multiplier).toFixed(2));
91
+ }
92
+ priceNear(centre) {
93
+ const inst = this.instrument;
94
+ const leg = this.leg;
95
+ const base = centre !== null && centre !== void 0 ? centre : (leg ? leg.refPx : inst.refPx);
96
+ return this.rng.around(base, 0.006, leg ? 0.01 : inst.tick);
97
+ }
98
+ static midnightUtc(d) {
99
+ return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), 0, 0, 0, 0));
100
+ }
101
+ static addBusinessDays(from, days) {
102
+ const d = new Date(from.getTime());
103
+ let left = days;
104
+ while (left > 0) {
105
+ d.setUTCDate(d.getUTCDate() + 1);
106
+ const day = d.getUTCDay();
107
+ if (day !== 0 && day !== 6)
108
+ left--;
109
+ }
110
+ return d;
111
+ }
112
+ static yyyymmdd(d) {
113
+ const m = d.getUTCMonth() + 1;
114
+ const day = d.getUTCDate();
115
+ return `${d.getUTCFullYear()}${m < 10 ? '0' : ''}${m}${day < 10 ? '0' : ''}${day}`;
116
+ }
117
+ }
118
+ exports.MarketContext = MarketContext;
119
+ //# sourceMappingURL=market-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"market-context.js","sourceRoot":"","sources":["../../src/generator/market-context.ts"],"names":[],"mappings":"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","sourcesContent":["import { Rng } from './rng'\nimport { AssetClass, IInstrumentSpec, ILegSpec, InstrumentUniverse } from './instrument-universe'\n\n/**\n * Everything one generated message agrees about.\n *\n * The old generator drew each field independently, which is why its output never added\n * up - an OrderQty of -483.04 filled at a price of 97319 on a security called `Nunc`.\n * A message is a statement about a single trade, so the trade is decided once, up front,\n * and every field is then read off it.\n *\n * The walker moves this context as it descends: entering a leg group instance sets\n * `leg`, so `LegPrice` is drawn from that leg rather than from the parent.\n */\n\nexport interface IParty {\n readonly id: string\n readonly idSource: string\n readonly role: number\n readonly desc: string\n}\n\nexport interface IMarketContextOptions {\n readonly rng: Rng\n readonly instrument?: IInstrumentSpec\n readonly assetClass?: AssetClass\n /** anchor for every date in the message; defaults to today */\n readonly asOf?: Date\n readonly senderCompId?: string\n readonly targetCompId?: string\n}\n\nconst firms: readonly IParty[] = [\n { id: 'MGSTGB2L', idSource: 'C', role: 1, desc: 'executing firm' },\n { id: 'CHASGB2L', idSource: 'C', role: 4, desc: 'clearing firm' },\n { id: 'BARCGB22', idSource: 'C', role: 17, desc: 'contra firm' },\n { id: 'ABNANL2A', idSource: 'C', role: 13, desc: 'order origination firm' }\n]\n\nconst traders: readonly IParty[] = [\n { id: 'jhalliday', idSource: 'D', role: 12, desc: 'executing trader' },\n { id: 'rmcdowall', idSource: 'D', role: 11, desc: 'order origination trader' },\n { id: 'kbannerjee', idSource: 'D', role: 12, desc: 'executing trader' }\n]\n\nconst accounts: readonly string[] = [\n 'ACCT-00417', 'ACCT-01925', 'OMNI-4471', 'SEGR-2210'\n]\n\nconst desks: readonly string[] = ['ENERGY', 'MACRO', 'CASH-EQ', 'FICC']\n\nconst freeText: readonly string[] = [\n 'give up to clearer',\n 'client instruction on file',\n 'worked over the close',\n 'cross with house book',\n 'blocked - settle T+2'\n]\n\nexport class MarketContext {\n public readonly rng: Rng\n public readonly instrument: IInstrumentSpec\n public readonly asOf: Date\n\n /** the trade this message describes */\n public readonly side: string\n public readonly orderQty: number\n public readonly price: number\n public readonly lastPx: number\n public readonly lastQty: number\n public readonly cumQty: number\n public readonly leavesQty: number\n public readonly avgPx: number\n\n public readonly tradeDate: Date\n public readonly transactTime: Date\n public readonly settlDate: Date\n\n public readonly clOrdId: string\n public readonly origClOrdId: string\n public readonly orderId: string\n public readonly execId: string\n public readonly tradeId: string\n public readonly account: string\n public readonly desk: string\n public readonly senderCompId: string\n public readonly targetCompId: string\n\n public readonly firms: readonly IParty[]\n public readonly traders: readonly IParty[]\n\n /** set while the walker is inside a leg, cleared on the way out */\n public leg: ILegSpec | null = null\n /** set while the walker is inside an underlying */\n public underlying: IInstrumentSpec | null = null\n\n private sequence: number = 0\n\n constructor (options: IMarketContextOptions) {\n const rng = this.rng = options.rng\n this.instrument = options.instrument ?? InstrumentUniverse.choose(rng, options.assetClass)\n const inst = this.instrument\n this.asOf = options.asOf ?? new Date()\n\n this.side = rng.bool(0.5) ? '1' : '2'\n const lots = rng.int(1, 40)\n this.orderQty = lots * inst.lotSize\n this.price = rng.around(inst.refPx, 0.004, inst.tick)\n this.lastPx = rng.around(this.price, 0.002, inst.tick)\n // a partial fill more often than not, which is what a real execution stream looks like\n const filledLots = rng.bool(0.35) ? lots : rng.int(1, Math.max(1, lots - 1))\n this.cumQty = filledLots * inst.lotSize\n this.leavesQty = this.orderQty - this.cumQty\n this.lastQty = this.cumQty\n this.avgPx = this.lastPx\n\n this.tradeDate = MarketContext.midnightUtc(this.asOf)\n this.transactTime = new Date(this.asOf.getTime() - rng.int(0, 90) * 1000)\n this.settlDate = MarketContext.addBusinessDays(this.tradeDate, inst.assetClass === AssetClass.Fx ? 2 : 2)\n\n const stamp = MarketContext.yyyymmdd(this.tradeDate)\n this.clOrdId = `CLORD-${stamp}-${rng.int(10000, 99999)}`\n this.origClOrdId = `CLORD-${stamp}-${rng.int(10000, 99999)}`\n this.orderId = `ORD-${rng.int(1000000, 9999999)}`\n this.execId = `EXE-${rng.int(1000000, 9999999)}`\n this.tradeId = `TRD-${stamp}-${rng.int(1000, 9999)}`\n this.account = rng.pick(accounts)\n this.desk = rng.pick(desks)\n this.senderCompId = options.senderCompId ?? 'init-comp'\n this.targetCompId = options.targetCompId ?? 'accept-comp'\n\n this.firms = rng.shuffled(firms)\n this.traders = rng.shuffled(traders)\n }\n\n /**\n * the instrument whose fields should be read right now - a leg is described by its own\n * contract, an underlying by the underlying, and anything else by the parent\n */\n public current (): { symbol: string, securityId: string, securityIdSource: string, securityType: string, refPx: number, maturityMonthYear?: string } {\n const leg = this.leg\n if (leg) return leg\n const under = this.underlying\n if (under) return under\n return this.instrument\n }\n\n public nextSeq (): number {\n return ++this.sequence\n }\n\n public text (): string {\n return this.rng.pick(freeText)\n }\n\n public party (index: number): IParty {\n const all = [...this.firms, ...this.traders]\n return all[index % all.length]\n }\n\n /**\n * a quantity that fits the contract - always a whole number of lots, so a message\n * never claims a fraction of a future\n */\n public quantity (max?: number): number {\n const lot = this.instrument.lotSize\n const cap = max != null ? Math.max(1, Math.floor(max / lot)) : 40\n return this.rng.int(1, cap) * lot\n }\n\n /**\n * what the trade is worth - quantity by price, and by the contract multiplier where\n * there is one, so a Brent fill does not report a notional of six dollars\n */\n public grossAmount (): number {\n const multiplier = this.instrument.contractMultiplier ?? 1\n return Number((this.lastQty * this.lastPx * multiplier).toFixed(2))\n }\n\n public priceNear (centre?: number): number {\n const inst = this.instrument\n const leg = this.leg\n const base = centre ?? (leg ? leg.refPx : inst.refPx)\n return this.rng.around(base, 0.006, leg ? 0.01 : inst.tick)\n }\n\n public static midnightUtc (d: Date): Date {\n return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), 0, 0, 0, 0))\n }\n\n public static addBusinessDays (from: Date, days: number): Date {\n const d = new Date(from.getTime())\n let left = days\n while (left > 0) {\n d.setUTCDate(d.getUTCDate() + 1)\n const day = d.getUTCDay()\n if (day !== 0 && day !== 6) left--\n }\n return d\n }\n\n public static yyyymmdd (d: Date): string {\n const m = d.getUTCMonth() + 1\n const day = d.getUTCDate()\n return `${d.getUTCFullYear()}${m < 10 ? '0' : ''}${m}${day < 10 ? '0' : ''}${day}`\n }\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import { ILooseObject } from '../collections/collection';
2
+ export declare enum CaseOutcome {
3
+ RoundTrips = "round-trips",
4
+ LosesFields = "loses-fields",
5
+ MisAttributes = "mis-attributes",
6
+ Rejected = "rejected"
7
+ }
8
+ export interface IOutcome {
9
+ readonly outcome: CaseOutcome;
10
+ readonly missing: string[];
11
+ readonly added: string[];
12
+ readonly changed: string[];
13
+ }
14
+ export declare function leaves(o: any, path?: string, into?: Map<string, string>): Map<string, string>;
15
+ export declare function classify(canonical: ILooseObject | null, scattered: ILooseObject | null): IOutcome;
16
+ export declare function describeOutcome(o: IOutcome): string;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CaseOutcome = void 0;
4
+ exports.leaves = leaves;
5
+ exports.classify = classify;
6
+ exports.describeOutcome = describeOutcome;
7
+ var CaseOutcome;
8
+ (function (CaseOutcome) {
9
+ CaseOutcome["RoundTrips"] = "round-trips";
10
+ CaseOutcome["LosesFields"] = "loses-fields";
11
+ CaseOutcome["MisAttributes"] = "mis-attributes";
12
+ CaseOutcome["Rejected"] = "rejected";
13
+ })(CaseOutcome || (exports.CaseOutcome = CaseOutcome = {}));
14
+ function leaves(o, path = '', into = new Map()) {
15
+ if (o === null || o === undefined || typeof o !== 'object' || o instanceof Date || Buffer.isBuffer(o)) {
16
+ into.set(path, JSON.stringify(o));
17
+ return into;
18
+ }
19
+ for (const key of Object.keys(o)) {
20
+ leaves(o[key], `${path}.${key}`, into);
21
+ }
22
+ return into;
23
+ }
24
+ function classify(canonical, scattered) {
25
+ if (!scattered) {
26
+ return { outcome: CaseOutcome.Rejected, missing: [], added: [], changed: [] };
27
+ }
28
+ const a = leaves(canonical);
29
+ const b = leaves(scattered);
30
+ const missing = [];
31
+ const added = [];
32
+ const changed = [];
33
+ for (const [k, v] of a) {
34
+ const other = b.get(k);
35
+ if (other === undefined) {
36
+ missing.push(k);
37
+ }
38
+ else if (other !== v) {
39
+ changed.push(k);
40
+ }
41
+ }
42
+ for (const k of b.keys()) {
43
+ if (!a.has(k))
44
+ added.push(k);
45
+ }
46
+ missing.sort();
47
+ added.sort();
48
+ changed.sort();
49
+ if (missing.length === 0 && added.length === 0 && changed.length === 0) {
50
+ return { outcome: CaseOutcome.RoundTrips, missing, added, changed };
51
+ }
52
+ if (added.length > 0 || changed.length > 0) {
53
+ return { outcome: CaseOutcome.MisAttributes, missing, added, changed };
54
+ }
55
+ return { outcome: CaseOutcome.LosesFields, missing, added, changed };
56
+ }
57
+ function describeOutcome(o) {
58
+ switch (o.outcome) {
59
+ case CaseOutcome.RoundTrips:
60
+ return 'round trips - the scattered ordering parses to the same object';
61
+ case CaseOutcome.LosesFields:
62
+ return `loses ${o.missing.length} field(s): ${o.missing.join(', ')}`;
63
+ case CaseOutcome.MisAttributes:
64
+ return `mis-attributes ${o.added.length + o.changed.length} value(s): ${[...o.added, ...o.changed].join(', ')}`;
65
+ case CaseOutcome.Rejected:
66
+ return 'rejected - the parser refused a legal ordering';
67
+ default:
68
+ return 'unknown';
69
+ }
70
+ }
71
+ //# sourceMappingURL=outcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outcome.js","sourceRoot":"","sources":["../../src/generator/outcome.ts"],"names":[],"mappings":"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","sourcesContent":["import { ILooseObject } from '../collections/collection'\n\n/**\n * What the engine did with a scattered message, stated more precisely than \"the objects\n * differ\".\n *\n * The distinction earns its place because the two failures found so far are not equally\n * serious, and an application can defend against only one of them:\n *\n * - **loses fields** — the scattered parse is a strict subset of the canonical one.\n * Data is missing, but nothing present is false. An application checking for the\n * fields it needs will notice.\n * - **mis-attributes** — the scattered parse contains something the canonical one does\n * not: a value moved into a component that never carried it, or a value changed.\n * Nothing looks wrong, and no amount of care at the application catches it.\n *\n * Keeping them apart is also what lets a corpus entry record an expectation sharp enough\n * to be worth freezing. \"Differs\" would pass for the wrong reason.\n */\n\nexport enum CaseOutcome {\n /** the scattered ordering parses to the same object - the engine handled it */\n RoundTrips = 'round-trips',\n /** parses, but with fields dropped; every value present is still correct */\n LosesFields = 'loses-fields',\n /** parses, and carries a value the message did not say - the dangerous one */\n MisAttributes = 'mis-attributes',\n /** refused outright, which for a legal ordering is a defect, but a loud one */\n Rejected = 'rejected'\n}\n\nexport interface IOutcome {\n readonly outcome: CaseOutcome\n /** leaf paths the canonical parse has and the scattered one does not */\n readonly missing: string[]\n /** leaf paths the scattered parse has and the canonical one does not */\n readonly added: string[]\n /** leaf paths in both, disagreeing */\n readonly changed: string[]\n}\n\n/**\n * every leaf of an object as `path -> json`, so two parses can be compared without\n * caring how the tree is shaped\n */\nexport function leaves (o: any, path: string = '', into: Map<string, string> = new Map<string, string>()): Map<string, string> {\n if (o === null || o === undefined || typeof o !== 'object' || o instanceof Date || Buffer.isBuffer(o)) {\n into.set(path, JSON.stringify(o))\n return into\n }\n for (const key of Object.keys(o)) {\n leaves(o[key], `${path}.${key}`, into)\n }\n return into\n}\n\nexport function classify (canonical: ILooseObject | null, scattered: ILooseObject | null): IOutcome {\n if (!scattered) {\n return { outcome: CaseOutcome.Rejected, missing: [], added: [], changed: [] }\n }\n const a = leaves(canonical)\n const b = leaves(scattered)\n const missing: string[] = []\n const added: string[] = []\n const changed: string[] = []\n for (const [k, v] of a) {\n const other = b.get(k)\n if (other === undefined) {\n missing.push(k)\n } else if (other !== v) {\n changed.push(k)\n }\n }\n for (const k of b.keys()) {\n if (!a.has(k)) added.push(k)\n }\n missing.sort()\n added.sort()\n changed.sort()\n if (missing.length === 0 && added.length === 0 && changed.length === 0) {\n return { outcome: CaseOutcome.RoundTrips, missing, added, changed }\n }\n if (added.length > 0 || changed.length > 0) {\n return { outcome: CaseOutcome.MisAttributes, missing, added, changed }\n }\n return { outcome: CaseOutcome.LosesFields, missing, added, changed }\n}\n\nexport function describeOutcome (o: IOutcome): string {\n switch (o.outcome) {\n case CaseOutcome.RoundTrips:\n return 'round trips - the scattered ordering parses to the same object'\n case CaseOutcome.LosesFields:\n return `loses ${o.missing.length} field(s): ${o.missing.join(', ')}`\n case CaseOutcome.MisAttributes:\n return `mis-attributes ${o.added.length + o.changed.length} value(s): ${[...o.added, ...o.changed].join(', ')}`\n case CaseOutcome.Rejected:\n return 'rejected - the parser refused a legal ordering'\n default:\n return 'unknown'\n }\n}\n"]}
@@ -0,0 +1,53 @@
1
+ import { ILooseObject } from '../collections/collection';
2
+ import { FixDefinitions, MessageDefinition } from '../dictionary/definition';
3
+ import { MarketContext } from './market-context';
4
+ import { AssetClass, IInstrumentSpec } from './instrument-universe';
5
+ export interface IGeneratorOptions {
6
+ readonly seed?: number;
7
+ readonly density?: number;
8
+ readonly asOf?: Date;
9
+ readonly instrument?: string | IInstrumentSpec;
10
+ readonly assetClass?: AssetClass;
11
+ readonly maxDepth?: number;
12
+ readonly maxGroupInstances?: number;
13
+ readonly include?: readonly string[];
14
+ readonly exclude?: readonly string[];
15
+ readonly senderCompId?: string;
16
+ readonly targetCompId?: string;
17
+ }
18
+ export interface IGeneratedMessage {
19
+ readonly msgType: string;
20
+ readonly name: string;
21
+ readonly object: ILooseObject;
22
+ readonly market: MarketContext;
23
+ readonly definition: MessageDefinition;
24
+ }
25
+ export declare class RealisticGenerator {
26
+ readonly definitions: FixDefinitions;
27
+ readonly options: IGeneratorOptions;
28
+ private readonly rng;
29
+ private readonly density;
30
+ private readonly maxDepth;
31
+ private readonly maxGroupInstances;
32
+ private readonly forced;
33
+ private readonly forcedTags;
34
+ private readonly suppressed;
35
+ private assetClass;
36
+ constructor(definitions: FixDefinitions, options?: IGeneratorOptions);
37
+ generate(msgType: string): IGeneratedMessage;
38
+ private resolveInstrument;
39
+ private build;
40
+ private simple;
41
+ private component;
42
+ private group;
43
+ private enter;
44
+ private probability;
45
+ private componentProbability;
46
+ private groupProbability;
47
+ private instances;
48
+ private isForced;
49
+ private static hasOwnSimple;
50
+ private static carriesAny;
51
+ private static rawFollows;
52
+ private static dot;
53
+ }
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RealisticGenerator = void 0;
4
+ const contained_1 = require("../dictionary/contained");
5
+ const tag_type_1 = require("../buffer/tag/tag-type");
6
+ const rng_1 = require("./rng");
7
+ const market_context_1 = require("./market-context");
8
+ const value_conventions_1 = require("./value-conventions");
9
+ const instrument_universe_1 = require("./instrument-universe");
10
+ const applicability_1 = require("./applicability");
11
+ const coreSets = /^(Instrument|InstrumentLeg|UnderlyingInstrument|OrderQtyData|Parties|NestedParties[0-9]?|InstrmtLegGrp|TrdInstrmtLegGrp|InstrmtMDReqGrp|MDIncGrp|MDFullGrp)$/;
12
+ const legSet = /Leg(s|Grp|$|[A-Z])|InstrmtLeg/;
13
+ const underlyingSet = /Und(erlying|Instrmt)/;
14
+ class RealisticGenerator {
15
+ constructor(definitions, options = {}) {
16
+ var _a, _b, _c, _d, _e, _f;
17
+ this.definitions = definitions;
18
+ this.options = options;
19
+ this.assetClass = instrument_universe_1.AssetClass.Equity;
20
+ this.rng = new rng_1.Rng((_a = options.seed) !== null && _a !== void 0 ? _a : 0x5eed);
21
+ this.density = (_b = options.density) !== null && _b !== void 0 ? _b : 1.0;
22
+ this.maxDepth = (_c = options.maxDepth) !== null && _c !== void 0 ? _c : 6;
23
+ this.maxGroupInstances = (_d = options.maxGroupInstances) !== null && _d !== void 0 ? _d : 2;
24
+ const include = (_e = options.include) !== null && _e !== void 0 ? _e : [];
25
+ this.forcedTags = include.filter(s => /^[0-9]+$/.test(s)).map(s => parseInt(s, 10));
26
+ this.forced = new Set(include.filter(s => !/^[0-9]+$/.test(s)));
27
+ this.suppressed = new Set((_f = options.exclude) !== null && _f !== void 0 ? _f : []);
28
+ }
29
+ generate(msgType) {
30
+ var _a, _b;
31
+ const definition = this.definitions.message.get(msgType);
32
+ if (!definition) {
33
+ throw new Error(`definitions do not contain type ${msgType}`);
34
+ }
35
+ const instrument = this.resolveInstrument();
36
+ const market = new market_context_1.MarketContext({
37
+ rng: this.rng,
38
+ instrument,
39
+ assetClass: this.options.assetClass,
40
+ asOf: this.options.asOf,
41
+ senderCompId: this.options.senderCompId,
42
+ targetCompId: this.options.targetCompId
43
+ });
44
+ const conventions = new value_conventions_1.ValueConventions(market);
45
+ this.assetClass = market.instrument.assetClass;
46
+ const state = { path: [], depth: 0, instance: 0, mustInclude: this.forcedTags };
47
+ const object = (_a = this.build(definition, market, conventions, state)) !== null && _a !== void 0 ? _a : {};
48
+ return {
49
+ msgType: (_b = definition.msgType) !== null && _b !== void 0 ? _b : msgType,
50
+ name: definition.name,
51
+ object,
52
+ market,
53
+ definition
54
+ };
55
+ }
56
+ resolveInstrument() {
57
+ const wanted = this.options.instrument;
58
+ if (!wanted)
59
+ return undefined;
60
+ if (typeof wanted !== 'string')
61
+ return wanted;
62
+ const found = instrument_universe_1.InstrumentUniverse.byName(wanted);
63
+ if (!found) {
64
+ throw new Error(`no instrument named ${wanted} - try one of ${instrument_universe_1.InstrumentUniverse.all.map(i => i.symbol).join(', ')}`);
65
+ }
66
+ return found;
67
+ }
68
+ build(set, market, conventions, state) {
69
+ const o = {};
70
+ const fields = set.fields;
71
+ for (let i = 0; i < fields.length; ++i) {
72
+ const field = fields[i];
73
+ switch (field.type) {
74
+ case contained_1.ContainedFieldType.Simple:
75
+ this.simple(o, set, field, i, conventions, state);
76
+ break;
77
+ case contained_1.ContainedFieldType.Component:
78
+ this.component(o, field, market, conventions, state);
79
+ break;
80
+ case contained_1.ContainedFieldType.Group:
81
+ this.group(o, field, market, conventions, state);
82
+ break;
83
+ default:
84
+ break;
85
+ }
86
+ }
87
+ return Object.keys(o).length > 0 ? o : null;
88
+ }
89
+ simple(o, set, sf, index, conventions, state) {
90
+ const definition = sf.definition;
91
+ if (definition.tagType === tag_type_1.TagType.Length && RealisticGenerator.rawFollows(set, index)) {
92
+ return;
93
+ }
94
+ const dotted = RealisticGenerator.dot(state.path, sf.name);
95
+ if (this.suppressed.has(dotted) || this.suppressed.has(sf.name))
96
+ return;
97
+ const must = state.mustInclude.includes(definition.tag);
98
+ if (!must && !sf.required && !this.isForced(dotted, sf.name)) {
99
+ if (!(0, applicability_1.fieldApplies)(sf.name, this.assetClass))
100
+ return;
101
+ if (!this.rng.bool(this.probability(sf)))
102
+ return;
103
+ }
104
+ const fieldContext = { path: state.path, instance: state.instance };
105
+ const value = conventions.value(definition, fieldContext);
106
+ if (value != null && value !== '') {
107
+ o[sf.name] = value;
108
+ }
109
+ }
110
+ component(o, cf, market, conventions, state) {
111
+ const name = cf.name;
112
+ if (name === 'StandardHeader' || name === 'StandardTrailer')
113
+ return;
114
+ const dotted = RealisticGenerator.dot(state.path, name);
115
+ if (this.suppressed.has(dotted) || this.suppressed.has(name))
116
+ return;
117
+ const definition = cf.definition;
118
+ const carries = RealisticGenerator.carriesAny(definition, state.mustInclude);
119
+ if (!cf.required && !carries && !this.isForced(dotted, name)) {
120
+ if (state.depth >= this.maxDepth)
121
+ return;
122
+ if (!(0, applicability_1.setApplies)(name, this.assetClass))
123
+ return;
124
+ if (!this.rng.bool(this.componentProbability(name, state.depth)))
125
+ return;
126
+ }
127
+ const restore = this.enter(name, market, state.instance);
128
+ const child = this.build(definition, market, conventions, {
129
+ path: [...state.path, name],
130
+ depth: state.depth + 1,
131
+ instance: state.instance,
132
+ mustInclude: state.mustInclude.filter(t => definition.containedTag[t])
133
+ });
134
+ restore();
135
+ if (child) {
136
+ o[name] = child;
137
+ }
138
+ }
139
+ group(o, gf, market, conventions, state) {
140
+ var _a;
141
+ const name = gf.name;
142
+ const dotted = RealisticGenerator.dot(state.path, name);
143
+ if (this.suppressed.has(dotted) || this.suppressed.has(name))
144
+ return;
145
+ const definition = gf.definition;
146
+ const carries = RealisticGenerator.carriesAny(definition, state.mustInclude);
147
+ if (!gf.required && !carries && !this.isForced(dotted, name)) {
148
+ if (state.depth >= this.maxDepth)
149
+ return;
150
+ if (!(0, applicability_1.setApplies)(name, this.assetClass))
151
+ return;
152
+ if (!this.rng.bool(this.groupProbability(name, state.depth)))
153
+ return;
154
+ }
155
+ const isLegs = legSet.test(name) || legSet.test(definition.name);
156
+ const legs = market.instrument.legs;
157
+ const count = isLegs && legs && legs.length > 0
158
+ ? legs.length
159
+ : this.instances(state.depth);
160
+ const delimiter = (_a = definition.firstSimple) === null || _a === void 0 ? void 0 : _a.definition.tag;
161
+ const inherited = state.mustInclude.filter(t => definition.containedTag[t]);
162
+ const must = delimiter ? [delimiter, ...inherited] : inherited;
163
+ const instances = [];
164
+ for (let i = 0; i < count; ++i) {
165
+ const restore = this.enter(name, market, i);
166
+ const instance = this.build(definition, market, conventions, {
167
+ path: [...state.path, name],
168
+ depth: state.depth + 1,
169
+ instance: i,
170
+ mustInclude: must
171
+ });
172
+ restore();
173
+ if (instance && RealisticGenerator.hasOwnSimple(instance, definition)) {
174
+ instances.push(instance);
175
+ }
176
+ }
177
+ if (instances.length > 0) {
178
+ o[name] = instances;
179
+ }
180
+ }
181
+ enter(name, market, instance) {
182
+ var _a;
183
+ const wasLeg = market.leg;
184
+ const wasUnderlying = market.underlying;
185
+ const legs = market.instrument.legs;
186
+ if (legSet.test(name) && legs && legs.length > 0) {
187
+ market.leg = legs[instance % legs.length];
188
+ }
189
+ else if (underlyingSet.test(name)) {
190
+ market.underlying = (_a = market.underlying) !== null && _a !== void 0 ? _a : instrument_universe_1.InstrumentUniverse.choose(this.rng, market.instrument.assetClass === instrument_universe_1.AssetClass.Spread ? instrument_universe_1.AssetClass.Future : instrument_universe_1.AssetClass.Equity);
191
+ }
192
+ else {
193
+ return () => { };
194
+ }
195
+ return () => {
196
+ market.leg = wasLeg;
197
+ market.underlying = wasUnderlying;
198
+ };
199
+ }
200
+ probability(sf) {
201
+ const tag = sf.definition.tag;
202
+ let base;
203
+ if (value_conventions_1.ValueConventions.knows(tag)) {
204
+ base = 0.75;
205
+ }
206
+ else if (tag < 500) {
207
+ base = 0.3;
208
+ }
209
+ else {
210
+ base = 0.08;
211
+ }
212
+ return Math.min(1, base * this.density);
213
+ }
214
+ componentProbability(name, depth) {
215
+ if (coreSets.test(name))
216
+ return Math.min(1, 0.9 * Math.max(0.5, this.density));
217
+ return Math.min(1, (0.45 / (depth + 1)) * this.density);
218
+ }
219
+ groupProbability(name, depth) {
220
+ if (coreSets.test(name))
221
+ return Math.min(1, 0.8 * Math.max(0.5, this.density));
222
+ return Math.min(1, (0.35 / (depth + 1)) * this.density);
223
+ }
224
+ instances(depth) {
225
+ const cap = Math.max(1, this.maxGroupInstances - depth + 1);
226
+ return this.rng.int(1, Math.min(this.maxGroupInstances, cap));
227
+ }
228
+ isForced(dotted, name) {
229
+ if (this.forced.has(dotted) || this.forced.has(name))
230
+ return true;
231
+ for (const f of this.forced) {
232
+ if (f.startsWith(`${dotted}.`))
233
+ return true;
234
+ }
235
+ return false;
236
+ }
237
+ static hasOwnSimple(o, set) {
238
+ for (const field of set.fields) {
239
+ switch (field.type) {
240
+ case contained_1.ContainedFieldType.Simple:
241
+ if (o[field.name] != null && o[field.name] !== '')
242
+ return true;
243
+ break;
244
+ case contained_1.ContainedFieldType.Component: {
245
+ const cf = field;
246
+ const child = o[cf.name];
247
+ if (child && RealisticGenerator.hasOwnSimple(child, cf.definition))
248
+ return true;
249
+ break;
250
+ }
251
+ default:
252
+ break;
253
+ }
254
+ }
255
+ return false;
256
+ }
257
+ static carriesAny(set, tags) {
258
+ for (const t of tags) {
259
+ if (set.containedTag[t])
260
+ return true;
261
+ }
262
+ return false;
263
+ }
264
+ static rawFollows(set, index) {
265
+ const next = set.fields[index + 1];
266
+ if (!next || next.type !== contained_1.ContainedFieldType.Simple)
267
+ return false;
268
+ return next.definition.tagType === tag_type_1.TagType.RawData;
269
+ }
270
+ static dot(path, name) {
271
+ return path.length > 0 ? `${path.join('.')}.${name}` : name;
272
+ }
273
+ }
274
+ exports.RealisticGenerator = RealisticGenerator;
275
+ //# sourceMappingURL=realistic-generator.js.map