@tradejs/cli 2.0.1 → 2.0.3
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/cli.js +293 -58
- package/dist/lib/aiPocketSearch.js +18 -0
- package/dist/lib/aiTrainCharts.js +49 -2
- package/dist/lib/aiTrainMetrics.js +49 -2
- package/dist/lib/aiTrainResearch.js +58 -3
- package/dist/lib/derivativesContextBackfill.js +143 -32
- package/dist/lib/marketContextPrepare.js +141 -32
- package/dist/lib/runtimeTradeSync.js +22 -15
- package/dist/scripts/aiExport.js +2 -1
- package/dist/scripts/aiExportSelect.js +2 -1
- package/dist/scripts/aiPocketSearch.js +67 -2
- package/dist/scripts/aiTrain.js +76 -3
- package/dist/scripts/backtest.js +141 -32
- package/dist/scripts/derivativesIngestCoinalyzeAll.js +30 -0
- package/dist/scripts/mlExport.js +2 -1
- package/dist/scripts/mlExportSelect.js +2 -1
- package/dist/scripts/mlTrainLatestSelect.js +2 -1
- package/dist/scripts/replay.js +161 -47
- package/dist/scripts/replayRunner.js +161 -47
- package/dist/scripts/resolveExportStrategy.js +2 -1
- package/dist/scripts/runtimeParity.js +141 -32
- package/dist/scripts/selectStrategy.js +2 -1
- package/dist/scripts/signals.js +141 -32
- package/dist/scripts/signalsDaemon.js +141 -32
- package/dist/scripts/signalsSummary.js +21 -17
- package/package.json +9 -9
|
@@ -2552,6 +2552,38 @@ var resolveDerivativesContextMissingFetchFromMs = (params) => {
|
|
|
2552
2552
|
return null;
|
|
2553
2553
|
};
|
|
2554
2554
|
var resolveDerivativesContextFetchFromMs = (params) => resolveDerivativesContextMissingFetchFromMs(params) ?? (params.refreshClosedTail ? params.toMs : null);
|
|
2555
|
+
var resolveDerivativesContextRequiredFetchFromMs = (params) => {
|
|
2556
|
+
const dataCoverageFromMs = params.dataCoverageKeyExists ? null : params.mode === "signals" ? params.fromMs : resolveDerivativesContextMissingCoverageFetchFromMs({
|
|
2557
|
+
ranges: params.dataCoverageRanges,
|
|
2558
|
+
fromMs: params.fromMs,
|
|
2559
|
+
toMs: params.toMs,
|
|
2560
|
+
intervalMs: params.intervalMs
|
|
2561
|
+
});
|
|
2562
|
+
const edgesFromMs = dataCoverageFromMs == null ? null : resolveDerivativesContextFetchFromMs({
|
|
2563
|
+
edges: params.edges,
|
|
2564
|
+
fromMs: params.fromMs,
|
|
2565
|
+
toMs: params.toMs,
|
|
2566
|
+
intervalMs: params.intervalMs,
|
|
2567
|
+
refreshClosedTail: params.mode === "signals"
|
|
2568
|
+
});
|
|
2569
|
+
const dataFetchFromMs = dataCoverageFromMs != null && edgesFromMs != null ? Math.max(dataCoverageFromMs, edgesFromMs) : null;
|
|
2570
|
+
const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
2571
|
+
interval: params.interval,
|
|
2572
|
+
fromMs: params.fromMs,
|
|
2573
|
+
toMs: params.toMs,
|
|
2574
|
+
nowMs: params.nowMs
|
|
2575
|
+
});
|
|
2576
|
+
const liquidationFetchFromMs = confirmedLiquidationWindow ? resolveDerivativesContextMissingCoverageFetchFromMs({
|
|
2577
|
+
ranges: params.liquidationCoverageRanges,
|
|
2578
|
+
fromMs: confirmedLiquidationWindow.fromMs,
|
|
2579
|
+
toMs: confirmedLiquidationWindow.toMs,
|
|
2580
|
+
intervalMs: params.intervalMs
|
|
2581
|
+
}) : null;
|
|
2582
|
+
const fetchStarts = [dataFetchFromMs, liquidationFetchFromMs].filter(
|
|
2583
|
+
(value) => value != null
|
|
2584
|
+
);
|
|
2585
|
+
return fetchStarts.length ? Math.min(...fetchStarts) : null;
|
|
2586
|
+
};
|
|
2555
2587
|
var countBackfillWindows = (params) => {
|
|
2556
2588
|
let count = 0;
|
|
2557
2589
|
let cursor = params.fromMs;
|
|
@@ -2887,7 +2919,8 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2887
2919
|
mode,
|
|
2888
2920
|
startMs,
|
|
2889
2921
|
endMs,
|
|
2890
|
-
preloadStartMs: params.preloadStartMs
|
|
2922
|
+
preloadStartMs: params.preloadStartMs,
|
|
2923
|
+
nowMs: Date.now()
|
|
2891
2924
|
});
|
|
2892
2925
|
if (safeEndMs <= fromMs) {
|
|
2893
2926
|
return skippedBackfillResult2();
|
|
@@ -2921,15 +2954,33 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2921
2954
|
);
|
|
2922
2955
|
const coverageKeysByInterval = /* @__PURE__ */ new Map();
|
|
2923
2956
|
const coverageRangesByInterval = /* @__PURE__ */ new Map();
|
|
2957
|
+
const liquidationCoverageRangesByInterval = /* @__PURE__ */ new Map();
|
|
2958
|
+
const coverageNowMs = Date.now();
|
|
2924
2959
|
await Promise.all(
|
|
2925
2960
|
intervalWindows.map(async (window2) => {
|
|
2926
|
-
const
|
|
2927
|
-
source: "coinalyze",
|
|
2928
|
-
symbols,
|
|
2961
|
+
const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
2929
2962
|
interval: window2.interval,
|
|
2930
2963
|
fromMs: window2.fromMs,
|
|
2931
|
-
toMs: window2.toMs
|
|
2964
|
+
toMs: window2.toMs,
|
|
2965
|
+
nowMs: coverageNowMs
|
|
2932
2966
|
});
|
|
2967
|
+
const [coverageRows, liquidationCoverageRows] = await Promise.all([
|
|
2968
|
+
(0, import_timescale2.getDerivativesBackfillCoverage)({
|
|
2969
|
+
source: "coinalyze",
|
|
2970
|
+
symbols,
|
|
2971
|
+
interval: window2.interval,
|
|
2972
|
+
fromMs: window2.fromMs,
|
|
2973
|
+
toMs: window2.toMs
|
|
2974
|
+
}),
|
|
2975
|
+
confirmedLiquidationWindow ? (0, import_timescale2.getDerivativesMetricCoverage)({
|
|
2976
|
+
source: "coinalyze",
|
|
2977
|
+
metric: "liquidation",
|
|
2978
|
+
symbols,
|
|
2979
|
+
interval: window2.interval,
|
|
2980
|
+
fromMs: confirmedLiquidationWindow.fromMs,
|
|
2981
|
+
toMs: confirmedLiquidationWindow.toMs
|
|
2982
|
+
}) : Promise.resolve([])
|
|
2983
|
+
]);
|
|
2933
2984
|
coverageKeysByInterval.set(
|
|
2934
2985
|
window2.interval,
|
|
2935
2986
|
new Set(
|
|
@@ -2951,6 +3002,17 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2951
3002
|
rangesBySymbol.set(symbol, ranges);
|
|
2952
3003
|
}
|
|
2953
3004
|
coverageRangesByInterval.set(window2.interval, rangesBySymbol);
|
|
3005
|
+
const liquidationRangesBySymbol = /* @__PURE__ */ new Map();
|
|
3006
|
+
for (const row of liquidationCoverageRows) {
|
|
3007
|
+
const symbol = row.symbol.toUpperCase();
|
|
3008
|
+
const ranges = liquidationRangesBySymbol.get(symbol) ?? [];
|
|
3009
|
+
ranges.push({ fromMs: row.fromMs, toMs: row.toMs });
|
|
3010
|
+
liquidationRangesBySymbol.set(symbol, ranges);
|
|
3011
|
+
}
|
|
3012
|
+
liquidationCoverageRangesByInterval.set(
|
|
3013
|
+
window2.interval,
|
|
3014
|
+
liquidationRangesBySymbol
|
|
3015
|
+
);
|
|
2954
3016
|
})
|
|
2955
3017
|
);
|
|
2956
3018
|
const cachedWindows = intervalWindows.reduce(
|
|
@@ -2975,7 +3037,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2975
3037
|
(symbol) => backfillWindows.every((backfillWindow) => {
|
|
2976
3038
|
const normalizedSymbol = symbol.toUpperCase();
|
|
2977
3039
|
const coverageRanges = coverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [];
|
|
2978
|
-
|
|
3040
|
+
const dataCovered = hasDerivativesWindowCoverage({
|
|
2979
3041
|
edges: edgesBySymbol?.get(normalizedSymbol),
|
|
2980
3042
|
fromMs: backfillWindow.fromMs,
|
|
2981
3043
|
toMs: backfillWindow.toMs
|
|
@@ -2992,6 +3054,19 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2992
3054
|
toMs: backfillWindow.toMs
|
|
2993
3055
|
})
|
|
2994
3056
|
);
|
|
3057
|
+
const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
3058
|
+
interval: window2.interval,
|
|
3059
|
+
fromMs: backfillWindow.fromMs,
|
|
3060
|
+
toMs: backfillWindow.toMs,
|
|
3061
|
+
nowMs: coverageNowMs
|
|
3062
|
+
});
|
|
3063
|
+
const liquidationCovered = confirmedLiquidationWindow == null || resolveDerivativesContextMissingCoverageFetchFromMs({
|
|
3064
|
+
ranges: liquidationCoverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [],
|
|
3065
|
+
fromMs: confirmedLiquidationWindow.fromMs,
|
|
3066
|
+
toMs: confirmedLiquidationWindow.toMs,
|
|
3067
|
+
intervalMs: window2.intervalMs
|
|
3068
|
+
}) == null;
|
|
3069
|
+
return dataCovered && liquidationCovered;
|
|
2995
3070
|
})
|
|
2996
3071
|
);
|
|
2997
3072
|
});
|
|
@@ -3076,6 +3151,11 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
3076
3151
|
coverageKeysByInterval.set(interval, coverageKeys);
|
|
3077
3152
|
const coverageRangesBySymbol = coverageRangesByInterval.get(interval) ?? /* @__PURE__ */ new Map();
|
|
3078
3153
|
coverageRangesByInterval.set(interval, coverageRangesBySymbol);
|
|
3154
|
+
const liquidationCoverageRangesBySymbol = liquidationCoverageRangesByInterval.get(interval) ?? /* @__PURE__ */ new Map();
|
|
3155
|
+
liquidationCoverageRangesByInterval.set(
|
|
3156
|
+
interval,
|
|
3157
|
+
liquidationCoverageRangesBySymbol
|
|
3158
|
+
);
|
|
3079
3159
|
for (let batchIdx = 0; batchIdx < symbolBatches.length; batchIdx += 1) {
|
|
3080
3160
|
const batch = symbolBatches[batchIdx];
|
|
3081
3161
|
let cursor = window2.fromMs;
|
|
@@ -3088,33 +3168,25 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
3088
3168
|
fromMs: cursor,
|
|
3089
3169
|
toMs
|
|
3090
3170
|
});
|
|
3091
|
-
if (coverageKeys.has(key)) {
|
|
3092
|
-
return null;
|
|
3093
|
-
}
|
|
3094
3171
|
const normalizedSymbol = item.symbol.toUpperCase();
|
|
3095
|
-
const
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
refreshClosedTail: mode === "signals"
|
|
3111
|
-
});
|
|
3112
|
-
if (edgesFromMs == null) {
|
|
3113
|
-
return null;
|
|
3114
|
-
}
|
|
3172
|
+
const requiredFromMs = resolveDerivativesContextRequiredFetchFromMs(
|
|
3173
|
+
{
|
|
3174
|
+
mode,
|
|
3175
|
+
interval,
|
|
3176
|
+
intervalMs,
|
|
3177
|
+
fromMs: cursor,
|
|
3178
|
+
toMs,
|
|
3179
|
+
nowMs: coverageNowMs,
|
|
3180
|
+
dataCoverageKeyExists: coverageKeys.has(key),
|
|
3181
|
+
dataCoverageRanges: coverageRangesBySymbol.get(normalizedSymbol),
|
|
3182
|
+
liquidationCoverageRanges: liquidationCoverageRangesBySymbol.get(normalizedSymbol),
|
|
3183
|
+
edges: edgesBySymbol.get(normalizedSymbol)
|
|
3184
|
+
}
|
|
3185
|
+
);
|
|
3186
|
+
if (requiredFromMs == null) return null;
|
|
3115
3187
|
return {
|
|
3116
3188
|
item,
|
|
3117
|
-
fromMs:
|
|
3189
|
+
fromMs: requiredFromMs
|
|
3118
3190
|
};
|
|
3119
3191
|
}).filter(
|
|
3120
3192
|
(item) => item != null
|
|
@@ -3139,6 +3211,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
3139
3211
|
);
|
|
3140
3212
|
let rows = [];
|
|
3141
3213
|
let missingClosedSymbols = [];
|
|
3214
|
+
let liquidationRowsByMarket = /* @__PURE__ */ new Map();
|
|
3142
3215
|
for (let attempt = 1; attempt <= maxClosedBarAttempts; attempt += 1) {
|
|
3143
3216
|
const oiMap = await fetchMetricBatch({
|
|
3144
3217
|
endpoint: oiPath,
|
|
@@ -3158,7 +3231,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
3158
3231
|
fromMs: group.fromMs,
|
|
3159
3232
|
toMs
|
|
3160
3233
|
});
|
|
3161
|
-
|
|
3234
|
+
liquidationRowsByMarket = await fetchMetricBatch({
|
|
3162
3235
|
endpoint: liqPath,
|
|
3163
3236
|
metric: "liq",
|
|
3164
3237
|
marketSymbols,
|
|
@@ -3173,7 +3246,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
3173
3246
|
symbol: item.symbol,
|
|
3174
3247
|
oiRaw: oiMap.get(marketSymbol) ?? [],
|
|
3175
3248
|
fundingRaw: fundingMap.get(marketSymbol) ?? [],
|
|
3176
|
-
liqRaw:
|
|
3249
|
+
liqRaw: liquidationRowsByMarket.get(marketSymbol) ?? []
|
|
3177
3250
|
});
|
|
3178
3251
|
return (0, import_indicators.coinalyzePointsToRows)(points, interval, "coinalyze");
|
|
3179
3252
|
});
|
|
@@ -3196,6 +3269,42 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
3196
3269
|
await (0, import_timescale2.upsertDerivatives)(rows);
|
|
3197
3270
|
totalRows += rows.length;
|
|
3198
3271
|
}
|
|
3272
|
+
const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
3273
|
+
interval,
|
|
3274
|
+
fromMs: group.fromMs,
|
|
3275
|
+
toMs,
|
|
3276
|
+
nowMs: coverageNowMs
|
|
3277
|
+
});
|
|
3278
|
+
if (confirmedLiquidationWindow) {
|
|
3279
|
+
const metricCoverageRows = missingBatch.map((item) => {
|
|
3280
|
+
const marketSymbol = item.marketSymbol.toUpperCase();
|
|
3281
|
+
const eventRowsCount = (liquidationRowsByMarket.get(marketSymbol) ?? []).filter((point) => {
|
|
3282
|
+
const timestamp = (0, import_indicators.toCoinalyzeTimestampMs)(
|
|
3283
|
+
point.t ?? point.ts ?? point.time ?? point.timestamp
|
|
3284
|
+
);
|
|
3285
|
+
return timestamp != null && timestamp >= confirmedLiquidationWindow.fromMs && timestamp <= confirmedLiquidationWindow.toMs;
|
|
3286
|
+
}).length;
|
|
3287
|
+
return {
|
|
3288
|
+
source: "coinalyze",
|
|
3289
|
+
metric: "liquidation",
|
|
3290
|
+
symbol: item.symbol,
|
|
3291
|
+
interval,
|
|
3292
|
+
fromMs: confirmedLiquidationWindow.fromMs,
|
|
3293
|
+
toMs: confirmedLiquidationWindow.toMs,
|
|
3294
|
+
eventRowsCount
|
|
3295
|
+
};
|
|
3296
|
+
});
|
|
3297
|
+
await (0, import_timescale2.applyDerivativesMetricCoverage)(metricCoverageRows);
|
|
3298
|
+
for (const coverageRow of metricCoverageRows) {
|
|
3299
|
+
const symbol = coverageRow.symbol.toUpperCase();
|
|
3300
|
+
const ranges = liquidationCoverageRangesBySymbol.get(symbol) ?? [];
|
|
3301
|
+
ranges.push({
|
|
3302
|
+
fromMs: coverageRow.fromMs,
|
|
3303
|
+
toMs: coverageRow.toMs
|
|
3304
|
+
});
|
|
3305
|
+
liquidationCoverageRangesBySymbol.set(symbol, ranges);
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3199
3308
|
if (mode === "backtest") {
|
|
3200
3309
|
const rowsCountBySymbol = /* @__PURE__ */ new Map();
|
|
3201
3310
|
for (const row of rows) {
|
|
@@ -23,6 +23,7 @@ __export(runtimeTradeSync_exports, {
|
|
|
23
23
|
EXCHANGE_HISTORY_MAX_RANGE_MS: () => EXCHANGE_HISTORY_MAX_RANGE_MS,
|
|
24
24
|
consumeClosedPnlMatch: () => consumeClosedPnlMatch,
|
|
25
25
|
formatRuntimeTradeSyncError: () => formatRuntimeTradeSyncError,
|
|
26
|
+
isRuntimeTradeSyncFallbackClose: () => isRuntimeTradeSyncFallbackClose,
|
|
26
27
|
loadClosedPnlRows: () => loadClosedPnlRows,
|
|
27
28
|
splitExchangeHistoryTimeRange: () => splitExchangeHistoryTimeRange,
|
|
28
29
|
syncRuntimeTrades: () => syncRuntimeTrades
|
|
@@ -32,7 +33,9 @@ var import_constants = require("@tradejs/core/constants");
|
|
|
32
33
|
var import_time = require("@tradejs/core/time");
|
|
33
34
|
var import_redis = require("@tradejs/infra/redis");
|
|
34
35
|
var CLOSED_PNL_LIMIT = 100;
|
|
36
|
+
var CLOSED_PNL_RECONCILIATION_LOOKBACK_MS = 24 * 60 * 60 * 1e3;
|
|
35
37
|
var EXCHANGE_HISTORY_MAX_RANGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
38
|
+
var isRuntimeTradeSyncFallbackClose = (trade) => trade.status === "closed" && trade.exitTimestamp === trade.lastSyncedAt && trade.exitPrice == null && trade.actualExitPrice == null && trade.closeFee == null && trade.fundingFee == null;
|
|
36
39
|
var splitExchangeHistoryTimeRange = ({
|
|
37
40
|
startTime,
|
|
38
41
|
endTime,
|
|
@@ -161,7 +164,7 @@ var syncRuntimeTrades = async ({
|
|
|
161
164
|
);
|
|
162
165
|
const closedPnlRows = await loadClosedPnlRows({
|
|
163
166
|
connector,
|
|
164
|
-
startTime,
|
|
167
|
+
startTime: Math.max(0, startTime - CLOSED_PNL_RECONCILIATION_LOOKBACK_MS),
|
|
165
168
|
endTime,
|
|
166
169
|
callbacks: closedPnlCallbacks
|
|
167
170
|
});
|
|
@@ -173,14 +176,14 @@ var syncRuntimeTrades = async ({
|
|
|
173
176
|
}
|
|
174
177
|
const syncedTrades = [];
|
|
175
178
|
for (const trade of trades) {
|
|
176
|
-
if (trade.status !== "active") {
|
|
179
|
+
if (trade.status !== "active" && !isRuntimeTradeSyncFallbackClose(trade)) {
|
|
177
180
|
syncedTrades.push(trade);
|
|
178
181
|
continue;
|
|
179
182
|
}
|
|
180
183
|
const openPosition = openPositionsBySymbol.get(trade.symbol);
|
|
181
184
|
const activeOrderId = activeOrderIdBySymbol.get(trade.symbol);
|
|
182
185
|
const isCurrentActiveTrade = activeOrderId === trade.orderId;
|
|
183
|
-
if (!openPositionsReliable) {
|
|
186
|
+
if (trade.status === "active" && !openPositionsReliable) {
|
|
184
187
|
syncedTrades.push({
|
|
185
188
|
...trade,
|
|
186
189
|
status: "active",
|
|
@@ -188,7 +191,7 @@ var syncRuntimeTrades = async ({
|
|
|
188
191
|
});
|
|
189
192
|
continue;
|
|
190
193
|
}
|
|
191
|
-
if (isCurrentActiveTrade && openPosition && openPosition.direction === trade.direction) {
|
|
194
|
+
if (trade.status === "active" && isCurrentActiveTrade && openPosition && openPosition.direction === trade.direction) {
|
|
192
195
|
const nextTrade2 = {
|
|
193
196
|
...trade,
|
|
194
197
|
status: "active",
|
|
@@ -216,21 +219,24 @@ var syncRuntimeTrades = async ({
|
|
|
216
219
|
continue;
|
|
217
220
|
}
|
|
218
221
|
const matchedClosedPnl = consumeClosedPnlMatch(closedPnlBuckets, trade);
|
|
222
|
+
if (!matchedClosedPnl) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
219
225
|
const nextTrade = {
|
|
220
226
|
...trade,
|
|
221
227
|
status: "closed",
|
|
222
|
-
currentPrice: matchedClosedPnl
|
|
223
|
-
currentPnl: matchedClosedPnl
|
|
224
|
-
closedPnl: matchedClosedPnl
|
|
225
|
-
actualEntryPrice: matchedClosedPnl
|
|
226
|
-
exitPrice: matchedClosedPnl
|
|
227
|
-
actualExitPrice: matchedClosedPnl
|
|
228
|
-
exitTimestamp: matchedClosedPnl
|
|
228
|
+
currentPrice: matchedClosedPnl.exitPrice ?? trade.currentPrice ?? null,
|
|
229
|
+
currentPnl: matchedClosedPnl.closedPnl,
|
|
230
|
+
closedPnl: matchedClosedPnl.closedPnl,
|
|
231
|
+
actualEntryPrice: matchedClosedPnl.entryPrice ?? trade.actualEntryPrice ?? null,
|
|
232
|
+
exitPrice: matchedClosedPnl.exitPrice ?? trade.exitPrice ?? null,
|
|
233
|
+
actualExitPrice: matchedClosedPnl.exitPrice ?? trade.actualExitPrice ?? null,
|
|
234
|
+
exitTimestamp: matchedClosedPnl.closedAt,
|
|
229
235
|
exitType: trade.exitType ?? null,
|
|
230
|
-
openFee: matchedClosedPnl
|
|
231
|
-
closeFee: matchedClosedPnl
|
|
232
|
-
fundingFee: matchedClosedPnl
|
|
233
|
-
totalFee: matchedClosedPnl
|
|
236
|
+
openFee: matchedClosedPnl.openFee ?? trade.openFee ?? null,
|
|
237
|
+
closeFee: matchedClosedPnl.closeFee ?? trade.closeFee ?? null,
|
|
238
|
+
fundingFee: matchedClosedPnl.fundingFee ?? trade.fundingFee ?? null,
|
|
239
|
+
totalFee: matchedClosedPnl.totalFee ?? trade.totalFee ?? null,
|
|
234
240
|
lastSyncedAt: endTime
|
|
235
241
|
};
|
|
236
242
|
await Promise.all([
|
|
@@ -257,6 +263,7 @@ var syncRuntimeTrades = async ({
|
|
|
257
263
|
EXCHANGE_HISTORY_MAX_RANGE_MS,
|
|
258
264
|
consumeClosedPnlMatch,
|
|
259
265
|
formatRuntimeTradeSyncError,
|
|
266
|
+
isRuntimeTradeSyncFallbackClose,
|
|
260
267
|
loadClosedPnlRows,
|
|
261
268
|
splitExchangeHistoryTimeRange,
|
|
262
269
|
syncRuntimeTrades
|
package/dist/scripts/aiExport.js
CHANGED
|
@@ -1697,7 +1697,8 @@ var getStrategyChoices = async () => {
|
|
|
1697
1697
|
"MaStrategy",
|
|
1698
1698
|
"AdaptiveMomentumRibbon",
|
|
1699
1699
|
"TrendLine",
|
|
1700
|
-
"VolumeDivergence"
|
|
1700
|
+
"VolumeDivergence",
|
|
1701
|
+
"Grid"
|
|
1701
1702
|
];
|
|
1702
1703
|
};
|
|
1703
1704
|
var selectStrategy = async (promptLabel = "Select strategy", options) => {
|
|
@@ -1663,6 +1663,7 @@ var divideOrNull = (num, denom) => {
|
|
|
1663
1663
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
1664
1664
|
var DAYS_PER_WEEK = 7;
|
|
1665
1665
|
var DAYS_PER_MONTH = 30.4375;
|
|
1666
|
+
var DAYS_PER_YEAR = 365;
|
|
1666
1667
|
var getEvaluationPeriodDays = (evaluations) => {
|
|
1667
1668
|
let minTimestamp = null;
|
|
1668
1669
|
let maxTimestamp = null;
|
|
@@ -1684,6 +1685,40 @@ var getEvaluationPeriodDays = (evaluations) => {
|
|
|
1684
1685
|
return Math.max((maxTimestamp - minTimestamp) / DAY_MS, 1);
|
|
1685
1686
|
};
|
|
1686
1687
|
var qualitySortKey = (quality) => quality == null ? Number.POSITIVE_INFINITY : quality;
|
|
1688
|
+
var calculateTradePnlRiskRatios = ({
|
|
1689
|
+
profits,
|
|
1690
|
+
totalProfit,
|
|
1691
|
+
maxDrawdown,
|
|
1692
|
+
periodDays
|
|
1693
|
+
}) => {
|
|
1694
|
+
if (!profits.length || periodDays == null || periodDays <= 0) {
|
|
1695
|
+
return {
|
|
1696
|
+
sharpeRatio: null,
|
|
1697
|
+
sortinoRatio: null,
|
|
1698
|
+
calmarRatio: null
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
const meanProfit = totalProfit / profits.length;
|
|
1702
|
+
const variance = profits.reduce((sum, profit) => {
|
|
1703
|
+
const diff = profit - meanProfit;
|
|
1704
|
+
return sum + diff * diff;
|
|
1705
|
+
}, 0) / profits.length;
|
|
1706
|
+
const stdDev = Math.sqrt(variance);
|
|
1707
|
+
const downsideDeviation = Math.sqrt(
|
|
1708
|
+
profits.reduce(
|
|
1709
|
+
(sum, profit) => profit < 0 ? sum + profit * profit : sum,
|
|
1710
|
+
0
|
|
1711
|
+
) / profits.length
|
|
1712
|
+
);
|
|
1713
|
+
const annualizationScale = Math.sqrt(
|
|
1714
|
+
profits.length / periodDays * DAYS_PER_YEAR
|
|
1715
|
+
);
|
|
1716
|
+
return {
|
|
1717
|
+
sharpeRatio: stdDev > 0 && annualizationScale > 0 ? meanProfit / stdDev * annualizationScale : null,
|
|
1718
|
+
sortinoRatio: downsideDeviation > 0 && annualizationScale > 0 ? meanProfit / downsideDeviation * annualizationScale : null,
|
|
1719
|
+
calmarRatio: maxDrawdown > 0 ? totalProfit / periodDays * DAYS_PER_YEAR / maxDrawdown : null
|
|
1720
|
+
};
|
|
1721
|
+
};
|
|
1687
1722
|
var emptyRiskSummary = () => ({
|
|
1688
1723
|
trades: 0,
|
|
1689
1724
|
totalProfit: 0,
|
|
@@ -1699,12 +1734,15 @@ var emptyRiskSummary = () => ({
|
|
|
1699
1734
|
maxDrawdown: 0,
|
|
1700
1735
|
maxDrawdownPctOfGrossProfit: null,
|
|
1701
1736
|
maxDrawdownPctOfTotalProfit: null,
|
|
1737
|
+
sharpeRatio: null,
|
|
1738
|
+
sortinoRatio: null,
|
|
1739
|
+
calmarRatio: null,
|
|
1702
1740
|
recoveryFactor: null,
|
|
1703
1741
|
ulcerIndex: null,
|
|
1704
1742
|
maxConsecutiveWins: 0,
|
|
1705
1743
|
maxConsecutiveLosses: 0
|
|
1706
1744
|
});
|
|
1707
|
-
var summarizeApprovedRisk = (evaluations) => {
|
|
1745
|
+
var summarizeApprovedRisk = (evaluations, periodDays = getEvaluationPeriodDays(evaluations)) => {
|
|
1708
1746
|
const approvedEvaluations = evaluations.filter((evaluation) => evaluation.aiApproved).sort((left, right) => {
|
|
1709
1747
|
const leftTimestamp = typeof left.timestamp === "number" && Number.isFinite(left.timestamp) ? left.timestamp : Number.POSITIVE_INFINITY;
|
|
1710
1748
|
const rightTimestamp = typeof right.timestamp === "number" && Number.isFinite(right.timestamp) ? right.timestamp : Number.POSITIVE_INFINITY;
|
|
@@ -1727,8 +1765,10 @@ var summarizeApprovedRisk = (evaluations) => {
|
|
|
1727
1765
|
let currentLossStreak = 0;
|
|
1728
1766
|
let maxConsecutiveWins = 0;
|
|
1729
1767
|
let maxConsecutiveLosses = 0;
|
|
1768
|
+
const approvedProfits = [];
|
|
1730
1769
|
for (const evaluation of approvedEvaluations) {
|
|
1731
1770
|
const profit = evaluation.profit;
|
|
1771
|
+
approvedProfits.push(profit);
|
|
1732
1772
|
if (profit > 0) {
|
|
1733
1773
|
grossProfit += profit;
|
|
1734
1774
|
wins += 1;
|
|
@@ -1756,6 +1796,12 @@ var summarizeApprovedRisk = (evaluations) => {
|
|
|
1756
1796
|
const totalProfit = grossProfit - grossLoss;
|
|
1757
1797
|
const avgWin = divideOrNull(grossProfit, wins);
|
|
1758
1798
|
const avgLoss = divideOrNull(grossLoss, losses);
|
|
1799
|
+
const riskRatios = calculateTradePnlRiskRatios({
|
|
1800
|
+
profits: approvedProfits,
|
|
1801
|
+
totalProfit,
|
|
1802
|
+
maxDrawdown,
|
|
1803
|
+
periodDays
|
|
1804
|
+
});
|
|
1759
1805
|
return {
|
|
1760
1806
|
trades: approvedEvaluations.length,
|
|
1761
1807
|
totalProfit,
|
|
@@ -1771,6 +1817,7 @@ var summarizeApprovedRisk = (evaluations) => {
|
|
|
1771
1817
|
maxDrawdown,
|
|
1772
1818
|
maxDrawdownPctOfGrossProfit: grossProfit > 0 ? maxDrawdown / grossProfit : null,
|
|
1773
1819
|
maxDrawdownPctOfTotalProfit: totalProfit > 0 ? maxDrawdown / totalProfit : null,
|
|
1820
|
+
...riskRatios,
|
|
1774
1821
|
recoveryFactor: maxDrawdown > 0 ? totalProfit / maxDrawdown : null,
|
|
1775
1822
|
ulcerIndex: Math.sqrt(drawdownSquares / approvedEvaluations.length),
|
|
1776
1823
|
maxConsecutiveWins,
|
|
@@ -1869,7 +1916,7 @@ var summarizeAiTrainEvaluations = (evaluations) => {
|
|
|
1869
1916
|
avgApprovedTradesPerDay,
|
|
1870
1917
|
avgApprovedTradesPerWeek,
|
|
1871
1918
|
expectancyDelta,
|
|
1872
|
-
approvedRisk: summarizeApprovedRisk(evaluations),
|
|
1919
|
+
approvedRisk: summarizeApprovedRisk(evaluations, periodDays),
|
|
1873
1920
|
qualityBuckets: [...bucketMap.values()].sort(
|
|
1874
1921
|
(a, b) => qualitySortKey(a.quality) - qualitySortKey(b.quality)
|
|
1875
1922
|
)
|
|
@@ -2263,6 +2310,23 @@ var addDirectionalDerivedFeatures = (features) => {
|
|
|
2263
2310
|
}
|
|
2264
2311
|
features["derived.directIndicatorSupportCount"] = supportCount;
|
|
2265
2312
|
};
|
|
2313
|
+
var addSignalRiskDistanceFeatures = ({
|
|
2314
|
+
features,
|
|
2315
|
+
signal
|
|
2316
|
+
}) => {
|
|
2317
|
+
const currentPrice = isFiniteNumber(signal.prices?.currentPrice) ? signal.prices.currentPrice : null;
|
|
2318
|
+
const stopLossPrice = isFiniteNumber(signal.prices?.stopLossPrice) ? signal.prices.stopLossPrice : null;
|
|
2319
|
+
const takeProfitPrice = isFiniteNumber(signal.prices?.takeProfitPrice) ? signal.prices.takeProfitPrice : null;
|
|
2320
|
+
if (currentPrice == null || currentPrice === 0) {
|
|
2321
|
+
return;
|
|
2322
|
+
}
|
|
2323
|
+
if (stopLossPrice != null) {
|
|
2324
|
+
features["derived.stopDistanceBps"] = Math.abs(currentPrice - stopLossPrice) / Math.abs(currentPrice) * 1e4;
|
|
2325
|
+
}
|
|
2326
|
+
if (takeProfitPrice != null) {
|
|
2327
|
+
features["derived.takeProfitDistanceBps"] = Math.abs(takeProfitPrice - currentPrice) / Math.abs(currentPrice) * 1e4;
|
|
2328
|
+
}
|
|
2329
|
+
};
|
|
2266
2330
|
var collectAiPocketFeatures = ({
|
|
2267
2331
|
payload,
|
|
2268
2332
|
gateContext,
|
|
@@ -2290,6 +2354,7 @@ var collectAiPocketFeatures = ({
|
|
|
2290
2354
|
maxDepth: 8,
|
|
2291
2355
|
shouldSkipPath: featureProfile === "compact" ? isCompactFeaturePathSkipped : void 0
|
|
2292
2356
|
});
|
|
2357
|
+
addSignalRiskDistanceFeatures({ features, signal });
|
|
2293
2358
|
addDirectionalDerivedFeatures(features);
|
|
2294
2359
|
return features;
|
|
2295
2360
|
};
|