@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
package/dist/strategies.js
CHANGED
|
@@ -2909,13 +2909,210 @@ var assessSignalWithJev = async ({
|
|
|
2909
2909
|
// src/strategyRuntime.ts
|
|
2910
2910
|
var import_constants5 = require("@tradejs/core/constants");
|
|
2911
2911
|
var import_strategies6 = require("@tradejs/core/strategies");
|
|
2912
|
-
var
|
|
2912
|
+
var import_logger11 = require("@tradejs/infra/logger");
|
|
2913
2913
|
|
|
2914
2914
|
// src/strategyHelpers/runtime.ts
|
|
2915
|
-
var
|
|
2915
|
+
var import_logger9 = require("@tradejs/infra/logger");
|
|
2916
2916
|
var import_constants3 = require("@tradejs/core/constants");
|
|
2917
2917
|
var import_ml2 = require("@tradejs/infra/ml");
|
|
2918
2918
|
|
|
2919
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
2920
|
+
var import_logger2 = require("@tradejs/infra/logger");
|
|
2921
|
+
|
|
2922
|
+
// src/strategy/entryExecutionValidation.ts
|
|
2923
|
+
var EntrySubmissionValidationError = class extends Error {
|
|
2924
|
+
constructor() {
|
|
2925
|
+
super(...arguments);
|
|
2926
|
+
this.name = "EntrySubmissionValidationError";
|
|
2927
|
+
}
|
|
2928
|
+
};
|
|
2929
|
+
var resolveEntryExecutionLimits = (config, direction) => {
|
|
2930
|
+
const side = config[direction];
|
|
2931
|
+
const minimum = side && typeof side === "object" && "minRiskRatio" in side ? side.minRiskRatio : void 0;
|
|
2932
|
+
const maximum = config.MAX_LOSS_VALUE;
|
|
2933
|
+
if (minimum == null && maximum == null) return void 0;
|
|
2934
|
+
const minimumNetRiskRatio = minimum == null ? void 0 : Number(minimum);
|
|
2935
|
+
const maxLossValue = maximum == null ? void 0 : Number(maximum);
|
|
2936
|
+
const feeRate = Number(config.RISK_FEE_RATE ?? 0);
|
|
2937
|
+
const slippage = Number(config.RISK_SLIPPAGE_BPS ?? 0);
|
|
2938
|
+
const impact = Number(config.RISK_MARKET_IMPACT_BPS ?? 0);
|
|
2939
|
+
const slippageBps = slippage + impact;
|
|
2940
|
+
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)
|
|
2941
|
+
throw new Error("INVALID_ENTRY_EXECUTION_LIMITS");
|
|
2942
|
+
return { minimumNetRiskRatio, maxLossValue, feeRate, slippageBps };
|
|
2943
|
+
};
|
|
2944
|
+
var validateEntryExecution = ({
|
|
2945
|
+
direction,
|
|
2946
|
+
price,
|
|
2947
|
+
qty,
|
|
2948
|
+
stopLossPrice,
|
|
2949
|
+
takeProfitPrice,
|
|
2950
|
+
limits
|
|
2951
|
+
}) => {
|
|
2952
|
+
if (!Number.isFinite(price) || price <= 0 || !Number.isFinite(qty) || qty <= 0 || stopLossPrice == null || !Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || !Number.isFinite(takeProfitPrice) || takeProfitPrice <= 0)
|
|
2953
|
+
throw new EntrySubmissionValidationError(
|
|
2954
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
2955
|
+
);
|
|
2956
|
+
const side = direction === "LONG" ? 1 : -1;
|
|
2957
|
+
if (side * (price - stopLossPrice) <= 0 || side * (takeProfitPrice - price) <= 0)
|
|
2958
|
+
throw new EntrySubmissionValidationError(
|
|
2959
|
+
"INVALID_ENTRY_PROTECTION_AT_SUBMISSION"
|
|
2960
|
+
);
|
|
2961
|
+
const slippage = limits.slippageBps / 1e4;
|
|
2962
|
+
const cost = (value, sign) => value * slippage + value * (1 + sign * slippage) * limits.feeRate;
|
|
2963
|
+
const entryCost = cost(price, side);
|
|
2964
|
+
const lossPerUnit = Math.abs(price - stopLossPrice) + entryCost + cost(stopLossPrice, -side);
|
|
2965
|
+
const rewardPerUnit = Math.max(
|
|
2966
|
+
0,
|
|
2967
|
+
Math.abs(takeProfitPrice - price) - entryCost - cost(takeProfitPrice, -side)
|
|
2968
|
+
);
|
|
2969
|
+
const netRiskRatio = rewardPerUnit / lossPerUnit;
|
|
2970
|
+
const plannedLossValue = qty * lossPerUnit;
|
|
2971
|
+
if (!Number.isFinite(netRiskRatio) || !Number.isFinite(plannedLossValue))
|
|
2972
|
+
throw new EntrySubmissionValidationError(
|
|
2973
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
2974
|
+
);
|
|
2975
|
+
if (limits.minimumNetRiskRatio != null && netRiskRatio <= limits.minimumNetRiskRatio)
|
|
2976
|
+
throw new EntrySubmissionValidationError(
|
|
2977
|
+
"ENTRY_RR_BELOW_MIN_AT_SUBMISSION"
|
|
2978
|
+
);
|
|
2979
|
+
const riskExceeded = limits.maxLossValue != null && plannedLossValue > limits.maxLossValue * (1 + 1e-9);
|
|
2980
|
+
return { netRiskRatio, plannedLossValue, riskExceeded };
|
|
2981
|
+
};
|
|
2982
|
+
|
|
2983
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
2984
|
+
var MAX_ENTRY_QUOTE_AGE_MS = 5e3;
|
|
2985
|
+
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
2986
|
+
var getOrderArrivalSnapshot = async ({
|
|
2987
|
+
connector,
|
|
2988
|
+
symbol,
|
|
2989
|
+
requireTimestamp = false
|
|
2990
|
+
}) => {
|
|
2991
|
+
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
2992
|
+
return {
|
|
2993
|
+
arrivalSnapshotTime: Date.now(),
|
|
2994
|
+
arrivalSource: "unavailable",
|
|
2995
|
+
bid: null,
|
|
2996
|
+
ask: null,
|
|
2997
|
+
arrivalMid: null,
|
|
2998
|
+
spreadBps: null
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
3001
|
+
try {
|
|
3002
|
+
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
3003
|
+
const arrivalSnapshotTime = toFiniteNumberOrNull(ticker?.timestamp);
|
|
3004
|
+
const timestampUnavailable = requireTimestamp && arrivalSnapshotTime == null;
|
|
3005
|
+
const bid = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.bidPrice);
|
|
3006
|
+
const ask = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.askPrice);
|
|
3007
|
+
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
3008
|
+
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
3009
|
+
return {
|
|
3010
|
+
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
3011
|
+
arrivalSource: "top_of_book",
|
|
3012
|
+
bid,
|
|
3013
|
+
ask,
|
|
3014
|
+
arrivalMid,
|
|
3015
|
+
spreadBps
|
|
3016
|
+
};
|
|
3017
|
+
} catch (error) {
|
|
3018
|
+
import_logger2.logger.warn(
|
|
3019
|
+
"runtime order arrival snapshot failed: %s %s",
|
|
3020
|
+
symbol,
|
|
3021
|
+
error?.message || String(error)
|
|
3022
|
+
);
|
|
3023
|
+
return {
|
|
3024
|
+
arrivalSnapshotTime: Date.now(),
|
|
3025
|
+
arrivalSource: "top_of_book_error",
|
|
3026
|
+
bid: null,
|
|
3027
|
+
ask: null,
|
|
3028
|
+
arrivalMid: null,
|
|
3029
|
+
spreadBps: null
|
|
3030
|
+
};
|
|
3031
|
+
}
|
|
3032
|
+
};
|
|
3033
|
+
var validateEntryProtectionAtArrival = ({
|
|
3034
|
+
direction,
|
|
3035
|
+
signalPrice,
|
|
3036
|
+
bid,
|
|
3037
|
+
ask,
|
|
3038
|
+
arrivalMid,
|
|
3039
|
+
takeProfits,
|
|
3040
|
+
stopLossPrice
|
|
3041
|
+
}) => {
|
|
3042
|
+
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
3043
|
+
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
3044
|
+
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
3045
|
+
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
3046
|
+
}
|
|
3047
|
+
for (const takeProfit of takeProfits) {
|
|
3048
|
+
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
3049
|
+
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
3053
|
+
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
3054
|
+
}
|
|
3055
|
+
};
|
|
3056
|
+
var createEntrySubmissionValidation = ({
|
|
3057
|
+
connector,
|
|
3058
|
+
symbol,
|
|
3059
|
+
direction,
|
|
3060
|
+
takeProfits,
|
|
3061
|
+
stopLossPrice,
|
|
3062
|
+
limits,
|
|
3063
|
+
onValidated
|
|
3064
|
+
}) => {
|
|
3065
|
+
if (!limits) return void 0;
|
|
3066
|
+
if (!connector.capabilities?.orderSubmissionValidation)
|
|
3067
|
+
throw new EntrySubmissionValidationError(
|
|
3068
|
+
"ENTRY_SUBMISSION_VALIDATION_UNSUPPORTED"
|
|
3069
|
+
);
|
|
3070
|
+
return async ({ qty, executionPrice }) => {
|
|
3071
|
+
const simulated = Reflect.get(connector, "__tradejsTestConnector") === true;
|
|
3072
|
+
const snapshot2 = simulated && executionPrice != null ? {
|
|
3073
|
+
arrivalSnapshotTime: Date.now(),
|
|
3074
|
+
arrivalSource: "simulation",
|
|
3075
|
+
bid: executionPrice,
|
|
3076
|
+
ask: executionPrice,
|
|
3077
|
+
arrivalMid: executionPrice,
|
|
3078
|
+
spreadBps: 0
|
|
3079
|
+
} : await getOrderArrivalSnapshot({
|
|
3080
|
+
connector,
|
|
3081
|
+
symbol,
|
|
3082
|
+
requireTimestamp: true
|
|
3083
|
+
});
|
|
3084
|
+
const age = Date.now() - snapshot2.arrivalSnapshotTime;
|
|
3085
|
+
if (snapshot2.bid == null || snapshot2.ask == null || !Number.isFinite(snapshot2.bid) || !Number.isFinite(snapshot2.ask) || snapshot2.bid <= 0 || snapshot2.ask < snapshot2.bid)
|
|
3086
|
+
throw new EntrySubmissionValidationError(
|
|
3087
|
+
"ENTRY_QUOTE_UNAVAILABLE_AT_SUBMISSION"
|
|
3088
|
+
);
|
|
3089
|
+
if (!Number.isFinite(age) || age < -MAX_ENTRY_QUOTE_AGE_MS || age > MAX_ENTRY_QUOTE_AGE_MS)
|
|
3090
|
+
throw new EntrySubmissionValidationError(
|
|
3091
|
+
"ENTRY_QUOTE_STALE_AT_SUBMISSION"
|
|
3092
|
+
);
|
|
3093
|
+
const price = direction === "LONG" ? snapshot2.ask : snapshot2.bid;
|
|
3094
|
+
validateEntryProtectionAtArrival({
|
|
3095
|
+
direction,
|
|
3096
|
+
signalPrice: price,
|
|
3097
|
+
bid: snapshot2.bid,
|
|
3098
|
+
ask: snapshot2.ask,
|
|
3099
|
+
arrivalMid: snapshot2.arrivalMid,
|
|
3100
|
+
takeProfits,
|
|
3101
|
+
stopLossPrice
|
|
3102
|
+
});
|
|
3103
|
+
const prices = takeProfits.map((tp) => tp.price);
|
|
3104
|
+
const economics2 = validateEntryExecution({
|
|
3105
|
+
direction,
|
|
3106
|
+
price,
|
|
3107
|
+
qty,
|
|
3108
|
+
stopLossPrice,
|
|
3109
|
+
takeProfitPrice: direction === "LONG" ? Math.max(...prices) : Math.min(...prices),
|
|
3110
|
+
limits
|
|
3111
|
+
});
|
|
3112
|
+
onValidated?.(snapshot2, economics2);
|
|
3113
|
+
};
|
|
3114
|
+
};
|
|
3115
|
+
|
|
2919
3116
|
// src/strategyAdapters/ml.ts
|
|
2920
3117
|
var defaultMlAdapter = {
|
|
2921
3118
|
normalizeStrategyConfig: (strategyConfig) => strategyConfig
|
|
@@ -2969,7 +3166,7 @@ var import_node_crypto = require("crypto");
|
|
|
2969
3166
|
var import_constants = require("@tradejs/core/constants");
|
|
2970
3167
|
var import_time = require("@tradejs/core/time");
|
|
2971
3168
|
var import_trade = require("@tradejs/core/trade");
|
|
2972
|
-
var
|
|
3169
|
+
var import_logger3 = require("@tradejs/infra/logger");
|
|
2973
3170
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
2974
3171
|
var now = () => Date.now();
|
|
2975
3172
|
var toRandomOrderSuffix = () => (0, import_node_crypto.randomUUID)().replace(/-/g, "").slice(0, 12).toLowerCase();
|
|
@@ -3028,7 +3225,7 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
3028
3225
|
)
|
|
3029
3226
|
]);
|
|
3030
3227
|
} catch (error) {
|
|
3031
|
-
|
|
3228
|
+
import_logger3.logger.error(
|
|
3032
3229
|
"runtime trade open journal failed: %s %s",
|
|
3033
3230
|
record.symbol,
|
|
3034
3231
|
error?.message || String(error)
|
|
@@ -3096,7 +3293,7 @@ var recordRuntimeTradeIncrease = async (params) => {
|
|
|
3096
3293
|
)
|
|
3097
3294
|
]);
|
|
3098
3295
|
} catch (error) {
|
|
3099
|
-
|
|
3296
|
+
import_logger3.logger.error(
|
|
3100
3297
|
"runtime trade increase journal failed: %s %s",
|
|
3101
3298
|
symbol,
|
|
3102
3299
|
error?.message || String(error)
|
|
@@ -3203,7 +3400,7 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3203
3400
|
)
|
|
3204
3401
|
]);
|
|
3205
3402
|
} catch (error) {
|
|
3206
|
-
|
|
3403
|
+
import_logger3.logger.error(
|
|
3207
3404
|
"runtime trade close journal failed: %s %s",
|
|
3208
3405
|
symbol,
|
|
3209
3406
|
error?.message || String(error)
|
|
@@ -3213,11 +3410,11 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3213
3410
|
};
|
|
3214
3411
|
|
|
3215
3412
|
// src/strategyHelpers/marketContextStages.ts
|
|
3216
|
-
var
|
|
3413
|
+
var import_logger8 = require("@tradejs/infra/logger");
|
|
3217
3414
|
|
|
3218
3415
|
// src/strategyHelpers/binanceMarketContext.ts
|
|
3219
3416
|
var import_marketContext = require("@tradejs/infra/timescale/marketContext");
|
|
3220
|
-
var
|
|
3417
|
+
var import_logger4 = require("@tradejs/infra/logger");
|
|
3221
3418
|
var import_strategies = require("@tradejs/core/strategies");
|
|
3222
3419
|
|
|
3223
3420
|
// src/binanceBreadthUniverses.ts
|
|
@@ -3593,7 +3790,7 @@ var parseEnabledFlag = (value, env) => {
|
|
|
3593
3790
|
if (normalized === "live") return env !== "BACKTEST";
|
|
3594
3791
|
return false;
|
|
3595
3792
|
};
|
|
3596
|
-
var
|
|
3793
|
+
var toFiniteNumberOrNull2 = (value) => {
|
|
3597
3794
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
3598
3795
|
return Number.isFinite(numeric) ? numeric : null;
|
|
3599
3796
|
};
|
|
@@ -3858,14 +4055,14 @@ var toTradeFlowContext = (row, interval) => row ? {
|
|
|
3858
4055
|
asOfTs: row.ts.getTime(),
|
|
3859
4056
|
ageMs: row.ageMs,
|
|
3860
4057
|
stale: row.stale,
|
|
3861
|
-
trades:
|
|
3862
|
-
buyPressurePct:
|
|
3863
|
-
buyBaseVolume:
|
|
3864
|
-
sellBaseVolume:
|
|
3865
|
-
buyQuoteVolume:
|
|
3866
|
-
sellQuoteVolume:
|
|
3867
|
-
netBaseDelta:
|
|
3868
|
-
netQuoteDelta:
|
|
4058
|
+
trades: toFiniteNumberOrNull2(row.trades),
|
|
4059
|
+
buyPressurePct: toFiniteNumberOrNull2(row.buyPressurePct),
|
|
4060
|
+
buyBaseVolume: toFiniteNumberOrNull2(row.buyBaseVolume),
|
|
4061
|
+
sellBaseVolume: toFiniteNumberOrNull2(row.sellBaseVolume),
|
|
4062
|
+
buyQuoteVolume: toFiniteNumberOrNull2(row.buyQuoteVolume),
|
|
4063
|
+
sellQuoteVolume: toFiniteNumberOrNull2(row.sellQuoteVolume),
|
|
4064
|
+
netBaseDelta: toFiniteNumberOrNull2(row.netBaseDelta),
|
|
4065
|
+
netQuoteDelta: toFiniteNumberOrNull2(row.netQuoteDelta)
|
|
3869
4066
|
} : null;
|
|
3870
4067
|
var getCachedReferenceRows = ({
|
|
3871
4068
|
referenceSymbols,
|
|
@@ -3954,16 +4151,16 @@ var toMarketBreadthContext = (breadth, interval) => ({
|
|
|
3954
4151
|
asOfTs: breadth.ts.getTime(),
|
|
3955
4152
|
ageMs: breadth.ageMs,
|
|
3956
4153
|
stale: breadth.stale,
|
|
3957
|
-
symbolsCount:
|
|
3958
|
-
advancers:
|
|
3959
|
-
decliners:
|
|
3960
|
-
unchanged:
|
|
3961
|
-
advanceDeclineRatio:
|
|
3962
|
-
pctAboveMa20:
|
|
3963
|
-
pctAboveMa50:
|
|
3964
|
-
equalWeightedReturn:
|
|
3965
|
-
volumeWeightedReturn:
|
|
3966
|
-
dispersion:
|
|
4154
|
+
symbolsCount: toFiniteNumberOrNull2(breadth.symbolsCount),
|
|
4155
|
+
advancers: toFiniteNumberOrNull2(breadth.advancers),
|
|
4156
|
+
decliners: toFiniteNumberOrNull2(breadth.decliners),
|
|
4157
|
+
unchanged: toFiniteNumberOrNull2(breadth.unchanged),
|
|
4158
|
+
advanceDeclineRatio: toFiniteNumberOrNull2(breadth.advanceDeclineRatio),
|
|
4159
|
+
pctAboveMa20: toFiniteNumberOrNull2(breadth.pctAboveMa20),
|
|
4160
|
+
pctAboveMa50: toFiniteNumberOrNull2(breadth.pctAboveMa50),
|
|
4161
|
+
equalWeightedReturn: toFiniteNumberOrNull2(breadth.equalWeightedReturn),
|
|
4162
|
+
volumeWeightedReturn: toFiniteNumberOrNull2(breadth.volumeWeightedReturn),
|
|
4163
|
+
dispersion: toFiniteNumberOrNull2(breadth.dispersion)
|
|
3967
4164
|
});
|
|
3968
4165
|
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
3969
4166
|
const {
|
|
@@ -4064,46 +4261,46 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
4064
4261
|
asOfTs: primaryBreadth.ts.getTime(),
|
|
4065
4262
|
ageMs: primaryBreadth.ageMs,
|
|
4066
4263
|
stale: primaryBreadth.stale,
|
|
4067
|
-
btcReturn1h:
|
|
4264
|
+
btcReturn1h: toFiniteNumberOrNull2(
|
|
4068
4265
|
primaryBreadth.btcReturn1h
|
|
4069
4266
|
),
|
|
4070
|
-
btcReturn4h:
|
|
4267
|
+
btcReturn4h: toFiniteNumberOrNull2(
|
|
4071
4268
|
primaryBreadth.btcReturn4h
|
|
4072
4269
|
),
|
|
4073
|
-
btcReturn24h:
|
|
4270
|
+
btcReturn24h: toFiniteNumberOrNull2(
|
|
4074
4271
|
primaryBreadth.btcReturn24h
|
|
4075
4272
|
),
|
|
4076
|
-
altBasketReturn1h:
|
|
4273
|
+
altBasketReturn1h: toFiniteNumberOrNull2(
|
|
4077
4274
|
primaryBreadth.altBasketReturn1h
|
|
4078
4275
|
),
|
|
4079
|
-
altBasketReturn4h:
|
|
4276
|
+
altBasketReturn4h: toFiniteNumberOrNull2(
|
|
4080
4277
|
primaryBreadth.altBasketReturn4h
|
|
4081
4278
|
),
|
|
4082
|
-
altBasketReturn24h:
|
|
4279
|
+
altBasketReturn24h: toFiniteNumberOrNull2(
|
|
4083
4280
|
primaryBreadth.altBasketReturn24h
|
|
4084
4281
|
),
|
|
4085
|
-
btcVsAltReturn1h:
|
|
4282
|
+
btcVsAltReturn1h: toFiniteNumberOrNull2(
|
|
4086
4283
|
primaryBreadth.btcVsAltReturn1h
|
|
4087
4284
|
),
|
|
4088
|
-
btcVsAltReturn4h:
|
|
4285
|
+
btcVsAltReturn4h: toFiniteNumberOrNull2(
|
|
4089
4286
|
primaryBreadth.btcVsAltReturn4h
|
|
4090
4287
|
),
|
|
4091
|
-
btcVsAltReturn24h:
|
|
4288
|
+
btcVsAltReturn24h: toFiniteNumberOrNull2(
|
|
4092
4289
|
primaryBreadth.btcVsAltReturn24h
|
|
4093
4290
|
),
|
|
4094
|
-
btcTurnoverShare1h:
|
|
4291
|
+
btcTurnoverShare1h: toFiniteNumberOrNull2(
|
|
4095
4292
|
primaryBreadth.btcTurnoverShare1h
|
|
4096
4293
|
),
|
|
4097
|
-
btcTurnoverShare24h:
|
|
4294
|
+
btcTurnoverShare24h: toFiniteNumberOrNull2(
|
|
4098
4295
|
primaryBreadth.btcTurnoverShare24h
|
|
4099
4296
|
),
|
|
4100
|
-
btcTurnoverShareChange24h:
|
|
4297
|
+
btcTurnoverShareChange24h: toFiniteNumberOrNull2(
|
|
4101
4298
|
primaryBreadth.btcTurnoverShareChange24h
|
|
4102
4299
|
),
|
|
4103
|
-
altVolToBtcVol24h:
|
|
4300
|
+
altVolToBtcVol24h: toFiniteNumberOrNull2(
|
|
4104
4301
|
primaryBreadth.altVolToBtcVol24h
|
|
4105
4302
|
),
|
|
4106
|
-
altDispersion24h:
|
|
4303
|
+
altDispersion24h: toFiniteNumberOrNull2(
|
|
4107
4304
|
primaryBreadth.altDispersion24h
|
|
4108
4305
|
),
|
|
4109
4306
|
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
@@ -4120,7 +4317,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
4120
4317
|
throw error;
|
|
4121
4318
|
}
|
|
4122
4319
|
binanceMarketContextUnavailable = true;
|
|
4123
|
-
|
|
4320
|
+
import_logger4.logger.warn(
|
|
4124
4321
|
"Binance market context disabled after Timescale read failure: %s",
|
|
4125
4322
|
String(error)
|
|
4126
4323
|
);
|
|
@@ -4130,7 +4327,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
4130
4327
|
|
|
4131
4328
|
// src/strategyHelpers/coinMarketCapContext.ts
|
|
4132
4329
|
var import_strategies2 = require("@tradejs/core/strategies");
|
|
4133
|
-
var
|
|
4330
|
+
var import_logger5 = require("@tradejs/infra/logger");
|
|
4134
4331
|
var import_marketContext2 = require("@tradejs/infra/timescale/marketContext");
|
|
4135
4332
|
var DEFAULT_MAX_AGE_MS = 48 * 60 * 6e4;
|
|
4136
4333
|
var SOURCE_GLOBAL_DAILY = "coinmarketcap_global";
|
|
@@ -4168,7 +4365,7 @@ var asInt = (value, fallback) => {
|
|
|
4168
4365
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
4169
4366
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
4170
4367
|
};
|
|
4171
|
-
var
|
|
4368
|
+
var toFiniteNumberOrNull3 = (value) => {
|
|
4172
4369
|
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
4173
4370
|
return Number.isFinite(numeric) ? numeric : null;
|
|
4174
4371
|
};
|
|
@@ -4402,12 +4599,12 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4402
4599
|
const ethRow = references.get("ETHUSDT") ?? null;
|
|
4403
4600
|
const previousBtcRow = previousReferences.get("BTCUSDT") ?? null;
|
|
4404
4601
|
const previousEthRow = previousReferences.get("ETHUSDT") ?? null;
|
|
4405
|
-
const btcMarketCapUsd =
|
|
4406
|
-
const ethMarketCapUsd =
|
|
4407
|
-
const previousBtcMarketCapUsd =
|
|
4602
|
+
const btcMarketCapUsd = toFiniteNumberOrNull3(btcRow?.marketCapUsd);
|
|
4603
|
+
const ethMarketCapUsd = toFiniteNumberOrNull3(ethRow?.marketCapUsd);
|
|
4604
|
+
const previousBtcMarketCapUsd = toFiniteNumberOrNull3(
|
|
4408
4605
|
previousBtcRow?.marketCapUsd
|
|
4409
4606
|
);
|
|
4410
|
-
const previousEthMarketCapUsd =
|
|
4607
|
+
const previousEthMarketCapUsd = toFiniteNumberOrNull3(
|
|
4411
4608
|
previousEthRow?.marketCapUsd
|
|
4412
4609
|
);
|
|
4413
4610
|
const ethBtcMarketCapRatio = safeDivide(ethMarketCapUsd, btcMarketCapUsd);
|
|
@@ -4416,16 +4613,16 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4416
4613
|
previousBtcMarketCapUsd
|
|
4417
4614
|
);
|
|
4418
4615
|
const ethBtcMarketCapRatioChange24hPct = ethBtcMarketCapRatio != null && previousEthBtcMarketCapRatio != null && previousEthBtcMarketCapRatio > 0 ? (ethBtcMarketCapRatio - previousEthBtcMarketCapRatio) / previousEthBtcMarketCapRatio : null;
|
|
4419
|
-
const btcVolumeUsd =
|
|
4420
|
-
const ethVolumeUsd =
|
|
4616
|
+
const btcVolumeUsd = toFiniteNumberOrNull3(btcRow?.volumeUsd);
|
|
4617
|
+
const ethVolumeUsd = toFiniteNumberOrNull3(ethRow?.volumeUsd);
|
|
4421
4618
|
const referenceStale = btcRow?.stale === true || ethRow?.stale === true || !btcRow || !ethRow;
|
|
4422
|
-
const btcDominanceChange24hPct =
|
|
4619
|
+
const btcDominanceChange24hPct = toFiniteNumberOrNull3(
|
|
4423
4620
|
globalRow?.btcDominanceChange24hPct
|
|
4424
4621
|
);
|
|
4425
|
-
const altMarketCapChange24hPct =
|
|
4622
|
+
const altMarketCapChange24hPct = toFiniteNumberOrNull3(
|
|
4426
4623
|
globalRow?.altMarketCapChange24hPct
|
|
4427
4624
|
);
|
|
4428
|
-
const altVolumeChange24hPct =
|
|
4625
|
+
const altVolumeChange24hPct = toFiniteNumberOrNull3(
|
|
4429
4626
|
globalRow?.altVolumeChange24hPct
|
|
4430
4627
|
);
|
|
4431
4628
|
const altLiquidityRegime = globalRow ? toAltLiquidityRegime({
|
|
@@ -4436,19 +4633,19 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4436
4633
|
}) : "unknown";
|
|
4437
4634
|
const exchangeLiquidityRegime = exchangeLiquidityRow ? toExchangeLiquidityRegime({
|
|
4438
4635
|
stale: exchangeLiquidityRow.stale,
|
|
4439
|
-
totalVolumeChange24hPct:
|
|
4636
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4440
4637
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4441
4638
|
),
|
|
4442
4639
|
fallback: exchangeLiquidityRow.liquidityRegime ?? "unknown"
|
|
4443
4640
|
}) : "unknown";
|
|
4444
4641
|
const cmc100Row = indexRows.get("cmc100") ?? null;
|
|
4445
4642
|
const cmc20Row = indexRows.get("cmc20") ?? null;
|
|
4446
|
-
const cmc100Value =
|
|
4447
|
-
const cmc20Value =
|
|
4448
|
-
const cmc100Change24hPct =
|
|
4643
|
+
const cmc100Value = toFiniteNumberOrNull3(cmc100Row?.value);
|
|
4644
|
+
const cmc20Value = toFiniteNumberOrNull3(cmc20Row?.value);
|
|
4645
|
+
const cmc100Change24hPct = toFiniteNumberOrNull3(
|
|
4449
4646
|
cmc100Row?.valueChange24hPct
|
|
4450
4647
|
);
|
|
4451
|
-
const cmc20Change24hPct =
|
|
4648
|
+
const cmc20Change24hPct = toFiniteNumberOrNull3(cmc20Row?.valueChange24hPct);
|
|
4452
4649
|
const cmc20ToCmc100Ratio = safeDivide(cmc20Value, cmc100Value);
|
|
4453
4650
|
const cmc20ToCmc100RatioChange24hPct = cmc20Change24hPct != null && cmc100Change24hPct != null ? (1 + cmc20Change24hPct) / (1 + cmc100Change24hPct) - 1 : null;
|
|
4454
4651
|
const indexStale = cmc100Row?.stale === true || cmc20Row?.stale === true || !cmc100Row || !cmc20Row;
|
|
@@ -4472,41 +4669,41 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4472
4669
|
asOfTs: globalRow.ts.getTime(),
|
|
4473
4670
|
ageMs: globalRow.ageMs,
|
|
4474
4671
|
stale: globalRow.stale,
|
|
4475
|
-
totalMarketCapUsd:
|
|
4672
|
+
totalMarketCapUsd: toFiniteNumberOrNull3(
|
|
4476
4673
|
globalRow.totalMarketCapUsd
|
|
4477
4674
|
),
|
|
4478
|
-
totalVolumeUsd:
|
|
4675
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4479
4676
|
globalRow.totalVolumeUsd
|
|
4480
4677
|
),
|
|
4481
|
-
totalVolumeReportedUsd:
|
|
4678
|
+
totalVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4482
4679
|
globalRow.totalVolumeReportedUsd
|
|
4483
4680
|
),
|
|
4484
|
-
altMarketCapUsd:
|
|
4681
|
+
altMarketCapUsd: toFiniteNumberOrNull3(
|
|
4485
4682
|
globalRow.altMarketCapUsd
|
|
4486
4683
|
),
|
|
4487
|
-
altVolumeUsd:
|
|
4488
|
-
altVolumeReportedUsd:
|
|
4684
|
+
altVolumeUsd: toFiniteNumberOrNull3(globalRow.altVolumeUsd),
|
|
4685
|
+
altVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4489
4686
|
globalRow.altVolumeReportedUsd
|
|
4490
4687
|
),
|
|
4491
|
-
btcDominancePct:
|
|
4688
|
+
btcDominancePct: toFiniteNumberOrNull3(
|
|
4492
4689
|
globalRow.btcDominancePct
|
|
4493
4690
|
),
|
|
4494
|
-
ethDominancePct:
|
|
4691
|
+
ethDominancePct: toFiniteNumberOrNull3(
|
|
4495
4692
|
globalRow.ethDominancePct
|
|
4496
4693
|
),
|
|
4497
4694
|
btcDominanceChange24hPct,
|
|
4498
|
-
ethDominanceChange24hPct:
|
|
4695
|
+
ethDominanceChange24hPct: toFiniteNumberOrNull3(
|
|
4499
4696
|
globalRow.ethDominanceChange24hPct
|
|
4500
4697
|
),
|
|
4501
4698
|
altMarketCapChange24hPct,
|
|
4502
4699
|
altVolumeChange24hPct,
|
|
4503
|
-
activeCryptocurrencies:
|
|
4700
|
+
activeCryptocurrencies: toFiniteNumberOrNull3(
|
|
4504
4701
|
globalRow.activeCryptocurrencies
|
|
4505
4702
|
),
|
|
4506
|
-
activeExchanges:
|
|
4703
|
+
activeExchanges: toFiniteNumberOrNull3(
|
|
4507
4704
|
globalRow.activeExchanges
|
|
4508
4705
|
),
|
|
4509
|
-
activeMarketPairs:
|
|
4706
|
+
activeMarketPairs: toFiniteNumberOrNull3(
|
|
4510
4707
|
globalRow.activeMarketPairs
|
|
4511
4708
|
),
|
|
4512
4709
|
altLiquidityRegime
|
|
@@ -4551,22 +4748,22 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4551
4748
|
asOfTs: exchangeLiquidityRow.ts.getTime(),
|
|
4552
4749
|
ageMs: exchangeLiquidityRow.ageMs,
|
|
4553
4750
|
stale: exchangeLiquidityRow.stale,
|
|
4554
|
-
exchangesCount:
|
|
4751
|
+
exchangesCount: toFiniteNumberOrNull3(
|
|
4555
4752
|
exchangeLiquidityRow.exchangesCount
|
|
4556
4753
|
),
|
|
4557
|
-
totalVolumeUsd:
|
|
4754
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4558
4755
|
exchangeLiquidityRow.totalVolumeUsd
|
|
4559
4756
|
),
|
|
4560
|
-
totalVolumeChange24hPct:
|
|
4757
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4561
4758
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4562
4759
|
),
|
|
4563
|
-
binanceVolumeUsd:
|
|
4760
|
+
binanceVolumeUsd: toFiniteNumberOrNull3(
|
|
4564
4761
|
exchangeLiquidityRow.binanceVolumeUsd
|
|
4565
4762
|
),
|
|
4566
|
-
binanceVolumeShare:
|
|
4763
|
+
binanceVolumeShare: toFiniteNumberOrNull3(
|
|
4567
4764
|
exchangeLiquidityRow.binanceVolumeShare
|
|
4568
4765
|
),
|
|
4569
|
-
topExchangeVolumeShare:
|
|
4766
|
+
topExchangeVolumeShare: toFiniteNumberOrNull3(
|
|
4570
4767
|
exchangeLiquidityRow.topExchangeVolumeShare
|
|
4571
4768
|
),
|
|
4572
4769
|
liquidityRegime: exchangeLiquidityRegime
|
|
@@ -4579,11 +4776,11 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4579
4776
|
asOfTs: fearGreedRow.ts.getTime(),
|
|
4580
4777
|
ageMs: fearGreedRow.ageMs,
|
|
4581
4778
|
stale: fearGreedRow.stale,
|
|
4582
|
-
value:
|
|
4583
|
-
valueChange24h:
|
|
4779
|
+
value: toFiniteNumberOrNull3(fearGreedRow.value),
|
|
4780
|
+
valueChange24h: toFiniteNumberOrNull3(
|
|
4584
4781
|
fearGreedRow.valueChange24h
|
|
4585
4782
|
),
|
|
4586
|
-
valueChange7d:
|
|
4783
|
+
valueChange7d: toFiniteNumberOrNull3(
|
|
4587
4784
|
fearGreedRow.valueChange7d
|
|
4588
4785
|
),
|
|
4589
4786
|
classification: fearGreedRow.classification ?? "Unknown",
|
|
@@ -4603,13 +4800,13 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4603
4800
|
cmc100Value,
|
|
4604
4801
|
cmc100Change24hPct,
|
|
4605
4802
|
cmc100TopConstituentSymbol: cmc100Row?.topConstituentSymbol ?? null,
|
|
4606
|
-
cmc100TopConstituentWeightPct:
|
|
4803
|
+
cmc100TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4607
4804
|
cmc100Row?.topConstituentWeightPct
|
|
4608
4805
|
),
|
|
4609
4806
|
cmc20Value,
|
|
4610
4807
|
cmc20Change24hPct,
|
|
4611
4808
|
cmc20TopConstituentSymbol: cmc20Row?.topConstituentSymbol ?? null,
|
|
4612
|
-
cmc20TopConstituentWeightPct:
|
|
4809
|
+
cmc20TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4613
4810
|
cmc20Row?.topConstituentWeightPct
|
|
4614
4811
|
),
|
|
4615
4812
|
cmc20ToCmc100Ratio,
|
|
@@ -4627,7 +4824,7 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4627
4824
|
throw error;
|
|
4628
4825
|
}
|
|
4629
4826
|
coinMarketCapContextUnavailable = true;
|
|
4630
|
-
|
|
4827
|
+
import_logger5.logger.warn(
|
|
4631
4828
|
"CoinMarketCap context disabled after Timescale read failure: %s",
|
|
4632
4829
|
String(error)
|
|
4633
4830
|
);
|
|
@@ -4641,7 +4838,7 @@ var import_data2 = require("@tradejs/core/data");
|
|
|
4641
4838
|
var import_strategies3 = require("@tradejs/core/strategies");
|
|
4642
4839
|
var import_constants2 = require("@tradejs/core/constants");
|
|
4643
4840
|
var import_derivatives = require("@tradejs/infra/timescale/derivatives");
|
|
4644
|
-
var
|
|
4841
|
+
var import_logger6 = require("@tradejs/infra/logger");
|
|
4645
4842
|
var STORED_INTERVALS = ["15m", "1h"];
|
|
4646
4843
|
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
4647
4844
|
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
@@ -4696,7 +4893,7 @@ var resolvePrimaryReferenceSymbol2 = () => PRIMARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
|
4696
4893
|
var resolveSecondaryReferenceSymbol = () => SECONDARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
4697
4894
|
var getPrimaryIntervalContext = (context) => context?.intervals["15m"] ?? context?.intervals["1h"] ?? null;
|
|
4698
4895
|
var hasDerivativesSymbolData = (context) => Object.keys(context.intervals).length > 0 && !context.summary.riskFlags.includes("missing_derivatives");
|
|
4699
|
-
var
|
|
4896
|
+
var toFiniteNumberOrNull4 = (value) => {
|
|
4700
4897
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
4701
4898
|
if (typeof value === "string" && value.trim()) {
|
|
4702
4899
|
const parsed = Number(value);
|
|
@@ -4710,8 +4907,8 @@ var roundNullable = (value, digits = 4) => {
|
|
|
4710
4907
|
return Math.round(value * multiplier) / multiplier;
|
|
4711
4908
|
};
|
|
4712
4909
|
var deltaNullable = (targetValue, referenceValue) => {
|
|
4713
|
-
const target =
|
|
4714
|
-
const reference =
|
|
4910
|
+
const target = toFiniteNumberOrNull4(targetValue);
|
|
4911
|
+
const reference = toFiniteNumberOrNull4(referenceValue);
|
|
4715
4912
|
return target == null || reference == null ? null : roundNullable(target - reference);
|
|
4716
4913
|
};
|
|
4717
4914
|
var buildTargetDerivedContext = (params) => {
|
|
@@ -4873,7 +5070,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4873
5070
|
throw error;
|
|
4874
5071
|
}
|
|
4875
5072
|
derivativesContextUnavailable = true;
|
|
4876
|
-
|
|
5073
|
+
import_logger6.logger.warn(
|
|
4877
5074
|
"Derivatives context disabled after Timescale read failure: %s",
|
|
4878
5075
|
String(error)
|
|
4879
5076
|
);
|
|
@@ -4885,7 +5082,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4885
5082
|
var import_data3 = require("@tradejs/core/data");
|
|
4886
5083
|
var import_strategies4 = require("@tradejs/core/strategies");
|
|
4887
5084
|
var import_hyperliquidWhales2 = require("@tradejs/infra/timescale/hyperliquidWhales");
|
|
4888
|
-
var
|
|
5085
|
+
var import_logger7 = require("@tradejs/infra/logger");
|
|
4889
5086
|
|
|
4890
5087
|
// src/hyperliquidWhaleUniverse.ts
|
|
4891
5088
|
var import_node_crypto3 = require("crypto");
|
|
@@ -5409,7 +5606,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
5409
5606
|
throw error;
|
|
5410
5607
|
}
|
|
5411
5608
|
hyperliquidWhaleContextUnavailable = true;
|
|
5412
|
-
|
|
5609
|
+
import_logger7.logger.warn(
|
|
5413
5610
|
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
5414
5611
|
String(error)
|
|
5415
5612
|
);
|
|
@@ -5492,7 +5689,7 @@ var runMarketContextStage = async ({
|
|
|
5492
5689
|
elapsedMs: Date.now() - startedAt
|
|
5493
5690
|
};
|
|
5494
5691
|
if (status === "timed_out") {
|
|
5495
|
-
|
|
5692
|
+
import_logger8.logger.warn(
|
|
5496
5693
|
"Market context stage timed out: %s after %sms",
|
|
5497
5694
|
stage,
|
|
5498
5695
|
result.elapsedMs
|
|
@@ -5682,7 +5879,7 @@ var enrichSignalWithAi = async ({
|
|
|
5682
5879
|
signal.aiAnalysis = analysis;
|
|
5683
5880
|
return resolveAiQuality(analysis, direction);
|
|
5684
5881
|
} catch (err) {
|
|
5685
|
-
|
|
5882
|
+
import_logger9.logger.error("AI analysis error: %s %s", symbol, formatAiError(err));
|
|
5686
5883
|
}
|
|
5687
5884
|
return void 0;
|
|
5688
5885
|
};
|
|
@@ -5699,75 +5896,6 @@ var enrichSignalWithMlAi = async ({
|
|
|
5699
5896
|
await enrichSignalWithMl({ signal, env, ml });
|
|
5700
5897
|
return enrichSignalWithAi({ signal, userName, symbol, direction, env, ai });
|
|
5701
5898
|
};
|
|
5702
|
-
var toFiniteNumberOrNull4 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
5703
|
-
var getOrderArrivalSnapshot = async ({
|
|
5704
|
-
connector,
|
|
5705
|
-
symbol
|
|
5706
|
-
}) => {
|
|
5707
|
-
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
5708
|
-
return {
|
|
5709
|
-
arrivalSnapshotTime: Date.now(),
|
|
5710
|
-
arrivalSource: "unavailable",
|
|
5711
|
-
bid: null,
|
|
5712
|
-
ask: null,
|
|
5713
|
-
arrivalMid: null,
|
|
5714
|
-
spreadBps: null
|
|
5715
|
-
};
|
|
5716
|
-
}
|
|
5717
|
-
try {
|
|
5718
|
-
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
5719
|
-
const arrivalSnapshotTime = toFiniteNumberOrNull4(ticker?.timestamp);
|
|
5720
|
-
const bid = toFiniteNumberOrNull4(ticker?.bidPrice);
|
|
5721
|
-
const ask = toFiniteNumberOrNull4(ticker?.askPrice);
|
|
5722
|
-
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
5723
|
-
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
5724
|
-
return {
|
|
5725
|
-
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
5726
|
-
arrivalSource: "top_of_book",
|
|
5727
|
-
bid,
|
|
5728
|
-
ask,
|
|
5729
|
-
arrivalMid,
|
|
5730
|
-
spreadBps
|
|
5731
|
-
};
|
|
5732
|
-
} catch (error) {
|
|
5733
|
-
import_logger8.logger.warn(
|
|
5734
|
-
"runtime order arrival snapshot failed: %s %s",
|
|
5735
|
-
symbol,
|
|
5736
|
-
error?.message || String(error)
|
|
5737
|
-
);
|
|
5738
|
-
return {
|
|
5739
|
-
arrivalSnapshotTime: Date.now(),
|
|
5740
|
-
arrivalSource: "top_of_book_error",
|
|
5741
|
-
bid: null,
|
|
5742
|
-
ask: null,
|
|
5743
|
-
arrivalMid: null,
|
|
5744
|
-
spreadBps: null
|
|
5745
|
-
};
|
|
5746
|
-
}
|
|
5747
|
-
};
|
|
5748
|
-
var validateEntryProtectionAtArrival = ({
|
|
5749
|
-
direction,
|
|
5750
|
-
signalPrice,
|
|
5751
|
-
bid,
|
|
5752
|
-
ask,
|
|
5753
|
-
arrivalMid,
|
|
5754
|
-
takeProfits,
|
|
5755
|
-
stopLossPrice
|
|
5756
|
-
}) => {
|
|
5757
|
-
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
5758
|
-
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
5759
|
-
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
5760
|
-
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
5761
|
-
}
|
|
5762
|
-
for (const takeProfit of takeProfits) {
|
|
5763
|
-
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
5764
|
-
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
5765
|
-
}
|
|
5766
|
-
}
|
|
5767
|
-
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
5768
|
-
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
5769
|
-
}
|
|
5770
|
-
};
|
|
5771
5899
|
var resolveRuntimeTelemetryQuality = ({
|
|
5772
5900
|
signalClosePrice,
|
|
5773
5901
|
arrivalMid,
|
|
@@ -5844,7 +5972,8 @@ var executeEntryOrder = async ({
|
|
|
5844
5972
|
signal,
|
|
5845
5973
|
beforePlaceOrder,
|
|
5846
5974
|
recordRuntimeTrade = true,
|
|
5847
|
-
leverage
|
|
5975
|
+
leverage,
|
|
5976
|
+
executionLimits
|
|
5848
5977
|
}) => {
|
|
5849
5978
|
await beforePlaceOrder?.();
|
|
5850
5979
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
@@ -5855,23 +5984,57 @@ var executeEntryOrder = async ({
|
|
|
5855
5984
|
signal.orderQty = qty;
|
|
5856
5985
|
signal.orderValue = qty * currentPrice;
|
|
5857
5986
|
signal.orderFailureReason = void 0;
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5987
|
+
let arrivalSnapshot = {
|
|
5988
|
+
arrivalSnapshotTime: Date.now(),
|
|
5989
|
+
arrivalSource: "unavailable",
|
|
5990
|
+
bid: null,
|
|
5991
|
+
ask: null,
|
|
5992
|
+
arrivalMid: null,
|
|
5993
|
+
spreadBps: null
|
|
5994
|
+
};
|
|
5995
|
+
let orderSubmitTime = Date.now();
|
|
5996
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
5997
|
+
connector,
|
|
5998
|
+
symbol,
|
|
5999
|
+
direction,
|
|
6000
|
+
takeProfits,
|
|
6001
|
+
stopLossPrice,
|
|
6002
|
+
limits: executionLimits,
|
|
6003
|
+
onValidated: (snapshot2, economics2) => {
|
|
6004
|
+
arrivalSnapshot = snapshot2;
|
|
6005
|
+
orderSubmitTime = Date.now();
|
|
6006
|
+
if (executionLimits?.maxLossValue != null) {
|
|
6007
|
+
signal.riskDecision = {
|
|
6008
|
+
stage: "order_plan",
|
|
6009
|
+
status: economics2.riskExceeded ? "warning" : "approved",
|
|
6010
|
+
reason: economics2.riskExceeded ? "ENTRY_RISK_EXCEEDS_MAX_LOSS_AT_SUBMISSION" : "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE",
|
|
6011
|
+
plannedLossValue: economics2.plannedLossValue,
|
|
6012
|
+
maxLossValue: executionLimits.maxLossValue,
|
|
6013
|
+
limitUtilization: economics2.plannedLossValue / executionLimits.maxLossValue,
|
|
6014
|
+
enforced: false
|
|
6015
|
+
};
|
|
6016
|
+
}
|
|
6017
|
+
}
|
|
6018
|
+
});
|
|
6019
|
+
if (!beforeSubmit) {
|
|
6020
|
+
arrivalSnapshot = await getOrderArrivalSnapshot({ connector, symbol });
|
|
6021
|
+
try {
|
|
6022
|
+
validateEntryProtectionAtArrival({
|
|
6023
|
+
direction,
|
|
6024
|
+
signalPrice: currentPrice,
|
|
6025
|
+
bid: arrivalSnapshot.bid,
|
|
6026
|
+
ask: arrivalSnapshot.ask,
|
|
6027
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6028
|
+
takeProfits,
|
|
6029
|
+
stopLossPrice
|
|
6030
|
+
});
|
|
6031
|
+
} catch (error) {
|
|
6032
|
+
signal.orderStatus = "failed";
|
|
6033
|
+
signal.orderFailureReason = error.message;
|
|
6034
|
+
throw error;
|
|
6035
|
+
}
|
|
6036
|
+
orderSubmitTime = Date.now();
|
|
5873
6037
|
}
|
|
5874
|
-
const orderSubmitTime = Date.now();
|
|
5875
6038
|
const orderPlaced = await connector.placeOrder({
|
|
5876
6039
|
symbol,
|
|
5877
6040
|
qty,
|
|
@@ -5882,7 +6045,8 @@ var executeEntryOrder = async ({
|
|
|
5882
6045
|
direction,
|
|
5883
6046
|
...typeof leverage === "number" && Number.isFinite(leverage) ? { leverage } : {},
|
|
5884
6047
|
orderId,
|
|
5885
|
-
signal
|
|
6048
|
+
signal,
|
|
6049
|
+
...beforeSubmit ? { beforeSubmit } : {}
|
|
5886
6050
|
});
|
|
5887
6051
|
const orderAckTime = Date.now();
|
|
5888
6052
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
@@ -6711,7 +6875,7 @@ var canUseSharedReplayState = ({
|
|
|
6711
6875
|
}) => (env === "BACKTEST" || env === "PARITY") && Boolean(sharedReplayKey);
|
|
6712
6876
|
|
|
6713
6877
|
// src/strategy/runtimeExecution.ts
|
|
6714
|
-
var
|
|
6878
|
+
var import_logger10 = require("@tradejs/infra/logger");
|
|
6715
6879
|
var import_types = require("@tradejs/types");
|
|
6716
6880
|
var buildExitOrderSignal = ({
|
|
6717
6881
|
strategyName,
|
|
@@ -6763,7 +6927,7 @@ var handleExitDecision = async ({
|
|
|
6763
6927
|
deploymentId: connector.deploymentId
|
|
6764
6928
|
});
|
|
6765
6929
|
if (!activeTrade) {
|
|
6766
|
-
|
|
6930
|
+
import_logger10.logger.warn(
|
|
6767
6931
|
"[%s] blocked closePosition for untracked runtime position: %s",
|
|
6768
6932
|
strategyName ?? "unknown",
|
|
6769
6933
|
symbol
|
|
@@ -6771,7 +6935,7 @@ var handleExitDecision = async ({
|
|
|
6771
6935
|
return "CLOSE_BLOCKED_BY_UNTRACKED_POSITION";
|
|
6772
6936
|
}
|
|
6773
6937
|
if (!strategyName || activeTrade.strategy !== strategyName) {
|
|
6774
|
-
|
|
6938
|
+
import_logger10.logger.warn(
|
|
6775
6939
|
"[%s] blocked closePosition for foreign runtime position: %s ownedBy=%s",
|
|
6776
6940
|
strategyName ?? "unknown",
|
|
6777
6941
|
symbol,
|
|
@@ -6824,7 +6988,7 @@ var handleExitDecision = async ({
|
|
|
6824
6988
|
exitType: closedTrade?.exitType ?? "exit"
|
|
6825
6989
|
});
|
|
6826
6990
|
} catch (notificationError) {
|
|
6827
|
-
|
|
6991
|
+
import_logger10.logger.error(
|
|
6828
6992
|
"runtime close notification error: %s %s",
|
|
6829
6993
|
symbol,
|
|
6830
6994
|
notificationError
|
|
@@ -6838,7 +7002,7 @@ var handleExitDecision = async ({
|
|
|
6838
7002
|
decision,
|
|
6839
7003
|
market
|
|
6840
7004
|
});
|
|
6841
|
-
|
|
7005
|
+
import_logger10.logger.error("close order error: %s %s", symbol, err);
|
|
6842
7006
|
return "ORDER_ERROR";
|
|
6843
7007
|
}
|
|
6844
7008
|
return decision.code;
|
|
@@ -6865,7 +7029,7 @@ var handleProtectDecision = async ({
|
|
|
6865
7029
|
decision,
|
|
6866
7030
|
market
|
|
6867
7031
|
});
|
|
6868
|
-
|
|
7032
|
+
import_logger10.logger.error("protect position error: %s %s", symbol, err);
|
|
6869
7033
|
return "ORDER_ERROR";
|
|
6870
7034
|
}
|
|
6871
7035
|
return decision.code;
|
|
@@ -6931,7 +7095,11 @@ var executeEntryDecision = async ({
|
|
|
6931
7095
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {},
|
|
6932
7096
|
signal,
|
|
6933
7097
|
beforePlaceOrder,
|
|
6934
|
-
recordRuntimeTrade: recordRuntimeJournal
|
|
7098
|
+
recordRuntimeTrade: recordRuntimeJournal,
|
|
7099
|
+
executionLimits: resolveEntryExecutionLimits(
|
|
7100
|
+
hookCtx.strategyConfig,
|
|
7101
|
+
decision.entryContext.direction
|
|
7102
|
+
)
|
|
6935
7103
|
});
|
|
6936
7104
|
await invokeStageHooks(
|
|
6937
7105
|
"afterPlaceOrder",
|
|
@@ -6953,19 +7121,32 @@ var executeEntryDecision = async ({
|
|
|
6953
7121
|
return signal;
|
|
6954
7122
|
}
|
|
6955
7123
|
await beforePlaceOrder();
|
|
6956
|
-
const
|
|
7124
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
6957
7125
|
connector,
|
|
6958
|
-
symbol
|
|
6959
|
-
});
|
|
6960
|
-
validateEntryProtectionAtArrival({
|
|
7126
|
+
symbol,
|
|
6961
7127
|
direction: decision.entryContext.direction,
|
|
6962
|
-
signalPrice: decision.entryContext.prices.currentPrice,
|
|
6963
|
-
bid: arrivalSnapshot.bid,
|
|
6964
|
-
ask: arrivalSnapshot.ask,
|
|
6965
|
-
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6966
7128
|
takeProfits: decision.orderPlan.takeProfits,
|
|
6967
|
-
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
7129
|
+
stopLossPrice: decision.orderPlan.stopLossPrice,
|
|
7130
|
+
limits: resolveEntryExecutionLimits(
|
|
7131
|
+
hookCtx.strategyConfig,
|
|
7132
|
+
decision.entryContext.direction
|
|
7133
|
+
)
|
|
6968
7134
|
});
|
|
7135
|
+
if (!beforeSubmit) {
|
|
7136
|
+
const arrivalSnapshot = await getOrderArrivalSnapshot({
|
|
7137
|
+
connector,
|
|
7138
|
+
symbol
|
|
7139
|
+
});
|
|
7140
|
+
validateEntryProtectionAtArrival({
|
|
7141
|
+
direction: decision.entryContext.direction,
|
|
7142
|
+
signalPrice: decision.entryContext.prices.currentPrice,
|
|
7143
|
+
bid: arrivalSnapshot.bid,
|
|
7144
|
+
ask: arrivalSnapshot.ask,
|
|
7145
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
7146
|
+
takeProfits: decision.orderPlan.takeProfits,
|
|
7147
|
+
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
7148
|
+
});
|
|
7149
|
+
}
|
|
6969
7150
|
const orderPlaced = await connector.placeOrder({
|
|
6970
7151
|
symbol,
|
|
6971
7152
|
qty: decision.orderPlan.qty,
|
|
@@ -6973,6 +7154,7 @@ var executeEntryDecision = async ({
|
|
|
6973
7154
|
timestamp: decision.entryContext.timestamp,
|
|
6974
7155
|
direction: decision.entryContext.direction,
|
|
6975
7156
|
positionIntent: decision.orderPlan.positionIntent,
|
|
7157
|
+
...beforeSubmit ? { beforeSubmit } : {},
|
|
6976
7158
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {}
|
|
6977
7159
|
});
|
|
6978
7160
|
if (!orderPlaced) {
|
|
@@ -7014,6 +7196,23 @@ var executeEntryDecision = async ({
|
|
|
7014
7196
|
{ decision, entry, market }
|
|
7015
7197
|
);
|
|
7016
7198
|
} catch (err) {
|
|
7199
|
+
if (err instanceof EntrySubmissionValidationError) {
|
|
7200
|
+
if (signal) {
|
|
7201
|
+
signal.orderStatus = "skipped";
|
|
7202
|
+
signal.orderSkipReason = err.message;
|
|
7203
|
+
signal.allocatorDecision = {
|
|
7204
|
+
stage: "runtime_admission",
|
|
7205
|
+
status: "rejected",
|
|
7206
|
+
reason: err.message
|
|
7207
|
+
};
|
|
7208
|
+
}
|
|
7209
|
+
import_logger10.logger.warn(
|
|
7210
|
+
"entry rejected before submission: %s %s",
|
|
7211
|
+
symbol,
|
|
7212
|
+
err.message
|
|
7213
|
+
);
|
|
7214
|
+
return signal ?? err.message;
|
|
7215
|
+
}
|
|
7017
7216
|
if (signal) {
|
|
7018
7217
|
signal.orderStatus = "failed";
|
|
7019
7218
|
if (typeof signal.orderFailureReason !== "string" || !signal.orderFailureReason.trim()) {
|
|
@@ -7028,9 +7227,9 @@ var executeEntryDecision = async ({
|
|
|
7028
7227
|
market
|
|
7029
7228
|
});
|
|
7030
7229
|
if (err?.message === import_types.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY) {
|
|
7031
|
-
|
|
7230
|
+
import_logger10.logger.warn("order warning: %s %s", symbol, err);
|
|
7032
7231
|
} else {
|
|
7033
|
-
|
|
7232
|
+
import_logger10.logger.error("order error: %s %s", symbol, err);
|
|
7034
7233
|
}
|
|
7035
7234
|
return signal ?? "ORDER_ERROR";
|
|
7036
7235
|
}
|
|
@@ -7138,6 +7337,9 @@ var createStrategyRuntime = ({
|
|
|
7138
7337
|
});
|
|
7139
7338
|
const strategyPolicyProfile = getPolicyProfile();
|
|
7140
7339
|
const indicatorPeriods = (0, import_strategies6.buildDefaultIndicatorPeriods)(config);
|
|
7340
|
+
const strategyConfig = { ...config };
|
|
7341
|
+
delete strategyConfig.RECORD_RUNTIME_TRADES;
|
|
7342
|
+
delete strategyConfig.SIMULATE_ORDERS;
|
|
7141
7343
|
const hookBase = {
|
|
7142
7344
|
connector,
|
|
7143
7345
|
strategyName,
|
|
@@ -7148,7 +7350,7 @@ var createStrategyRuntime = ({
|
|
|
7148
7350
|
accountId,
|
|
7149
7351
|
deploymentId,
|
|
7150
7352
|
policyProfileId: strategyPolicyProfile?.id ?? requestedPolicyProfileId,
|
|
7151
|
-
strategyConfig
|
|
7353
|
+
strategyConfig,
|
|
7152
7354
|
env,
|
|
7153
7355
|
isConfigFromBacktest
|
|
7154
7356
|
};
|
|
@@ -7195,7 +7397,7 @@ var createStrategyRuntime = ({
|
|
|
7195
7397
|
try {
|
|
7196
7398
|
await projectHook(errorParams);
|
|
7197
7399
|
} catch (hookError) {
|
|
7198
|
-
|
|
7400
|
+
import_logger11.logger.error(
|
|
7199
7401
|
"project hook onRuntimeError failed: %s %s",
|
|
7200
7402
|
strategyName,
|
|
7201
7403
|
hookError
|
|
@@ -7209,21 +7411,21 @@ var createStrategyRuntime = ({
|
|
|
7209
7411
|
try {
|
|
7210
7412
|
await onRuntimeError(errorParams);
|
|
7211
7413
|
} catch (hookError) {
|
|
7212
|
-
|
|
7414
|
+
import_logger11.logger.error(
|
|
7213
7415
|
"runtime hook onRuntimeError failed: %s %s",
|
|
7214
7416
|
strategyName,
|
|
7215
7417
|
hookError
|
|
7216
7418
|
);
|
|
7217
7419
|
}
|
|
7218
7420
|
};
|
|
7219
|
-
const invokeHook = async (stage, hook, params, errorContext = {}) => {
|
|
7421
|
+
const invokeHook = async (stage, hook, params, errorContext = {}, failureResult) => {
|
|
7220
7422
|
if (!hook) {
|
|
7221
7423
|
return void 0;
|
|
7222
7424
|
}
|
|
7223
7425
|
try {
|
|
7224
7426
|
return await hook(params);
|
|
7225
7427
|
} catch (error) {
|
|
7226
|
-
|
|
7428
|
+
import_logger11.logger.error(
|
|
7227
7429
|
'strategy hook "%s" failed for %s: %s',
|
|
7228
7430
|
stage,
|
|
7229
7431
|
strategyName,
|
|
@@ -7236,17 +7438,18 @@ var createStrategyRuntime = ({
|
|
|
7236
7438
|
entry: errorContext.entry,
|
|
7237
7439
|
market: errorContext.market
|
|
7238
7440
|
});
|
|
7239
|
-
return
|
|
7441
|
+
return failureResult;
|
|
7240
7442
|
}
|
|
7241
7443
|
};
|
|
7242
|
-
const invokeProjectHooks = async (stage, params, errorContext = {}) => {
|
|
7444
|
+
const invokeProjectHooks = async (stage, params, errorContext = {}, failureResult) => {
|
|
7243
7445
|
const results = [];
|
|
7244
7446
|
for (const hook of getProjectHookList(stage)) {
|
|
7245
7447
|
const result = await invokeHook(
|
|
7246
7448
|
stage,
|
|
7247
7449
|
hook,
|
|
7248
7450
|
params,
|
|
7249
|
-
errorContext
|
|
7451
|
+
errorContext,
|
|
7452
|
+
failureResult
|
|
7250
7453
|
);
|
|
7251
7454
|
if (result !== void 0) {
|
|
7252
7455
|
results.push(result);
|
|
@@ -7261,10 +7464,12 @@ var createStrategyRuntime = ({
|
|
|
7261
7464
|
return invokeHook(stage, hook, params, errorContext);
|
|
7262
7465
|
};
|
|
7263
7466
|
const invokeGateHooks = async (stage, hook, params, errorContext = {}) => {
|
|
7467
|
+
const failureResult = stage === "beforeEntryGate" ? { allow: false, reason: "ERROR" } : void 0;
|
|
7264
7468
|
const projectResults = await invokeProjectHooks(
|
|
7265
7469
|
stage,
|
|
7266
7470
|
params,
|
|
7267
|
-
errorContext
|
|
7471
|
+
errorContext,
|
|
7472
|
+
failureResult
|
|
7268
7473
|
);
|
|
7269
7474
|
const projectBlock = projectResults.find(
|
|
7270
7475
|
(result) => result?.allow === false
|
|
@@ -7276,7 +7481,8 @@ var createStrategyRuntime = ({
|
|
|
7276
7481
|
stage,
|
|
7277
7482
|
hook,
|
|
7278
7483
|
params,
|
|
7279
|
-
errorContext
|
|
7484
|
+
errorContext,
|
|
7485
|
+
failureResult
|
|
7280
7486
|
);
|
|
7281
7487
|
};
|
|
7282
7488
|
const applyProjectAfterCoreDecisionHooks = async ({
|
|
@@ -7422,11 +7628,8 @@ var createStrategyRuntime = ({
|
|
|
7422
7628
|
return enrichedBaseContext;
|
|
7423
7629
|
}
|
|
7424
7630
|
});
|
|
7425
|
-
const coreConfig = { ...config };
|
|
7426
|
-
delete coreConfig.RECORD_RUNTIME_TRADES;
|
|
7427
|
-
delete coreConfig.SIMULATE_ORDERS;
|
|
7428
7631
|
const core = await createCore({
|
|
7429
|
-
config:
|
|
7632
|
+
config: strategyConfig,
|
|
7430
7633
|
data,
|
|
7431
7634
|
strategyApi,
|
|
7432
7635
|
indicatorsState
|
|
@@ -8046,7 +8249,7 @@ setStrategyRuntimeFactory(createStrategyRuntime);
|
|
|
8046
8249
|
|
|
8047
8250
|
// src/strategyHooks/closeOppositePositionsBeforeOpen.ts
|
|
8048
8251
|
var import_lodash2 = __toESM(require("lodash"));
|
|
8049
|
-
var
|
|
8252
|
+
var import_logger12 = require("@tradejs/infra/logger");
|
|
8050
8253
|
var closeOppositePositionsBeforeOpen = async ({
|
|
8051
8254
|
connector,
|
|
8052
8255
|
entryContext
|
|
@@ -8060,7 +8263,7 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
8060
8263
|
} = entryContext;
|
|
8061
8264
|
const price = prices.currentPrice;
|
|
8062
8265
|
try {
|
|
8063
|
-
|
|
8266
|
+
import_logger12.logger.log(
|
|
8064
8267
|
"info",
|
|
8065
8268
|
"[%s] checking open positions before open: %s %s",
|
|
8066
8269
|
strategyName,
|
|
@@ -8071,7 +8274,7 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
8071
8274
|
const openPositions = (positions || []).filter(
|
|
8072
8275
|
(item) => item && Number(item.qty) > 0
|
|
8073
8276
|
);
|
|
8074
|
-
|
|
8277
|
+
import_logger12.logger.log(
|
|
8075
8278
|
"info",
|
|
8076
8279
|
"[%s] open positions found: %s",
|
|
8077
8280
|
strategyName,
|
|
@@ -8081,7 +8284,7 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
8081
8284
|
(item) => item.symbol !== currentSymbol && item.direction !== currentDirection
|
|
8082
8285
|
);
|
|
8083
8286
|
if (import_lodash2.default.isEmpty(oppositePositions)) {
|
|
8084
|
-
|
|
8287
|
+
import_logger12.logger.log(
|
|
8085
8288
|
"info",
|
|
8086
8289
|
"[%s] no opposite positions to close before open: %s",
|
|
8087
8290
|
strategyName,
|
|
@@ -8090,7 +8293,7 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
8090
8293
|
return;
|
|
8091
8294
|
}
|
|
8092
8295
|
for (const position of oppositePositions) {
|
|
8093
|
-
|
|
8296
|
+
import_logger12.logger.log(
|
|
8094
8297
|
"info",
|
|
8095
8298
|
"[%s] closing opposite position: %s %s qty=%s",
|
|
8096
8299
|
strategyName,
|
|
@@ -8105,14 +8308,14 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
8105
8308
|
timestamp,
|
|
8106
8309
|
direction: position.direction
|
|
8107
8310
|
});
|
|
8108
|
-
|
|
8311
|
+
import_logger12.logger.log(
|
|
8109
8312
|
"info",
|
|
8110
8313
|
"[%s] opposite position closed: %s",
|
|
8111
8314
|
strategyName,
|
|
8112
8315
|
position.symbol
|
|
8113
8316
|
);
|
|
8114
8317
|
} catch (err) {
|
|
8115
|
-
|
|
8318
|
+
import_logger12.logger.log(
|
|
8116
8319
|
"error",
|
|
8117
8320
|
"[%s] failed to close opposite position: %s %s",
|
|
8118
8321
|
strategyName,
|
|
@@ -8122,7 +8325,7 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
8122
8325
|
}
|
|
8123
8326
|
}
|
|
8124
8327
|
} catch (err) {
|
|
8125
|
-
|
|
8328
|
+
import_logger12.logger.log(
|
|
8126
8329
|
"error",
|
|
8127
8330
|
"[%s] failed to load open positions before open: %s %s",
|
|
8128
8331
|
strategyName,
|
|
@@ -8334,7 +8537,7 @@ var createMoveStopToBreakEvenOnBarHook = ({
|
|
|
8334
8537
|
var createMoveStopToBreakEvenAfterCoreDecisionHook = createMoveStopToBreakEvenOnBarHook;
|
|
8335
8538
|
|
|
8336
8539
|
// src/signalsHooks/closeAllPositionsOnGlobalProfitBeforeSignals.ts
|
|
8337
|
-
var
|
|
8540
|
+
var import_logger13 = require("@tradejs/infra/logger");
|
|
8338
8541
|
var createCloseAllOnGlobalProfitBeforeSignalsHook = ({
|
|
8339
8542
|
getStrategyDefaultConfig = () => void 0,
|
|
8340
8543
|
profitRiskMultiplier = DEFAULT_GLOBAL_UNREALIZED_PNL_TRIGGER_RISK_MULTIPLIER
|
|
@@ -8373,7 +8576,7 @@ var createCloseAllOnGlobalProfitBeforeSignalsHook = ({
|
|
|
8373
8576
|
if (!Number.isFinite(unrealizedPnlThreshold) || unrealizedPnlThreshold <= 0 || totalUnrealizedPnl < unrealizedPnlThreshold) {
|
|
8374
8577
|
return;
|
|
8375
8578
|
}
|
|
8376
|
-
|
|
8579
|
+
import_logger13.logger.info(
|
|
8377
8580
|
"closing all positions before signals by global unrealized pnl threshold: totalPnl=%s threshold=%s positions=%s",
|
|
8378
8581
|
totalUnrealizedPnl,
|
|
8379
8582
|
unrealizedPnlThreshold,
|
|
@@ -8399,7 +8602,7 @@ var createCloseAllOnGlobalProfitBeforeSignalsHook = ({
|
|
|
8399
8602
|
];
|
|
8400
8603
|
});
|
|
8401
8604
|
if (failedClosures.length) {
|
|
8402
|
-
|
|
8605
|
+
import_logger13.logger.warn(
|
|
8403
8606
|
"close-all before signals hook could not confirm closures for %s",
|
|
8404
8607
|
failedClosures.join(", ")
|
|
8405
8608
|
);
|