@tradejs/node 3.1.29-beta.288 → 3.1.29-beta.290
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/dist/backtest.js +442 -234
- package/dist/backtest.mjs +10 -5
- package/dist/{chunk-F7CLF3PT.mjs → chunk-PB2KHBGH.mjs} +415 -212
- package/dist/cli.js +3 -2
- package/dist/cli.mjs +3 -2
- package/dist/diagnostics.js +416 -213
- package/dist/diagnostics.mjs +1 -1
- package/dist/mcp.d.mts +3 -1
- package/dist/mcp.d.ts +3 -1
- package/dist/mcp.js +455 -230
- package/dist/mcp.mjs +24 -3
- package/dist/registry.js +416 -213
- package/dist/registry.mjs +1 -1
- package/dist/runtimeDashboard.js +418 -215
- package/dist/runtimeDashboard.mjs +1 -1
- package/dist/runtimeStrategies.js +416 -213
- package/dist/runtimeStrategies.mjs +1 -1
- package/dist/strategies.d.mts +32 -22
- package/dist/strategies.d.ts +32 -22
- package/dist/strategies.js +424 -221
- package/dist/strategies.mjs +1 -1
- package/package.json +5 -5
|
@@ -16,6 +16,203 @@ import {
|
|
|
16
16
|
assessSignalWithJev
|
|
17
17
|
} from "./chunk-YR55Q355.mjs";
|
|
18
18
|
|
|
19
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
20
|
+
import { logger } from "@tradejs/infra/logger";
|
|
21
|
+
|
|
22
|
+
// src/strategy/entryExecutionValidation.ts
|
|
23
|
+
var EntrySubmissionValidationError = class extends Error {
|
|
24
|
+
constructor() {
|
|
25
|
+
super(...arguments);
|
|
26
|
+
this.name = "EntrySubmissionValidationError";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var resolveEntryExecutionLimits = (config, direction) => {
|
|
30
|
+
const side = config[direction];
|
|
31
|
+
const minimum = side && typeof side === "object" && "minRiskRatio" in side ? side.minRiskRatio : void 0;
|
|
32
|
+
const maximum = config.MAX_LOSS_VALUE;
|
|
33
|
+
if (minimum == null && maximum == null) return void 0;
|
|
34
|
+
const minimumNetRiskRatio = minimum == null ? void 0 : Number(minimum);
|
|
35
|
+
const maxLossValue = maximum == null ? void 0 : Number(maximum);
|
|
36
|
+
const feeRate = Number(config.RISK_FEE_RATE ?? 0);
|
|
37
|
+
const slippage = Number(config.RISK_SLIPPAGE_BPS ?? 0);
|
|
38
|
+
const impact = Number(config.RISK_MARKET_IMPACT_BPS ?? 0);
|
|
39
|
+
const slippageBps = slippage + impact;
|
|
40
|
+
if (minimumNetRiskRatio != null && (!Number.isFinite(minimumNetRiskRatio) || minimumNetRiskRatio < 0) || maxLossValue != null && (!Number.isFinite(maxLossValue) || maxLossValue <= 0) || !Number.isFinite(feeRate) || feeRate < 0 || feeRate >= 1 || !Number.isFinite(slippage) || slippage < 0 || !Number.isFinite(impact) || impact < 0 || !Number.isFinite(slippageBps) || slippageBps < 0 || slippageBps >= 1e4)
|
|
41
|
+
throw new Error("INVALID_ENTRY_EXECUTION_LIMITS");
|
|
42
|
+
return { minimumNetRiskRatio, maxLossValue, feeRate, slippageBps };
|
|
43
|
+
};
|
|
44
|
+
var validateEntryExecution = ({
|
|
45
|
+
direction,
|
|
46
|
+
price,
|
|
47
|
+
qty,
|
|
48
|
+
stopLossPrice,
|
|
49
|
+
takeProfitPrice,
|
|
50
|
+
limits
|
|
51
|
+
}) => {
|
|
52
|
+
if (!Number.isFinite(price) || price <= 0 || !Number.isFinite(qty) || qty <= 0 || stopLossPrice == null || !Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || !Number.isFinite(takeProfitPrice) || takeProfitPrice <= 0)
|
|
53
|
+
throw new EntrySubmissionValidationError(
|
|
54
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
55
|
+
);
|
|
56
|
+
const side = direction === "LONG" ? 1 : -1;
|
|
57
|
+
if (side * (price - stopLossPrice) <= 0 || side * (takeProfitPrice - price) <= 0)
|
|
58
|
+
throw new EntrySubmissionValidationError(
|
|
59
|
+
"INVALID_ENTRY_PROTECTION_AT_SUBMISSION"
|
|
60
|
+
);
|
|
61
|
+
const slippage = limits.slippageBps / 1e4;
|
|
62
|
+
const cost = (value, sign) => value * slippage + value * (1 + sign * slippage) * limits.feeRate;
|
|
63
|
+
const entryCost = cost(price, side);
|
|
64
|
+
const lossPerUnit = Math.abs(price - stopLossPrice) + entryCost + cost(stopLossPrice, -side);
|
|
65
|
+
const rewardPerUnit = Math.max(
|
|
66
|
+
0,
|
|
67
|
+
Math.abs(takeProfitPrice - price) - entryCost - cost(takeProfitPrice, -side)
|
|
68
|
+
);
|
|
69
|
+
const netRiskRatio = rewardPerUnit / lossPerUnit;
|
|
70
|
+
const plannedLossValue = qty * lossPerUnit;
|
|
71
|
+
if (!Number.isFinite(netRiskRatio) || !Number.isFinite(plannedLossValue))
|
|
72
|
+
throw new EntrySubmissionValidationError(
|
|
73
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
74
|
+
);
|
|
75
|
+
if (limits.minimumNetRiskRatio != null && netRiskRatio <= limits.minimumNetRiskRatio)
|
|
76
|
+
throw new EntrySubmissionValidationError(
|
|
77
|
+
"ENTRY_RR_BELOW_MIN_AT_SUBMISSION"
|
|
78
|
+
);
|
|
79
|
+
const riskExceeded = limits.maxLossValue != null && plannedLossValue > limits.maxLossValue * (1 + 1e-9);
|
|
80
|
+
return { netRiskRatio, plannedLossValue, riskExceeded };
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
84
|
+
var MAX_ENTRY_QUOTE_AGE_MS = 5e3;
|
|
85
|
+
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
86
|
+
var getOrderArrivalSnapshot = async ({
|
|
87
|
+
connector,
|
|
88
|
+
symbol,
|
|
89
|
+
requireTimestamp = false
|
|
90
|
+
}) => {
|
|
91
|
+
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
92
|
+
return {
|
|
93
|
+
arrivalSnapshotTime: Date.now(),
|
|
94
|
+
arrivalSource: "unavailable",
|
|
95
|
+
bid: null,
|
|
96
|
+
ask: null,
|
|
97
|
+
arrivalMid: null,
|
|
98
|
+
spreadBps: null
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
103
|
+
const arrivalSnapshotTime = toFiniteNumberOrNull(ticker?.timestamp);
|
|
104
|
+
const timestampUnavailable = requireTimestamp && arrivalSnapshotTime == null;
|
|
105
|
+
const bid = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.bidPrice);
|
|
106
|
+
const ask = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.askPrice);
|
|
107
|
+
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
108
|
+
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
109
|
+
return {
|
|
110
|
+
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
111
|
+
arrivalSource: "top_of_book",
|
|
112
|
+
bid,
|
|
113
|
+
ask,
|
|
114
|
+
arrivalMid,
|
|
115
|
+
spreadBps
|
|
116
|
+
};
|
|
117
|
+
} catch (error) {
|
|
118
|
+
logger.warn(
|
|
119
|
+
"runtime order arrival snapshot failed: %s %s",
|
|
120
|
+
symbol,
|
|
121
|
+
error?.message || String(error)
|
|
122
|
+
);
|
|
123
|
+
return {
|
|
124
|
+
arrivalSnapshotTime: Date.now(),
|
|
125
|
+
arrivalSource: "top_of_book_error",
|
|
126
|
+
bid: null,
|
|
127
|
+
ask: null,
|
|
128
|
+
arrivalMid: null,
|
|
129
|
+
spreadBps: null
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
var validateEntryProtectionAtArrival = ({
|
|
134
|
+
direction,
|
|
135
|
+
signalPrice,
|
|
136
|
+
bid,
|
|
137
|
+
ask,
|
|
138
|
+
arrivalMid,
|
|
139
|
+
takeProfits,
|
|
140
|
+
stopLossPrice
|
|
141
|
+
}) => {
|
|
142
|
+
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
143
|
+
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
144
|
+
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
145
|
+
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
146
|
+
}
|
|
147
|
+
for (const takeProfit of takeProfits) {
|
|
148
|
+
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
149
|
+
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
153
|
+
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
var createEntrySubmissionValidation = ({
|
|
157
|
+
connector,
|
|
158
|
+
symbol,
|
|
159
|
+
direction,
|
|
160
|
+
takeProfits,
|
|
161
|
+
stopLossPrice,
|
|
162
|
+
limits,
|
|
163
|
+
onValidated
|
|
164
|
+
}) => {
|
|
165
|
+
if (!limits) return void 0;
|
|
166
|
+
if (!connector.capabilities?.orderSubmissionValidation)
|
|
167
|
+
throw new EntrySubmissionValidationError(
|
|
168
|
+
"ENTRY_SUBMISSION_VALIDATION_UNSUPPORTED"
|
|
169
|
+
);
|
|
170
|
+
return async ({ qty, executionPrice }) => {
|
|
171
|
+
const simulated = Reflect.get(connector, "__tradejsTestConnector") === true;
|
|
172
|
+
const snapshot2 = simulated && executionPrice != null ? {
|
|
173
|
+
arrivalSnapshotTime: Date.now(),
|
|
174
|
+
arrivalSource: "simulation",
|
|
175
|
+
bid: executionPrice,
|
|
176
|
+
ask: executionPrice,
|
|
177
|
+
arrivalMid: executionPrice,
|
|
178
|
+
spreadBps: 0
|
|
179
|
+
} : await getOrderArrivalSnapshot({
|
|
180
|
+
connector,
|
|
181
|
+
symbol,
|
|
182
|
+
requireTimestamp: true
|
|
183
|
+
});
|
|
184
|
+
const age = Date.now() - snapshot2.arrivalSnapshotTime;
|
|
185
|
+
if (snapshot2.bid == null || snapshot2.ask == null || !Number.isFinite(snapshot2.bid) || !Number.isFinite(snapshot2.ask) || snapshot2.bid <= 0 || snapshot2.ask < snapshot2.bid)
|
|
186
|
+
throw new EntrySubmissionValidationError(
|
|
187
|
+
"ENTRY_QUOTE_UNAVAILABLE_AT_SUBMISSION"
|
|
188
|
+
);
|
|
189
|
+
if (!Number.isFinite(age) || age < -MAX_ENTRY_QUOTE_AGE_MS || age > MAX_ENTRY_QUOTE_AGE_MS)
|
|
190
|
+
throw new EntrySubmissionValidationError(
|
|
191
|
+
"ENTRY_QUOTE_STALE_AT_SUBMISSION"
|
|
192
|
+
);
|
|
193
|
+
const price = direction === "LONG" ? snapshot2.ask : snapshot2.bid;
|
|
194
|
+
validateEntryProtectionAtArrival({
|
|
195
|
+
direction,
|
|
196
|
+
signalPrice: price,
|
|
197
|
+
bid: snapshot2.bid,
|
|
198
|
+
ask: snapshot2.ask,
|
|
199
|
+
arrivalMid: snapshot2.arrivalMid,
|
|
200
|
+
takeProfits,
|
|
201
|
+
stopLossPrice
|
|
202
|
+
});
|
|
203
|
+
const prices = takeProfits.map((tp) => tp.price);
|
|
204
|
+
const economics = validateEntryExecution({
|
|
205
|
+
direction,
|
|
206
|
+
price,
|
|
207
|
+
qty,
|
|
208
|
+
stopLossPrice,
|
|
209
|
+
takeProfitPrice: direction === "LONG" ? Math.max(...prices) : Math.min(...prices),
|
|
210
|
+
limits
|
|
211
|
+
});
|
|
212
|
+
onValidated?.(snapshot2, economics);
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
|
|
19
216
|
// src/binanceBreadthUniverses.ts
|
|
20
217
|
import { createHash } from "crypto";
|
|
21
218
|
|
|
@@ -328,7 +525,7 @@ import {
|
|
|
328
525
|
getLatestMarketBreadth,
|
|
329
526
|
getLatestMarketTradeFlow
|
|
330
527
|
} from "@tradejs/infra/timescale/marketContext";
|
|
331
|
-
import { logger } from "@tradejs/infra/logger";
|
|
528
|
+
import { logger as logger2 } from "@tradejs/infra/logger";
|
|
332
529
|
import { refreshSignalBaseContextGateFeatures } from "@tradejs/core/strategies";
|
|
333
530
|
|
|
334
531
|
// src/strategyHelpers/marketContextErrors.ts
|
|
@@ -399,7 +596,7 @@ var parseEnabledFlag = (value, env) => {
|
|
|
399
596
|
if (normalized === "live") return env !== "BACKTEST";
|
|
400
597
|
return false;
|
|
401
598
|
};
|
|
402
|
-
var
|
|
599
|
+
var toFiniteNumberOrNull2 = (value) => {
|
|
403
600
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
404
601
|
return Number.isFinite(numeric) ? numeric : null;
|
|
405
602
|
};
|
|
@@ -664,14 +861,14 @@ var toTradeFlowContext = (row, interval) => row ? {
|
|
|
664
861
|
asOfTs: row.ts.getTime(),
|
|
665
862
|
ageMs: row.ageMs,
|
|
666
863
|
stale: row.stale,
|
|
667
|
-
trades:
|
|
668
|
-
buyPressurePct:
|
|
669
|
-
buyBaseVolume:
|
|
670
|
-
sellBaseVolume:
|
|
671
|
-
buyQuoteVolume:
|
|
672
|
-
sellQuoteVolume:
|
|
673
|
-
netBaseDelta:
|
|
674
|
-
netQuoteDelta:
|
|
864
|
+
trades: toFiniteNumberOrNull2(row.trades),
|
|
865
|
+
buyPressurePct: toFiniteNumberOrNull2(row.buyPressurePct),
|
|
866
|
+
buyBaseVolume: toFiniteNumberOrNull2(row.buyBaseVolume),
|
|
867
|
+
sellBaseVolume: toFiniteNumberOrNull2(row.sellBaseVolume),
|
|
868
|
+
buyQuoteVolume: toFiniteNumberOrNull2(row.buyQuoteVolume),
|
|
869
|
+
sellQuoteVolume: toFiniteNumberOrNull2(row.sellQuoteVolume),
|
|
870
|
+
netBaseDelta: toFiniteNumberOrNull2(row.netBaseDelta),
|
|
871
|
+
netQuoteDelta: toFiniteNumberOrNull2(row.netQuoteDelta)
|
|
675
872
|
} : null;
|
|
676
873
|
var getCachedReferenceRows = ({
|
|
677
874
|
referenceSymbols,
|
|
@@ -760,16 +957,16 @@ var toMarketBreadthContext = (breadth, interval) => ({
|
|
|
760
957
|
asOfTs: breadth.ts.getTime(),
|
|
761
958
|
ageMs: breadth.ageMs,
|
|
762
959
|
stale: breadth.stale,
|
|
763
|
-
symbolsCount:
|
|
764
|
-
advancers:
|
|
765
|
-
decliners:
|
|
766
|
-
unchanged:
|
|
767
|
-
advanceDeclineRatio:
|
|
768
|
-
pctAboveMa20:
|
|
769
|
-
pctAboveMa50:
|
|
770
|
-
equalWeightedReturn:
|
|
771
|
-
volumeWeightedReturn:
|
|
772
|
-
dispersion:
|
|
960
|
+
symbolsCount: toFiniteNumberOrNull2(breadth.symbolsCount),
|
|
961
|
+
advancers: toFiniteNumberOrNull2(breadth.advancers),
|
|
962
|
+
decliners: toFiniteNumberOrNull2(breadth.decliners),
|
|
963
|
+
unchanged: toFiniteNumberOrNull2(breadth.unchanged),
|
|
964
|
+
advanceDeclineRatio: toFiniteNumberOrNull2(breadth.advanceDeclineRatio),
|
|
965
|
+
pctAboveMa20: toFiniteNumberOrNull2(breadth.pctAboveMa20),
|
|
966
|
+
pctAboveMa50: toFiniteNumberOrNull2(breadth.pctAboveMa50),
|
|
967
|
+
equalWeightedReturn: toFiniteNumberOrNull2(breadth.equalWeightedReturn),
|
|
968
|
+
volumeWeightedReturn: toFiniteNumberOrNull2(breadth.volumeWeightedReturn),
|
|
969
|
+
dispersion: toFiniteNumberOrNull2(breadth.dispersion)
|
|
773
970
|
});
|
|
774
971
|
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
775
972
|
const {
|
|
@@ -870,46 +1067,46 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
870
1067
|
asOfTs: primaryBreadth.ts.getTime(),
|
|
871
1068
|
ageMs: primaryBreadth.ageMs,
|
|
872
1069
|
stale: primaryBreadth.stale,
|
|
873
|
-
btcReturn1h:
|
|
1070
|
+
btcReturn1h: toFiniteNumberOrNull2(
|
|
874
1071
|
primaryBreadth.btcReturn1h
|
|
875
1072
|
),
|
|
876
|
-
btcReturn4h:
|
|
1073
|
+
btcReturn4h: toFiniteNumberOrNull2(
|
|
877
1074
|
primaryBreadth.btcReturn4h
|
|
878
1075
|
),
|
|
879
|
-
btcReturn24h:
|
|
1076
|
+
btcReturn24h: toFiniteNumberOrNull2(
|
|
880
1077
|
primaryBreadth.btcReturn24h
|
|
881
1078
|
),
|
|
882
|
-
altBasketReturn1h:
|
|
1079
|
+
altBasketReturn1h: toFiniteNumberOrNull2(
|
|
883
1080
|
primaryBreadth.altBasketReturn1h
|
|
884
1081
|
),
|
|
885
|
-
altBasketReturn4h:
|
|
1082
|
+
altBasketReturn4h: toFiniteNumberOrNull2(
|
|
886
1083
|
primaryBreadth.altBasketReturn4h
|
|
887
1084
|
),
|
|
888
|
-
altBasketReturn24h:
|
|
1085
|
+
altBasketReturn24h: toFiniteNumberOrNull2(
|
|
889
1086
|
primaryBreadth.altBasketReturn24h
|
|
890
1087
|
),
|
|
891
|
-
btcVsAltReturn1h:
|
|
1088
|
+
btcVsAltReturn1h: toFiniteNumberOrNull2(
|
|
892
1089
|
primaryBreadth.btcVsAltReturn1h
|
|
893
1090
|
),
|
|
894
|
-
btcVsAltReturn4h:
|
|
1091
|
+
btcVsAltReturn4h: toFiniteNumberOrNull2(
|
|
895
1092
|
primaryBreadth.btcVsAltReturn4h
|
|
896
1093
|
),
|
|
897
|
-
btcVsAltReturn24h:
|
|
1094
|
+
btcVsAltReturn24h: toFiniteNumberOrNull2(
|
|
898
1095
|
primaryBreadth.btcVsAltReturn24h
|
|
899
1096
|
),
|
|
900
|
-
btcTurnoverShare1h:
|
|
1097
|
+
btcTurnoverShare1h: toFiniteNumberOrNull2(
|
|
901
1098
|
primaryBreadth.btcTurnoverShare1h
|
|
902
1099
|
),
|
|
903
|
-
btcTurnoverShare24h:
|
|
1100
|
+
btcTurnoverShare24h: toFiniteNumberOrNull2(
|
|
904
1101
|
primaryBreadth.btcTurnoverShare24h
|
|
905
1102
|
),
|
|
906
|
-
btcTurnoverShareChange24h:
|
|
1103
|
+
btcTurnoverShareChange24h: toFiniteNumberOrNull2(
|
|
907
1104
|
primaryBreadth.btcTurnoverShareChange24h
|
|
908
1105
|
),
|
|
909
|
-
altVolToBtcVol24h:
|
|
1106
|
+
altVolToBtcVol24h: toFiniteNumberOrNull2(
|
|
910
1107
|
primaryBreadth.altVolToBtcVol24h
|
|
911
1108
|
),
|
|
912
|
-
altDispersion24h:
|
|
1109
|
+
altDispersion24h: toFiniteNumberOrNull2(
|
|
913
1110
|
primaryBreadth.altDispersion24h
|
|
914
1111
|
),
|
|
915
1112
|
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
@@ -926,7 +1123,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
926
1123
|
throw error;
|
|
927
1124
|
}
|
|
928
1125
|
binanceMarketContextUnavailable = true;
|
|
929
|
-
|
|
1126
|
+
logger2.warn(
|
|
930
1127
|
"Binance market context disabled after Timescale read failure: %s",
|
|
931
1128
|
String(error)
|
|
932
1129
|
);
|
|
@@ -936,7 +1133,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
936
1133
|
|
|
937
1134
|
// src/strategyHelpers/coinMarketCapContext.ts
|
|
938
1135
|
import { refreshSignalBaseContextGateFeatures as refreshSignalBaseContextGateFeatures2 } from "@tradejs/core/strategies";
|
|
939
|
-
import { logger as
|
|
1136
|
+
import { logger as logger3 } from "@tradejs/infra/logger";
|
|
940
1137
|
import {
|
|
941
1138
|
getLatestMarketCmcExchangeLiquidityContext,
|
|
942
1139
|
getLatestMarketCmcFearGreedContext,
|
|
@@ -980,7 +1177,7 @@ var asInt = (value, fallback) => {
|
|
|
980
1177
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
981
1178
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
982
1179
|
};
|
|
983
|
-
var
|
|
1180
|
+
var toFiniteNumberOrNull3 = (value) => {
|
|
984
1181
|
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
985
1182
|
return Number.isFinite(numeric) ? numeric : null;
|
|
986
1183
|
};
|
|
@@ -1214,12 +1411,12 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
1214
1411
|
const ethRow = references.get("ETHUSDT") ?? null;
|
|
1215
1412
|
const previousBtcRow = previousReferences.get("BTCUSDT") ?? null;
|
|
1216
1413
|
const previousEthRow = previousReferences.get("ETHUSDT") ?? null;
|
|
1217
|
-
const btcMarketCapUsd =
|
|
1218
|
-
const ethMarketCapUsd =
|
|
1219
|
-
const previousBtcMarketCapUsd =
|
|
1414
|
+
const btcMarketCapUsd = toFiniteNumberOrNull3(btcRow?.marketCapUsd);
|
|
1415
|
+
const ethMarketCapUsd = toFiniteNumberOrNull3(ethRow?.marketCapUsd);
|
|
1416
|
+
const previousBtcMarketCapUsd = toFiniteNumberOrNull3(
|
|
1220
1417
|
previousBtcRow?.marketCapUsd
|
|
1221
1418
|
);
|
|
1222
|
-
const previousEthMarketCapUsd =
|
|
1419
|
+
const previousEthMarketCapUsd = toFiniteNumberOrNull3(
|
|
1223
1420
|
previousEthRow?.marketCapUsd
|
|
1224
1421
|
);
|
|
1225
1422
|
const ethBtcMarketCapRatio = safeDivide(ethMarketCapUsd, btcMarketCapUsd);
|
|
@@ -1228,16 +1425,16 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
1228
1425
|
previousBtcMarketCapUsd
|
|
1229
1426
|
);
|
|
1230
1427
|
const ethBtcMarketCapRatioChange24hPct = ethBtcMarketCapRatio != null && previousEthBtcMarketCapRatio != null && previousEthBtcMarketCapRatio > 0 ? (ethBtcMarketCapRatio - previousEthBtcMarketCapRatio) / previousEthBtcMarketCapRatio : null;
|
|
1231
|
-
const btcVolumeUsd =
|
|
1232
|
-
const ethVolumeUsd =
|
|
1428
|
+
const btcVolumeUsd = toFiniteNumberOrNull3(btcRow?.volumeUsd);
|
|
1429
|
+
const ethVolumeUsd = toFiniteNumberOrNull3(ethRow?.volumeUsd);
|
|
1233
1430
|
const referenceStale = btcRow?.stale === true || ethRow?.stale === true || !btcRow || !ethRow;
|
|
1234
|
-
const btcDominanceChange24hPct =
|
|
1431
|
+
const btcDominanceChange24hPct = toFiniteNumberOrNull3(
|
|
1235
1432
|
globalRow?.btcDominanceChange24hPct
|
|
1236
1433
|
);
|
|
1237
|
-
const altMarketCapChange24hPct =
|
|
1434
|
+
const altMarketCapChange24hPct = toFiniteNumberOrNull3(
|
|
1238
1435
|
globalRow?.altMarketCapChange24hPct
|
|
1239
1436
|
);
|
|
1240
|
-
const altVolumeChange24hPct =
|
|
1437
|
+
const altVolumeChange24hPct = toFiniteNumberOrNull3(
|
|
1241
1438
|
globalRow?.altVolumeChange24hPct
|
|
1242
1439
|
);
|
|
1243
1440
|
const altLiquidityRegime = globalRow ? toAltLiquidityRegime({
|
|
@@ -1248,19 +1445,19 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
1248
1445
|
}) : "unknown";
|
|
1249
1446
|
const exchangeLiquidityRegime = exchangeLiquidityRow ? toExchangeLiquidityRegime({
|
|
1250
1447
|
stale: exchangeLiquidityRow.stale,
|
|
1251
|
-
totalVolumeChange24hPct:
|
|
1448
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
1252
1449
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
1253
1450
|
),
|
|
1254
1451
|
fallback: exchangeLiquidityRow.liquidityRegime ?? "unknown"
|
|
1255
1452
|
}) : "unknown";
|
|
1256
1453
|
const cmc100Row = indexRows.get("cmc100") ?? null;
|
|
1257
1454
|
const cmc20Row = indexRows.get("cmc20") ?? null;
|
|
1258
|
-
const cmc100Value =
|
|
1259
|
-
const cmc20Value =
|
|
1260
|
-
const cmc100Change24hPct =
|
|
1455
|
+
const cmc100Value = toFiniteNumberOrNull3(cmc100Row?.value);
|
|
1456
|
+
const cmc20Value = toFiniteNumberOrNull3(cmc20Row?.value);
|
|
1457
|
+
const cmc100Change24hPct = toFiniteNumberOrNull3(
|
|
1261
1458
|
cmc100Row?.valueChange24hPct
|
|
1262
1459
|
);
|
|
1263
|
-
const cmc20Change24hPct =
|
|
1460
|
+
const cmc20Change24hPct = toFiniteNumberOrNull3(cmc20Row?.valueChange24hPct);
|
|
1264
1461
|
const cmc20ToCmc100Ratio = safeDivide(cmc20Value, cmc100Value);
|
|
1265
1462
|
const cmc20ToCmc100RatioChange24hPct = cmc20Change24hPct != null && cmc100Change24hPct != null ? (1 + cmc20Change24hPct) / (1 + cmc100Change24hPct) - 1 : null;
|
|
1266
1463
|
const indexStale = cmc100Row?.stale === true || cmc20Row?.stale === true || !cmc100Row || !cmc20Row;
|
|
@@ -1284,41 +1481,41 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
1284
1481
|
asOfTs: globalRow.ts.getTime(),
|
|
1285
1482
|
ageMs: globalRow.ageMs,
|
|
1286
1483
|
stale: globalRow.stale,
|
|
1287
|
-
totalMarketCapUsd:
|
|
1484
|
+
totalMarketCapUsd: toFiniteNumberOrNull3(
|
|
1288
1485
|
globalRow.totalMarketCapUsd
|
|
1289
1486
|
),
|
|
1290
|
-
totalVolumeUsd:
|
|
1487
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
1291
1488
|
globalRow.totalVolumeUsd
|
|
1292
1489
|
),
|
|
1293
|
-
totalVolumeReportedUsd:
|
|
1490
|
+
totalVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
1294
1491
|
globalRow.totalVolumeReportedUsd
|
|
1295
1492
|
),
|
|
1296
|
-
altMarketCapUsd:
|
|
1493
|
+
altMarketCapUsd: toFiniteNumberOrNull3(
|
|
1297
1494
|
globalRow.altMarketCapUsd
|
|
1298
1495
|
),
|
|
1299
|
-
altVolumeUsd:
|
|
1300
|
-
altVolumeReportedUsd:
|
|
1496
|
+
altVolumeUsd: toFiniteNumberOrNull3(globalRow.altVolumeUsd),
|
|
1497
|
+
altVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
1301
1498
|
globalRow.altVolumeReportedUsd
|
|
1302
1499
|
),
|
|
1303
|
-
btcDominancePct:
|
|
1500
|
+
btcDominancePct: toFiniteNumberOrNull3(
|
|
1304
1501
|
globalRow.btcDominancePct
|
|
1305
1502
|
),
|
|
1306
|
-
ethDominancePct:
|
|
1503
|
+
ethDominancePct: toFiniteNumberOrNull3(
|
|
1307
1504
|
globalRow.ethDominancePct
|
|
1308
1505
|
),
|
|
1309
1506
|
btcDominanceChange24hPct,
|
|
1310
|
-
ethDominanceChange24hPct:
|
|
1507
|
+
ethDominanceChange24hPct: toFiniteNumberOrNull3(
|
|
1311
1508
|
globalRow.ethDominanceChange24hPct
|
|
1312
1509
|
),
|
|
1313
1510
|
altMarketCapChange24hPct,
|
|
1314
1511
|
altVolumeChange24hPct,
|
|
1315
|
-
activeCryptocurrencies:
|
|
1512
|
+
activeCryptocurrencies: toFiniteNumberOrNull3(
|
|
1316
1513
|
globalRow.activeCryptocurrencies
|
|
1317
1514
|
),
|
|
1318
|
-
activeExchanges:
|
|
1515
|
+
activeExchanges: toFiniteNumberOrNull3(
|
|
1319
1516
|
globalRow.activeExchanges
|
|
1320
1517
|
),
|
|
1321
|
-
activeMarketPairs:
|
|
1518
|
+
activeMarketPairs: toFiniteNumberOrNull3(
|
|
1322
1519
|
globalRow.activeMarketPairs
|
|
1323
1520
|
),
|
|
1324
1521
|
altLiquidityRegime
|
|
@@ -1363,22 +1560,22 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
1363
1560
|
asOfTs: exchangeLiquidityRow.ts.getTime(),
|
|
1364
1561
|
ageMs: exchangeLiquidityRow.ageMs,
|
|
1365
1562
|
stale: exchangeLiquidityRow.stale,
|
|
1366
|
-
exchangesCount:
|
|
1563
|
+
exchangesCount: toFiniteNumberOrNull3(
|
|
1367
1564
|
exchangeLiquidityRow.exchangesCount
|
|
1368
1565
|
),
|
|
1369
|
-
totalVolumeUsd:
|
|
1566
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
1370
1567
|
exchangeLiquidityRow.totalVolumeUsd
|
|
1371
1568
|
),
|
|
1372
|
-
totalVolumeChange24hPct:
|
|
1569
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
1373
1570
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
1374
1571
|
),
|
|
1375
|
-
binanceVolumeUsd:
|
|
1572
|
+
binanceVolumeUsd: toFiniteNumberOrNull3(
|
|
1376
1573
|
exchangeLiquidityRow.binanceVolumeUsd
|
|
1377
1574
|
),
|
|
1378
|
-
binanceVolumeShare:
|
|
1575
|
+
binanceVolumeShare: toFiniteNumberOrNull3(
|
|
1379
1576
|
exchangeLiquidityRow.binanceVolumeShare
|
|
1380
1577
|
),
|
|
1381
|
-
topExchangeVolumeShare:
|
|
1578
|
+
topExchangeVolumeShare: toFiniteNumberOrNull3(
|
|
1382
1579
|
exchangeLiquidityRow.topExchangeVolumeShare
|
|
1383
1580
|
),
|
|
1384
1581
|
liquidityRegime: exchangeLiquidityRegime
|
|
@@ -1391,11 +1588,11 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
1391
1588
|
asOfTs: fearGreedRow.ts.getTime(),
|
|
1392
1589
|
ageMs: fearGreedRow.ageMs,
|
|
1393
1590
|
stale: fearGreedRow.stale,
|
|
1394
|
-
value:
|
|
1395
|
-
valueChange24h:
|
|
1591
|
+
value: toFiniteNumberOrNull3(fearGreedRow.value),
|
|
1592
|
+
valueChange24h: toFiniteNumberOrNull3(
|
|
1396
1593
|
fearGreedRow.valueChange24h
|
|
1397
1594
|
),
|
|
1398
|
-
valueChange7d:
|
|
1595
|
+
valueChange7d: toFiniteNumberOrNull3(
|
|
1399
1596
|
fearGreedRow.valueChange7d
|
|
1400
1597
|
),
|
|
1401
1598
|
classification: fearGreedRow.classification ?? "Unknown",
|
|
@@ -1415,13 +1612,13 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
1415
1612
|
cmc100Value,
|
|
1416
1613
|
cmc100Change24hPct,
|
|
1417
1614
|
cmc100TopConstituentSymbol: cmc100Row?.topConstituentSymbol ?? null,
|
|
1418
|
-
cmc100TopConstituentWeightPct:
|
|
1615
|
+
cmc100TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
1419
1616
|
cmc100Row?.topConstituentWeightPct
|
|
1420
1617
|
),
|
|
1421
1618
|
cmc20Value,
|
|
1422
1619
|
cmc20Change24hPct,
|
|
1423
1620
|
cmc20TopConstituentSymbol: cmc20Row?.topConstituentSymbol ?? null,
|
|
1424
|
-
cmc20TopConstituentWeightPct:
|
|
1621
|
+
cmc20TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
1425
1622
|
cmc20Row?.topConstituentWeightPct
|
|
1426
1623
|
),
|
|
1427
1624
|
cmc20ToCmc100Ratio,
|
|
@@ -1439,7 +1636,7 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
1439
1636
|
throw error;
|
|
1440
1637
|
}
|
|
1441
1638
|
coinMarketCapContextUnavailable = true;
|
|
1442
|
-
|
|
1639
|
+
logger3.warn(
|
|
1443
1640
|
"CoinMarketCap context disabled after Timescale read failure: %s",
|
|
1444
1641
|
String(error)
|
|
1445
1642
|
);
|
|
@@ -1673,7 +1870,7 @@ import {
|
|
|
1673
1870
|
getHyperliquidWhaleFlowAggregate,
|
|
1674
1871
|
getHyperliquidWhaleFlowSeriesRows
|
|
1675
1872
|
} from "@tradejs/infra/timescale/hyperliquidWhales";
|
|
1676
|
-
import { logger as
|
|
1873
|
+
import { logger as logger4 } from "@tradejs/infra/logger";
|
|
1677
1874
|
var MAX_CACHE_ENTRIES = 2048;
|
|
1678
1875
|
var MAX_COVERAGE_SERIES_CACHE_ENTRIES = 20;
|
|
1679
1876
|
var MAX_FLOW_SERIES_CACHE_ENTRIES = 64;
|
|
@@ -1977,7 +2174,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
1977
2174
|
throw error;
|
|
1978
2175
|
}
|
|
1979
2176
|
hyperliquidWhaleContextUnavailable = true;
|
|
1980
|
-
|
|
2177
|
+
logger4.warn(
|
|
1981
2178
|
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
1982
2179
|
String(error)
|
|
1983
2180
|
);
|
|
@@ -2016,7 +2213,7 @@ var enrichSignalWithHyperliquidWhaleContext = async (params) => {
|
|
|
2016
2213
|
};
|
|
2017
2214
|
|
|
2018
2215
|
// src/strategyHelpers/runtime.ts
|
|
2019
|
-
import { logger as
|
|
2216
|
+
import { logger as logger8 } from "@tradejs/infra/logger";
|
|
2020
2217
|
import { FEE_PERCENT } from "@tradejs/core/constants";
|
|
2021
2218
|
import {
|
|
2022
2219
|
buildMlFeatures,
|
|
@@ -2078,7 +2275,7 @@ import { randomUUID } from "crypto";
|
|
|
2078
2275
|
import { TTL_1M } from "@tradejs/core/constants";
|
|
2079
2276
|
import { getRuntimeStorageDayKey } from "@tradejs/core/time";
|
|
2080
2277
|
import { createRuntimeOrderLinkPrefix } from "@tradejs/core/trade";
|
|
2081
|
-
import { logger as
|
|
2278
|
+
import { logger as logger5 } from "@tradejs/infra/logger";
|
|
2082
2279
|
import {
|
|
2083
2280
|
delKey,
|
|
2084
2281
|
getData,
|
|
@@ -2143,7 +2340,7 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
2143
2340
|
)
|
|
2144
2341
|
]);
|
|
2145
2342
|
} catch (error) {
|
|
2146
|
-
|
|
2343
|
+
logger5.error(
|
|
2147
2344
|
"runtime trade open journal failed: %s %s",
|
|
2148
2345
|
record.symbol,
|
|
2149
2346
|
error?.message || String(error)
|
|
@@ -2211,7 +2408,7 @@ var recordRuntimeTradeIncrease = async (params) => {
|
|
|
2211
2408
|
)
|
|
2212
2409
|
]);
|
|
2213
2410
|
} catch (error) {
|
|
2214
|
-
|
|
2411
|
+
logger5.error(
|
|
2215
2412
|
"runtime trade increase journal failed: %s %s",
|
|
2216
2413
|
symbol,
|
|
2217
2414
|
error?.message || String(error)
|
|
@@ -2318,7 +2515,7 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
2318
2515
|
)
|
|
2319
2516
|
]);
|
|
2320
2517
|
} catch (error) {
|
|
2321
|
-
|
|
2518
|
+
logger5.error(
|
|
2322
2519
|
"runtime trade close journal failed: %s %s",
|
|
2323
2520
|
symbol,
|
|
2324
2521
|
error?.message || String(error)
|
|
@@ -2328,7 +2525,7 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
2328
2525
|
};
|
|
2329
2526
|
|
|
2330
2527
|
// src/strategyHelpers/marketContextStages.ts
|
|
2331
|
-
import { logger as
|
|
2528
|
+
import { logger as logger7 } from "@tradejs/infra/logger";
|
|
2332
2529
|
|
|
2333
2530
|
// src/strategyHelpers/derivativesContext.ts
|
|
2334
2531
|
import {
|
|
@@ -2343,7 +2540,7 @@ import {
|
|
|
2343
2540
|
resolveDerivativesContextReferenceSymbols
|
|
2344
2541
|
} from "@tradejs/core/constants";
|
|
2345
2542
|
import { getDerivativesWindow } from "@tradejs/infra/timescale/derivatives";
|
|
2346
|
-
import { logger as
|
|
2543
|
+
import { logger as logger6 } from "@tradejs/infra/logger";
|
|
2347
2544
|
var STORED_INTERVALS = ["15m", "1h"];
|
|
2348
2545
|
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
2349
2546
|
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
@@ -2398,7 +2595,7 @@ var resolvePrimaryReferenceSymbol2 = () => PRIMARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
|
2398
2595
|
var resolveSecondaryReferenceSymbol = () => SECONDARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
2399
2596
|
var getPrimaryIntervalContext = (context) => context?.intervals["15m"] ?? context?.intervals["1h"] ?? null;
|
|
2400
2597
|
var hasDerivativesSymbolData = (context) => Object.keys(context.intervals).length > 0 && !context.summary.riskFlags.includes("missing_derivatives");
|
|
2401
|
-
var
|
|
2598
|
+
var toFiniteNumberOrNull4 = (value) => {
|
|
2402
2599
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
2403
2600
|
if (typeof value === "string" && value.trim()) {
|
|
2404
2601
|
const parsed = Number(value);
|
|
@@ -2412,8 +2609,8 @@ var roundNullable = (value, digits = 4) => {
|
|
|
2412
2609
|
return Math.round(value * multiplier) / multiplier;
|
|
2413
2610
|
};
|
|
2414
2611
|
var deltaNullable = (targetValue, referenceValue) => {
|
|
2415
|
-
const target =
|
|
2416
|
-
const reference =
|
|
2612
|
+
const target = toFiniteNumberOrNull4(targetValue);
|
|
2613
|
+
const reference = toFiniteNumberOrNull4(referenceValue);
|
|
2417
2614
|
return target == null || reference == null ? null : roundNullable(target - reference);
|
|
2418
2615
|
};
|
|
2419
2616
|
var buildTargetDerivedContext = (params) => {
|
|
@@ -2575,7 +2772,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
2575
2772
|
throw error;
|
|
2576
2773
|
}
|
|
2577
2774
|
derivativesContextUnavailable = true;
|
|
2578
|
-
|
|
2775
|
+
logger6.warn(
|
|
2579
2776
|
"Derivatives context disabled after Timescale read failure: %s",
|
|
2580
2777
|
String(error)
|
|
2581
2778
|
);
|
|
@@ -2628,7 +2825,7 @@ var runMarketContextStage = async ({
|
|
|
2628
2825
|
elapsedMs: Date.now() - startedAt
|
|
2629
2826
|
};
|
|
2630
2827
|
if (status === "timed_out") {
|
|
2631
|
-
|
|
2828
|
+
logger7.warn(
|
|
2632
2829
|
"Market context stage timed out: %s after %sms",
|
|
2633
2830
|
stage,
|
|
2634
2831
|
result.elapsedMs
|
|
@@ -2818,7 +3015,7 @@ var enrichSignalWithAi = async ({
|
|
|
2818
3015
|
signal.aiAnalysis = analysis;
|
|
2819
3016
|
return resolveAiQuality(analysis, direction);
|
|
2820
3017
|
} catch (err) {
|
|
2821
|
-
|
|
3018
|
+
logger8.error("AI analysis error: %s %s", symbol, formatAiError(err));
|
|
2822
3019
|
}
|
|
2823
3020
|
return void 0;
|
|
2824
3021
|
};
|
|
@@ -2835,75 +3032,6 @@ var enrichSignalWithMlAi = async ({
|
|
|
2835
3032
|
await enrichSignalWithMl({ signal, env, ml });
|
|
2836
3033
|
return enrichSignalWithAi({ signal, userName, symbol, direction, env, ai });
|
|
2837
3034
|
};
|
|
2838
|
-
var toFiniteNumberOrNull4 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
2839
|
-
var getOrderArrivalSnapshot = async ({
|
|
2840
|
-
connector,
|
|
2841
|
-
symbol
|
|
2842
|
-
}) => {
|
|
2843
|
-
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
2844
|
-
return {
|
|
2845
|
-
arrivalSnapshotTime: Date.now(),
|
|
2846
|
-
arrivalSource: "unavailable",
|
|
2847
|
-
bid: null,
|
|
2848
|
-
ask: null,
|
|
2849
|
-
arrivalMid: null,
|
|
2850
|
-
spreadBps: null
|
|
2851
|
-
};
|
|
2852
|
-
}
|
|
2853
|
-
try {
|
|
2854
|
-
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
2855
|
-
const arrivalSnapshotTime = toFiniteNumberOrNull4(ticker?.timestamp);
|
|
2856
|
-
const bid = toFiniteNumberOrNull4(ticker?.bidPrice);
|
|
2857
|
-
const ask = toFiniteNumberOrNull4(ticker?.askPrice);
|
|
2858
|
-
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
2859
|
-
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
2860
|
-
return {
|
|
2861
|
-
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
2862
|
-
arrivalSource: "top_of_book",
|
|
2863
|
-
bid,
|
|
2864
|
-
ask,
|
|
2865
|
-
arrivalMid,
|
|
2866
|
-
spreadBps
|
|
2867
|
-
};
|
|
2868
|
-
} catch (error) {
|
|
2869
|
-
logger7.warn(
|
|
2870
|
-
"runtime order arrival snapshot failed: %s %s",
|
|
2871
|
-
symbol,
|
|
2872
|
-
error?.message || String(error)
|
|
2873
|
-
);
|
|
2874
|
-
return {
|
|
2875
|
-
arrivalSnapshotTime: Date.now(),
|
|
2876
|
-
arrivalSource: "top_of_book_error",
|
|
2877
|
-
bid: null,
|
|
2878
|
-
ask: null,
|
|
2879
|
-
arrivalMid: null,
|
|
2880
|
-
spreadBps: null
|
|
2881
|
-
};
|
|
2882
|
-
}
|
|
2883
|
-
};
|
|
2884
|
-
var validateEntryProtectionAtArrival = ({
|
|
2885
|
-
direction,
|
|
2886
|
-
signalPrice,
|
|
2887
|
-
bid,
|
|
2888
|
-
ask,
|
|
2889
|
-
arrivalMid,
|
|
2890
|
-
takeProfits,
|
|
2891
|
-
stopLossPrice
|
|
2892
|
-
}) => {
|
|
2893
|
-
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
2894
|
-
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
2895
|
-
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
2896
|
-
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
2897
|
-
}
|
|
2898
|
-
for (const takeProfit of takeProfits) {
|
|
2899
|
-
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
2900
|
-
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
2901
|
-
}
|
|
2902
|
-
}
|
|
2903
|
-
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
2904
|
-
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
2905
|
-
}
|
|
2906
|
-
};
|
|
2907
3035
|
var resolveRuntimeTelemetryQuality = ({
|
|
2908
3036
|
signalClosePrice,
|
|
2909
3037
|
arrivalMid,
|
|
@@ -2980,7 +3108,8 @@ var executeEntryOrder = async ({
|
|
|
2980
3108
|
signal,
|
|
2981
3109
|
beforePlaceOrder,
|
|
2982
3110
|
recordRuntimeTrade = true,
|
|
2983
|
-
leverage
|
|
3111
|
+
leverage,
|
|
3112
|
+
executionLimits
|
|
2984
3113
|
}) => {
|
|
2985
3114
|
await beforePlaceOrder?.();
|
|
2986
3115
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
@@ -2991,23 +3120,57 @@ var executeEntryOrder = async ({
|
|
|
2991
3120
|
signal.orderQty = qty;
|
|
2992
3121
|
signal.orderValue = qty * currentPrice;
|
|
2993
3122
|
signal.orderFailureReason = void 0;
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3123
|
+
let arrivalSnapshot = {
|
|
3124
|
+
arrivalSnapshotTime: Date.now(),
|
|
3125
|
+
arrivalSource: "unavailable",
|
|
3126
|
+
bid: null,
|
|
3127
|
+
ask: null,
|
|
3128
|
+
arrivalMid: null,
|
|
3129
|
+
spreadBps: null
|
|
3130
|
+
};
|
|
3131
|
+
let orderSubmitTime = Date.now();
|
|
3132
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
3133
|
+
connector,
|
|
3134
|
+
symbol,
|
|
3135
|
+
direction,
|
|
3136
|
+
takeProfits,
|
|
3137
|
+
stopLossPrice,
|
|
3138
|
+
limits: executionLimits,
|
|
3139
|
+
onValidated: (snapshot2, economics) => {
|
|
3140
|
+
arrivalSnapshot = snapshot2;
|
|
3141
|
+
orderSubmitTime = Date.now();
|
|
3142
|
+
if (executionLimits?.maxLossValue != null) {
|
|
3143
|
+
signal.riskDecision = {
|
|
3144
|
+
stage: "order_plan",
|
|
3145
|
+
status: economics.riskExceeded ? "warning" : "approved",
|
|
3146
|
+
reason: economics.riskExceeded ? "ENTRY_RISK_EXCEEDS_MAX_LOSS_AT_SUBMISSION" : "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE",
|
|
3147
|
+
plannedLossValue: economics.plannedLossValue,
|
|
3148
|
+
maxLossValue: executionLimits.maxLossValue,
|
|
3149
|
+
limitUtilization: economics.plannedLossValue / executionLimits.maxLossValue,
|
|
3150
|
+
enforced: false
|
|
3151
|
+
};
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
});
|
|
3155
|
+
if (!beforeSubmit) {
|
|
3156
|
+
arrivalSnapshot = await getOrderArrivalSnapshot({ connector, symbol });
|
|
3157
|
+
try {
|
|
3158
|
+
validateEntryProtectionAtArrival({
|
|
3159
|
+
direction,
|
|
3160
|
+
signalPrice: currentPrice,
|
|
3161
|
+
bid: arrivalSnapshot.bid,
|
|
3162
|
+
ask: arrivalSnapshot.ask,
|
|
3163
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
3164
|
+
takeProfits,
|
|
3165
|
+
stopLossPrice
|
|
3166
|
+
});
|
|
3167
|
+
} catch (error) {
|
|
3168
|
+
signal.orderStatus = "failed";
|
|
3169
|
+
signal.orderFailureReason = error.message;
|
|
3170
|
+
throw error;
|
|
3171
|
+
}
|
|
3172
|
+
orderSubmitTime = Date.now();
|
|
3009
3173
|
}
|
|
3010
|
-
const orderSubmitTime = Date.now();
|
|
3011
3174
|
const orderPlaced = await connector.placeOrder({
|
|
3012
3175
|
symbol,
|
|
3013
3176
|
qty,
|
|
@@ -3018,7 +3181,8 @@ var executeEntryOrder = async ({
|
|
|
3018
3181
|
direction,
|
|
3019
3182
|
...typeof leverage === "number" && Number.isFinite(leverage) ? { leverage } : {},
|
|
3020
3183
|
orderId,
|
|
3021
|
-
signal
|
|
3184
|
+
signal,
|
|
3185
|
+
...beforeSubmit ? { beforeSubmit } : {}
|
|
3022
3186
|
});
|
|
3023
3187
|
const orderAckTime = Date.now();
|
|
3024
3188
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
@@ -3305,7 +3469,7 @@ import {
|
|
|
3305
3469
|
createStrategyIndicatorsState,
|
|
3306
3470
|
getSharedStrategyReplayState
|
|
3307
3471
|
} from "@tradejs/core/strategies";
|
|
3308
|
-
import { logger as
|
|
3472
|
+
import { logger as logger10 } from "@tradejs/infra/logger";
|
|
3309
3473
|
|
|
3310
3474
|
// src/strategy/runtimeBacktestDelay.ts
|
|
3311
3475
|
import { BACKTEST_EXECUTION_INTERVAL } from "@tradejs/core/constants";
|
|
@@ -3867,7 +4031,7 @@ var canUseSharedReplayState = ({
|
|
|
3867
4031
|
}) => (env === "BACKTEST" || env === "PARITY") && Boolean(sharedReplayKey);
|
|
3868
4032
|
|
|
3869
4033
|
// src/strategy/runtimeExecution.ts
|
|
3870
|
-
import { logger as
|
|
4034
|
+
import { logger as logger9 } from "@tradejs/infra/logger";
|
|
3871
4035
|
import {
|
|
3872
4036
|
BACKTEST_WARNING_CODES
|
|
3873
4037
|
} from "@tradejs/types";
|
|
@@ -3921,7 +4085,7 @@ var handleExitDecision = async ({
|
|
|
3921
4085
|
deploymentId: connector.deploymentId
|
|
3922
4086
|
});
|
|
3923
4087
|
if (!activeTrade) {
|
|
3924
|
-
|
|
4088
|
+
logger9.warn(
|
|
3925
4089
|
"[%s] blocked closePosition for untracked runtime position: %s",
|
|
3926
4090
|
strategyName ?? "unknown",
|
|
3927
4091
|
symbol
|
|
@@ -3929,7 +4093,7 @@ var handleExitDecision = async ({
|
|
|
3929
4093
|
return "CLOSE_BLOCKED_BY_UNTRACKED_POSITION";
|
|
3930
4094
|
}
|
|
3931
4095
|
if (!strategyName || activeTrade.strategy !== strategyName) {
|
|
3932
|
-
|
|
4096
|
+
logger9.warn(
|
|
3933
4097
|
"[%s] blocked closePosition for foreign runtime position: %s ownedBy=%s",
|
|
3934
4098
|
strategyName ?? "unknown",
|
|
3935
4099
|
symbol,
|
|
@@ -3982,7 +4146,7 @@ var handleExitDecision = async ({
|
|
|
3982
4146
|
exitType: closedTrade?.exitType ?? "exit"
|
|
3983
4147
|
});
|
|
3984
4148
|
} catch (notificationError) {
|
|
3985
|
-
|
|
4149
|
+
logger9.error(
|
|
3986
4150
|
"runtime close notification error: %s %s",
|
|
3987
4151
|
symbol,
|
|
3988
4152
|
notificationError
|
|
@@ -3996,7 +4160,7 @@ var handleExitDecision = async ({
|
|
|
3996
4160
|
decision,
|
|
3997
4161
|
market
|
|
3998
4162
|
});
|
|
3999
|
-
|
|
4163
|
+
logger9.error("close order error: %s %s", symbol, err);
|
|
4000
4164
|
return "ORDER_ERROR";
|
|
4001
4165
|
}
|
|
4002
4166
|
return decision.code;
|
|
@@ -4023,7 +4187,7 @@ var handleProtectDecision = async ({
|
|
|
4023
4187
|
decision,
|
|
4024
4188
|
market
|
|
4025
4189
|
});
|
|
4026
|
-
|
|
4190
|
+
logger9.error("protect position error: %s %s", symbol, err);
|
|
4027
4191
|
return "ORDER_ERROR";
|
|
4028
4192
|
}
|
|
4029
4193
|
return decision.code;
|
|
@@ -4089,7 +4253,11 @@ var executeEntryDecision = async ({
|
|
|
4089
4253
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {},
|
|
4090
4254
|
signal,
|
|
4091
4255
|
beforePlaceOrder,
|
|
4092
|
-
recordRuntimeTrade: recordRuntimeJournal
|
|
4256
|
+
recordRuntimeTrade: recordRuntimeJournal,
|
|
4257
|
+
executionLimits: resolveEntryExecutionLimits(
|
|
4258
|
+
hookCtx.strategyConfig,
|
|
4259
|
+
decision.entryContext.direction
|
|
4260
|
+
)
|
|
4093
4261
|
});
|
|
4094
4262
|
await invokeStageHooks(
|
|
4095
4263
|
"afterPlaceOrder",
|
|
@@ -4111,19 +4279,32 @@ var executeEntryDecision = async ({
|
|
|
4111
4279
|
return signal;
|
|
4112
4280
|
}
|
|
4113
4281
|
await beforePlaceOrder();
|
|
4114
|
-
const
|
|
4282
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
4115
4283
|
connector,
|
|
4116
|
-
symbol
|
|
4117
|
-
});
|
|
4118
|
-
validateEntryProtectionAtArrival({
|
|
4284
|
+
symbol,
|
|
4119
4285
|
direction: decision.entryContext.direction,
|
|
4120
|
-
signalPrice: decision.entryContext.prices.currentPrice,
|
|
4121
|
-
bid: arrivalSnapshot.bid,
|
|
4122
|
-
ask: arrivalSnapshot.ask,
|
|
4123
|
-
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
4124
4286
|
takeProfits: decision.orderPlan.takeProfits,
|
|
4125
|
-
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
4287
|
+
stopLossPrice: decision.orderPlan.stopLossPrice,
|
|
4288
|
+
limits: resolveEntryExecutionLimits(
|
|
4289
|
+
hookCtx.strategyConfig,
|
|
4290
|
+
decision.entryContext.direction
|
|
4291
|
+
)
|
|
4126
4292
|
});
|
|
4293
|
+
if (!beforeSubmit) {
|
|
4294
|
+
const arrivalSnapshot = await getOrderArrivalSnapshot({
|
|
4295
|
+
connector,
|
|
4296
|
+
symbol
|
|
4297
|
+
});
|
|
4298
|
+
validateEntryProtectionAtArrival({
|
|
4299
|
+
direction: decision.entryContext.direction,
|
|
4300
|
+
signalPrice: decision.entryContext.prices.currentPrice,
|
|
4301
|
+
bid: arrivalSnapshot.bid,
|
|
4302
|
+
ask: arrivalSnapshot.ask,
|
|
4303
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
4304
|
+
takeProfits: decision.orderPlan.takeProfits,
|
|
4305
|
+
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
4306
|
+
});
|
|
4307
|
+
}
|
|
4127
4308
|
const orderPlaced = await connector.placeOrder({
|
|
4128
4309
|
symbol,
|
|
4129
4310
|
qty: decision.orderPlan.qty,
|
|
@@ -4131,6 +4312,7 @@ var executeEntryDecision = async ({
|
|
|
4131
4312
|
timestamp: decision.entryContext.timestamp,
|
|
4132
4313
|
direction: decision.entryContext.direction,
|
|
4133
4314
|
positionIntent: decision.orderPlan.positionIntent,
|
|
4315
|
+
...beforeSubmit ? { beforeSubmit } : {},
|
|
4134
4316
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {}
|
|
4135
4317
|
});
|
|
4136
4318
|
if (!orderPlaced) {
|
|
@@ -4172,6 +4354,23 @@ var executeEntryDecision = async ({
|
|
|
4172
4354
|
{ decision, entry, market }
|
|
4173
4355
|
);
|
|
4174
4356
|
} catch (err) {
|
|
4357
|
+
if (err instanceof EntrySubmissionValidationError) {
|
|
4358
|
+
if (signal) {
|
|
4359
|
+
signal.orderStatus = "skipped";
|
|
4360
|
+
signal.orderSkipReason = err.message;
|
|
4361
|
+
signal.allocatorDecision = {
|
|
4362
|
+
stage: "runtime_admission",
|
|
4363
|
+
status: "rejected",
|
|
4364
|
+
reason: err.message
|
|
4365
|
+
};
|
|
4366
|
+
}
|
|
4367
|
+
logger9.warn(
|
|
4368
|
+
"entry rejected before submission: %s %s",
|
|
4369
|
+
symbol,
|
|
4370
|
+
err.message
|
|
4371
|
+
);
|
|
4372
|
+
return signal ?? err.message;
|
|
4373
|
+
}
|
|
4175
4374
|
if (signal) {
|
|
4176
4375
|
signal.orderStatus = "failed";
|
|
4177
4376
|
if (typeof signal.orderFailureReason !== "string" || !signal.orderFailureReason.trim()) {
|
|
@@ -4186,9 +4385,9 @@ var executeEntryDecision = async ({
|
|
|
4186
4385
|
market
|
|
4187
4386
|
});
|
|
4188
4387
|
if (err?.message === BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY) {
|
|
4189
|
-
|
|
4388
|
+
logger9.warn("order warning: %s %s", symbol, err);
|
|
4190
4389
|
} else {
|
|
4191
|
-
|
|
4390
|
+
logger9.error("order error: %s %s", symbol, err);
|
|
4192
4391
|
}
|
|
4193
4392
|
return signal ?? "ORDER_ERROR";
|
|
4194
4393
|
}
|
|
@@ -4296,6 +4495,9 @@ var createStrategyRuntime = ({
|
|
|
4296
4495
|
});
|
|
4297
4496
|
const strategyPolicyProfile = getPolicyProfile();
|
|
4298
4497
|
const indicatorPeriods = buildDefaultIndicatorPeriods(config);
|
|
4498
|
+
const strategyConfig = { ...config };
|
|
4499
|
+
delete strategyConfig.RECORD_RUNTIME_TRADES;
|
|
4500
|
+
delete strategyConfig.SIMULATE_ORDERS;
|
|
4299
4501
|
const hookBase = {
|
|
4300
4502
|
connector,
|
|
4301
4503
|
strategyName,
|
|
@@ -4306,7 +4508,7 @@ var createStrategyRuntime = ({
|
|
|
4306
4508
|
accountId,
|
|
4307
4509
|
deploymentId,
|
|
4308
4510
|
policyProfileId: strategyPolicyProfile?.id ?? requestedPolicyProfileId,
|
|
4309
|
-
strategyConfig
|
|
4511
|
+
strategyConfig,
|
|
4310
4512
|
env,
|
|
4311
4513
|
isConfigFromBacktest
|
|
4312
4514
|
};
|
|
@@ -4353,7 +4555,7 @@ var createStrategyRuntime = ({
|
|
|
4353
4555
|
try {
|
|
4354
4556
|
await projectHook(errorParams);
|
|
4355
4557
|
} catch (hookError) {
|
|
4356
|
-
|
|
4558
|
+
logger10.error(
|
|
4357
4559
|
"project hook onRuntimeError failed: %s %s",
|
|
4358
4560
|
strategyName,
|
|
4359
4561
|
hookError
|
|
@@ -4367,21 +4569,21 @@ var createStrategyRuntime = ({
|
|
|
4367
4569
|
try {
|
|
4368
4570
|
await onRuntimeError(errorParams);
|
|
4369
4571
|
} catch (hookError) {
|
|
4370
|
-
|
|
4572
|
+
logger10.error(
|
|
4371
4573
|
"runtime hook onRuntimeError failed: %s %s",
|
|
4372
4574
|
strategyName,
|
|
4373
4575
|
hookError
|
|
4374
4576
|
);
|
|
4375
4577
|
}
|
|
4376
4578
|
};
|
|
4377
|
-
const invokeHook = async (stage, hook, params, errorContext = {}) => {
|
|
4579
|
+
const invokeHook = async (stage, hook, params, errorContext = {}, failureResult) => {
|
|
4378
4580
|
if (!hook) {
|
|
4379
4581
|
return void 0;
|
|
4380
4582
|
}
|
|
4381
4583
|
try {
|
|
4382
4584
|
return await hook(params);
|
|
4383
4585
|
} catch (error) {
|
|
4384
|
-
|
|
4586
|
+
logger10.error(
|
|
4385
4587
|
'strategy hook "%s" failed for %s: %s',
|
|
4386
4588
|
stage,
|
|
4387
4589
|
strategyName,
|
|
@@ -4394,17 +4596,18 @@ var createStrategyRuntime = ({
|
|
|
4394
4596
|
entry: errorContext.entry,
|
|
4395
4597
|
market: errorContext.market
|
|
4396
4598
|
});
|
|
4397
|
-
return
|
|
4599
|
+
return failureResult;
|
|
4398
4600
|
}
|
|
4399
4601
|
};
|
|
4400
|
-
const invokeProjectHooks = async (stage, params, errorContext = {}) => {
|
|
4602
|
+
const invokeProjectHooks = async (stage, params, errorContext = {}, failureResult) => {
|
|
4401
4603
|
const results = [];
|
|
4402
4604
|
for (const hook of getProjectHookList(stage)) {
|
|
4403
4605
|
const result = await invokeHook(
|
|
4404
4606
|
stage,
|
|
4405
4607
|
hook,
|
|
4406
4608
|
params,
|
|
4407
|
-
errorContext
|
|
4609
|
+
errorContext,
|
|
4610
|
+
failureResult
|
|
4408
4611
|
);
|
|
4409
4612
|
if (result !== void 0) {
|
|
4410
4613
|
results.push(result);
|
|
@@ -4419,10 +4622,12 @@ var createStrategyRuntime = ({
|
|
|
4419
4622
|
return invokeHook(stage, hook, params, errorContext);
|
|
4420
4623
|
};
|
|
4421
4624
|
const invokeGateHooks = async (stage, hook, params, errorContext = {}) => {
|
|
4625
|
+
const failureResult = stage === "beforeEntryGate" ? { allow: false, reason: "ERROR" } : void 0;
|
|
4422
4626
|
const projectResults = await invokeProjectHooks(
|
|
4423
4627
|
stage,
|
|
4424
4628
|
params,
|
|
4425
|
-
errorContext
|
|
4629
|
+
errorContext,
|
|
4630
|
+
failureResult
|
|
4426
4631
|
);
|
|
4427
4632
|
const projectBlock = projectResults.find(
|
|
4428
4633
|
(result) => result?.allow === false
|
|
@@ -4434,7 +4639,8 @@ var createStrategyRuntime = ({
|
|
|
4434
4639
|
stage,
|
|
4435
4640
|
hook,
|
|
4436
4641
|
params,
|
|
4437
|
-
errorContext
|
|
4642
|
+
errorContext,
|
|
4643
|
+
failureResult
|
|
4438
4644
|
);
|
|
4439
4645
|
};
|
|
4440
4646
|
const applyProjectAfterCoreDecisionHooks = async ({
|
|
@@ -4580,11 +4786,8 @@ var createStrategyRuntime = ({
|
|
|
4580
4786
|
return enrichedBaseContext;
|
|
4581
4787
|
}
|
|
4582
4788
|
});
|
|
4583
|
-
const coreConfig = { ...config };
|
|
4584
|
-
delete coreConfig.RECORD_RUNTIME_TRADES;
|
|
4585
|
-
delete coreConfig.SIMULATE_ORDERS;
|
|
4586
4789
|
const core = await createCore({
|
|
4587
|
-
config:
|
|
4790
|
+
config: strategyConfig,
|
|
4588
4791
|
data,
|
|
4589
4792
|
strategyApi,
|
|
4590
4793
|
indicatorsState
|
|
@@ -5203,6 +5406,8 @@ var createStrategyRuntime = ({
|
|
|
5203
5406
|
setStrategyRuntimeFactory(createStrategyRuntime);
|
|
5204
5407
|
|
|
5205
5408
|
export {
|
|
5409
|
+
getOrderArrivalSnapshot,
|
|
5410
|
+
validateEntryProtectionAtArrival,
|
|
5206
5411
|
buildMlPayload,
|
|
5207
5412
|
BINANCE_BREADTH_UNIVERSE_KEYS,
|
|
5208
5413
|
buildBinanceBreadthUniverseSnapshot,
|
|
@@ -5224,8 +5429,6 @@ export {
|
|
|
5224
5429
|
enrichSignalWithMl,
|
|
5225
5430
|
enrichSignalWithAi,
|
|
5226
5431
|
enrichSignalWithMlAi,
|
|
5227
|
-
getOrderArrivalSnapshot,
|
|
5228
|
-
validateEntryProtectionAtArrival,
|
|
5229
5432
|
executeEntryOrder,
|
|
5230
5433
|
resolveStrategyConfig,
|
|
5231
5434
|
RESEARCH_ENTRY_POLICY_CAPABILITIES,
|