@tradejs/node 3.1.29-beta.291 → 3.1.29-beta.292
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 +92 -22
- package/dist/backtest.mjs +2 -2
- package/dist/{chunk-HRON2EAI.mjs → chunk-EVKQ4DI6.mjs} +52 -15
- package/dist/{chunk-UUNEWAT4.mjs → chunk-NJEYNGRU.mjs} +41 -8
- package/dist/diagnostics.js +92 -22
- package/dist/diagnostics.mjs +2 -2
- package/dist/mcp.js +92 -22
- package/dist/mcp.mjs +2 -2
- package/dist/portfolio.d.mts +1 -1
- package/dist/portfolio.d.ts +1 -1
- package/dist/portfolio.js +41 -8
- package/dist/portfolio.mjs +1 -1
- package/dist/registry.js +92 -22
- package/dist/registry.mjs +2 -2
- package/dist/runtimeDashboard.js +92 -22
- package/dist/runtimeDashboard.mjs +2 -2
- package/dist/runtimeStrategies.js +92 -22
- package/dist/runtimeStrategies.mjs +2 -2
- package/dist/strategies.d.mts +2 -2
- package/dist/strategies.d.ts +2 -2
- package/dist/strategies.js +92 -22
- package/dist/strategies.mjs +2 -2
- package/package.json +5 -5
package/dist/registry.js
CHANGED
|
@@ -928,39 +928,72 @@ function createPortfolioCoordinator({
|
|
|
928
928
|
);
|
|
929
929
|
if (admitted.result) return { reason: admitted.result };
|
|
930
930
|
let submitted = false;
|
|
931
|
+
let retry = 0;
|
|
932
|
+
let attemptId = entry.id;
|
|
933
|
+
let needsReservation = false;
|
|
931
934
|
try {
|
|
932
935
|
const value = await send(
|
|
933
936
|
async (qty) => {
|
|
934
937
|
if (!Number.isFinite(qty) || qty <= 0)
|
|
935
938
|
throw new Error("Invalid submitted portfolio quantity");
|
|
936
|
-
|
|
937
|
-
|
|
939
|
+
if (needsReservation) {
|
|
940
|
+
attemptId = `${entry.id.slice(0, 30)}r${++retry}`;
|
|
941
|
+
const admission = await transact(
|
|
942
|
+
now2,
|
|
943
|
+
(s, o) => {
|
|
944
|
+
const r = (0, import_portfolio.reservePortfolioEntry)(policy, strategy, s, o, {
|
|
945
|
+
...entry,
|
|
946
|
+
id: attemptId,
|
|
947
|
+
qty,
|
|
948
|
+
submitted: true,
|
|
949
|
+
oppositeHandled: false
|
|
950
|
+
});
|
|
951
|
+
return { state: r.state, result: r.reason };
|
|
952
|
+
},
|
|
953
|
+
true
|
|
954
|
+
);
|
|
955
|
+
if (admission.result) throw new Error(admission.result);
|
|
956
|
+
needsReservation = false;
|
|
957
|
+
submitted = true;
|
|
958
|
+
return attemptId;
|
|
959
|
+
}
|
|
960
|
+
const update = await updateReservation(now2, attemptId, true, qty);
|
|
938
961
|
if (!update.result)
|
|
939
962
|
throw new Error("PORTFOLIO_SUBMISSION_NO_LONGER_ADMITTED");
|
|
963
|
+
submitted = true;
|
|
964
|
+
return attemptId;
|
|
940
965
|
},
|
|
941
|
-
async () => {
|
|
966
|
+
async (retrying = false) => {
|
|
942
967
|
await transact(
|
|
943
968
|
now2,
|
|
944
969
|
(s) => ({
|
|
945
970
|
state: {
|
|
946
971
|
...s,
|
|
947
|
-
reservations: s.reservations.filter(
|
|
972
|
+
reservations: s.reservations.filter(
|
|
973
|
+
(r) => r.id !== attemptId
|
|
974
|
+
),
|
|
948
975
|
completedSignals: {
|
|
949
|
-
...
|
|
950
|
-
|
|
976
|
+
...Object.fromEntries(
|
|
977
|
+
Object.entries(s.completedSignals).filter(
|
|
978
|
+
([id]) => !retrying || id !== entry.signalId
|
|
979
|
+
)
|
|
980
|
+
),
|
|
981
|
+
...retrying ? {} : { [entry.signalId]: now2 }
|
|
951
982
|
}
|
|
952
983
|
},
|
|
953
984
|
result: void 0
|
|
954
985
|
}),
|
|
955
986
|
true
|
|
956
987
|
);
|
|
988
|
+
submitted = false;
|
|
989
|
+
needsReservation = retrying;
|
|
957
990
|
}
|
|
958
991
|
);
|
|
959
|
-
if (!submitted) await updateReservation(now2,
|
|
992
|
+
if (!submitted) await updateReservation(now2, attemptId, false);
|
|
960
993
|
else await tick(now2, true);
|
|
961
994
|
return { value };
|
|
962
995
|
} catch (error) {
|
|
963
|
-
if (!submitted) await updateReservation(now2,
|
|
996
|
+
if (!submitted) await updateReservation(now2, attemptId, false);
|
|
964
997
|
throw error;
|
|
965
998
|
}
|
|
966
999
|
}
|
|
@@ -1328,7 +1361,9 @@ function getPortfolioRuntime({
|
|
|
1328
1361
|
env,
|
|
1329
1362
|
userName,
|
|
1330
1363
|
accountId,
|
|
1331
|
-
deploymentId
|
|
1364
|
+
deploymentId,
|
|
1365
|
+
symbol,
|
|
1366
|
+
canClose
|
|
1332
1367
|
}) {
|
|
1333
1368
|
const policy = (0, import_portfolio2.parsePortfolioPolicy)(value);
|
|
1334
1369
|
if (!policy) return void 0;
|
|
@@ -1359,8 +1394,13 @@ function getPortfolioRuntime({
|
|
|
1359
1394
|
if (existing) {
|
|
1360
1395
|
if (existing.fingerprint !== fingerprint3)
|
|
1361
1396
|
throw new Error("Conflicting portfolio configuration for the same scope");
|
|
1397
|
+
if (symbol && canClose)
|
|
1398
|
+
existing.permissions.set(JSON.stringify([strategy, symbol]), canClose);
|
|
1362
1399
|
return existing.coordinator;
|
|
1363
1400
|
}
|
|
1401
|
+
const permissions = /* @__PURE__ */ new Map();
|
|
1402
|
+
if (symbol && canClose)
|
|
1403
|
+
permissions.set(JSON.stringify([strategy, symbol]), canClose);
|
|
1364
1404
|
const coordinator = createPortfolioCoordinator({
|
|
1365
1405
|
policy,
|
|
1366
1406
|
strategy,
|
|
@@ -1381,6 +1421,10 @@ function getPortfolioRuntime({
|
|
|
1381
1421
|
},
|
|
1382
1422
|
observe: (asOf, reservations) => replay ? connector.getPortfolioObservation(asOf, reservations) : observeRuntimePortfolio(connector, binding, asOf, reservations),
|
|
1383
1423
|
async close(position, intent, asOf) {
|
|
1424
|
+
const permission = permissions.get(
|
|
1425
|
+
JSON.stringify([position.strategy, position.symbol])
|
|
1426
|
+
);
|
|
1427
|
+
if (!permission || !await permission(position, intent, asOf)) return;
|
|
1384
1428
|
if (!replay) {
|
|
1385
1429
|
const trade = await getActiveRuntimeTrade({
|
|
1386
1430
|
...binding,
|
|
@@ -1415,7 +1459,7 @@ function getPortfolioRuntime({
|
|
|
1415
1459
|
}
|
|
1416
1460
|
}
|
|
1417
1461
|
});
|
|
1418
|
-
registry.set(cacheKey, { fingerprint: fingerprint3, coordinator });
|
|
1462
|
+
registry.set(cacheKey, { fingerprint: fingerprint3, coordinator, permissions });
|
|
1419
1463
|
return coordinator;
|
|
1420
1464
|
}
|
|
1421
1465
|
|
|
@@ -6274,7 +6318,7 @@ var executeEntryOrder = async ({
|
|
|
6274
6318
|
}) => {
|
|
6275
6319
|
await beforePlaceOrder?.();
|
|
6276
6320
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
6277
|
-
|
|
6321
|
+
let orderId = signal.orderId || createRuntimeOrderId(signal.strategy);
|
|
6278
6322
|
const signalTimestamp = signal.timestamp;
|
|
6279
6323
|
const signalClosePrice = currentPrice;
|
|
6280
6324
|
signal.orderId = orderId;
|
|
@@ -6347,11 +6391,12 @@ var executeEntryOrder = async ({
|
|
|
6347
6391
|
...beforeSubmit || markPortfolioSubmitted ? {
|
|
6348
6392
|
beforeSubmit: async (context) => {
|
|
6349
6393
|
await beforeSubmit?.(context);
|
|
6350
|
-
|
|
6394
|
+
return markPortfolioSubmitted?.(context.qty);
|
|
6351
6395
|
}
|
|
6352
6396
|
} : {}
|
|
6353
6397
|
});
|
|
6354
6398
|
const orderAckTime = Date.now();
|
|
6399
|
+
if (markPortfolioSubmitted && signal.orderId) orderId = signal.orderId;
|
|
6355
6400
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
6356
6401
|
const currentPosition = await connector.getPosition(symbol);
|
|
6357
6402
|
const fillTime = Date.now();
|
|
@@ -7639,7 +7684,27 @@ var createStrategyRuntime = ({
|
|
|
7639
7684
|
const projectConfig = await loadTradejsConfig(projectRoot);
|
|
7640
7685
|
const projectHooks = projectConfig.hooks;
|
|
7641
7686
|
const env = String(config.ENV ?? "BACKTEST");
|
|
7687
|
+
let portfolioMarket;
|
|
7642
7688
|
const portfolio = getPortfolioRuntime({
|
|
7689
|
+
symbol,
|
|
7690
|
+
canClose: async (position, intent, asOf) => {
|
|
7691
|
+
if (!portfolioMarket || portfolioMarket.candle.timestamp > asOf || !isRuntimeOrderExecutionEnabled({ config, env, connector }))
|
|
7692
|
+
return false;
|
|
7693
|
+
const gate = await authorizeClose(
|
|
7694
|
+
{
|
|
7695
|
+
kind: "exit",
|
|
7696
|
+
code: intent.reason,
|
|
7697
|
+
closePlan: {
|
|
7698
|
+
direction: position.direction,
|
|
7699
|
+
price: position.markPrice,
|
|
7700
|
+
timestamp: asOf
|
|
7701
|
+
}
|
|
7702
|
+
},
|
|
7703
|
+
portfolioMarket,
|
|
7704
|
+
position.strategy
|
|
7705
|
+
);
|
|
7706
|
+
return gate?.allow !== false;
|
|
7707
|
+
},
|
|
7643
7708
|
connectorName,
|
|
7644
7709
|
value: baseConfig?.PORTFOLIO_POLICY ?? config.PORTFOLIO_POLICY,
|
|
7645
7710
|
strategy: strategyName,
|
|
@@ -7814,7 +7879,10 @@ var createStrategyRuntime = ({
|
|
|
7814
7879
|
return invokeHook(stage, hook, params, errorContext);
|
|
7815
7880
|
};
|
|
7816
7881
|
const invokeGateHooks = async (stage, hook, params, errorContext = {}) => {
|
|
7817
|
-
const failureResult =
|
|
7882
|
+
const failureResult = {
|
|
7883
|
+
allow: false,
|
|
7884
|
+
reason: "ERROR"
|
|
7885
|
+
};
|
|
7818
7886
|
const projectResults = await invokeProjectHooks(
|
|
7819
7887
|
stage,
|
|
7820
7888
|
params,
|
|
@@ -7835,6 +7903,12 @@ var createStrategyRuntime = ({
|
|
|
7835
7903
|
failureResult
|
|
7836
7904
|
);
|
|
7837
7905
|
};
|
|
7906
|
+
const authorizeClose = (decision, market, owner = strategyName) => invokeGateHooks(
|
|
7907
|
+
"beforeClosePosition",
|
|
7908
|
+
resolveManifest(owner)?.hooks?.beforeClosePosition,
|
|
7909
|
+
{ ctx: getHookCtx(owner), market, decision },
|
|
7910
|
+
{ decision, market }
|
|
7911
|
+
);
|
|
7838
7912
|
const applyProjectAfterCoreDecisionHooks = async ({
|
|
7839
7913
|
hookCtx,
|
|
7840
7914
|
market,
|
|
@@ -8148,6 +8222,7 @@ var createStrategyRuntime = ({
|
|
|
8148
8222
|
});
|
|
8149
8223
|
};
|
|
8150
8224
|
const runWithDecisionOverride = async (candle, btcCandle, options = {}) => {
|
|
8225
|
+
portfolioMarket = { candle, btcCandle };
|
|
8151
8226
|
if (portfolioContext)
|
|
8152
8227
|
await portfolioContext(
|
|
8153
8228
|
env === "BACKTEST" || env === "PARITY" ? candle.timestamp : Date.now()
|
|
@@ -8254,15 +8329,10 @@ var createStrategyRuntime = ({
|
|
|
8254
8329
|
if (!makeOrdersEnabled) {
|
|
8255
8330
|
return decision.code;
|
|
8256
8331
|
}
|
|
8257
|
-
const closeGate = await
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
ctx: decisionHookCtx,
|
|
8262
|
-
market,
|
|
8263
|
-
decision
|
|
8264
|
-
},
|
|
8265
|
-
{ decision, market }
|
|
8332
|
+
const closeGate = await authorizeClose(
|
|
8333
|
+
decision,
|
|
8334
|
+
market,
|
|
8335
|
+
decisionHookCtx.strategyName
|
|
8266
8336
|
);
|
|
8267
8337
|
if (closeGate?.allow === false) {
|
|
8268
8338
|
return closeGate.reason ? `CLOSE_BLOCKED_BY_HOOK:${closeGate.reason}` : "CLOSE_BLOCKED_BY_HOOK";
|
package/dist/registry.mjs
CHANGED
package/dist/runtimeDashboard.js
CHANGED
|
@@ -2225,39 +2225,72 @@ function createPortfolioCoordinator({
|
|
|
2225
2225
|
);
|
|
2226
2226
|
if (admitted.result) return { reason: admitted.result };
|
|
2227
2227
|
let submitted = false;
|
|
2228
|
+
let retry = 0;
|
|
2229
|
+
let attemptId = entry.id;
|
|
2230
|
+
let needsReservation = false;
|
|
2228
2231
|
try {
|
|
2229
2232
|
const value = await send(
|
|
2230
2233
|
async (qty) => {
|
|
2231
2234
|
if (!Number.isFinite(qty) || qty <= 0)
|
|
2232
2235
|
throw new Error("Invalid submitted portfolio quantity");
|
|
2233
|
-
|
|
2234
|
-
|
|
2236
|
+
if (needsReservation) {
|
|
2237
|
+
attemptId = `${entry.id.slice(0, 30)}r${++retry}`;
|
|
2238
|
+
const admission = await transact(
|
|
2239
|
+
now2,
|
|
2240
|
+
(s, o) => {
|
|
2241
|
+
const r = (0, import_portfolio.reservePortfolioEntry)(policy, strategy, s, o, {
|
|
2242
|
+
...entry,
|
|
2243
|
+
id: attemptId,
|
|
2244
|
+
qty,
|
|
2245
|
+
submitted: true,
|
|
2246
|
+
oppositeHandled: false
|
|
2247
|
+
});
|
|
2248
|
+
return { state: r.state, result: r.reason };
|
|
2249
|
+
},
|
|
2250
|
+
true
|
|
2251
|
+
);
|
|
2252
|
+
if (admission.result) throw new Error(admission.result);
|
|
2253
|
+
needsReservation = false;
|
|
2254
|
+
submitted = true;
|
|
2255
|
+
return attemptId;
|
|
2256
|
+
}
|
|
2257
|
+
const update = await updateReservation(now2, attemptId, true, qty);
|
|
2235
2258
|
if (!update.result)
|
|
2236
2259
|
throw new Error("PORTFOLIO_SUBMISSION_NO_LONGER_ADMITTED");
|
|
2260
|
+
submitted = true;
|
|
2261
|
+
return attemptId;
|
|
2237
2262
|
},
|
|
2238
|
-
async () => {
|
|
2263
|
+
async (retrying = false) => {
|
|
2239
2264
|
await transact(
|
|
2240
2265
|
now2,
|
|
2241
2266
|
(s) => ({
|
|
2242
2267
|
state: {
|
|
2243
2268
|
...s,
|
|
2244
|
-
reservations: s.reservations.filter(
|
|
2269
|
+
reservations: s.reservations.filter(
|
|
2270
|
+
(r) => r.id !== attemptId
|
|
2271
|
+
),
|
|
2245
2272
|
completedSignals: {
|
|
2246
|
-
...
|
|
2247
|
-
|
|
2273
|
+
...Object.fromEntries(
|
|
2274
|
+
Object.entries(s.completedSignals).filter(
|
|
2275
|
+
([id]) => !retrying || id !== entry.signalId
|
|
2276
|
+
)
|
|
2277
|
+
),
|
|
2278
|
+
...retrying ? {} : { [entry.signalId]: now2 }
|
|
2248
2279
|
}
|
|
2249
2280
|
},
|
|
2250
2281
|
result: void 0
|
|
2251
2282
|
}),
|
|
2252
2283
|
true
|
|
2253
2284
|
);
|
|
2285
|
+
submitted = false;
|
|
2286
|
+
needsReservation = retrying;
|
|
2254
2287
|
}
|
|
2255
2288
|
);
|
|
2256
|
-
if (!submitted) await updateReservation(now2,
|
|
2289
|
+
if (!submitted) await updateReservation(now2, attemptId, false);
|
|
2257
2290
|
else await tick(now2, true);
|
|
2258
2291
|
return { value };
|
|
2259
2292
|
} catch (error) {
|
|
2260
|
-
if (!submitted) await updateReservation(now2,
|
|
2293
|
+
if (!submitted) await updateReservation(now2, attemptId, false);
|
|
2261
2294
|
throw error;
|
|
2262
2295
|
}
|
|
2263
2296
|
}
|
|
@@ -2625,7 +2658,9 @@ function getPortfolioRuntime({
|
|
|
2625
2658
|
env,
|
|
2626
2659
|
userName,
|
|
2627
2660
|
accountId,
|
|
2628
|
-
deploymentId
|
|
2661
|
+
deploymentId,
|
|
2662
|
+
symbol,
|
|
2663
|
+
canClose
|
|
2629
2664
|
}) {
|
|
2630
2665
|
const policy = (0, import_portfolio2.parsePortfolioPolicy)(value);
|
|
2631
2666
|
if (!policy) return void 0;
|
|
@@ -2656,8 +2691,13 @@ function getPortfolioRuntime({
|
|
|
2656
2691
|
if (existing) {
|
|
2657
2692
|
if (existing.fingerprint !== fingerprint3)
|
|
2658
2693
|
throw new Error("Conflicting portfolio configuration for the same scope");
|
|
2694
|
+
if (symbol && canClose)
|
|
2695
|
+
existing.permissions.set(JSON.stringify([strategy, symbol]), canClose);
|
|
2659
2696
|
return existing.coordinator;
|
|
2660
2697
|
}
|
|
2698
|
+
const permissions = /* @__PURE__ */ new Map();
|
|
2699
|
+
if (symbol && canClose)
|
|
2700
|
+
permissions.set(JSON.stringify([strategy, symbol]), canClose);
|
|
2661
2701
|
const coordinator = createPortfolioCoordinator({
|
|
2662
2702
|
policy,
|
|
2663
2703
|
strategy,
|
|
@@ -2678,6 +2718,10 @@ function getPortfolioRuntime({
|
|
|
2678
2718
|
},
|
|
2679
2719
|
observe: (asOf, reservations) => replay ? connector.getPortfolioObservation(asOf, reservations) : observeRuntimePortfolio(connector, binding, asOf, reservations),
|
|
2680
2720
|
async close(position, intent, asOf) {
|
|
2721
|
+
const permission = permissions.get(
|
|
2722
|
+
JSON.stringify([position.strategy, position.symbol])
|
|
2723
|
+
);
|
|
2724
|
+
if (!permission || !await permission(position, intent, asOf)) return;
|
|
2681
2725
|
if (!replay) {
|
|
2682
2726
|
const trade = await getActiveRuntimeTrade({
|
|
2683
2727
|
...binding,
|
|
@@ -2712,7 +2756,7 @@ function getPortfolioRuntime({
|
|
|
2712
2756
|
}
|
|
2713
2757
|
}
|
|
2714
2758
|
});
|
|
2715
|
-
registry.set(cacheKey, { fingerprint: fingerprint3, coordinator });
|
|
2759
|
+
registry.set(cacheKey, { fingerprint: fingerprint3, coordinator, permissions });
|
|
2716
2760
|
return coordinator;
|
|
2717
2761
|
}
|
|
2718
2762
|
|
|
@@ -7260,7 +7304,7 @@ var executeEntryOrder = async ({
|
|
|
7260
7304
|
}) => {
|
|
7261
7305
|
await beforePlaceOrder?.();
|
|
7262
7306
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
7263
|
-
|
|
7307
|
+
let orderId = signal.orderId || createRuntimeOrderId(signal.strategy);
|
|
7264
7308
|
const signalTimestamp = signal.timestamp;
|
|
7265
7309
|
const signalClosePrice = currentPrice;
|
|
7266
7310
|
signal.orderId = orderId;
|
|
@@ -7333,11 +7377,12 @@ var executeEntryOrder = async ({
|
|
|
7333
7377
|
...beforeSubmit || markPortfolioSubmitted ? {
|
|
7334
7378
|
beforeSubmit: async (context) => {
|
|
7335
7379
|
await beforeSubmit?.(context);
|
|
7336
|
-
|
|
7380
|
+
return markPortfolioSubmitted?.(context.qty);
|
|
7337
7381
|
}
|
|
7338
7382
|
} : {}
|
|
7339
7383
|
});
|
|
7340
7384
|
const orderAckTime = Date.now();
|
|
7385
|
+
if (markPortfolioSubmitted && signal.orderId) orderId = signal.orderId;
|
|
7341
7386
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
7342
7387
|
const currentPosition = await connector.getPosition(symbol);
|
|
7343
7388
|
const fillTime = Date.now();
|
|
@@ -8625,7 +8670,27 @@ var createStrategyRuntime = ({
|
|
|
8625
8670
|
const projectConfig = await loadTradejsConfig(projectRoot);
|
|
8626
8671
|
const projectHooks = projectConfig.hooks;
|
|
8627
8672
|
const env = String(config.ENV ?? "BACKTEST");
|
|
8673
|
+
let portfolioMarket;
|
|
8628
8674
|
const portfolio = getPortfolioRuntime({
|
|
8675
|
+
symbol,
|
|
8676
|
+
canClose: async (position, intent, asOf) => {
|
|
8677
|
+
if (!portfolioMarket || portfolioMarket.candle.timestamp > asOf || !isRuntimeOrderExecutionEnabled({ config, env, connector }))
|
|
8678
|
+
return false;
|
|
8679
|
+
const gate = await authorizeClose(
|
|
8680
|
+
{
|
|
8681
|
+
kind: "exit",
|
|
8682
|
+
code: intent.reason,
|
|
8683
|
+
closePlan: {
|
|
8684
|
+
direction: position.direction,
|
|
8685
|
+
price: position.markPrice,
|
|
8686
|
+
timestamp: asOf
|
|
8687
|
+
}
|
|
8688
|
+
},
|
|
8689
|
+
portfolioMarket,
|
|
8690
|
+
position.strategy
|
|
8691
|
+
);
|
|
8692
|
+
return gate?.allow !== false;
|
|
8693
|
+
},
|
|
8629
8694
|
connectorName,
|
|
8630
8695
|
value: baseConfig?.PORTFOLIO_POLICY ?? config.PORTFOLIO_POLICY,
|
|
8631
8696
|
strategy: strategyName,
|
|
@@ -8800,7 +8865,10 @@ var createStrategyRuntime = ({
|
|
|
8800
8865
|
return invokeHook(stage, hook, params, errorContext);
|
|
8801
8866
|
};
|
|
8802
8867
|
const invokeGateHooks = async (stage, hook, params, errorContext = {}) => {
|
|
8803
|
-
const failureResult =
|
|
8868
|
+
const failureResult = {
|
|
8869
|
+
allow: false,
|
|
8870
|
+
reason: "ERROR"
|
|
8871
|
+
};
|
|
8804
8872
|
const projectResults = await invokeProjectHooks(
|
|
8805
8873
|
stage,
|
|
8806
8874
|
params,
|
|
@@ -8821,6 +8889,12 @@ var createStrategyRuntime = ({
|
|
|
8821
8889
|
failureResult
|
|
8822
8890
|
);
|
|
8823
8891
|
};
|
|
8892
|
+
const authorizeClose = (decision, market, owner = strategyName) => invokeGateHooks(
|
|
8893
|
+
"beforeClosePosition",
|
|
8894
|
+
resolveManifest(owner)?.hooks?.beforeClosePosition,
|
|
8895
|
+
{ ctx: getHookCtx(owner), market, decision },
|
|
8896
|
+
{ decision, market }
|
|
8897
|
+
);
|
|
8824
8898
|
const applyProjectAfterCoreDecisionHooks = async ({
|
|
8825
8899
|
hookCtx,
|
|
8826
8900
|
market,
|
|
@@ -9134,6 +9208,7 @@ var createStrategyRuntime = ({
|
|
|
9134
9208
|
});
|
|
9135
9209
|
};
|
|
9136
9210
|
const runWithDecisionOverride = async (candle, btcCandle, options = {}) => {
|
|
9211
|
+
portfolioMarket = { candle, btcCandle };
|
|
9137
9212
|
if (portfolioContext)
|
|
9138
9213
|
await portfolioContext(
|
|
9139
9214
|
env === "BACKTEST" || env === "PARITY" ? candle.timestamp : Date.now()
|
|
@@ -9240,15 +9315,10 @@ var createStrategyRuntime = ({
|
|
|
9240
9315
|
if (!makeOrdersEnabled) {
|
|
9241
9316
|
return decision.code;
|
|
9242
9317
|
}
|
|
9243
|
-
const closeGate = await
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
ctx: decisionHookCtx,
|
|
9248
|
-
market,
|
|
9249
|
-
decision
|
|
9250
|
-
},
|
|
9251
|
-
{ decision, market }
|
|
9318
|
+
const closeGate = await authorizeClose(
|
|
9319
|
+
decision,
|
|
9320
|
+
market,
|
|
9321
|
+
decisionHookCtx.strategyName
|
|
9252
9322
|
);
|
|
9253
9323
|
if (closeGate?.allow === false) {
|
|
9254
9324
|
return closeGate.reason ? `CLOSE_BLOCKED_BY_HOOK:${closeGate.reason}` : "CLOSE_BLOCKED_BY_HOOK";
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
listRuntimeDeployments,
|
|
10
10
|
loadResolvedRuntimeStrategies
|
|
11
11
|
} from "./chunk-W6PPAADK.mjs";
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-EVKQ4DI6.mjs";
|
|
13
|
+
import "./chunk-NJEYNGRU.mjs";
|
|
14
14
|
import "./chunk-PYJWXAYR.mjs";
|
|
15
15
|
import "./chunk-LZDXRXIU.mjs";
|
|
16
16
|
import "./chunk-YR55Q355.mjs";
|