@tradejs/node 3.1.26 → 3.1.27-beta.245

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 CHANGED
@@ -4927,6 +4927,68 @@ var buildAiHookContext = ({
4927
4927
  };
4928
4928
  };
4929
4929
 
4930
+ // src/strategy/runtimeDecisionTelemetry.ts
4931
+ var finitePositive = (value) => {
4932
+ const parsed = Number(value);
4933
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
4934
+ };
4935
+ var round = (value) => Number(value.toFixed(12));
4936
+ var assessRuntimeEntryRisk = ({
4937
+ qty,
4938
+ entryPrice,
4939
+ stopLossPrice,
4940
+ maxLossValue
4941
+ }) => {
4942
+ const normalizedQty = finitePositive(qty);
4943
+ const normalizedEntryPrice = finitePositive(entryPrice);
4944
+ const normalizedStopLossPrice = finitePositive(stopLossPrice);
4945
+ const normalizedMaxLossValue = finitePositive(maxLossValue);
4946
+ const plannedLossValue = normalizedQty != null && normalizedEntryPrice != null && normalizedStopLossPrice != null ? round(
4947
+ Math.abs(normalizedEntryPrice - normalizedStopLossPrice) * normalizedQty
4948
+ ) : null;
4949
+ if (plannedLossValue == null) {
4950
+ return {
4951
+ stage: "order_plan",
4952
+ status: "unavailable",
4953
+ reason: "ORDER_PLAN_RISK_INPUTS_UNAVAILABLE",
4954
+ plannedLossValue: null,
4955
+ maxLossValue: normalizedMaxLossValue,
4956
+ limitUtilization: null,
4957
+ enforced: false
4958
+ };
4959
+ }
4960
+ if (normalizedMaxLossValue == null) {
4961
+ return {
4962
+ stage: "order_plan",
4963
+ status: "unavailable",
4964
+ reason: "MAX_LOSS_VALUE_UNAVAILABLE",
4965
+ plannedLossValue,
4966
+ maxLossValue: null,
4967
+ limitUtilization: null,
4968
+ enforced: false
4969
+ };
4970
+ }
4971
+ const limitUtilization = round(plannedLossValue / normalizedMaxLossValue);
4972
+ const withinLimit = plannedLossValue <= normalizedMaxLossValue * (1 + 1e-9);
4973
+ return {
4974
+ stage: "order_plan",
4975
+ status: withinLimit ? "approved" : "rejected",
4976
+ reason: withinLimit ? "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE" : "PLANNED_LOSS_EXCEEDS_MAX_LOSS_VALUE",
4977
+ plannedLossValue,
4978
+ maxLossValue: normalizedMaxLossValue,
4979
+ limitUtilization,
4980
+ enforced: false
4981
+ };
4982
+ };
4983
+ var buildRuntimeAdmissionDecision = ({
4984
+ status,
4985
+ reason
4986
+ }) => ({
4987
+ stage: "runtime_admission",
4988
+ status,
4989
+ reason
4990
+ });
4991
+
4930
4992
  // src/strategy/runtimeHooks.ts
4931
4993
  var normalizeConfigHookList = (value) => {
4932
4994
  if (Array.isArray(value)) {
@@ -6050,6 +6112,12 @@ var createStrategyRuntime = ({
6050
6112
  if (decisionHookCtx.policyProfileId) {
6051
6113
  signal.policyProfileId = decisionHookCtx.policyProfileId;
6052
6114
  }
6115
+ signal.riskDecision = assessRuntimeEntryRisk({
6116
+ qty: decision.orderPlan.qty,
6117
+ entryPrice: decision.entryContext.prices.currentPrice,
6118
+ stopLossPrice: decision.orderPlan.stopLossPrice,
6119
+ maxLossValue: runtimeLineage?.maxLossValue ?? config.MAX_LOSS_VALUE
6120
+ });
6053
6121
  }
6054
6122
  const entry = buildHookEntry({
6055
6123
  decision,
@@ -6159,13 +6227,16 @@ var createStrategyRuntime = ({
6159
6227
  signal.orderStatus = "skipped";
6160
6228
  signal.orderSkipReason = skipReason;
6161
6229
  signal.strategyRevision = strategyRevision;
6230
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6231
+ status: "rejected",
6232
+ reason: skipReason
6233
+ });
6162
6234
  }
6163
6235
  return signal ?? skipReason;
6164
6236
  }
6165
6237
  if (!shouldMakeOrder) {
6166
6238
  if (signal) {
6167
- signal.orderStatus = "skipped";
6168
- signal.orderSkipReason = getEntrySkipReason({
6239
+ const skipReason = getEntrySkipReason({
6169
6240
  makeOrdersEnabled,
6170
6241
  env,
6171
6242
  ml,
@@ -6173,6 +6244,12 @@ var createStrategyRuntime = ({
6173
6244
  quality,
6174
6245
  minAiQuality
6175
6246
  });
6247
+ signal.orderStatus = "skipped";
6248
+ signal.orderSkipReason = skipReason;
6249
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6250
+ status: "not_applicable",
6251
+ reason: `ENTRY_POLICY_REJECTED:${skipReason}`
6252
+ });
6176
6253
  }
6177
6254
  return signal ?? decision.code;
6178
6255
  }
@@ -6195,10 +6272,20 @@ var createStrategyRuntime = ({
6195
6272
  if (signal) {
6196
6273
  signal.orderStatus = "skipped";
6197
6274
  signal.orderSkipReason = skipReason;
6275
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6276
+ status: "rejected",
6277
+ reason: skipReason
6278
+ });
6198
6279
  }
6199
6280
  return signal ?? skipReason;
6200
6281
  }
6201
6282
  if (backtestEntryDelayBars > 0) {
6283
+ if (signal) {
6284
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6285
+ status: "approved",
6286
+ reason: "ENTRY_ADMITTED"
6287
+ });
6288
+ }
6202
6289
  pendingBacktestEntry = {
6203
6290
  delayBars: backtestEntryDelayBars,
6204
6291
  delayBarsRemaining: backtestEntryDelayBars,
@@ -6212,6 +6299,12 @@ var createStrategyRuntime = ({
6212
6299
  };
6213
6300
  return `BACKTEST_ENTRY_DELAY_QUEUED:${backtestEntryDelayBars}`;
6214
6301
  }
6302
+ if (signal) {
6303
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6304
+ status: "approved",
6305
+ reason: "ENTRY_ADMITTED"
6306
+ });
6307
+ }
6215
6308
  return executeEntryDecision({
6216
6309
  connector,
6217
6310
  symbol,
package/dist/backtest.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  buildMlPayload,
7
7
  enrichSignalWithMarketContextStages
8
- } from "./chunk-6DGLTGLW.mjs";
8
+ } from "./chunk-XHNJ757K.mjs";
9
9
  import {
10
10
  buildAiPayload,
11
11
  getStrategyCreator
@@ -3272,6 +3272,68 @@ var buildAiHookContext = ({
3272
3272
  };
3273
3273
  };
3274
3274
 
3275
+ // src/strategy/runtimeDecisionTelemetry.ts
3276
+ var finitePositive = (value) => {
3277
+ const parsed = Number(value);
3278
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
3279
+ };
3280
+ var round = (value) => Number(value.toFixed(12));
3281
+ var assessRuntimeEntryRisk = ({
3282
+ qty,
3283
+ entryPrice,
3284
+ stopLossPrice,
3285
+ maxLossValue
3286
+ }) => {
3287
+ const normalizedQty = finitePositive(qty);
3288
+ const normalizedEntryPrice = finitePositive(entryPrice);
3289
+ const normalizedStopLossPrice = finitePositive(stopLossPrice);
3290
+ const normalizedMaxLossValue = finitePositive(maxLossValue);
3291
+ const plannedLossValue = normalizedQty != null && normalizedEntryPrice != null && normalizedStopLossPrice != null ? round(
3292
+ Math.abs(normalizedEntryPrice - normalizedStopLossPrice) * normalizedQty
3293
+ ) : null;
3294
+ if (plannedLossValue == null) {
3295
+ return {
3296
+ stage: "order_plan",
3297
+ status: "unavailable",
3298
+ reason: "ORDER_PLAN_RISK_INPUTS_UNAVAILABLE",
3299
+ plannedLossValue: null,
3300
+ maxLossValue: normalizedMaxLossValue,
3301
+ limitUtilization: null,
3302
+ enforced: false
3303
+ };
3304
+ }
3305
+ if (normalizedMaxLossValue == null) {
3306
+ return {
3307
+ stage: "order_plan",
3308
+ status: "unavailable",
3309
+ reason: "MAX_LOSS_VALUE_UNAVAILABLE",
3310
+ plannedLossValue,
3311
+ maxLossValue: null,
3312
+ limitUtilization: null,
3313
+ enforced: false
3314
+ };
3315
+ }
3316
+ const limitUtilization = round(plannedLossValue / normalizedMaxLossValue);
3317
+ const withinLimit = plannedLossValue <= normalizedMaxLossValue * (1 + 1e-9);
3318
+ return {
3319
+ stage: "order_plan",
3320
+ status: withinLimit ? "approved" : "rejected",
3321
+ reason: withinLimit ? "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE" : "PLANNED_LOSS_EXCEEDS_MAX_LOSS_VALUE",
3322
+ plannedLossValue,
3323
+ maxLossValue: normalizedMaxLossValue,
3324
+ limitUtilization,
3325
+ enforced: false
3326
+ };
3327
+ };
3328
+ var buildRuntimeAdmissionDecision = ({
3329
+ status,
3330
+ reason
3331
+ }) => ({
3332
+ stage: "runtime_admission",
3333
+ status,
3334
+ reason
3335
+ });
3336
+
3275
3337
  // src/strategy/runtimeHooks.ts
3276
3338
  var normalizeConfigHookList = (value) => {
3277
3339
  if (Array.isArray(value)) {
@@ -4397,6 +4459,12 @@ var createStrategyRuntime = ({
4397
4459
  if (decisionHookCtx.policyProfileId) {
4398
4460
  signal.policyProfileId = decisionHookCtx.policyProfileId;
4399
4461
  }
4462
+ signal.riskDecision = assessRuntimeEntryRisk({
4463
+ qty: decision.orderPlan.qty,
4464
+ entryPrice: decision.entryContext.prices.currentPrice,
4465
+ stopLossPrice: decision.orderPlan.stopLossPrice,
4466
+ maxLossValue: runtimeLineage?.maxLossValue ?? config.MAX_LOSS_VALUE
4467
+ });
4400
4468
  }
4401
4469
  const entry = buildHookEntry({
4402
4470
  decision,
@@ -4506,13 +4574,16 @@ var createStrategyRuntime = ({
4506
4574
  signal.orderStatus = "skipped";
4507
4575
  signal.orderSkipReason = skipReason;
4508
4576
  signal.strategyRevision = strategyRevision;
4577
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
4578
+ status: "rejected",
4579
+ reason: skipReason
4580
+ });
4509
4581
  }
4510
4582
  return signal ?? skipReason;
4511
4583
  }
4512
4584
  if (!shouldMakeOrder) {
4513
4585
  if (signal) {
4514
- signal.orderStatus = "skipped";
4515
- signal.orderSkipReason = getEntrySkipReason({
4586
+ const skipReason = getEntrySkipReason({
4516
4587
  makeOrdersEnabled,
4517
4588
  env,
4518
4589
  ml,
@@ -4520,6 +4591,12 @@ var createStrategyRuntime = ({
4520
4591
  quality,
4521
4592
  minAiQuality
4522
4593
  });
4594
+ signal.orderStatus = "skipped";
4595
+ signal.orderSkipReason = skipReason;
4596
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
4597
+ status: "not_applicable",
4598
+ reason: `ENTRY_POLICY_REJECTED:${skipReason}`
4599
+ });
4523
4600
  }
4524
4601
  return signal ?? decision.code;
4525
4602
  }
@@ -4542,10 +4619,20 @@ var createStrategyRuntime = ({
4542
4619
  if (signal) {
4543
4620
  signal.orderStatus = "skipped";
4544
4621
  signal.orderSkipReason = skipReason;
4622
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
4623
+ status: "rejected",
4624
+ reason: skipReason
4625
+ });
4545
4626
  }
4546
4627
  return signal ?? skipReason;
4547
4628
  }
4548
4629
  if (backtestEntryDelayBars > 0) {
4630
+ if (signal) {
4631
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
4632
+ status: "approved",
4633
+ reason: "ENTRY_ADMITTED"
4634
+ });
4635
+ }
4549
4636
  pendingBacktestEntry = {
4550
4637
  delayBars: backtestEntryDelayBars,
4551
4638
  delayBarsRemaining: backtestEntryDelayBars,
@@ -4559,6 +4646,12 @@ var createStrategyRuntime = ({
4559
4646
  };
4560
4647
  return `BACKTEST_ENTRY_DELAY_QUEUED:${backtestEntryDelayBars}`;
4561
4648
  }
4649
+ if (signal) {
4650
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
4651
+ status: "approved",
4652
+ reason: "ENTRY_ADMITTED"
4653
+ });
4654
+ }
4562
4655
  return executeEntryDecision({
4563
4656
  connector,
4564
4657
  symbol,
package/dist/registry.js CHANGED
@@ -4965,6 +4965,68 @@ var buildAiHookContext = ({
4965
4965
  };
4966
4966
  };
4967
4967
 
4968
+ // src/strategy/runtimeDecisionTelemetry.ts
4969
+ var finitePositive = (value) => {
4970
+ const parsed = Number(value);
4971
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
4972
+ };
4973
+ var round = (value) => Number(value.toFixed(12));
4974
+ var assessRuntimeEntryRisk = ({
4975
+ qty,
4976
+ entryPrice,
4977
+ stopLossPrice,
4978
+ maxLossValue
4979
+ }) => {
4980
+ const normalizedQty = finitePositive(qty);
4981
+ const normalizedEntryPrice = finitePositive(entryPrice);
4982
+ const normalizedStopLossPrice = finitePositive(stopLossPrice);
4983
+ const normalizedMaxLossValue = finitePositive(maxLossValue);
4984
+ const plannedLossValue = normalizedQty != null && normalizedEntryPrice != null && normalizedStopLossPrice != null ? round(
4985
+ Math.abs(normalizedEntryPrice - normalizedStopLossPrice) * normalizedQty
4986
+ ) : null;
4987
+ if (plannedLossValue == null) {
4988
+ return {
4989
+ stage: "order_plan",
4990
+ status: "unavailable",
4991
+ reason: "ORDER_PLAN_RISK_INPUTS_UNAVAILABLE",
4992
+ plannedLossValue: null,
4993
+ maxLossValue: normalizedMaxLossValue,
4994
+ limitUtilization: null,
4995
+ enforced: false
4996
+ };
4997
+ }
4998
+ if (normalizedMaxLossValue == null) {
4999
+ return {
5000
+ stage: "order_plan",
5001
+ status: "unavailable",
5002
+ reason: "MAX_LOSS_VALUE_UNAVAILABLE",
5003
+ plannedLossValue,
5004
+ maxLossValue: null,
5005
+ limitUtilization: null,
5006
+ enforced: false
5007
+ };
5008
+ }
5009
+ const limitUtilization = round(plannedLossValue / normalizedMaxLossValue);
5010
+ const withinLimit = plannedLossValue <= normalizedMaxLossValue * (1 + 1e-9);
5011
+ return {
5012
+ stage: "order_plan",
5013
+ status: withinLimit ? "approved" : "rejected",
5014
+ reason: withinLimit ? "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE" : "PLANNED_LOSS_EXCEEDS_MAX_LOSS_VALUE",
5015
+ plannedLossValue,
5016
+ maxLossValue: normalizedMaxLossValue,
5017
+ limitUtilization,
5018
+ enforced: false
5019
+ };
5020
+ };
5021
+ var buildRuntimeAdmissionDecision = ({
5022
+ status,
5023
+ reason
5024
+ }) => ({
5025
+ stage: "runtime_admission",
5026
+ status,
5027
+ reason
5028
+ });
5029
+
4968
5030
  // src/strategy/runtimeHooks.ts
4969
5031
  var normalizeConfigHookList = (value) => {
4970
5032
  if (Array.isArray(value)) {
@@ -6088,6 +6150,12 @@ var createStrategyRuntime = ({
6088
6150
  if (decisionHookCtx.policyProfileId) {
6089
6151
  signal.policyProfileId = decisionHookCtx.policyProfileId;
6090
6152
  }
6153
+ signal.riskDecision = assessRuntimeEntryRisk({
6154
+ qty: decision.orderPlan.qty,
6155
+ entryPrice: decision.entryContext.prices.currentPrice,
6156
+ stopLossPrice: decision.orderPlan.stopLossPrice,
6157
+ maxLossValue: runtimeLineage?.maxLossValue ?? config.MAX_LOSS_VALUE
6158
+ });
6091
6159
  }
6092
6160
  const entry = buildHookEntry({
6093
6161
  decision,
@@ -6197,13 +6265,16 @@ var createStrategyRuntime = ({
6197
6265
  signal.orderStatus = "skipped";
6198
6266
  signal.orderSkipReason = skipReason;
6199
6267
  signal.strategyRevision = strategyRevision;
6268
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6269
+ status: "rejected",
6270
+ reason: skipReason
6271
+ });
6200
6272
  }
6201
6273
  return signal ?? skipReason;
6202
6274
  }
6203
6275
  if (!shouldMakeOrder) {
6204
6276
  if (signal) {
6205
- signal.orderStatus = "skipped";
6206
- signal.orderSkipReason = getEntrySkipReason({
6277
+ const skipReason = getEntrySkipReason({
6207
6278
  makeOrdersEnabled,
6208
6279
  env,
6209
6280
  ml,
@@ -6211,6 +6282,12 @@ var createStrategyRuntime = ({
6211
6282
  quality,
6212
6283
  minAiQuality
6213
6284
  });
6285
+ signal.orderStatus = "skipped";
6286
+ signal.orderSkipReason = skipReason;
6287
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6288
+ status: "not_applicable",
6289
+ reason: `ENTRY_POLICY_REJECTED:${skipReason}`
6290
+ });
6214
6291
  }
6215
6292
  return signal ?? decision.code;
6216
6293
  }
@@ -6233,10 +6310,20 @@ var createStrategyRuntime = ({
6233
6310
  if (signal) {
6234
6311
  signal.orderStatus = "skipped";
6235
6312
  signal.orderSkipReason = skipReason;
6313
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6314
+ status: "rejected",
6315
+ reason: skipReason
6316
+ });
6236
6317
  }
6237
6318
  return signal ?? skipReason;
6238
6319
  }
6239
6320
  if (backtestEntryDelayBars > 0) {
6321
+ if (signal) {
6322
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6323
+ status: "approved",
6324
+ reason: "ENTRY_ADMITTED"
6325
+ });
6326
+ }
6240
6327
  pendingBacktestEntry = {
6241
6328
  delayBars: backtestEntryDelayBars,
6242
6329
  delayBarsRemaining: backtestEntryDelayBars,
@@ -6250,6 +6337,12 @@ var createStrategyRuntime = ({
6250
6337
  };
6251
6338
  return `BACKTEST_ENTRY_DELAY_QUEUED:${backtestEntryDelayBars}`;
6252
6339
  }
6340
+ if (signal) {
6341
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6342
+ status: "approved",
6343
+ reason: "ENTRY_ADMITTED"
6344
+ });
6345
+ }
6253
6346
  return executeEntryDecision({
6254
6347
  connector,
6255
6348
  symbol,
package/dist/registry.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import "./chunk-6DGLTGLW.mjs";
1
+ import "./chunk-XHNJ757K.mjs";
2
2
  import {
3
3
  ensureIndicatorPluginsLoaded,
4
4
  ensureStrategyPluginsLoaded,
@@ -5934,6 +5934,68 @@ var buildAiHookContext = ({
5934
5934
  };
5935
5935
  };
5936
5936
 
5937
+ // src/strategy/runtimeDecisionTelemetry.ts
5938
+ var finitePositive = (value) => {
5939
+ const parsed = Number(value);
5940
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
5941
+ };
5942
+ var round = (value) => Number(value.toFixed(12));
5943
+ var assessRuntimeEntryRisk = ({
5944
+ qty,
5945
+ entryPrice,
5946
+ stopLossPrice,
5947
+ maxLossValue
5948
+ }) => {
5949
+ const normalizedQty = finitePositive(qty);
5950
+ const normalizedEntryPrice = finitePositive(entryPrice);
5951
+ const normalizedStopLossPrice = finitePositive(stopLossPrice);
5952
+ const normalizedMaxLossValue = finitePositive(maxLossValue);
5953
+ const plannedLossValue = normalizedQty != null && normalizedEntryPrice != null && normalizedStopLossPrice != null ? round(
5954
+ Math.abs(normalizedEntryPrice - normalizedStopLossPrice) * normalizedQty
5955
+ ) : null;
5956
+ if (plannedLossValue == null) {
5957
+ return {
5958
+ stage: "order_plan",
5959
+ status: "unavailable",
5960
+ reason: "ORDER_PLAN_RISK_INPUTS_UNAVAILABLE",
5961
+ plannedLossValue: null,
5962
+ maxLossValue: normalizedMaxLossValue,
5963
+ limitUtilization: null,
5964
+ enforced: false
5965
+ };
5966
+ }
5967
+ if (normalizedMaxLossValue == null) {
5968
+ return {
5969
+ stage: "order_plan",
5970
+ status: "unavailable",
5971
+ reason: "MAX_LOSS_VALUE_UNAVAILABLE",
5972
+ plannedLossValue,
5973
+ maxLossValue: null,
5974
+ limitUtilization: null,
5975
+ enforced: false
5976
+ };
5977
+ }
5978
+ const limitUtilization = round(plannedLossValue / normalizedMaxLossValue);
5979
+ const withinLimit = plannedLossValue <= normalizedMaxLossValue * (1 + 1e-9);
5980
+ return {
5981
+ stage: "order_plan",
5982
+ status: withinLimit ? "approved" : "rejected",
5983
+ reason: withinLimit ? "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE" : "PLANNED_LOSS_EXCEEDS_MAX_LOSS_VALUE",
5984
+ plannedLossValue,
5985
+ maxLossValue: normalizedMaxLossValue,
5986
+ limitUtilization,
5987
+ enforced: false
5988
+ };
5989
+ };
5990
+ var buildRuntimeAdmissionDecision = ({
5991
+ status,
5992
+ reason
5993
+ }) => ({
5994
+ stage: "runtime_admission",
5995
+ status,
5996
+ reason
5997
+ });
5998
+
5937
5999
  // src/strategy/runtimeHooks.ts
5938
6000
  var normalizeConfigHookList = (value) => {
5939
6001
  if (Array.isArray(value)) {
@@ -7057,6 +7119,12 @@ var createStrategyRuntime = ({
7057
7119
  if (decisionHookCtx.policyProfileId) {
7058
7120
  signal.policyProfileId = decisionHookCtx.policyProfileId;
7059
7121
  }
7122
+ signal.riskDecision = assessRuntimeEntryRisk({
7123
+ qty: decision.orderPlan.qty,
7124
+ entryPrice: decision.entryContext.prices.currentPrice,
7125
+ stopLossPrice: decision.orderPlan.stopLossPrice,
7126
+ maxLossValue: runtimeLineage?.maxLossValue ?? config.MAX_LOSS_VALUE
7127
+ });
7060
7128
  }
7061
7129
  const entry = buildHookEntry({
7062
7130
  decision,
@@ -7166,13 +7234,16 @@ var createStrategyRuntime = ({
7166
7234
  signal.orderStatus = "skipped";
7167
7235
  signal.orderSkipReason = skipReason;
7168
7236
  signal.strategyRevision = strategyRevision;
7237
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
7238
+ status: "rejected",
7239
+ reason: skipReason
7240
+ });
7169
7241
  }
7170
7242
  return signal ?? skipReason;
7171
7243
  }
7172
7244
  if (!shouldMakeOrder) {
7173
7245
  if (signal) {
7174
- signal.orderStatus = "skipped";
7175
- signal.orderSkipReason = getEntrySkipReason({
7246
+ const skipReason = getEntrySkipReason({
7176
7247
  makeOrdersEnabled,
7177
7248
  env,
7178
7249
  ml,
@@ -7180,6 +7251,12 @@ var createStrategyRuntime = ({
7180
7251
  quality,
7181
7252
  minAiQuality
7182
7253
  });
7254
+ signal.orderStatus = "skipped";
7255
+ signal.orderSkipReason = skipReason;
7256
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
7257
+ status: "not_applicable",
7258
+ reason: `ENTRY_POLICY_REJECTED:${skipReason}`
7259
+ });
7183
7260
  }
7184
7261
  return signal ?? decision.code;
7185
7262
  }
@@ -7202,10 +7279,20 @@ var createStrategyRuntime = ({
7202
7279
  if (signal) {
7203
7280
  signal.orderStatus = "skipped";
7204
7281
  signal.orderSkipReason = skipReason;
7282
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
7283
+ status: "rejected",
7284
+ reason: skipReason
7285
+ });
7205
7286
  }
7206
7287
  return signal ?? skipReason;
7207
7288
  }
7208
7289
  if (backtestEntryDelayBars > 0) {
7290
+ if (signal) {
7291
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
7292
+ status: "approved",
7293
+ reason: "ENTRY_ADMITTED"
7294
+ });
7295
+ }
7209
7296
  pendingBacktestEntry = {
7210
7297
  delayBars: backtestEntryDelayBars,
7211
7298
  delayBarsRemaining: backtestEntryDelayBars,
@@ -7219,6 +7306,12 @@ var createStrategyRuntime = ({
7219
7306
  };
7220
7307
  return `BACKTEST_ENTRY_DELAY_QUEUED:${backtestEntryDelayBars}`;
7221
7308
  }
7309
+ if (signal) {
7310
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
7311
+ status: "approved",
7312
+ reason: "ENTRY_ADMITTED"
7313
+ });
7314
+ }
7222
7315
  return executeEntryDecision({
7223
7316
  connector,
7224
7317
  symbol,
@@ -9,7 +9,7 @@ import {
9
9
  import {
10
10
  getConnectorCreatorByProvider
11
11
  } from "./chunk-QVKUROR2.mjs";
12
- import "./chunk-6DGLTGLW.mjs";
12
+ import "./chunk-XHNJ757K.mjs";
13
13
  import "./chunk-IKRSNRP6.mjs";
14
14
  import "./chunk-LZDXRXIU.mjs";
15
15
  import "./chunk-Y6FXYEAI.mjs";
@@ -4935,6 +4935,68 @@ var buildAiHookContext = ({
4935
4935
  };
4936
4936
  };
4937
4937
 
4938
+ // src/strategy/runtimeDecisionTelemetry.ts
4939
+ var finitePositive = (value) => {
4940
+ const parsed = Number(value);
4941
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
4942
+ };
4943
+ var round = (value) => Number(value.toFixed(12));
4944
+ var assessRuntimeEntryRisk = ({
4945
+ qty,
4946
+ entryPrice,
4947
+ stopLossPrice,
4948
+ maxLossValue
4949
+ }) => {
4950
+ const normalizedQty = finitePositive(qty);
4951
+ const normalizedEntryPrice = finitePositive(entryPrice);
4952
+ const normalizedStopLossPrice = finitePositive(stopLossPrice);
4953
+ const normalizedMaxLossValue = finitePositive(maxLossValue);
4954
+ const plannedLossValue = normalizedQty != null && normalizedEntryPrice != null && normalizedStopLossPrice != null ? round(
4955
+ Math.abs(normalizedEntryPrice - normalizedStopLossPrice) * normalizedQty
4956
+ ) : null;
4957
+ if (plannedLossValue == null) {
4958
+ return {
4959
+ stage: "order_plan",
4960
+ status: "unavailable",
4961
+ reason: "ORDER_PLAN_RISK_INPUTS_UNAVAILABLE",
4962
+ plannedLossValue: null,
4963
+ maxLossValue: normalizedMaxLossValue,
4964
+ limitUtilization: null,
4965
+ enforced: false
4966
+ };
4967
+ }
4968
+ if (normalizedMaxLossValue == null) {
4969
+ return {
4970
+ stage: "order_plan",
4971
+ status: "unavailable",
4972
+ reason: "MAX_LOSS_VALUE_UNAVAILABLE",
4973
+ plannedLossValue,
4974
+ maxLossValue: null,
4975
+ limitUtilization: null,
4976
+ enforced: false
4977
+ };
4978
+ }
4979
+ const limitUtilization = round(plannedLossValue / normalizedMaxLossValue);
4980
+ const withinLimit = plannedLossValue <= normalizedMaxLossValue * (1 + 1e-9);
4981
+ return {
4982
+ stage: "order_plan",
4983
+ status: withinLimit ? "approved" : "rejected",
4984
+ reason: withinLimit ? "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE" : "PLANNED_LOSS_EXCEEDS_MAX_LOSS_VALUE",
4985
+ plannedLossValue,
4986
+ maxLossValue: normalizedMaxLossValue,
4987
+ limitUtilization,
4988
+ enforced: false
4989
+ };
4990
+ };
4991
+ var buildRuntimeAdmissionDecision = ({
4992
+ status,
4993
+ reason
4994
+ }) => ({
4995
+ stage: "runtime_admission",
4996
+ status,
4997
+ reason
4998
+ });
4999
+
4938
5000
  // src/strategy/runtimeHooks.ts
4939
5001
  var normalizeConfigHookList = (value) => {
4940
5002
  if (Array.isArray(value)) {
@@ -6058,6 +6120,12 @@ var createStrategyRuntime = ({
6058
6120
  if (decisionHookCtx.policyProfileId) {
6059
6121
  signal.policyProfileId = decisionHookCtx.policyProfileId;
6060
6122
  }
6123
+ signal.riskDecision = assessRuntimeEntryRisk({
6124
+ qty: decision.orderPlan.qty,
6125
+ entryPrice: decision.entryContext.prices.currentPrice,
6126
+ stopLossPrice: decision.orderPlan.stopLossPrice,
6127
+ maxLossValue: runtimeLineage?.maxLossValue ?? config.MAX_LOSS_VALUE
6128
+ });
6061
6129
  }
6062
6130
  const entry = buildHookEntry({
6063
6131
  decision,
@@ -6167,13 +6235,16 @@ var createStrategyRuntime = ({
6167
6235
  signal.orderStatus = "skipped";
6168
6236
  signal.orderSkipReason = skipReason;
6169
6237
  signal.strategyRevision = strategyRevision;
6238
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6239
+ status: "rejected",
6240
+ reason: skipReason
6241
+ });
6170
6242
  }
6171
6243
  return signal ?? skipReason;
6172
6244
  }
6173
6245
  if (!shouldMakeOrder) {
6174
6246
  if (signal) {
6175
- signal.orderStatus = "skipped";
6176
- signal.orderSkipReason = getEntrySkipReason({
6247
+ const skipReason = getEntrySkipReason({
6177
6248
  makeOrdersEnabled,
6178
6249
  env,
6179
6250
  ml,
@@ -6181,6 +6252,12 @@ var createStrategyRuntime = ({
6181
6252
  quality,
6182
6253
  minAiQuality
6183
6254
  });
6255
+ signal.orderStatus = "skipped";
6256
+ signal.orderSkipReason = skipReason;
6257
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6258
+ status: "not_applicable",
6259
+ reason: `ENTRY_POLICY_REJECTED:${skipReason}`
6260
+ });
6184
6261
  }
6185
6262
  return signal ?? decision.code;
6186
6263
  }
@@ -6203,10 +6280,20 @@ var createStrategyRuntime = ({
6203
6280
  if (signal) {
6204
6281
  signal.orderStatus = "skipped";
6205
6282
  signal.orderSkipReason = skipReason;
6283
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6284
+ status: "rejected",
6285
+ reason: skipReason
6286
+ });
6206
6287
  }
6207
6288
  return signal ?? skipReason;
6208
6289
  }
6209
6290
  if (backtestEntryDelayBars > 0) {
6291
+ if (signal) {
6292
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6293
+ status: "approved",
6294
+ reason: "ENTRY_ADMITTED"
6295
+ });
6296
+ }
6210
6297
  pendingBacktestEntry = {
6211
6298
  delayBars: backtestEntryDelayBars,
6212
6299
  delayBarsRemaining: backtestEntryDelayBars,
@@ -6220,6 +6307,12 @@ var createStrategyRuntime = ({
6220
6307
  };
6221
6308
  return `BACKTEST_ENTRY_DELAY_QUEUED:${backtestEntryDelayBars}`;
6222
6309
  }
6310
+ if (signal) {
6311
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6312
+ status: "approved",
6313
+ reason: "ENTRY_ADMITTED"
6314
+ });
6315
+ }
6223
6316
  return executeEntryDecision({
6224
6317
  connector,
6225
6318
  symbol,
@@ -9,7 +9,7 @@ import {
9
9
  resolveRuntimeComposition,
10
10
  verifyRuntimeDeclaration
11
11
  } from "./chunk-NQO2MFP6.mjs";
12
- import "./chunk-6DGLTGLW.mjs";
12
+ import "./chunk-XHNJ757K.mjs";
13
13
  import "./chunk-IKRSNRP6.mjs";
14
14
  import "./chunk-LZDXRXIU.mjs";
15
15
  import "./chunk-Y6FXYEAI.mjs";
@@ -5051,6 +5051,68 @@ var buildAiHookContext = ({
5051
5051
  };
5052
5052
  };
5053
5053
 
5054
+ // src/strategy/runtimeDecisionTelemetry.ts
5055
+ var finitePositive = (value) => {
5056
+ const parsed = Number(value);
5057
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
5058
+ };
5059
+ var round = (value) => Number(value.toFixed(12));
5060
+ var assessRuntimeEntryRisk = ({
5061
+ qty,
5062
+ entryPrice,
5063
+ stopLossPrice,
5064
+ maxLossValue
5065
+ }) => {
5066
+ const normalizedQty = finitePositive(qty);
5067
+ const normalizedEntryPrice = finitePositive(entryPrice);
5068
+ const normalizedStopLossPrice = finitePositive(stopLossPrice);
5069
+ const normalizedMaxLossValue = finitePositive(maxLossValue);
5070
+ const plannedLossValue = normalizedQty != null && normalizedEntryPrice != null && normalizedStopLossPrice != null ? round(
5071
+ Math.abs(normalizedEntryPrice - normalizedStopLossPrice) * normalizedQty
5072
+ ) : null;
5073
+ if (plannedLossValue == null) {
5074
+ return {
5075
+ stage: "order_plan",
5076
+ status: "unavailable",
5077
+ reason: "ORDER_PLAN_RISK_INPUTS_UNAVAILABLE",
5078
+ plannedLossValue: null,
5079
+ maxLossValue: normalizedMaxLossValue,
5080
+ limitUtilization: null,
5081
+ enforced: false
5082
+ };
5083
+ }
5084
+ if (normalizedMaxLossValue == null) {
5085
+ return {
5086
+ stage: "order_plan",
5087
+ status: "unavailable",
5088
+ reason: "MAX_LOSS_VALUE_UNAVAILABLE",
5089
+ plannedLossValue,
5090
+ maxLossValue: null,
5091
+ limitUtilization: null,
5092
+ enforced: false
5093
+ };
5094
+ }
5095
+ const limitUtilization = round(plannedLossValue / normalizedMaxLossValue);
5096
+ const withinLimit = plannedLossValue <= normalizedMaxLossValue * (1 + 1e-9);
5097
+ return {
5098
+ stage: "order_plan",
5099
+ status: withinLimit ? "approved" : "rejected",
5100
+ reason: withinLimit ? "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE" : "PLANNED_LOSS_EXCEEDS_MAX_LOSS_VALUE",
5101
+ plannedLossValue,
5102
+ maxLossValue: normalizedMaxLossValue,
5103
+ limitUtilization,
5104
+ enforced: false
5105
+ };
5106
+ };
5107
+ var buildRuntimeAdmissionDecision = ({
5108
+ status,
5109
+ reason
5110
+ }) => ({
5111
+ stage: "runtime_admission",
5112
+ status,
5113
+ reason
5114
+ });
5115
+
5054
5116
  // src/strategy/runtimeHooks.ts
5055
5117
  var normalizeConfigHookList = (value) => {
5056
5118
  if (Array.isArray(value)) {
@@ -6174,6 +6236,12 @@ var createStrategyRuntime = ({
6174
6236
  if (decisionHookCtx.policyProfileId) {
6175
6237
  signal.policyProfileId = decisionHookCtx.policyProfileId;
6176
6238
  }
6239
+ signal.riskDecision = assessRuntimeEntryRisk({
6240
+ qty: decision.orderPlan.qty,
6241
+ entryPrice: decision.entryContext.prices.currentPrice,
6242
+ stopLossPrice: decision.orderPlan.stopLossPrice,
6243
+ maxLossValue: runtimeLineage?.maxLossValue ?? config.MAX_LOSS_VALUE
6244
+ });
6177
6245
  }
6178
6246
  const entry = buildHookEntry({
6179
6247
  decision,
@@ -6283,13 +6351,16 @@ var createStrategyRuntime = ({
6283
6351
  signal.orderStatus = "skipped";
6284
6352
  signal.orderSkipReason = skipReason;
6285
6353
  signal.strategyRevision = strategyRevision;
6354
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6355
+ status: "rejected",
6356
+ reason: skipReason
6357
+ });
6286
6358
  }
6287
6359
  return signal ?? skipReason;
6288
6360
  }
6289
6361
  if (!shouldMakeOrder) {
6290
6362
  if (signal) {
6291
- signal.orderStatus = "skipped";
6292
- signal.orderSkipReason = getEntrySkipReason({
6363
+ const skipReason = getEntrySkipReason({
6293
6364
  makeOrdersEnabled,
6294
6365
  env,
6295
6366
  ml,
@@ -6297,6 +6368,12 @@ var createStrategyRuntime = ({
6297
6368
  quality,
6298
6369
  minAiQuality
6299
6370
  });
6371
+ signal.orderStatus = "skipped";
6372
+ signal.orderSkipReason = skipReason;
6373
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6374
+ status: "not_applicable",
6375
+ reason: `ENTRY_POLICY_REJECTED:${skipReason}`
6376
+ });
6300
6377
  }
6301
6378
  return signal ?? decision.code;
6302
6379
  }
@@ -6319,10 +6396,20 @@ var createStrategyRuntime = ({
6319
6396
  if (signal) {
6320
6397
  signal.orderStatus = "skipped";
6321
6398
  signal.orderSkipReason = skipReason;
6399
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6400
+ status: "rejected",
6401
+ reason: skipReason
6402
+ });
6322
6403
  }
6323
6404
  return signal ?? skipReason;
6324
6405
  }
6325
6406
  if (backtestEntryDelayBars > 0) {
6407
+ if (signal) {
6408
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6409
+ status: "approved",
6410
+ reason: "ENTRY_ADMITTED"
6411
+ });
6412
+ }
6326
6413
  pendingBacktestEntry = {
6327
6414
  delayBars: backtestEntryDelayBars,
6328
6415
  delayBarsRemaining: backtestEntryDelayBars,
@@ -6336,6 +6423,12 @@ var createStrategyRuntime = ({
6336
6423
  };
6337
6424
  return `BACKTEST_ENTRY_DELAY_QUEUED:${backtestEntryDelayBars}`;
6338
6425
  }
6426
+ if (signal) {
6427
+ signal.allocatorDecision = buildRuntimeAdmissionDecision({
6428
+ status: "approved",
6429
+ reason: "ENTRY_ADMITTED"
6430
+ });
6431
+ }
6339
6432
  return executeEntryDecision({
6340
6433
  connector,
6341
6434
  symbol,
@@ -22,7 +22,7 @@ import {
22
22
  resolveHyperliquidPerpFromSignalSymbol,
23
23
  resolveStrategyConfig,
24
24
  validateEntryProtectionAtArrival
25
- } from "./chunk-6DGLTGLW.mjs";
25
+ } from "./chunk-XHNJ757K.mjs";
26
26
  import {
27
27
  DEFAULT_AI_MODEL,
28
28
  MAX_AI_SERIES_POINTS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/node",
3
- "version": "3.1.26",
3
+ "version": "3.1.27-beta.245",
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",
@@ -89,9 +89,9 @@
89
89
  "dependencies": {
90
90
  "@langchain/core": "^1.2.3",
91
91
  "@langchain/openai": "^1.5.5",
92
- "@tradejs/core": "^3.1.26",
93
- "@tradejs/infra": "^3.1.26",
94
- "@tradejs/types": "^3.1.26",
92
+ "@tradejs/core": "^3.1.27-beta.245",
93
+ "@tradejs/infra": "^3.1.27-beta.245",
94
+ "@tradejs/types": "^3.1.27-beta.245",
95
95
  "@types/node": "^24",
96
96
  "chalk": "4.1.2",
97
97
  "ioredis": "5.11.1",
@@ -104,7 +104,7 @@
104
104
  "typescript": "^5.9"
105
105
  },
106
106
  "devDependencies": {
107
- "@tradejs/indicators": "^3.1.26",
107
+ "@tradejs/indicators": "^3.1.27-beta.245",
108
108
  "@tradejs/strategy-adaptive-momentum-ribbon": "3.0.2",
109
109
  "@tradejs/strategy-hyperliquid-consensus": "3.0.2",
110
110
  "@tradejs/strategy-kit": "3.0.4",