@tradejs/cli 2.0.0 → 2.0.2
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 +384 -122
- package/dist/lib/aiTrainCharts.js +49 -2
- package/dist/lib/aiTrainMetrics.js +49 -2
- package/dist/lib/aiTrainResearch.js +58 -4
- package/dist/lib/derivativesContextBackfill.js +262 -107
- package/dist/lib/marketContextPrepare.js +256 -107
- package/dist/scripts/aiPocketSearch.js +49 -2
- package/dist/scripts/aiTrain.js +76 -4
- package/dist/scripts/backtest.js +256 -107
- package/dist/scripts/derivativesIngest.js +11 -4
- package/dist/scripts/derivativesIngestCoinalyzeAll.js +38 -4
- package/dist/scripts/replay.js +256 -107
- package/dist/scripts/replayRunner.js +256 -107
- package/dist/scripts/runtimeParity.js +256 -107
- package/dist/scripts/signals.js +256 -107
- package/dist/scripts/signalsDaemon.js +256 -107
- package/package.json +9 -9
|
@@ -2434,7 +2434,7 @@ var import_userSettings = require("@tradejs/infra/userSettings");
|
|
|
2434
2434
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
2435
2435
|
var DAY_MS2 = 24 * HOUR_MS;
|
|
2436
2436
|
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
2437
|
-
var
|
|
2437
|
+
var SOURCE_INTERVALS = ["15m"];
|
|
2438
2438
|
var coinalyzeIntervalMap = {
|
|
2439
2439
|
"15m": "15min",
|
|
2440
2440
|
"1h": "1hour"
|
|
@@ -2495,10 +2495,7 @@ var isDerivativesContextBackfillEnabled = (env) => {
|
|
|
2495
2495
|
var shouldBackfillDerivativesContextForBacktest = (params) => !params.cacheOnly && (params.aiEnabled || params.mlEnabled) && isBacktestDerivativesContextEnabled();
|
|
2496
2496
|
var shouldBackfillDerivativesContextForSignals = (params) => !params.cacheOnly && isSignalsDerivativesContextEnabled();
|
|
2497
2497
|
var resolveDerivativesContextIntervals = () => {
|
|
2498
|
-
|
|
2499
|
-
process.env.DERIVATIVES_CONTEXT_INTERVALS
|
|
2500
|
-
);
|
|
2501
|
-
return intervals.length ? intervals : DEFAULT_INTERVALS;
|
|
2498
|
+
return [...SOURCE_INTERVALS];
|
|
2502
2499
|
};
|
|
2503
2500
|
var resolveDerivativesContextLookbackMs = () => {
|
|
2504
2501
|
const hours = Number(process.env.DERIVATIVES_CONTEXT_LOOKBACK_HOURS);
|
|
@@ -2533,9 +2530,10 @@ var resolveDerivativesContextBackfillWindow = (params) => {
|
|
|
2533
2530
|
};
|
|
2534
2531
|
var resolveDerivativesContextIntervalBackfillWindow = (params) => {
|
|
2535
2532
|
const intervalMs = derivativesIntervalMs(params.interval);
|
|
2533
|
+
const alignedToMs = params.closedOnly ? (0, import_indicators.getLastClosedDerivativesBarStartMs)(params.toMs, params.interval) : Math.floor(params.toMs / intervalMs) * intervalMs;
|
|
2536
2534
|
return {
|
|
2537
2535
|
fromMs: Math.floor(params.fromMs / intervalMs) * intervalMs,
|
|
2538
|
-
toMs:
|
|
2536
|
+
toMs: alignedToMs,
|
|
2539
2537
|
intervalMs
|
|
2540
2538
|
};
|
|
2541
2539
|
};
|
|
@@ -2553,6 +2551,39 @@ var resolveDerivativesContextMissingFetchFromMs = (params) => {
|
|
|
2553
2551
|
}
|
|
2554
2552
|
return null;
|
|
2555
2553
|
};
|
|
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
|
+
};
|
|
2556
2587
|
var countBackfillWindows = (params) => {
|
|
2557
2588
|
let count = 0;
|
|
2558
2589
|
let cursor = params.fromMs;
|
|
@@ -2616,6 +2647,14 @@ var groupDerivativesContextMissingFetchRanges = (ranges) => {
|
|
|
2616
2647
|
}
|
|
2617
2648
|
return [...groups.entries()].sort(([a], [b]) => a - b).map(([fromMs, items]) => ({ fromMs, items }));
|
|
2618
2649
|
};
|
|
2650
|
+
var getMissingClosedDerivativesSymbols = (params) => {
|
|
2651
|
+
const availableSymbols = new Set(
|
|
2652
|
+
params.rows.filter((row) => row.ts.getTime() === params.expectedTimestamp).map((row) => row.symbol.trim().toUpperCase())
|
|
2653
|
+
);
|
|
2654
|
+
return params.symbols.filter(
|
|
2655
|
+
(symbol) => !availableSymbols.has(symbol.trim().toUpperCase())
|
|
2656
|
+
);
|
|
2657
|
+
};
|
|
2619
2658
|
var coverageKey = (params) => [
|
|
2620
2659
|
params.symbol.trim().toUpperCase(),
|
|
2621
2660
|
params.interval,
|
|
@@ -2880,7 +2919,8 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2880
2919
|
mode,
|
|
2881
2920
|
startMs,
|
|
2882
2921
|
endMs,
|
|
2883
|
-
preloadStartMs: params.preloadStartMs
|
|
2922
|
+
preloadStartMs: params.preloadStartMs,
|
|
2923
|
+
nowMs: Date.now()
|
|
2884
2924
|
});
|
|
2885
2925
|
if (safeEndMs <= fromMs) {
|
|
2886
2926
|
return skippedBackfillResult2();
|
|
@@ -2895,7 +2935,8 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2895
2935
|
...resolveDerivativesContextIntervalBackfillWindow({
|
|
2896
2936
|
fromMs,
|
|
2897
2937
|
toMs: safeEndMs,
|
|
2898
|
-
interval
|
|
2938
|
+
interval,
|
|
2939
|
+
closedOnly: mode === "signals"
|
|
2899
2940
|
})
|
|
2900
2941
|
})).filter((item) => item.toMs > item.fromMs);
|
|
2901
2942
|
if (!intervalWindows.length) {
|
|
@@ -2913,15 +2954,33 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2913
2954
|
);
|
|
2914
2955
|
const coverageKeysByInterval = /* @__PURE__ */ new Map();
|
|
2915
2956
|
const coverageRangesByInterval = /* @__PURE__ */ new Map();
|
|
2957
|
+
const liquidationCoverageRangesByInterval = /* @__PURE__ */ new Map();
|
|
2958
|
+
const coverageNowMs = Date.now();
|
|
2916
2959
|
await Promise.all(
|
|
2917
2960
|
intervalWindows.map(async (window2) => {
|
|
2918
|
-
const
|
|
2919
|
-
source: "coinalyze",
|
|
2920
|
-
symbols,
|
|
2961
|
+
const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
2921
2962
|
interval: window2.interval,
|
|
2922
2963
|
fromMs: window2.fromMs,
|
|
2923
|
-
toMs: window2.toMs
|
|
2964
|
+
toMs: window2.toMs,
|
|
2965
|
+
nowMs: coverageNowMs
|
|
2924
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
|
+
]);
|
|
2925
2984
|
coverageKeysByInterval.set(
|
|
2926
2985
|
window2.interval,
|
|
2927
2986
|
new Set(
|
|
@@ -2943,6 +3002,17 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2943
3002
|
rangesBySymbol.set(symbol, ranges);
|
|
2944
3003
|
}
|
|
2945
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
|
+
);
|
|
2946
3016
|
})
|
|
2947
3017
|
);
|
|
2948
3018
|
const cachedWindows = intervalWindows.reduce(
|
|
@@ -2954,7 +3024,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2954
3024
|
}),
|
|
2955
3025
|
0
|
|
2956
3026
|
);
|
|
2957
|
-
const allBackfillWindowsCached = intervalWindows.every((window2) => {
|
|
3027
|
+
const allBackfillWindowsCached = mode !== "signals" && intervalWindows.every((window2) => {
|
|
2958
3028
|
const edgesBySymbol = edgesByInterval.get(window2.interval);
|
|
2959
3029
|
const coverageKeys = coverageKeysByInterval.get(window2.interval);
|
|
2960
3030
|
const backfillWindows = buildBackfillWindows({
|
|
@@ -2967,7 +3037,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2967
3037
|
(symbol) => backfillWindows.every((backfillWindow) => {
|
|
2968
3038
|
const normalizedSymbol = symbol.toUpperCase();
|
|
2969
3039
|
const coverageRanges = coverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [];
|
|
2970
|
-
|
|
3040
|
+
const dataCovered = hasDerivativesWindowCoverage({
|
|
2971
3041
|
edges: edgesBySymbol?.get(normalizedSymbol),
|
|
2972
3042
|
fromMs: backfillWindow.fromMs,
|
|
2973
3043
|
toMs: backfillWindow.toMs
|
|
@@ -2984,6 +3054,19 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2984
3054
|
toMs: backfillWindow.toMs
|
|
2985
3055
|
})
|
|
2986
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;
|
|
2987
3070
|
})
|
|
2988
3071
|
);
|
|
2989
3072
|
});
|
|
@@ -3068,6 +3151,11 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
3068
3151
|
coverageKeysByInterval.set(interval, coverageKeys);
|
|
3069
3152
|
const coverageRangesBySymbol = coverageRangesByInterval.get(interval) ?? /* @__PURE__ */ new Map();
|
|
3070
3153
|
coverageRangesByInterval.set(interval, coverageRangesBySymbol);
|
|
3154
|
+
const liquidationCoverageRangesBySymbol = liquidationCoverageRangesByInterval.get(interval) ?? /* @__PURE__ */ new Map();
|
|
3155
|
+
liquidationCoverageRangesByInterval.set(
|
|
3156
|
+
interval,
|
|
3157
|
+
liquidationCoverageRangesBySymbol
|
|
3158
|
+
);
|
|
3071
3159
|
for (let batchIdx = 0; batchIdx < symbolBatches.length; batchIdx += 1) {
|
|
3072
3160
|
const batch = symbolBatches[batchIdx];
|
|
3073
3161
|
let cursor = window2.fromMs;
|
|
@@ -3080,30 +3168,26 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
3080
3168
|
fromMs: cursor,
|
|
3081
3169
|
toMs
|
|
3082
3170
|
});
|
|
3083
|
-
if (coverageKeys.has(key)) {
|
|
3084
|
-
return null;
|
|
3085
|
-
}
|
|
3086
3171
|
const normalizedSymbol = item.symbol.toUpperCase();
|
|
3087
|
-
const
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
}
|
|
3106
|
-
return { item, fromMs: Math.max(coverageFromMs, edgesFromMs) };
|
|
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;
|
|
3187
|
+
return {
|
|
3188
|
+
item,
|
|
3189
|
+
fromMs: requiredFromMs
|
|
3190
|
+
};
|
|
3107
3191
|
}).filter(
|
|
3108
3192
|
(item) => item != null
|
|
3109
3193
|
);
|
|
@@ -3117,85 +3201,150 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
3117
3201
|
const marketSymbols = missingBatch.map(
|
|
3118
3202
|
(item) => item.marketSymbol
|
|
3119
3203
|
);
|
|
3120
|
-
const
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
metric: "liq",
|
|
3141
|
-
marketSymbols,
|
|
3142
|
-
apiKey,
|
|
3143
|
-
interval,
|
|
3144
|
-
fromMs: group.fromMs,
|
|
3145
|
-
toMs
|
|
3146
|
-
});
|
|
3147
|
-
const rows = missingBatch.flatMap((item) => {
|
|
3148
|
-
const marketSymbol = item.marketSymbol.toUpperCase();
|
|
3149
|
-
const points = (0, import_indicators.mergeCoinalyzeMetrics)({
|
|
3150
|
-
symbol: item.symbol,
|
|
3151
|
-
oiRaw: oiMap.get(marketSymbol) ?? [],
|
|
3152
|
-
fundingRaw: fundingMap.get(marketSymbol) ?? [],
|
|
3153
|
-
liqRaw: liqMap.get(marketSymbol) ?? []
|
|
3204
|
+
const maxClosedBarAttempts = mode === "signals" ? asInt2(
|
|
3205
|
+
process.env.DERIVATIVES_CONTEXT_CLOSED_BAR_MAX_ATTEMPTS,
|
|
3206
|
+
3
|
|
3207
|
+
) : 1;
|
|
3208
|
+
const closedBarRetryDelayMs = asInt2(
|
|
3209
|
+
process.env.DERIVATIVES_CONTEXT_CLOSED_BAR_RETRY_DELAY_MS,
|
|
3210
|
+
2e3
|
|
3211
|
+
);
|
|
3212
|
+
let rows = [];
|
|
3213
|
+
let missingClosedSymbols = [];
|
|
3214
|
+
let liquidationRowsByMarket = /* @__PURE__ */ new Map();
|
|
3215
|
+
for (let attempt = 1; attempt <= maxClosedBarAttempts; attempt += 1) {
|
|
3216
|
+
const oiMap = await fetchMetricBatch({
|
|
3217
|
+
endpoint: oiPath,
|
|
3218
|
+
metric: "oi",
|
|
3219
|
+
marketSymbols,
|
|
3220
|
+
apiKey,
|
|
3221
|
+
interval,
|
|
3222
|
+
fromMs: group.fromMs,
|
|
3223
|
+
toMs
|
|
3154
3224
|
});
|
|
3155
|
-
|
|
3156
|
-
|
|
3225
|
+
const fundingMap = await fetchMetricBatch({
|
|
3226
|
+
endpoint: fundingPath,
|
|
3227
|
+
metric: "funding",
|
|
3228
|
+
marketSymbols,
|
|
3229
|
+
apiKey,
|
|
3230
|
+
interval,
|
|
3231
|
+
fromMs: group.fromMs,
|
|
3232
|
+
toMs
|
|
3233
|
+
});
|
|
3234
|
+
liquidationRowsByMarket = await fetchMetricBatch({
|
|
3235
|
+
endpoint: liqPath,
|
|
3236
|
+
metric: "liq",
|
|
3237
|
+
marketSymbols,
|
|
3238
|
+
apiKey,
|
|
3239
|
+
interval,
|
|
3240
|
+
fromMs: group.fromMs,
|
|
3241
|
+
toMs
|
|
3242
|
+
});
|
|
3243
|
+
rows = missingBatch.flatMap((item) => {
|
|
3244
|
+
const marketSymbol = item.marketSymbol.toUpperCase();
|
|
3245
|
+
const points = (0, import_indicators.mergeCoinalyzeMetrics)({
|
|
3246
|
+
symbol: item.symbol,
|
|
3247
|
+
oiRaw: oiMap.get(marketSymbol) ?? [],
|
|
3248
|
+
fundingRaw: fundingMap.get(marketSymbol) ?? [],
|
|
3249
|
+
liqRaw: liquidationRowsByMarket.get(marketSymbol) ?? []
|
|
3250
|
+
});
|
|
3251
|
+
return (0, import_indicators.coinalyzePointsToRows)(points, interval, "coinalyze");
|
|
3252
|
+
});
|
|
3253
|
+
missingClosedSymbols = mode === "signals" ? getMissingClosedDerivativesSymbols({
|
|
3254
|
+
symbols: missingBatch.map((item) => item.symbol),
|
|
3255
|
+
rows,
|
|
3256
|
+
expectedTimestamp: toMs
|
|
3257
|
+
}) : [];
|
|
3258
|
+
if (!missingClosedSymbols.length) break;
|
|
3259
|
+
if (attempt < maxClosedBarAttempts) {
|
|
3260
|
+
await (0, import_async.delay)(closedBarRetryDelayMs);
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
if (missingClosedSymbols.length) {
|
|
3264
|
+
throw new Error(
|
|
3265
|
+
`Coinalyze closed ${interval} bar ${new Date(toMs).toISOString()} unavailable for ${missingClosedSymbols.join(",")}`
|
|
3266
|
+
);
|
|
3267
|
+
}
|
|
3157
3268
|
if (rows.length) {
|
|
3158
3269
|
await (0, import_timescale2.upsertDerivatives)(rows);
|
|
3159
3270
|
totalRows += rows.length;
|
|
3160
3271
|
}
|
|
3161
|
-
const
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
(rowsCountBySymbol.get(symbol) ?? 0) + 1
|
|
3167
|
-
);
|
|
3168
|
-
}
|
|
3169
|
-
const coverageRows = missingBatch.map((item) => {
|
|
3170
|
-
const normalizedSymbol = item.symbol.toUpperCase();
|
|
3171
|
-
const rowsCount = rowsCountBySymbol.get(normalizedSymbol) ?? 0;
|
|
3172
|
-
return {
|
|
3173
|
-
source: "coinalyze",
|
|
3174
|
-
symbol: item.symbol,
|
|
3175
|
-
interval,
|
|
3176
|
-
fromMs: cursor,
|
|
3177
|
-
toMs,
|
|
3178
|
-
rowsCount
|
|
3179
|
-
};
|
|
3272
|
+
const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
3273
|
+
interval,
|
|
3274
|
+
fromMs: group.fromMs,
|
|
3275
|
+
toMs,
|
|
3276
|
+
nowMs: coverageNowMs
|
|
3180
3277
|
});
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
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
|
+
}
|
|
3308
|
+
if (mode === "backtest") {
|
|
3309
|
+
const rowsCountBySymbol = /* @__PURE__ */ new Map();
|
|
3310
|
+
for (const row of rows) {
|
|
3311
|
+
const symbol = row.symbol.toUpperCase();
|
|
3312
|
+
rowsCountBySymbol.set(
|
|
3190
3313
|
symbol,
|
|
3314
|
+
(rowsCountBySymbol.get(symbol) ?? 0) + 1
|
|
3315
|
+
);
|
|
3316
|
+
}
|
|
3317
|
+
const coverageRows = missingBatch.map((item) => {
|
|
3318
|
+
const normalizedSymbol = item.symbol.toUpperCase();
|
|
3319
|
+
const rowsCount = rowsCountBySymbol.get(normalizedSymbol) ?? 0;
|
|
3320
|
+
return {
|
|
3321
|
+
source: "coinalyze",
|
|
3322
|
+
symbol: item.symbol,
|
|
3191
3323
|
interval,
|
|
3192
3324
|
fromMs: cursor,
|
|
3193
|
-
toMs
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3325
|
+
toMs,
|
|
3326
|
+
rowsCount
|
|
3327
|
+
};
|
|
3328
|
+
});
|
|
3329
|
+
await (0, import_timescale2.upsertDerivativesBackfillCoverage)(coverageRows);
|
|
3330
|
+
for (const coverageRow of coverageRows) {
|
|
3331
|
+
const symbol = coverageRow.symbol.toUpperCase();
|
|
3332
|
+
edgesBySymbol.set(
|
|
3333
|
+
symbol,
|
|
3334
|
+
extendEdges(edgesBySymbol.get(symbol), cursor, toMs)
|
|
3335
|
+
);
|
|
3336
|
+
coverageKeys.add(
|
|
3337
|
+
coverageKey({
|
|
3338
|
+
symbol,
|
|
3339
|
+
interval,
|
|
3340
|
+
fromMs: cursor,
|
|
3341
|
+
toMs
|
|
3342
|
+
})
|
|
3343
|
+
);
|
|
3344
|
+
const coverageRanges = coverageRangesBySymbol.get(symbol) ?? [];
|
|
3345
|
+
coverageRanges.push({ fromMs: cursor, toMs });
|
|
3346
|
+
coverageRangesBySymbol.set(symbol, coverageRanges);
|
|
3347
|
+
}
|
|
3199
3348
|
}
|
|
3200
3349
|
}
|
|
3201
3350
|
}
|
|
@@ -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
|
)
|