@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.
@@ -956,39 +956,72 @@ function createPortfolioCoordinator({
956
956
  );
957
957
  if (admitted.result) return { reason: admitted.result };
958
958
  let submitted = false;
959
+ let retry = 0;
960
+ let attemptId = entry.id;
961
+ let needsReservation = false;
959
962
  try {
960
963
  const value = await send(
961
964
  async (qty) => {
962
965
  if (!Number.isFinite(qty) || qty <= 0)
963
966
  throw new Error("Invalid submitted portfolio quantity");
964
- submitted = true;
965
- const update = await updateReservation(now2, entry.id, true, qty);
967
+ if (needsReservation) {
968
+ attemptId = `${entry.id.slice(0, 30)}r${++retry}`;
969
+ const admission = await transact(
970
+ now2,
971
+ (s, o) => {
972
+ const r = (0, import_portfolio.reservePortfolioEntry)(policy, strategy, s, o, {
973
+ ...entry,
974
+ id: attemptId,
975
+ qty,
976
+ submitted: true,
977
+ oppositeHandled: false
978
+ });
979
+ return { state: r.state, result: r.reason };
980
+ },
981
+ true
982
+ );
983
+ if (admission.result) throw new Error(admission.result);
984
+ needsReservation = false;
985
+ submitted = true;
986
+ return attemptId;
987
+ }
988
+ const update = await updateReservation(now2, attemptId, true, qty);
966
989
  if (!update.result)
967
990
  throw new Error("PORTFOLIO_SUBMISSION_NO_LONGER_ADMITTED");
991
+ submitted = true;
992
+ return attemptId;
968
993
  },
969
- async () => {
994
+ async (retrying = false) => {
970
995
  await transact(
971
996
  now2,
972
997
  (s) => ({
973
998
  state: {
974
999
  ...s,
975
- reservations: s.reservations.filter((r) => r.id !== entry.id),
1000
+ reservations: s.reservations.filter(
1001
+ (r) => r.id !== attemptId
1002
+ ),
976
1003
  completedSignals: {
977
- ...s.completedSignals,
978
- [entry.signalId]: now2
1004
+ ...Object.fromEntries(
1005
+ Object.entries(s.completedSignals).filter(
1006
+ ([id]) => !retrying || id !== entry.signalId
1007
+ )
1008
+ ),
1009
+ ...retrying ? {} : { [entry.signalId]: now2 }
979
1010
  }
980
1011
  },
981
1012
  result: void 0
982
1013
  }),
983
1014
  true
984
1015
  );
1016
+ submitted = false;
1017
+ needsReservation = retrying;
985
1018
  }
986
1019
  );
987
- if (!submitted) await updateReservation(now2, entry.id, false);
1020
+ if (!submitted) await updateReservation(now2, attemptId, false);
988
1021
  else await tick(now2, true);
989
1022
  return { value };
990
1023
  } catch (error) {
991
- if (!submitted) await updateReservation(now2, entry.id, false);
1024
+ if (!submitted) await updateReservation(now2, attemptId, false);
992
1025
  throw error;
993
1026
  }
994
1027
  }
@@ -1356,7 +1389,9 @@ function getPortfolioRuntime({
1356
1389
  env,
1357
1390
  userName,
1358
1391
  accountId,
1359
- deploymentId
1392
+ deploymentId,
1393
+ symbol,
1394
+ canClose
1360
1395
  }) {
1361
1396
  const policy = (0, import_portfolio2.parsePortfolioPolicy)(value);
1362
1397
  if (!policy) return void 0;
@@ -1387,8 +1422,13 @@ function getPortfolioRuntime({
1387
1422
  if (existing) {
1388
1423
  if (existing.fingerprint !== fingerprint3)
1389
1424
  throw new Error("Conflicting portfolio configuration for the same scope");
1425
+ if (symbol && canClose)
1426
+ existing.permissions.set(JSON.stringify([strategy, symbol]), canClose);
1390
1427
  return existing.coordinator;
1391
1428
  }
1429
+ const permissions = /* @__PURE__ */ new Map();
1430
+ if (symbol && canClose)
1431
+ permissions.set(JSON.stringify([strategy, symbol]), canClose);
1392
1432
  const coordinator = createPortfolioCoordinator({
1393
1433
  policy,
1394
1434
  strategy,
@@ -1409,6 +1449,10 @@ function getPortfolioRuntime({
1409
1449
  },
1410
1450
  observe: (asOf, reservations) => replay ? connector.getPortfolioObservation(asOf, reservations) : observeRuntimePortfolio(connector, binding, asOf, reservations),
1411
1451
  async close(position, intent, asOf) {
1452
+ const permission = permissions.get(
1453
+ JSON.stringify([position.strategy, position.symbol])
1454
+ );
1455
+ if (!permission || !await permission(position, intent, asOf)) return;
1412
1456
  if (!replay) {
1413
1457
  const trade = await getActiveRuntimeTrade({
1414
1458
  ...binding,
@@ -1443,7 +1487,7 @@ function getPortfolioRuntime({
1443
1487
  }
1444
1488
  }
1445
1489
  });
1446
- registry.set(cacheKey, { fingerprint: fingerprint3, coordinator });
1490
+ registry.set(cacheKey, { fingerprint: fingerprint3, coordinator, permissions });
1447
1491
  return coordinator;
1448
1492
  }
1449
1493
 
@@ -6278,7 +6322,7 @@ var executeEntryOrder = async ({
6278
6322
  }) => {
6279
6323
  await beforePlaceOrder?.();
6280
6324
  const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
6281
- const orderId = signal.orderId || createRuntimeOrderId(signal.strategy);
6325
+ let orderId = signal.orderId || createRuntimeOrderId(signal.strategy);
6282
6326
  const signalTimestamp = signal.timestamp;
6283
6327
  const signalClosePrice = currentPrice;
6284
6328
  signal.orderId = orderId;
@@ -6351,11 +6395,12 @@ var executeEntryOrder = async ({
6351
6395
  ...beforeSubmit || markPortfolioSubmitted ? {
6352
6396
  beforeSubmit: async (context) => {
6353
6397
  await beforeSubmit?.(context);
6354
- await markPortfolioSubmitted?.(context.qty);
6398
+ return markPortfolioSubmitted?.(context.qty);
6355
6399
  }
6356
6400
  } : {}
6357
6401
  });
6358
6402
  const orderAckTime = Date.now();
6403
+ if (markPortfolioSubmitted && signal.orderId) orderId = signal.orderId;
6359
6404
  const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
6360
6405
  const currentPosition = await connector.getPosition(symbol);
6361
6406
  const fillTime = Date.now();
@@ -7643,7 +7688,27 @@ var createStrategyRuntime = ({
7643
7688
  const projectConfig = await loadTradejsConfig(projectRoot);
7644
7689
  const projectHooks = projectConfig.hooks;
7645
7690
  const env = String(config.ENV ?? "BACKTEST");
7691
+ let portfolioMarket;
7646
7692
  const portfolio = getPortfolioRuntime({
7693
+ symbol,
7694
+ canClose: async (position, intent, asOf) => {
7695
+ if (!portfolioMarket || portfolioMarket.candle.timestamp > asOf || !isRuntimeOrderExecutionEnabled({ config, env, connector }))
7696
+ return false;
7697
+ const gate = await authorizeClose(
7698
+ {
7699
+ kind: "exit",
7700
+ code: intent.reason,
7701
+ closePlan: {
7702
+ direction: position.direction,
7703
+ price: position.markPrice,
7704
+ timestamp: asOf
7705
+ }
7706
+ },
7707
+ portfolioMarket,
7708
+ position.strategy
7709
+ );
7710
+ return gate?.allow !== false;
7711
+ },
7647
7712
  connectorName,
7648
7713
  value: baseConfig?.PORTFOLIO_POLICY ?? config.PORTFOLIO_POLICY,
7649
7714
  strategy: strategyName,
@@ -7818,7 +7883,10 @@ var createStrategyRuntime = ({
7818
7883
  return invokeHook(stage, hook, params, errorContext);
7819
7884
  };
7820
7885
  const invokeGateHooks = async (stage, hook, params, errorContext = {}) => {
7821
- const failureResult = stage === "beforeEntryGate" ? { allow: false, reason: "ERROR" } : void 0;
7886
+ const failureResult = {
7887
+ allow: false,
7888
+ reason: "ERROR"
7889
+ };
7822
7890
  const projectResults = await invokeProjectHooks(
7823
7891
  stage,
7824
7892
  params,
@@ -7839,6 +7907,12 @@ var createStrategyRuntime = ({
7839
7907
  failureResult
7840
7908
  );
7841
7909
  };
7910
+ const authorizeClose = (decision, market, owner = strategyName) => invokeGateHooks(
7911
+ "beforeClosePosition",
7912
+ resolveManifest(owner)?.hooks?.beforeClosePosition,
7913
+ { ctx: getHookCtx(owner), market, decision },
7914
+ { decision, market }
7915
+ );
7842
7916
  const applyProjectAfterCoreDecisionHooks = async ({
7843
7917
  hookCtx,
7844
7918
  market,
@@ -8152,6 +8226,7 @@ var createStrategyRuntime = ({
8152
8226
  });
8153
8227
  };
8154
8228
  const runWithDecisionOverride = async (candle, btcCandle, options = {}) => {
8229
+ portfolioMarket = { candle, btcCandle };
8155
8230
  if (portfolioContext)
8156
8231
  await portfolioContext(
8157
8232
  env === "BACKTEST" || env === "PARITY" ? candle.timestamp : Date.now()
@@ -8258,15 +8333,10 @@ var createStrategyRuntime = ({
8258
8333
  if (!makeOrdersEnabled) {
8259
8334
  return decision.code;
8260
8335
  }
8261
- const closeGate = await invokeGateHooks(
8262
- "beforeClosePosition",
8263
- decisionManifest?.hooks?.beforeClosePosition,
8264
- {
8265
- ctx: decisionHookCtx,
8266
- market,
8267
- decision
8268
- },
8269
- { decision, market }
8336
+ const closeGate = await authorizeClose(
8337
+ decision,
8338
+ market,
8339
+ decisionHookCtx.strategyName
8270
8340
  );
8271
8341
  if (closeGate?.allow === false) {
8272
8342
  return closeGate.reason ? `CLOSE_BLOCKED_BY_HOOK:${closeGate.reason}` : "CLOSE_BLOCKED_BY_HOOK";
@@ -2,8 +2,8 @@ import {
2
2
  getRuntimeDeployment,
3
3
  loadResolvedRuntimeStrategies
4
4
  } from "./chunk-W6PPAADK.mjs";
5
- import "./chunk-HRON2EAI.mjs";
6
- import "./chunk-UUNEWAT4.mjs";
5
+ import "./chunk-EVKQ4DI6.mjs";
6
+ import "./chunk-NJEYNGRU.mjs";
7
7
  import "./chunk-PYJWXAYR.mjs";
8
8
  import "./chunk-LZDXRXIU.mjs";
9
9
  import "./chunk-YR55Q355.mjs";
package/dist/mcp.js CHANGED
@@ -950,39 +950,72 @@ function createPortfolioCoordinator({
950
950
  );
951
951
  if (admitted.result) return { reason: admitted.result };
952
952
  let submitted = false;
953
+ let retry = 0;
954
+ let attemptId = entry.id;
955
+ let needsReservation = false;
953
956
  try {
954
957
  const value = await send(
955
958
  async (qty) => {
956
959
  if (!Number.isFinite(qty) || qty <= 0)
957
960
  throw new Error("Invalid submitted portfolio quantity");
958
- submitted = true;
959
- const update = await updateReservation(now2, entry.id, true, qty);
961
+ if (needsReservation) {
962
+ attemptId = `${entry.id.slice(0, 30)}r${++retry}`;
963
+ const admission = await transact(
964
+ now2,
965
+ (s, o) => {
966
+ const r = (0, import_portfolio.reservePortfolioEntry)(policy, strategy, s, o, {
967
+ ...entry,
968
+ id: attemptId,
969
+ qty,
970
+ submitted: true,
971
+ oppositeHandled: false
972
+ });
973
+ return { state: r.state, result: r.reason };
974
+ },
975
+ true
976
+ );
977
+ if (admission.result) throw new Error(admission.result);
978
+ needsReservation = false;
979
+ submitted = true;
980
+ return attemptId;
981
+ }
982
+ const update = await updateReservation(now2, attemptId, true, qty);
960
983
  if (!update.result)
961
984
  throw new Error("PORTFOLIO_SUBMISSION_NO_LONGER_ADMITTED");
985
+ submitted = true;
986
+ return attemptId;
962
987
  },
963
- async () => {
988
+ async (retrying = false) => {
964
989
  await transact(
965
990
  now2,
966
991
  (s) => ({
967
992
  state: {
968
993
  ...s,
969
- reservations: s.reservations.filter((r) => r.id !== entry.id),
994
+ reservations: s.reservations.filter(
995
+ (r) => r.id !== attemptId
996
+ ),
970
997
  completedSignals: {
971
- ...s.completedSignals,
972
- [entry.signalId]: now2
998
+ ...Object.fromEntries(
999
+ Object.entries(s.completedSignals).filter(
1000
+ ([id]) => !retrying || id !== entry.signalId
1001
+ )
1002
+ ),
1003
+ ...retrying ? {} : { [entry.signalId]: now2 }
973
1004
  }
974
1005
  },
975
1006
  result: void 0
976
1007
  }),
977
1008
  true
978
1009
  );
1010
+ submitted = false;
1011
+ needsReservation = retrying;
979
1012
  }
980
1013
  );
981
- if (!submitted) await updateReservation(now2, entry.id, false);
1014
+ if (!submitted) await updateReservation(now2, attemptId, false);
982
1015
  else await tick(now2, true);
983
1016
  return { value };
984
1017
  } catch (error) {
985
- if (!submitted) await updateReservation(now2, entry.id, false);
1018
+ if (!submitted) await updateReservation(now2, attemptId, false);
986
1019
  throw error;
987
1020
  }
988
1021
  }
@@ -1350,7 +1383,9 @@ function getPortfolioRuntime({
1350
1383
  env,
1351
1384
  userName,
1352
1385
  accountId,
1353
- deploymentId
1386
+ deploymentId,
1387
+ symbol,
1388
+ canClose
1354
1389
  }) {
1355
1390
  const policy = (0, import_portfolio2.parsePortfolioPolicy)(value);
1356
1391
  if (!policy) return void 0;
@@ -1381,8 +1416,13 @@ function getPortfolioRuntime({
1381
1416
  if (existing) {
1382
1417
  if (existing.fingerprint !== fingerprint3)
1383
1418
  throw new Error("Conflicting portfolio configuration for the same scope");
1419
+ if (symbol && canClose)
1420
+ existing.permissions.set(JSON.stringify([strategy, symbol]), canClose);
1384
1421
  return existing.coordinator;
1385
1422
  }
1423
+ const permissions = /* @__PURE__ */ new Map();
1424
+ if (symbol && canClose)
1425
+ permissions.set(JSON.stringify([strategy, symbol]), canClose);
1386
1426
  const coordinator = createPortfolioCoordinator({
1387
1427
  policy,
1388
1428
  strategy,
@@ -1403,6 +1443,10 @@ function getPortfolioRuntime({
1403
1443
  },
1404
1444
  observe: (asOf, reservations) => replay ? connector.getPortfolioObservation(asOf, reservations) : observeRuntimePortfolio(connector, binding, asOf, reservations),
1405
1445
  async close(position, intent, asOf) {
1446
+ const permission = permissions.get(
1447
+ JSON.stringify([position.strategy, position.symbol])
1448
+ );
1449
+ if (!permission || !await permission(position, intent, asOf)) return;
1406
1450
  if (!replay) {
1407
1451
  const trade = await getActiveRuntimeTrade({
1408
1452
  ...binding,
@@ -1437,7 +1481,7 @@ function getPortfolioRuntime({
1437
1481
  }
1438
1482
  }
1439
1483
  });
1440
- registry.set(cacheKey, { fingerprint: fingerprint3, coordinator });
1484
+ registry.set(cacheKey, { fingerprint: fingerprint3, coordinator, permissions });
1441
1485
  return coordinator;
1442
1486
  }
1443
1487
 
@@ -6273,7 +6317,7 @@ var executeEntryOrder = async ({
6273
6317
  }) => {
6274
6318
  await beforePlaceOrder?.();
6275
6319
  const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
6276
- const orderId = signal.orderId || createRuntimeOrderId(signal.strategy);
6320
+ let orderId = signal.orderId || createRuntimeOrderId(signal.strategy);
6277
6321
  const signalTimestamp = signal.timestamp;
6278
6322
  const signalClosePrice = currentPrice;
6279
6323
  signal.orderId = orderId;
@@ -6346,11 +6390,12 @@ var executeEntryOrder = async ({
6346
6390
  ...beforeSubmit || markPortfolioSubmitted ? {
6347
6391
  beforeSubmit: async (context) => {
6348
6392
  await beforeSubmit?.(context);
6349
- await markPortfolioSubmitted?.(context.qty);
6393
+ return markPortfolioSubmitted?.(context.qty);
6350
6394
  }
6351
6395
  } : {}
6352
6396
  });
6353
6397
  const orderAckTime = Date.now();
6398
+ if (markPortfolioSubmitted && signal.orderId) orderId = signal.orderId;
6354
6399
  const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
6355
6400
  const currentPosition = await connector.getPosition(symbol);
6356
6401
  const fillTime = Date.now();
@@ -7638,7 +7683,27 @@ var createStrategyRuntime = ({
7638
7683
  const projectConfig = await loadTradejsConfig(projectRoot);
7639
7684
  const projectHooks = projectConfig.hooks;
7640
7685
  const env = String(config.ENV ?? "BACKTEST");
7686
+ let portfolioMarket;
7641
7687
  const portfolio = getPortfolioRuntime({
7688
+ symbol,
7689
+ canClose: async (position, intent, asOf) => {
7690
+ if (!portfolioMarket || portfolioMarket.candle.timestamp > asOf || !isRuntimeOrderExecutionEnabled({ config, env, connector }))
7691
+ return false;
7692
+ const gate = await authorizeClose(
7693
+ {
7694
+ kind: "exit",
7695
+ code: intent.reason,
7696
+ closePlan: {
7697
+ direction: position.direction,
7698
+ price: position.markPrice,
7699
+ timestamp: asOf
7700
+ }
7701
+ },
7702
+ portfolioMarket,
7703
+ position.strategy
7704
+ );
7705
+ return gate?.allow !== false;
7706
+ },
7642
7707
  connectorName,
7643
7708
  value: baseConfig?.PORTFOLIO_POLICY ?? config.PORTFOLIO_POLICY,
7644
7709
  strategy: strategyName,
@@ -7813,7 +7878,10 @@ var createStrategyRuntime = ({
7813
7878
  return invokeHook(stage, hook, params, errorContext);
7814
7879
  };
7815
7880
  const invokeGateHooks = async (stage, hook, params, errorContext = {}) => {
7816
- const failureResult = stage === "beforeEntryGate" ? { allow: false, reason: "ERROR" } : void 0;
7881
+ const failureResult = {
7882
+ allow: false,
7883
+ reason: "ERROR"
7884
+ };
7817
7885
  const projectResults = await invokeProjectHooks(
7818
7886
  stage,
7819
7887
  params,
@@ -7834,6 +7902,12 @@ var createStrategyRuntime = ({
7834
7902
  failureResult
7835
7903
  );
7836
7904
  };
7905
+ const authorizeClose = (decision, market, owner = strategyName) => invokeGateHooks(
7906
+ "beforeClosePosition",
7907
+ resolveManifest(owner)?.hooks?.beforeClosePosition,
7908
+ { ctx: getHookCtx(owner), market, decision },
7909
+ { decision, market }
7910
+ );
7837
7911
  const applyProjectAfterCoreDecisionHooks = async ({
7838
7912
  hookCtx,
7839
7913
  market,
@@ -8147,6 +8221,7 @@ var createStrategyRuntime = ({
8147
8221
  });
8148
8222
  };
8149
8223
  const runWithDecisionOverride = async (candle, btcCandle, options = {}) => {
8224
+ portfolioMarket = { candle, btcCandle };
8150
8225
  if (portfolioContext)
8151
8226
  await portfolioContext(
8152
8227
  env === "BACKTEST" || env === "PARITY" ? candle.timestamp : Date.now()
@@ -8253,15 +8328,10 @@ var createStrategyRuntime = ({
8253
8328
  if (!makeOrdersEnabled) {
8254
8329
  return decision.code;
8255
8330
  }
8256
- const closeGate = await invokeGateHooks(
8257
- "beforeClosePosition",
8258
- decisionManifest?.hooks?.beforeClosePosition,
8259
- {
8260
- ctx: decisionHookCtx,
8261
- market,
8262
- decision
8263
- },
8264
- { decision, market }
8331
+ const closeGate = await authorizeClose(
8332
+ decision,
8333
+ market,
8334
+ decisionHookCtx.strategyName
8265
8335
  );
8266
8336
  if (closeGate?.allow === false) {
8267
8337
  return closeGate.reason ? `CLOSE_BLOCKED_BY_HOOK:${closeGate.reason}` : "CLOSE_BLOCKED_BY_HOOK";
package/dist/mcp.mjs CHANGED
@@ -7,8 +7,8 @@ import {
7
7
  listRuntimeDeployments,
8
8
  loadResolvedRuntimeStrategies
9
9
  } from "./chunk-W6PPAADK.mjs";
10
- import "./chunk-HRON2EAI.mjs";
11
- import "./chunk-UUNEWAT4.mjs";
10
+ import "./chunk-EVKQ4DI6.mjs";
11
+ import "./chunk-NJEYNGRU.mjs";
12
12
  import {
13
13
  ensureIndicatorPluginsLoaded
14
14
  } from "./chunk-PYJWXAYR.mjs";
@@ -16,7 +16,7 @@ declare function createPortfolioCoordinator({ policy, strategy, store, execution
16
16
  }): {
17
17
  context(now: number): Promise<_tradejs_types.PortfolioContext>;
18
18
  tick: (now: number, force?: boolean) => Promise<_tradejs_types.PortfolioContext>;
19
- submit<T>(now: number, entry: Omit<PortfolioReservation, "submitted" | "oppositeHandled">, send: (markSubmitted: (qty: number) => Promise<void>, markRejected: () => Promise<void>) => Promise<T>): Promise<{
19
+ submit<T>(now: number, entry: Omit<PortfolioReservation, "submitted" | "oppositeHandled">, send: (markSubmitted: (qty: number) => Promise<string>, markRejected: (retrying?: boolean) => Promise<void>) => Promise<T>): Promise<{
20
20
  reason?: string;
21
21
  value?: T;
22
22
  }>;
@@ -16,7 +16,7 @@ declare function createPortfolioCoordinator({ policy, strategy, store, execution
16
16
  }): {
17
17
  context(now: number): Promise<_tradejs_types.PortfolioContext>;
18
18
  tick: (now: number, force?: boolean) => Promise<_tradejs_types.PortfolioContext>;
19
- submit<T>(now: number, entry: Omit<PortfolioReservation, "submitted" | "oppositeHandled">, send: (markSubmitted: (qty: number) => Promise<void>, markRejected: () => Promise<void>) => Promise<T>): Promise<{
19
+ submit<T>(now: number, entry: Omit<PortfolioReservation, "submitted" | "oppositeHandled">, send: (markSubmitted: (qty: number) => Promise<string>, markRejected: (retrying?: boolean) => Promise<void>) => Promise<T>): Promise<{
20
20
  reason?: string;
21
21
  value?: T;
22
22
  }>;
package/dist/portfolio.js CHANGED
@@ -154,39 +154,72 @@ function createPortfolioCoordinator({
154
154
  );
155
155
  if (admitted.result) return { reason: admitted.result };
156
156
  let submitted = false;
157
+ let retry = 0;
158
+ let attemptId = entry.id;
159
+ let needsReservation = false;
157
160
  try {
158
161
  const value = await send(
159
162
  async (qty) => {
160
163
  if (!Number.isFinite(qty) || qty <= 0)
161
164
  throw new Error("Invalid submitted portfolio quantity");
162
- submitted = true;
163
- const update = await updateReservation(now, entry.id, true, qty);
165
+ if (needsReservation) {
166
+ attemptId = `${entry.id.slice(0, 30)}r${++retry}`;
167
+ const admission = await transact(
168
+ now,
169
+ (s, o) => {
170
+ const r = (0, import_portfolio.reservePortfolioEntry)(policy, strategy, s, o, {
171
+ ...entry,
172
+ id: attemptId,
173
+ qty,
174
+ submitted: true,
175
+ oppositeHandled: false
176
+ });
177
+ return { state: r.state, result: r.reason };
178
+ },
179
+ true
180
+ );
181
+ if (admission.result) throw new Error(admission.result);
182
+ needsReservation = false;
183
+ submitted = true;
184
+ return attemptId;
185
+ }
186
+ const update = await updateReservation(now, attemptId, true, qty);
164
187
  if (!update.result)
165
188
  throw new Error("PORTFOLIO_SUBMISSION_NO_LONGER_ADMITTED");
189
+ submitted = true;
190
+ return attemptId;
166
191
  },
167
- async () => {
192
+ async (retrying = false) => {
168
193
  await transact(
169
194
  now,
170
195
  (s) => ({
171
196
  state: {
172
197
  ...s,
173
- reservations: s.reservations.filter((r) => r.id !== entry.id),
198
+ reservations: s.reservations.filter(
199
+ (r) => r.id !== attemptId
200
+ ),
174
201
  completedSignals: {
175
- ...s.completedSignals,
176
- [entry.signalId]: now
202
+ ...Object.fromEntries(
203
+ Object.entries(s.completedSignals).filter(
204
+ ([id]) => !retrying || id !== entry.signalId
205
+ )
206
+ ),
207
+ ...retrying ? {} : { [entry.signalId]: now }
177
208
  }
178
209
  },
179
210
  result: void 0
180
211
  }),
181
212
  true
182
213
  );
214
+ submitted = false;
215
+ needsReservation = retrying;
183
216
  }
184
217
  );
185
- if (!submitted) await updateReservation(now, entry.id, false);
218
+ if (!submitted) await updateReservation(now, attemptId, false);
186
219
  else await tick(now, true);
187
220
  return { value };
188
221
  } catch (error) {
189
- if (!submitted) await updateReservation(now, entry.id, false);
222
+ if (!submitted) await updateReservation(now, attemptId, false);
190
223
  throw error;
191
224
  }
192
225
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createMemoryPortfolioStore,
3
3
  createPortfolioCoordinator
4
- } from "./chunk-UUNEWAT4.mjs";
4
+ } from "./chunk-NJEYNGRU.mjs";
5
5
  import "./chunk-Y6FXYEAI.mjs";
6
6
  export {
7
7
  createMemoryPortfolioStore,