@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
|
@@ -931,39 +931,72 @@ function createPortfolioCoordinator({
|
|
|
931
931
|
);
|
|
932
932
|
if (admitted.result) return { reason: admitted.result };
|
|
933
933
|
let submitted = false;
|
|
934
|
+
let retry = 0;
|
|
935
|
+
let attemptId = entry.id;
|
|
936
|
+
let needsReservation = false;
|
|
934
937
|
try {
|
|
935
938
|
const value = await send(
|
|
936
939
|
async (qty) => {
|
|
937
940
|
if (!Number.isFinite(qty) || qty <= 0)
|
|
938
941
|
throw new Error("Invalid submitted portfolio quantity");
|
|
939
|
-
|
|
940
|
-
|
|
942
|
+
if (needsReservation) {
|
|
943
|
+
attemptId = `${entry.id.slice(0, 30)}r${++retry}`;
|
|
944
|
+
const admission = await transact(
|
|
945
|
+
now2,
|
|
946
|
+
(s, o) => {
|
|
947
|
+
const r = (0, import_portfolio.reservePortfolioEntry)(policy, strategy, s, o, {
|
|
948
|
+
...entry,
|
|
949
|
+
id: attemptId,
|
|
950
|
+
qty,
|
|
951
|
+
submitted: true,
|
|
952
|
+
oppositeHandled: false
|
|
953
|
+
});
|
|
954
|
+
return { state: r.state, result: r.reason };
|
|
955
|
+
},
|
|
956
|
+
true
|
|
957
|
+
);
|
|
958
|
+
if (admission.result) throw new Error(admission.result);
|
|
959
|
+
needsReservation = false;
|
|
960
|
+
submitted = true;
|
|
961
|
+
return attemptId;
|
|
962
|
+
}
|
|
963
|
+
const update = await updateReservation(now2, attemptId, true, qty);
|
|
941
964
|
if (!update.result)
|
|
942
965
|
throw new Error("PORTFOLIO_SUBMISSION_NO_LONGER_ADMITTED");
|
|
966
|
+
submitted = true;
|
|
967
|
+
return attemptId;
|
|
943
968
|
},
|
|
944
|
-
async () => {
|
|
969
|
+
async (retrying = false) => {
|
|
945
970
|
await transact(
|
|
946
971
|
now2,
|
|
947
972
|
(s) => ({
|
|
948
973
|
state: {
|
|
949
974
|
...s,
|
|
950
|
-
reservations: s.reservations.filter(
|
|
975
|
+
reservations: s.reservations.filter(
|
|
976
|
+
(r) => r.id !== attemptId
|
|
977
|
+
),
|
|
951
978
|
completedSignals: {
|
|
952
|
-
...
|
|
953
|
-
|
|
979
|
+
...Object.fromEntries(
|
|
980
|
+
Object.entries(s.completedSignals).filter(
|
|
981
|
+
([id]) => !retrying || id !== entry.signalId
|
|
982
|
+
)
|
|
983
|
+
),
|
|
984
|
+
...retrying ? {} : { [entry.signalId]: now2 }
|
|
954
985
|
}
|
|
955
986
|
},
|
|
956
987
|
result: void 0
|
|
957
988
|
}),
|
|
958
989
|
true
|
|
959
990
|
);
|
|
991
|
+
submitted = false;
|
|
992
|
+
needsReservation = retrying;
|
|
960
993
|
}
|
|
961
994
|
);
|
|
962
|
-
if (!submitted) await updateReservation(now2,
|
|
995
|
+
if (!submitted) await updateReservation(now2, attemptId, false);
|
|
963
996
|
else await tick(now2, true);
|
|
964
997
|
return { value };
|
|
965
998
|
} catch (error) {
|
|
966
|
-
if (!submitted) await updateReservation(now2,
|
|
999
|
+
if (!submitted) await updateReservation(now2, attemptId, false);
|
|
967
1000
|
throw error;
|
|
968
1001
|
}
|
|
969
1002
|
}
|
|
@@ -1331,7 +1364,9 @@ function getPortfolioRuntime({
|
|
|
1331
1364
|
env,
|
|
1332
1365
|
userName,
|
|
1333
1366
|
accountId,
|
|
1334
|
-
deploymentId
|
|
1367
|
+
deploymentId,
|
|
1368
|
+
symbol,
|
|
1369
|
+
canClose
|
|
1335
1370
|
}) {
|
|
1336
1371
|
const policy = (0, import_portfolio2.parsePortfolioPolicy)(value);
|
|
1337
1372
|
if (!policy) return void 0;
|
|
@@ -1362,8 +1397,13 @@ function getPortfolioRuntime({
|
|
|
1362
1397
|
if (existing) {
|
|
1363
1398
|
if (existing.fingerprint !== fingerprint3)
|
|
1364
1399
|
throw new Error("Conflicting portfolio configuration for the same scope");
|
|
1400
|
+
if (symbol && canClose)
|
|
1401
|
+
existing.permissions.set(JSON.stringify([strategy, symbol]), canClose);
|
|
1365
1402
|
return existing.coordinator;
|
|
1366
1403
|
}
|
|
1404
|
+
const permissions = /* @__PURE__ */ new Map();
|
|
1405
|
+
if (symbol && canClose)
|
|
1406
|
+
permissions.set(JSON.stringify([strategy, symbol]), canClose);
|
|
1367
1407
|
const coordinator = createPortfolioCoordinator({
|
|
1368
1408
|
policy,
|
|
1369
1409
|
strategy,
|
|
@@ -1384,6 +1424,10 @@ function getPortfolioRuntime({
|
|
|
1384
1424
|
},
|
|
1385
1425
|
observe: (asOf, reservations) => replay ? connector.getPortfolioObservation(asOf, reservations) : observeRuntimePortfolio(connector, binding, asOf, reservations),
|
|
1386
1426
|
async close(position, intent, asOf) {
|
|
1427
|
+
const permission = permissions.get(
|
|
1428
|
+
JSON.stringify([position.strategy, position.symbol])
|
|
1429
|
+
);
|
|
1430
|
+
if (!permission || !await permission(position, intent, asOf)) return;
|
|
1387
1431
|
if (!replay) {
|
|
1388
1432
|
const trade = await getActiveRuntimeTrade({
|
|
1389
1433
|
...binding,
|
|
@@ -1418,7 +1462,7 @@ function getPortfolioRuntime({
|
|
|
1418
1462
|
}
|
|
1419
1463
|
}
|
|
1420
1464
|
});
|
|
1421
|
-
registry.set(cacheKey, { fingerprint: fingerprint3, coordinator });
|
|
1465
|
+
registry.set(cacheKey, { fingerprint: fingerprint3, coordinator, permissions });
|
|
1422
1466
|
return coordinator;
|
|
1423
1467
|
}
|
|
1424
1468
|
|
|
@@ -6253,7 +6297,7 @@ var executeEntryOrder = async ({
|
|
|
6253
6297
|
}) => {
|
|
6254
6298
|
await beforePlaceOrder?.();
|
|
6255
6299
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
6256
|
-
|
|
6300
|
+
let orderId = signal.orderId || createRuntimeOrderId(signal.strategy);
|
|
6257
6301
|
const signalTimestamp = signal.timestamp;
|
|
6258
6302
|
const signalClosePrice = currentPrice;
|
|
6259
6303
|
signal.orderId = orderId;
|
|
@@ -6326,11 +6370,12 @@ var executeEntryOrder = async ({
|
|
|
6326
6370
|
...beforeSubmit || markPortfolioSubmitted ? {
|
|
6327
6371
|
beforeSubmit: async (context) => {
|
|
6328
6372
|
await beforeSubmit?.(context);
|
|
6329
|
-
|
|
6373
|
+
return markPortfolioSubmitted?.(context.qty);
|
|
6330
6374
|
}
|
|
6331
6375
|
} : {}
|
|
6332
6376
|
});
|
|
6333
6377
|
const orderAckTime = Date.now();
|
|
6378
|
+
if (markPortfolioSubmitted && signal.orderId) orderId = signal.orderId;
|
|
6334
6379
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
6335
6380
|
const currentPosition = await connector.getPosition(symbol);
|
|
6336
6381
|
const fillTime = Date.now();
|
|
@@ -7618,7 +7663,27 @@ var createStrategyRuntime = ({
|
|
|
7618
7663
|
const projectConfig = await loadTradejsConfig(projectRoot);
|
|
7619
7664
|
const projectHooks = projectConfig.hooks;
|
|
7620
7665
|
const env = String(config.ENV ?? "BACKTEST");
|
|
7666
|
+
let portfolioMarket;
|
|
7621
7667
|
const portfolio = getPortfolioRuntime({
|
|
7668
|
+
symbol,
|
|
7669
|
+
canClose: async (position, intent, asOf) => {
|
|
7670
|
+
if (!portfolioMarket || portfolioMarket.candle.timestamp > asOf || !isRuntimeOrderExecutionEnabled({ config, env, connector }))
|
|
7671
|
+
return false;
|
|
7672
|
+
const gate = await authorizeClose(
|
|
7673
|
+
{
|
|
7674
|
+
kind: "exit",
|
|
7675
|
+
code: intent.reason,
|
|
7676
|
+
closePlan: {
|
|
7677
|
+
direction: position.direction,
|
|
7678
|
+
price: position.markPrice,
|
|
7679
|
+
timestamp: asOf
|
|
7680
|
+
}
|
|
7681
|
+
},
|
|
7682
|
+
portfolioMarket,
|
|
7683
|
+
position.strategy
|
|
7684
|
+
);
|
|
7685
|
+
return gate?.allow !== false;
|
|
7686
|
+
},
|
|
7622
7687
|
connectorName,
|
|
7623
7688
|
value: baseConfig?.PORTFOLIO_POLICY ?? config.PORTFOLIO_POLICY,
|
|
7624
7689
|
strategy: strategyName,
|
|
@@ -7793,7 +7858,10 @@ var createStrategyRuntime = ({
|
|
|
7793
7858
|
return invokeHook(stage, hook, params, errorContext);
|
|
7794
7859
|
};
|
|
7795
7860
|
const invokeGateHooks = async (stage, hook, params, errorContext = {}) => {
|
|
7796
|
-
const failureResult =
|
|
7861
|
+
const failureResult = {
|
|
7862
|
+
allow: false,
|
|
7863
|
+
reason: "ERROR"
|
|
7864
|
+
};
|
|
7797
7865
|
const projectResults = await invokeProjectHooks(
|
|
7798
7866
|
stage,
|
|
7799
7867
|
params,
|
|
@@ -7814,6 +7882,12 @@ var createStrategyRuntime = ({
|
|
|
7814
7882
|
failureResult
|
|
7815
7883
|
);
|
|
7816
7884
|
};
|
|
7885
|
+
const authorizeClose = (decision, market, owner = strategyName) => invokeGateHooks(
|
|
7886
|
+
"beforeClosePosition",
|
|
7887
|
+
resolveManifest(owner)?.hooks?.beforeClosePosition,
|
|
7888
|
+
{ ctx: getHookCtx(owner), market, decision },
|
|
7889
|
+
{ decision, market }
|
|
7890
|
+
);
|
|
7817
7891
|
const applyProjectAfterCoreDecisionHooks = async ({
|
|
7818
7892
|
hookCtx,
|
|
7819
7893
|
market,
|
|
@@ -8127,6 +8201,7 @@ var createStrategyRuntime = ({
|
|
|
8127
8201
|
});
|
|
8128
8202
|
};
|
|
8129
8203
|
const runWithDecisionOverride = async (candle, btcCandle, options = {}) => {
|
|
8204
|
+
portfolioMarket = { candle, btcCandle };
|
|
8130
8205
|
if (portfolioContext)
|
|
8131
8206
|
await portfolioContext(
|
|
8132
8207
|
env === "BACKTEST" || env === "PARITY" ? candle.timestamp : Date.now()
|
|
@@ -8233,15 +8308,10 @@ var createStrategyRuntime = ({
|
|
|
8233
8308
|
if (!makeOrdersEnabled) {
|
|
8234
8309
|
return decision.code;
|
|
8235
8310
|
}
|
|
8236
|
-
const closeGate = await
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
ctx: decisionHookCtx,
|
|
8241
|
-
market,
|
|
8242
|
-
decision
|
|
8243
|
-
},
|
|
8244
|
-
{ decision, market }
|
|
8311
|
+
const closeGate = await authorizeClose(
|
|
8312
|
+
decision,
|
|
8313
|
+
market,
|
|
8314
|
+
decisionHookCtx.strategyName
|
|
8245
8315
|
);
|
|
8246
8316
|
if (closeGate?.allow === false) {
|
|
8247
8317
|
return closeGate.reason ? `CLOSE_BLOCKED_BY_HOOK:${closeGate.reason}` : "CLOSE_BLOCKED_BY_HOOK";
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
resolveRuntimeComposition,
|
|
10
10
|
verifyRuntimeDeclaration
|
|
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";
|
package/dist/strategies.d.mts
CHANGED
|
@@ -86,8 +86,8 @@ interface ExecuteEntryOrderParams {
|
|
|
86
86
|
beforePlaceOrder?: () => Promise<void>;
|
|
87
87
|
recordRuntimeTrade?: boolean;
|
|
88
88
|
executionLimits?: EntryExecutionLimits;
|
|
89
|
-
markPortfolioSubmitted?: (qty: number) => Promise<
|
|
90
|
-
rejectPortfolioSubmission?: () => Promise<void>;
|
|
89
|
+
markPortfolioSubmitted?: (qty: number) => Promise<string>;
|
|
90
|
+
rejectPortfolioSubmission?: (retrying?: boolean) => Promise<void>;
|
|
91
91
|
}
|
|
92
92
|
declare const executeEntryOrder: ({ connector, userName, symbol, direction, qty, currentPrice, timestamp, takeProfits, stopLossPrice, positionIntent, signal, beforePlaceOrder, recordRuntimeTrade, leverage, executionLimits, markPortfolioSubmitted, rejectPortfolioSubmission, }: ExecuteEntryOrderParams) => Promise<number>;
|
|
93
93
|
|
package/dist/strategies.d.ts
CHANGED
|
@@ -86,8 +86,8 @@ interface ExecuteEntryOrderParams {
|
|
|
86
86
|
beforePlaceOrder?: () => Promise<void>;
|
|
87
87
|
recordRuntimeTrade?: boolean;
|
|
88
88
|
executionLimits?: EntryExecutionLimits;
|
|
89
|
-
markPortfolioSubmitted?: (qty: number) => Promise<
|
|
90
|
-
rejectPortfolioSubmission?: () => Promise<void>;
|
|
89
|
+
markPortfolioSubmitted?: (qty: number) => Promise<string>;
|
|
90
|
+
rejectPortfolioSubmission?: (retrying?: boolean) => Promise<void>;
|
|
91
91
|
}
|
|
92
92
|
declare const executeEntryOrder: ({ connector, userName, symbol, direction, qty, currentPrice, timestamp, takeProfits, stopLossPrice, positionIntent, signal, beforePlaceOrder, recordRuntimeTrade, leverage, executionLimits, markPortfolioSubmitted, rejectPortfolioSubmission, }: ExecuteEntryOrderParams) => Promise<number>;
|
|
93
93
|
|
package/dist/strategies.js
CHANGED
|
@@ -2853,39 +2853,72 @@ function createPortfolioCoordinator({
|
|
|
2853
2853
|
);
|
|
2854
2854
|
if (admitted.result) return { reason: admitted.result };
|
|
2855
2855
|
let submitted = false;
|
|
2856
|
+
let retry = 0;
|
|
2857
|
+
let attemptId = entry.id;
|
|
2858
|
+
let needsReservation = false;
|
|
2856
2859
|
try {
|
|
2857
2860
|
const value = await send(
|
|
2858
2861
|
async (qty) => {
|
|
2859
2862
|
if (!Number.isFinite(qty) || qty <= 0)
|
|
2860
2863
|
throw new Error("Invalid submitted portfolio quantity");
|
|
2861
|
-
|
|
2862
|
-
|
|
2864
|
+
if (needsReservation) {
|
|
2865
|
+
attemptId = `${entry.id.slice(0, 30)}r${++retry}`;
|
|
2866
|
+
const admission = await transact(
|
|
2867
|
+
now2,
|
|
2868
|
+
(s, o) => {
|
|
2869
|
+
const r = (0, import_portfolio.reservePortfolioEntry)(policy, strategy, s, o, {
|
|
2870
|
+
...entry,
|
|
2871
|
+
id: attemptId,
|
|
2872
|
+
qty,
|
|
2873
|
+
submitted: true,
|
|
2874
|
+
oppositeHandled: false
|
|
2875
|
+
});
|
|
2876
|
+
return { state: r.state, result: r.reason };
|
|
2877
|
+
},
|
|
2878
|
+
true
|
|
2879
|
+
);
|
|
2880
|
+
if (admission.result) throw new Error(admission.result);
|
|
2881
|
+
needsReservation = false;
|
|
2882
|
+
submitted = true;
|
|
2883
|
+
return attemptId;
|
|
2884
|
+
}
|
|
2885
|
+
const update = await updateReservation(now2, attemptId, true, qty);
|
|
2863
2886
|
if (!update.result)
|
|
2864
2887
|
throw new Error("PORTFOLIO_SUBMISSION_NO_LONGER_ADMITTED");
|
|
2888
|
+
submitted = true;
|
|
2889
|
+
return attemptId;
|
|
2865
2890
|
},
|
|
2866
|
-
async () => {
|
|
2891
|
+
async (retrying = false) => {
|
|
2867
2892
|
await transact(
|
|
2868
2893
|
now2,
|
|
2869
2894
|
(s) => ({
|
|
2870
2895
|
state: {
|
|
2871
2896
|
...s,
|
|
2872
|
-
reservations: s.reservations.filter(
|
|
2897
|
+
reservations: s.reservations.filter(
|
|
2898
|
+
(r) => r.id !== attemptId
|
|
2899
|
+
),
|
|
2873
2900
|
completedSignals: {
|
|
2874
|
-
...
|
|
2875
|
-
|
|
2901
|
+
...Object.fromEntries(
|
|
2902
|
+
Object.entries(s.completedSignals).filter(
|
|
2903
|
+
([id]) => !retrying || id !== entry.signalId
|
|
2904
|
+
)
|
|
2905
|
+
),
|
|
2906
|
+
...retrying ? {} : { [entry.signalId]: now2 }
|
|
2876
2907
|
}
|
|
2877
2908
|
},
|
|
2878
2909
|
result: void 0
|
|
2879
2910
|
}),
|
|
2880
2911
|
true
|
|
2881
2912
|
);
|
|
2913
|
+
submitted = false;
|
|
2914
|
+
needsReservation = retrying;
|
|
2882
2915
|
}
|
|
2883
2916
|
);
|
|
2884
|
-
if (!submitted) await updateReservation(now2,
|
|
2917
|
+
if (!submitted) await updateReservation(now2, attemptId, false);
|
|
2885
2918
|
else await tick(now2, true);
|
|
2886
2919
|
return { value };
|
|
2887
2920
|
} catch (error) {
|
|
2888
|
-
if (!submitted) await updateReservation(now2,
|
|
2921
|
+
if (!submitted) await updateReservation(now2, attemptId, false);
|
|
2889
2922
|
throw error;
|
|
2890
2923
|
}
|
|
2891
2924
|
}
|
|
@@ -3253,7 +3286,9 @@ function getPortfolioRuntime({
|
|
|
3253
3286
|
env,
|
|
3254
3287
|
userName,
|
|
3255
3288
|
accountId,
|
|
3256
|
-
deploymentId
|
|
3289
|
+
deploymentId,
|
|
3290
|
+
symbol,
|
|
3291
|
+
canClose
|
|
3257
3292
|
}) {
|
|
3258
3293
|
const policy = (0, import_portfolio2.parsePortfolioPolicy)(value);
|
|
3259
3294
|
if (!policy) return void 0;
|
|
@@ -3284,8 +3319,13 @@ function getPortfolioRuntime({
|
|
|
3284
3319
|
if (existing) {
|
|
3285
3320
|
if (existing.fingerprint !== fingerprint3)
|
|
3286
3321
|
throw new Error("Conflicting portfolio configuration for the same scope");
|
|
3322
|
+
if (symbol && canClose)
|
|
3323
|
+
existing.permissions.set(JSON.stringify([strategy, symbol]), canClose);
|
|
3287
3324
|
return existing.coordinator;
|
|
3288
3325
|
}
|
|
3326
|
+
const permissions = /* @__PURE__ */ new Map();
|
|
3327
|
+
if (symbol && canClose)
|
|
3328
|
+
permissions.set(JSON.stringify([strategy, symbol]), canClose);
|
|
3289
3329
|
const coordinator = createPortfolioCoordinator({
|
|
3290
3330
|
policy,
|
|
3291
3331
|
strategy,
|
|
@@ -3306,6 +3346,10 @@ function getPortfolioRuntime({
|
|
|
3306
3346
|
},
|
|
3307
3347
|
observe: (asOf, reservations) => replay ? connector.getPortfolioObservation(asOf, reservations) : observeRuntimePortfolio(connector, binding, asOf, reservations),
|
|
3308
3348
|
async close(position, intent, asOf) {
|
|
3349
|
+
const permission = permissions.get(
|
|
3350
|
+
JSON.stringify([position.strategy, position.symbol])
|
|
3351
|
+
);
|
|
3352
|
+
if (!permission || !await permission(position, intent, asOf)) return;
|
|
3309
3353
|
if (!replay) {
|
|
3310
3354
|
const trade = await getActiveRuntimeTrade({
|
|
3311
3355
|
...binding,
|
|
@@ -3340,7 +3384,7 @@ function getPortfolioRuntime({
|
|
|
3340
3384
|
}
|
|
3341
3385
|
}
|
|
3342
3386
|
});
|
|
3343
|
-
registry.set(cacheKey, { fingerprint: fingerprint3, coordinator });
|
|
3387
|
+
registry.set(cacheKey, { fingerprint: fingerprint3, coordinator, permissions });
|
|
3344
3388
|
return coordinator;
|
|
3345
3389
|
}
|
|
3346
3390
|
|
|
@@ -6515,7 +6559,7 @@ var executeEntryOrder = async ({
|
|
|
6515
6559
|
}) => {
|
|
6516
6560
|
await beforePlaceOrder?.();
|
|
6517
6561
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
6518
|
-
|
|
6562
|
+
let orderId = signal.orderId || createRuntimeOrderId(signal.strategy);
|
|
6519
6563
|
const signalTimestamp = signal.timestamp;
|
|
6520
6564
|
const signalClosePrice = currentPrice;
|
|
6521
6565
|
signal.orderId = orderId;
|
|
@@ -6588,11 +6632,12 @@ var executeEntryOrder = async ({
|
|
|
6588
6632
|
...beforeSubmit || markPortfolioSubmitted ? {
|
|
6589
6633
|
beforeSubmit: async (context) => {
|
|
6590
6634
|
await beforeSubmit?.(context);
|
|
6591
|
-
|
|
6635
|
+
return markPortfolioSubmitted?.(context.qty);
|
|
6592
6636
|
}
|
|
6593
6637
|
} : {}
|
|
6594
6638
|
});
|
|
6595
6639
|
const orderAckTime = Date.now();
|
|
6640
|
+
if (markPortfolioSubmitted && signal.orderId) orderId = signal.orderId;
|
|
6596
6641
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
6597
6642
|
const currentPosition = await connector.getPosition(symbol);
|
|
6598
6643
|
const fillTime = Date.now();
|
|
@@ -7880,7 +7925,27 @@ var createStrategyRuntime = ({
|
|
|
7880
7925
|
const projectConfig = await loadTradejsConfig(projectRoot);
|
|
7881
7926
|
const projectHooks = projectConfig.hooks;
|
|
7882
7927
|
const env = String(config.ENV ?? "BACKTEST");
|
|
7928
|
+
let portfolioMarket;
|
|
7883
7929
|
const portfolio = getPortfolioRuntime({
|
|
7930
|
+
symbol,
|
|
7931
|
+
canClose: async (position, intent, asOf) => {
|
|
7932
|
+
if (!portfolioMarket || portfolioMarket.candle.timestamp > asOf || !isRuntimeOrderExecutionEnabled({ config, env, connector }))
|
|
7933
|
+
return false;
|
|
7934
|
+
const gate = await authorizeClose(
|
|
7935
|
+
{
|
|
7936
|
+
kind: "exit",
|
|
7937
|
+
code: intent.reason,
|
|
7938
|
+
closePlan: {
|
|
7939
|
+
direction: position.direction,
|
|
7940
|
+
price: position.markPrice,
|
|
7941
|
+
timestamp: asOf
|
|
7942
|
+
}
|
|
7943
|
+
},
|
|
7944
|
+
portfolioMarket,
|
|
7945
|
+
position.strategy
|
|
7946
|
+
);
|
|
7947
|
+
return gate?.allow !== false;
|
|
7948
|
+
},
|
|
7884
7949
|
connectorName,
|
|
7885
7950
|
value: baseConfig?.PORTFOLIO_POLICY ?? config.PORTFOLIO_POLICY,
|
|
7886
7951
|
strategy: strategyName,
|
|
@@ -8055,7 +8120,10 @@ var createStrategyRuntime = ({
|
|
|
8055
8120
|
return invokeHook(stage, hook, params, errorContext);
|
|
8056
8121
|
};
|
|
8057
8122
|
const invokeGateHooks = async (stage, hook, params, errorContext = {}) => {
|
|
8058
|
-
const failureResult =
|
|
8123
|
+
const failureResult = {
|
|
8124
|
+
allow: false,
|
|
8125
|
+
reason: "ERROR"
|
|
8126
|
+
};
|
|
8059
8127
|
const projectResults = await invokeProjectHooks(
|
|
8060
8128
|
stage,
|
|
8061
8129
|
params,
|
|
@@ -8076,6 +8144,12 @@ var createStrategyRuntime = ({
|
|
|
8076
8144
|
failureResult
|
|
8077
8145
|
);
|
|
8078
8146
|
};
|
|
8147
|
+
const authorizeClose = (decision, market, owner = strategyName) => invokeGateHooks(
|
|
8148
|
+
"beforeClosePosition",
|
|
8149
|
+
resolveManifest(owner)?.hooks?.beforeClosePosition,
|
|
8150
|
+
{ ctx: getHookCtx(owner), market, decision },
|
|
8151
|
+
{ decision, market }
|
|
8152
|
+
);
|
|
8079
8153
|
const applyProjectAfterCoreDecisionHooks = async ({
|
|
8080
8154
|
hookCtx,
|
|
8081
8155
|
market,
|
|
@@ -8389,6 +8463,7 @@ var createStrategyRuntime = ({
|
|
|
8389
8463
|
});
|
|
8390
8464
|
};
|
|
8391
8465
|
const runWithDecisionOverride = async (candle, btcCandle, options = {}) => {
|
|
8466
|
+
portfolioMarket = { candle, btcCandle };
|
|
8392
8467
|
if (portfolioContext)
|
|
8393
8468
|
await portfolioContext(
|
|
8394
8469
|
env === "BACKTEST" || env === "PARITY" ? candle.timestamp : Date.now()
|
|
@@ -8495,15 +8570,10 @@ var createStrategyRuntime = ({
|
|
|
8495
8570
|
if (!makeOrdersEnabled) {
|
|
8496
8571
|
return decision.code;
|
|
8497
8572
|
}
|
|
8498
|
-
const closeGate = await
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
ctx: decisionHookCtx,
|
|
8503
|
-
market,
|
|
8504
|
-
decision
|
|
8505
|
-
},
|
|
8506
|
-
{ decision, market }
|
|
8573
|
+
const closeGate = await authorizeClose(
|
|
8574
|
+
decision,
|
|
8575
|
+
market,
|
|
8576
|
+
decisionHookCtx.strategyName
|
|
8507
8577
|
);
|
|
8508
8578
|
if (closeGate?.allow === false) {
|
|
8509
8579
|
return closeGate.reason ? `CLOSE_BLOCKED_BY_HOOK:${closeGate.reason}` : "CLOSE_BLOCKED_BY_HOOK";
|
package/dist/strategies.mjs
CHANGED
|
@@ -23,8 +23,8 @@ import {
|
|
|
23
23
|
resolveHyperliquidPerpFromSignalSymbol,
|
|
24
24
|
resolveStrategyConfig,
|
|
25
25
|
validateEntryProtectionAtArrival
|
|
26
|
-
} from "./chunk-
|
|
27
|
-
import "./chunk-
|
|
26
|
+
} from "./chunk-EVKQ4DI6.mjs";
|
|
27
|
+
import "./chunk-NJEYNGRU.mjs";
|
|
28
28
|
import {
|
|
29
29
|
DEFAULT_AI_MODEL,
|
|
30
30
|
MAX_AI_SERIES_POINTS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/node",
|
|
3
|
-
"version": "3.1.29-beta.
|
|
3
|
+
"version": "3.1.29-beta.292",
|
|
4
4
|
"description": "Node-only runtime for the TradeJS TypeScript framework: strategies, backtests, Pine strategy loading, and plugin registries.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tradejs",
|
|
@@ -110,9 +110,9 @@
|
|
|
110
110
|
"dependencies": {
|
|
111
111
|
"@langchain/core": "^1.2.3",
|
|
112
112
|
"@langchain/openai": "^1.5.5",
|
|
113
|
-
"@tradejs/core": "^3.1.29-beta.
|
|
114
|
-
"@tradejs/infra": "^3.1.29-beta.
|
|
115
|
-
"@tradejs/types": "^3.1.29-beta.
|
|
113
|
+
"@tradejs/core": "^3.1.29-beta.292",
|
|
114
|
+
"@tradejs/infra": "^3.1.29-beta.292",
|
|
115
|
+
"@tradejs/types": "^3.1.29-beta.292",
|
|
116
116
|
"@types/node": "^24",
|
|
117
117
|
"chalk": "4.1.2",
|
|
118
118
|
"ioredis": "5.11.1",
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
"typescript": "^5.9"
|
|
126
126
|
},
|
|
127
127
|
"devDependencies": {
|
|
128
|
-
"@tradejs/indicators": "^3.1.29-beta.
|
|
128
|
+
"@tradejs/indicators": "^3.1.29-beta.292",
|
|
129
129
|
"@tradejs/strategy-adaptive-momentum-ribbon": "3.0.5",
|
|
130
130
|
"@tradejs/strategy-hyperliquid-consensus": "3.0.5",
|
|
131
131
|
"@tradejs/strategy-kit": "3.0.6",
|