@riocrypto/common-server 1.0.2766 → 1.0.2769
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/clients/cluster-client.d.ts +5 -1
- package/build/clients/cluster-client.js +51 -5
- package/build/models/external-trade.d.ts +4 -0
- package/build/models/external-trade.js +15 -0
- package/build/models/stonex-fx-trade.d.ts +8 -0
- package/build/models/stonex-fx-trade.js +27 -0
- package/package.json +2 -2
|
@@ -241,14 +241,16 @@ declare class ClusterClient {
|
|
|
241
241
|
quantity: number;
|
|
242
242
|
valueDate?: string;
|
|
243
243
|
fiat?: string;
|
|
244
|
+
tradeKey: string;
|
|
244
245
|
}): Promise<StonexFXTrade>;
|
|
245
|
-
takeStonexQuote(tradeId: string, side: "buy" | "sell"): Promise<StonexFXTrade>;
|
|
246
|
+
takeStonexQuote(tradeId: string, side: "buy" | "sell", tradeKey: string): Promise<StonexFXTrade>;
|
|
246
247
|
cancelStonexTrade(tradeId: string): Promise<StonexFXTrade>;
|
|
247
248
|
updateStonexOrder(tradeId: string, params: {
|
|
248
249
|
quantity?: number;
|
|
249
250
|
price?: number;
|
|
250
251
|
stopPx?: number;
|
|
251
252
|
timeInForce?: string;
|
|
253
|
+
tradeKey: string;
|
|
252
254
|
}): Promise<StonexFXTrade>;
|
|
253
255
|
placeStonexDirectOrder(params: {
|
|
254
256
|
symbol: string;
|
|
@@ -261,8 +263,10 @@ declare class ClusterClient {
|
|
|
261
263
|
stopPx?: number;
|
|
262
264
|
valueDate?: string;
|
|
263
265
|
execInst?: string;
|
|
266
|
+
tradeKey: string;
|
|
264
267
|
}): Promise<StonexFXTrade>;
|
|
265
268
|
getStonexTrade(tradeId: string): Promise<StonexFXTrade>;
|
|
269
|
+
getStonexTradeByKey(tradeKey: string): Promise<StonexFXTrade | null>;
|
|
266
270
|
getStonexTrades(offset: number, limit: number): Promise<{
|
|
267
271
|
data: StonexFXTrade[];
|
|
268
272
|
hasMore: boolean;
|
|
@@ -794,13 +794,24 @@ class ClusterClient {
|
|
|
794
794
|
// ─── StoneX FIX ───────────────────────────────────────────────────
|
|
795
795
|
createStonexTrade(data) {
|
|
796
796
|
return __awaiter(this, void 0, void 0, function* () {
|
|
797
|
-
const
|
|
797
|
+
const { tradeKey } = data;
|
|
798
|
+
const response = yield this.axios.post(`${this.baseUrl}/api/stonex/create-quote-stream`, data, {
|
|
799
|
+
headers: {
|
|
800
|
+
"x-cluster-api-key": this.clusterApiKey,
|
|
801
|
+
"X-Trade-Key": tradeKey,
|
|
802
|
+
},
|
|
803
|
+
});
|
|
798
804
|
return response.data;
|
|
799
805
|
});
|
|
800
806
|
}
|
|
801
|
-
takeStonexQuote(tradeId, side) {
|
|
807
|
+
takeStonexQuote(tradeId, side, tradeKey) {
|
|
802
808
|
return __awaiter(this, void 0, void 0, function* () {
|
|
803
|
-
const response = yield this.axios.post(`${this.baseUrl}/api/stonex/take-quote/${tradeId}`, { side }, {
|
|
809
|
+
const response = yield this.axios.post(`${this.baseUrl}/api/stonex/take-quote/${tradeId}`, { side, tradeKey }, {
|
|
810
|
+
headers: {
|
|
811
|
+
"x-cluster-api-key": this.clusterApiKey,
|
|
812
|
+
"X-Trade-Key": tradeKey,
|
|
813
|
+
},
|
|
814
|
+
});
|
|
804
815
|
return response.data;
|
|
805
816
|
});
|
|
806
817
|
}
|
|
@@ -812,13 +823,25 @@ class ClusterClient {
|
|
|
812
823
|
}
|
|
813
824
|
updateStonexOrder(tradeId, params) {
|
|
814
825
|
return __awaiter(this, void 0, void 0, function* () {
|
|
815
|
-
const
|
|
826
|
+
const { tradeKey } = params;
|
|
827
|
+
const response = yield this.axios.post(`${this.baseUrl}/api/stonex/update-order/${tradeId}`, params, {
|
|
828
|
+
headers: {
|
|
829
|
+
"x-cluster-api-key": this.clusterApiKey,
|
|
830
|
+
"X-Trade-Key": tradeKey,
|
|
831
|
+
},
|
|
832
|
+
});
|
|
816
833
|
return response.data;
|
|
817
834
|
});
|
|
818
835
|
}
|
|
819
836
|
placeStonexDirectOrder(params) {
|
|
820
837
|
return __awaiter(this, void 0, void 0, function* () {
|
|
821
|
-
const
|
|
838
|
+
const { tradeKey } = params;
|
|
839
|
+
const response = yield this.axios.post(`${this.baseUrl}/api/stonex/place-order`, params, {
|
|
840
|
+
headers: {
|
|
841
|
+
"x-cluster-api-key": this.clusterApiKey,
|
|
842
|
+
"X-Trade-Key": tradeKey,
|
|
843
|
+
},
|
|
844
|
+
});
|
|
822
845
|
return response.data;
|
|
823
846
|
});
|
|
824
847
|
}
|
|
@@ -828,6 +851,29 @@ class ClusterClient {
|
|
|
828
851
|
return response.data;
|
|
829
852
|
});
|
|
830
853
|
}
|
|
854
|
+
// Reconcile helper: look up a Stonex trade by caller-supplied trade key.
|
|
855
|
+
// Returns null when the key has no matching trade (the server either
|
|
856
|
+
// never received the request or failed before persisting). External-trading
|
|
857
|
+
// calls this before stamping a local trade as Failed so a lost HTTP
|
|
858
|
+
// response does not desync the two systems.
|
|
859
|
+
getStonexTradeByKey(tradeKey) {
|
|
860
|
+
var _a;
|
|
861
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
862
|
+
try {
|
|
863
|
+
const response = yield this.axios.get(`${this.baseUrl}/api/stonex/trades/by-trade-key/${encodeURIComponent(tradeKey)}`, { headers: { "x-cluster-api-key": this.clusterApiKey } });
|
|
864
|
+
return response.data;
|
|
865
|
+
}
|
|
866
|
+
catch (err) {
|
|
867
|
+
if (typeof err === "object" &&
|
|
868
|
+
err !== null &&
|
|
869
|
+
"response" in err &&
|
|
870
|
+
((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
|
|
871
|
+
return null;
|
|
872
|
+
}
|
|
873
|
+
throw err;
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
}
|
|
831
877
|
getStonexTrades(offset, limit) {
|
|
832
878
|
return __awaiter(this, void 0, void 0, function* () {
|
|
833
879
|
const response = yield this.axios.get(`${this.baseUrl}/api/stonex/trades?offset=${offset}&limit=${limit}`, { headers: { "x-cluster-api-key": this.clusterApiKey } });
|
|
@@ -3,6 +3,7 @@ import { Mongoose, Model, Document, HydratedDocument } from "mongoose";
|
|
|
3
3
|
interface ExternalTradeAttrs {
|
|
4
4
|
createdAt: Date;
|
|
5
5
|
providerOrderId?: string;
|
|
6
|
+
tradeKey?: string;
|
|
6
7
|
type: ExternalTradeType;
|
|
7
8
|
side: Side;
|
|
8
9
|
provider: ExternalTradingProvider;
|
|
@@ -31,6 +32,7 @@ interface ExternalTradeAttrs {
|
|
|
31
32
|
ordType?: string;
|
|
32
33
|
amountReceived?: number;
|
|
33
34
|
stopPx?: number;
|
|
35
|
+
takeQuoteTradeKey?: string;
|
|
34
36
|
};
|
|
35
37
|
};
|
|
36
38
|
externalTradingAlgorithmId?: string;
|
|
@@ -40,6 +42,7 @@ interface ExternalTradeAttrs {
|
|
|
40
42
|
interface ExternalTradeDoc extends Document {
|
|
41
43
|
createdAt: Date;
|
|
42
44
|
providerOrderId?: string;
|
|
45
|
+
tradeKey?: string;
|
|
43
46
|
type: ExternalTradeType;
|
|
44
47
|
side: Side;
|
|
45
48
|
amountType: ExternalTradeAmountType;
|
|
@@ -69,6 +72,7 @@ interface ExternalTradeDoc extends Document {
|
|
|
69
72
|
ordType?: string;
|
|
70
73
|
amountReceived?: number;
|
|
71
74
|
stopPx?: number;
|
|
75
|
+
takeQuoteTradeKey?: string;
|
|
72
76
|
};
|
|
73
77
|
};
|
|
74
78
|
externalTradingAlgorithmId?: string;
|
|
@@ -14,6 +14,9 @@ const buildExternalTrade = (mongoose) => {
|
|
|
14
14
|
providerOrderId: {
|
|
15
15
|
type: String,
|
|
16
16
|
},
|
|
17
|
+
tradeKey: {
|
|
18
|
+
type: String,
|
|
19
|
+
},
|
|
17
20
|
requestedAmountType: {
|
|
18
21
|
type: String,
|
|
19
22
|
required: true,
|
|
@@ -87,6 +90,18 @@ const buildExternalTrade = (mongoose) => {
|
|
|
87
90
|
},
|
|
88
91
|
},
|
|
89
92
|
});
|
|
93
|
+
// Partial unique compound index: guarantees at most one ExternalTrade per
|
|
94
|
+
// (provider, tradeKey) pair. Legacy rows without tradeKey are
|
|
95
|
+
// excluded by the partial filter, so no backfill is required.
|
|
96
|
+
ExternalTradeSchema.index({ provider: 1, tradeKey: 1 }, {
|
|
97
|
+
unique: true,
|
|
98
|
+
partialFilterExpression: { tradeKey: { $type: "string" } },
|
|
99
|
+
});
|
|
100
|
+
// Defense-in-depth: at most one ExternalTrade per (provider, providerOrderId).
|
|
101
|
+
ExternalTradeSchema.index({ provider: 1, providerOrderId: 1 }, {
|
|
102
|
+
unique: true,
|
|
103
|
+
partialFilterExpression: { providerOrderId: { $type: "string" } },
|
|
104
|
+
});
|
|
90
105
|
ExternalTradeSchema.statics.build = (attrs) => {
|
|
91
106
|
return new ExternalTrade(attrs);
|
|
92
107
|
};
|
|
@@ -30,6 +30,10 @@ interface StonexFXTradeAttrs {
|
|
|
30
30
|
expireTime?: string;
|
|
31
31
|
leavesQty?: number;
|
|
32
32
|
avgPx?: number;
|
|
33
|
+
tradeKey?: string;
|
|
34
|
+
takeQuoteTradeKey?: string;
|
|
35
|
+
fixSubmitted?: boolean;
|
|
36
|
+
submitAttempts?: number;
|
|
33
37
|
}
|
|
34
38
|
interface StonexFXTradeDoc extends Document {
|
|
35
39
|
id: string;
|
|
@@ -62,6 +66,10 @@ interface StonexFXTradeDoc extends Document {
|
|
|
62
66
|
expireTime?: string;
|
|
63
67
|
leavesQty?: number;
|
|
64
68
|
avgPx?: number;
|
|
69
|
+
tradeKey?: string;
|
|
70
|
+
takeQuoteTradeKey?: string;
|
|
71
|
+
fixSubmitted?: boolean;
|
|
72
|
+
submitAttempts?: number;
|
|
65
73
|
}
|
|
66
74
|
interface StonexFXTradeModel extends Model<StonexFXTradeDoc> {
|
|
67
75
|
build(attrs: StonexFXTradeAttrs): StonexFXTradeDoc;
|
|
@@ -99,6 +99,20 @@ const buildStonexFXTrade = (mongoose) => {
|
|
|
99
99
|
avgPx: {
|
|
100
100
|
type: Number,
|
|
101
101
|
},
|
|
102
|
+
tradeKey: {
|
|
103
|
+
type: String,
|
|
104
|
+
},
|
|
105
|
+
takeQuoteTradeKey: {
|
|
106
|
+
type: String,
|
|
107
|
+
},
|
|
108
|
+
fixSubmitted: {
|
|
109
|
+
type: Boolean,
|
|
110
|
+
default: false,
|
|
111
|
+
},
|
|
112
|
+
submitAttempts: {
|
|
113
|
+
type: Number,
|
|
114
|
+
default: 0,
|
|
115
|
+
},
|
|
102
116
|
metadata: {
|
|
103
117
|
type: Map,
|
|
104
118
|
of: mongoose.Schema.Types.Mixed,
|
|
@@ -112,6 +126,19 @@ const buildStonexFXTrade = (mongoose) => {
|
|
|
112
126
|
},
|
|
113
127
|
},
|
|
114
128
|
});
|
|
129
|
+
// Partial unique index: only docs with a string tradeKey are indexed,
|
|
130
|
+
// so existing docs (no field) don't collide. Guarantees at most one StonexFXTrade
|
|
131
|
+
// per caller-supplied trade key.
|
|
132
|
+
StonexFXTradeSchema.index({ tradeKey: 1 }, {
|
|
133
|
+
unique: true,
|
|
134
|
+
partialFilterExpression: { tradeKey: { $type: "string" } },
|
|
135
|
+
});
|
|
136
|
+
StonexFXTradeSchema.index({ takeQuoteTradeKey: 1 }, {
|
|
137
|
+
unique: true,
|
|
138
|
+
partialFilterExpression: {
|
|
139
|
+
takeQuoteTradeKey: { $type: "string" },
|
|
140
|
+
},
|
|
141
|
+
});
|
|
115
142
|
StonexFXTradeSchema.statics.build = (attrs) => {
|
|
116
143
|
return new StonexFXTrade(attrs);
|
|
117
144
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riocrypto/common-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2769",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@google-cloud/secret-manager": "^5.6.0",
|
|
25
25
|
"@google-cloud/storage": "^7.19.0",
|
|
26
26
|
"@hyperdx/node-opentelemetry": "^0.10.3",
|
|
27
|
-
"@riocrypto/common": "1.0.
|
|
27
|
+
"@riocrypto/common": "1.0.2566",
|
|
28
28
|
"@slack/web-api": "^7.15.0",
|
|
29
29
|
"@types/express": "^4.17.25",
|
|
30
30
|
"axios": "1.13.6",
|