@tradejs/cli 2.0.0 → 2.0.2

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.
@@ -34,6 +34,7 @@ var divideOrNull = (num, denom) => {
34
34
  var DAY_MS = 24 * 60 * 60 * 1e3;
35
35
  var DAYS_PER_WEEK = 7;
36
36
  var DAYS_PER_MONTH = 30.4375;
37
+ var DAYS_PER_YEAR = 365;
37
38
  var getEvaluationPeriodDays = (evaluations) => {
38
39
  let minTimestamp = null;
39
40
  let maxTimestamp = null;
@@ -55,6 +56,40 @@ var getEvaluationPeriodDays = (evaluations) => {
55
56
  return Math.max((maxTimestamp - minTimestamp) / DAY_MS, 1);
56
57
  };
57
58
  var qualitySortKey = (quality) => quality == null ? Number.POSITIVE_INFINITY : quality;
59
+ var calculateTradePnlRiskRatios = ({
60
+ profits,
61
+ totalProfit,
62
+ maxDrawdown,
63
+ periodDays
64
+ }) => {
65
+ if (!profits.length || periodDays == null || periodDays <= 0) {
66
+ return {
67
+ sharpeRatio: null,
68
+ sortinoRatio: null,
69
+ calmarRatio: null
70
+ };
71
+ }
72
+ const meanProfit = totalProfit / profits.length;
73
+ const variance = profits.reduce((sum, profit) => {
74
+ const diff = profit - meanProfit;
75
+ return sum + diff * diff;
76
+ }, 0) / profits.length;
77
+ const stdDev = Math.sqrt(variance);
78
+ const downsideDeviation = Math.sqrt(
79
+ profits.reduce(
80
+ (sum, profit) => profit < 0 ? sum + profit * profit : sum,
81
+ 0
82
+ ) / profits.length
83
+ );
84
+ const annualizationScale = Math.sqrt(
85
+ profits.length / periodDays * DAYS_PER_YEAR
86
+ );
87
+ return {
88
+ sharpeRatio: stdDev > 0 && annualizationScale > 0 ? meanProfit / stdDev * annualizationScale : null,
89
+ sortinoRatio: downsideDeviation > 0 && annualizationScale > 0 ? meanProfit / downsideDeviation * annualizationScale : null,
90
+ calmarRatio: maxDrawdown > 0 ? totalProfit / periodDays * DAYS_PER_YEAR / maxDrawdown : null
91
+ };
92
+ };
58
93
  var emptyRiskSummary = () => ({
59
94
  trades: 0,
60
95
  totalProfit: 0,
@@ -70,12 +105,15 @@ var emptyRiskSummary = () => ({
70
105
  maxDrawdown: 0,
71
106
  maxDrawdownPctOfGrossProfit: null,
72
107
  maxDrawdownPctOfTotalProfit: null,
108
+ sharpeRatio: null,
109
+ sortinoRatio: null,
110
+ calmarRatio: null,
73
111
  recoveryFactor: null,
74
112
  ulcerIndex: null,
75
113
  maxConsecutiveWins: 0,
76
114
  maxConsecutiveLosses: 0
77
115
  });
78
- var summarizeApprovedRisk = (evaluations) => {
116
+ var summarizeApprovedRisk = (evaluations, periodDays = getEvaluationPeriodDays(evaluations)) => {
79
117
  const approvedEvaluations = evaluations.filter((evaluation) => evaluation.aiApproved).sort((left, right) => {
80
118
  const leftTimestamp = typeof left.timestamp === "number" && Number.isFinite(left.timestamp) ? left.timestamp : Number.POSITIVE_INFINITY;
81
119
  const rightTimestamp = typeof right.timestamp === "number" && Number.isFinite(right.timestamp) ? right.timestamp : Number.POSITIVE_INFINITY;
@@ -98,8 +136,10 @@ var summarizeApprovedRisk = (evaluations) => {
98
136
  let currentLossStreak = 0;
99
137
  let maxConsecutiveWins = 0;
100
138
  let maxConsecutiveLosses = 0;
139
+ const approvedProfits = [];
101
140
  for (const evaluation of approvedEvaluations) {
102
141
  const profit = evaluation.profit;
142
+ approvedProfits.push(profit);
103
143
  if (profit > 0) {
104
144
  grossProfit += profit;
105
145
  wins += 1;
@@ -127,6 +167,12 @@ var summarizeApprovedRisk = (evaluations) => {
127
167
  const totalProfit = grossProfit - grossLoss;
128
168
  const avgWin = divideOrNull(grossProfit, wins);
129
169
  const avgLoss = divideOrNull(grossLoss, losses);
170
+ const riskRatios = calculateTradePnlRiskRatios({
171
+ profits: approvedProfits,
172
+ totalProfit,
173
+ maxDrawdown,
174
+ periodDays
175
+ });
130
176
  return {
131
177
  trades: approvedEvaluations.length,
132
178
  totalProfit,
@@ -142,6 +188,7 @@ var summarizeApprovedRisk = (evaluations) => {
142
188
  maxDrawdown,
143
189
  maxDrawdownPctOfGrossProfit: grossProfit > 0 ? maxDrawdown / grossProfit : null,
144
190
  maxDrawdownPctOfTotalProfit: totalProfit > 0 ? maxDrawdown / totalProfit : null,
191
+ ...riskRatios,
145
192
  recoveryFactor: maxDrawdown > 0 ? totalProfit / maxDrawdown : null,
146
193
  ulcerIndex: Math.sqrt(drawdownSquares / approvedEvaluations.length),
147
194
  maxConsecutiveWins,
@@ -240,7 +287,7 @@ var summarizeAiTrainEvaluations = (evaluations) => {
240
287
  avgApprovedTradesPerDay,
241
288
  avgApprovedTradesPerWeek,
242
289
  expectancyDelta,
243
- approvedRisk: summarizeApprovedRisk(evaluations),
290
+ approvedRisk: summarizeApprovedRisk(evaluations, periodDays),
244
291
  qualityBuckets: [...bucketMap.values()].sort(
245
292
  (a, b) => qualitySortKey(a.quality) - qualitySortKey(b.quality)
246
293
  )
@@ -35,6 +35,7 @@ var divideOrNull = (num, denom) => {
35
35
  var DAY_MS = 24 * 60 * 60 * 1e3;
36
36
  var DAYS_PER_WEEK = 7;
37
37
  var DAYS_PER_MONTH = 30.4375;
38
+ var DAYS_PER_YEAR = 365;
38
39
  var getEvaluationPeriodDays = (evaluations) => {
39
40
  let minTimestamp = null;
40
41
  let maxTimestamp = null;
@@ -56,6 +57,40 @@ var getEvaluationPeriodDays = (evaluations) => {
56
57
  return Math.max((maxTimestamp - minTimestamp) / DAY_MS, 1);
57
58
  };
58
59
  var qualitySortKey = (quality) => quality == null ? Number.POSITIVE_INFINITY : quality;
60
+ var calculateTradePnlRiskRatios = ({
61
+ profits,
62
+ totalProfit,
63
+ maxDrawdown,
64
+ periodDays
65
+ }) => {
66
+ if (!profits.length || periodDays == null || periodDays <= 0) {
67
+ return {
68
+ sharpeRatio: null,
69
+ sortinoRatio: null,
70
+ calmarRatio: null
71
+ };
72
+ }
73
+ const meanProfit = totalProfit / profits.length;
74
+ const variance = profits.reduce((sum, profit) => {
75
+ const diff = profit - meanProfit;
76
+ return sum + diff * diff;
77
+ }, 0) / profits.length;
78
+ const stdDev = Math.sqrt(variance);
79
+ const downsideDeviation = Math.sqrt(
80
+ profits.reduce(
81
+ (sum, profit) => profit < 0 ? sum + profit * profit : sum,
82
+ 0
83
+ ) / profits.length
84
+ );
85
+ const annualizationScale = Math.sqrt(
86
+ profits.length / periodDays * DAYS_PER_YEAR
87
+ );
88
+ return {
89
+ sharpeRatio: stdDev > 0 && annualizationScale > 0 ? meanProfit / stdDev * annualizationScale : null,
90
+ sortinoRatio: downsideDeviation > 0 && annualizationScale > 0 ? meanProfit / downsideDeviation * annualizationScale : null,
91
+ calmarRatio: maxDrawdown > 0 ? totalProfit / periodDays * DAYS_PER_YEAR / maxDrawdown : null
92
+ };
93
+ };
59
94
  var emptyRiskSummary = () => ({
60
95
  trades: 0,
61
96
  totalProfit: 0,
@@ -71,12 +106,15 @@ var emptyRiskSummary = () => ({
71
106
  maxDrawdown: 0,
72
107
  maxDrawdownPctOfGrossProfit: null,
73
108
  maxDrawdownPctOfTotalProfit: null,
109
+ sharpeRatio: null,
110
+ sortinoRatio: null,
111
+ calmarRatio: null,
74
112
  recoveryFactor: null,
75
113
  ulcerIndex: null,
76
114
  maxConsecutiveWins: 0,
77
115
  maxConsecutiveLosses: 0
78
116
  });
79
- var summarizeApprovedRisk = (evaluations) => {
117
+ var summarizeApprovedRisk = (evaluations, periodDays = getEvaluationPeriodDays(evaluations)) => {
80
118
  const approvedEvaluations = evaluations.filter((evaluation) => evaluation.aiApproved).sort((left, right) => {
81
119
  const leftTimestamp = typeof left.timestamp === "number" && Number.isFinite(left.timestamp) ? left.timestamp : Number.POSITIVE_INFINITY;
82
120
  const rightTimestamp = typeof right.timestamp === "number" && Number.isFinite(right.timestamp) ? right.timestamp : Number.POSITIVE_INFINITY;
@@ -99,8 +137,10 @@ var summarizeApprovedRisk = (evaluations) => {
99
137
  let currentLossStreak = 0;
100
138
  let maxConsecutiveWins = 0;
101
139
  let maxConsecutiveLosses = 0;
140
+ const approvedProfits = [];
102
141
  for (const evaluation of approvedEvaluations) {
103
142
  const profit = evaluation.profit;
143
+ approvedProfits.push(profit);
104
144
  if (profit > 0) {
105
145
  grossProfit += profit;
106
146
  wins += 1;
@@ -128,6 +168,12 @@ var summarizeApprovedRisk = (evaluations) => {
128
168
  const totalProfit = grossProfit - grossLoss;
129
169
  const avgWin = divideOrNull(grossProfit, wins);
130
170
  const avgLoss = divideOrNull(grossLoss, losses);
171
+ const riskRatios = calculateTradePnlRiskRatios({
172
+ profits: approvedProfits,
173
+ totalProfit,
174
+ maxDrawdown,
175
+ periodDays
176
+ });
131
177
  return {
132
178
  trades: approvedEvaluations.length,
133
179
  totalProfit,
@@ -143,6 +189,7 @@ var summarizeApprovedRisk = (evaluations) => {
143
189
  maxDrawdown,
144
190
  maxDrawdownPctOfGrossProfit: grossProfit > 0 ? maxDrawdown / grossProfit : null,
145
191
  maxDrawdownPctOfTotalProfit: totalProfit > 0 ? maxDrawdown / totalProfit : null,
192
+ ...riskRatios,
146
193
  recoveryFactor: maxDrawdown > 0 ? totalProfit / maxDrawdown : null,
147
194
  ulcerIndex: Math.sqrt(drawdownSquares / approvedEvaluations.length),
148
195
  maxConsecutiveWins,
@@ -241,7 +288,7 @@ var summarizeAiTrainEvaluations = (evaluations) => {
241
288
  avgApprovedTradesPerDay,
242
289
  avgApprovedTradesPerWeek,
243
290
  expectancyDelta,
244
- approvedRisk: summarizeApprovedRisk(evaluations),
291
+ approvedRisk: summarizeApprovedRisk(evaluations, periodDays),
245
292
  qualityBuckets: [...bucketMap.values()].sort(
246
293
  (a, b) => qualitySortKey(a.quality) - qualitySortKey(b.quality)
247
294
  )
@@ -52,6 +52,7 @@ var divideOrNull = (num, denom) => {
52
52
  var DAY_MS = 24 * 60 * 60 * 1e3;
53
53
  var DAYS_PER_WEEK = 7;
54
54
  var DAYS_PER_MONTH = 30.4375;
55
+ var DAYS_PER_YEAR = 365;
55
56
  var getEvaluationPeriodDays = (evaluations) => {
56
57
  let minTimestamp = null;
57
58
  let maxTimestamp = null;
@@ -73,6 +74,40 @@ var getEvaluationPeriodDays = (evaluations) => {
73
74
  return Math.max((maxTimestamp - minTimestamp) / DAY_MS, 1);
74
75
  };
75
76
  var qualitySortKey = (quality) => quality == null ? Number.POSITIVE_INFINITY : quality;
77
+ var calculateTradePnlRiskRatios = ({
78
+ profits,
79
+ totalProfit,
80
+ maxDrawdown,
81
+ periodDays
82
+ }) => {
83
+ if (!profits.length || periodDays == null || periodDays <= 0) {
84
+ return {
85
+ sharpeRatio: null,
86
+ sortinoRatio: null,
87
+ calmarRatio: null
88
+ };
89
+ }
90
+ const meanProfit = totalProfit / profits.length;
91
+ const variance = profits.reduce((sum, profit) => {
92
+ const diff = profit - meanProfit;
93
+ return sum + diff * diff;
94
+ }, 0) / profits.length;
95
+ const stdDev = Math.sqrt(variance);
96
+ const downsideDeviation = Math.sqrt(
97
+ profits.reduce(
98
+ (sum, profit) => profit < 0 ? sum + profit * profit : sum,
99
+ 0
100
+ ) / profits.length
101
+ );
102
+ const annualizationScale = Math.sqrt(
103
+ profits.length / periodDays * DAYS_PER_YEAR
104
+ );
105
+ return {
106
+ sharpeRatio: stdDev > 0 && annualizationScale > 0 ? meanProfit / stdDev * annualizationScale : null,
107
+ sortinoRatio: downsideDeviation > 0 && annualizationScale > 0 ? meanProfit / downsideDeviation * annualizationScale : null,
108
+ calmarRatio: maxDrawdown > 0 ? totalProfit / periodDays * DAYS_PER_YEAR / maxDrawdown : null
109
+ };
110
+ };
76
111
  var emptyRiskSummary = () => ({
77
112
  trades: 0,
78
113
  totalProfit: 0,
@@ -88,12 +123,15 @@ var emptyRiskSummary = () => ({
88
123
  maxDrawdown: 0,
89
124
  maxDrawdownPctOfGrossProfit: null,
90
125
  maxDrawdownPctOfTotalProfit: null,
126
+ sharpeRatio: null,
127
+ sortinoRatio: null,
128
+ calmarRatio: null,
91
129
  recoveryFactor: null,
92
130
  ulcerIndex: null,
93
131
  maxConsecutiveWins: 0,
94
132
  maxConsecutiveLosses: 0
95
133
  });
96
- var summarizeApprovedRisk = (evaluations) => {
134
+ var summarizeApprovedRisk = (evaluations, periodDays = getEvaluationPeriodDays(evaluations)) => {
97
135
  const approvedEvaluations = evaluations.filter((evaluation) => evaluation.aiApproved).sort((left, right) => {
98
136
  const leftTimestamp = typeof left.timestamp === "number" && Number.isFinite(left.timestamp) ? left.timestamp : Number.POSITIVE_INFINITY;
99
137
  const rightTimestamp = typeof right.timestamp === "number" && Number.isFinite(right.timestamp) ? right.timestamp : Number.POSITIVE_INFINITY;
@@ -116,8 +154,10 @@ var summarizeApprovedRisk = (evaluations) => {
116
154
  let currentLossStreak = 0;
117
155
  let maxConsecutiveWins = 0;
118
156
  let maxConsecutiveLosses = 0;
157
+ const approvedProfits = [];
119
158
  for (const evaluation of approvedEvaluations) {
120
159
  const profit = evaluation.profit;
160
+ approvedProfits.push(profit);
121
161
  if (profit > 0) {
122
162
  grossProfit += profit;
123
163
  wins += 1;
@@ -145,6 +185,12 @@ var summarizeApprovedRisk = (evaluations) => {
145
185
  const totalProfit = grossProfit - grossLoss;
146
186
  const avgWin = divideOrNull(grossProfit, wins);
147
187
  const avgLoss = divideOrNull(grossLoss, losses);
188
+ const riskRatios = calculateTradePnlRiskRatios({
189
+ profits: approvedProfits,
190
+ totalProfit,
191
+ maxDrawdown,
192
+ periodDays
193
+ });
148
194
  return {
149
195
  trades: approvedEvaluations.length,
150
196
  totalProfit,
@@ -160,6 +206,7 @@ var summarizeApprovedRisk = (evaluations) => {
160
206
  maxDrawdown,
161
207
  maxDrawdownPctOfGrossProfit: grossProfit > 0 ? maxDrawdown / grossProfit : null,
162
208
  maxDrawdownPctOfTotalProfit: totalProfit > 0 ? maxDrawdown / totalProfit : null,
209
+ ...riskRatios,
163
210
  recoveryFactor: maxDrawdown > 0 ? totalProfit / maxDrawdown : null,
164
211
  ulcerIndex: Math.sqrt(drawdownSquares / approvedEvaluations.length),
165
212
  maxConsecutiveWins,
@@ -258,7 +305,7 @@ var summarizeAiTrainEvaluations = (evaluations) => {
258
305
  avgApprovedTradesPerDay,
259
306
  avgApprovedTradesPerWeek,
260
307
  expectancyDelta,
261
- approvedRisk: summarizeApprovedRisk(evaluations),
308
+ approvedRisk: summarizeApprovedRisk(evaluations, periodDays),
262
309
  qualityBuckets: [...bucketMap.values()].sort(
263
310
  (a, b) => qualitySortKey(a.quality) - qualitySortKey(b.quality)
264
311
  )
@@ -273,7 +320,6 @@ var RESEARCH_CONTEXT_ENV_KEYS = [
273
320
  "INTERVAL",
274
321
  "DERIVATIVES_CONTEXT_ENABLED",
275
322
  "DERIVATIVES_CONTEXT_TARGET_ENABLED",
276
- "DERIVATIVES_CONTEXT_INTERVALS",
277
323
  "DERIVATIVES_CONTEXT_LOOKBACK_HOURS",
278
324
  "DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS",
279
325
  "DERIVATIVES_CONTEXT_EXCHANGE_PRIORITY",
@@ -287,6 +333,12 @@ var RESEARCH_CONTEXT_ENV_KEYS = [
287
333
  "COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
288
334
  "COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
289
335
  ];
336
+ var DERIVATIVES_CONTEXT_DATA_MODEL = {
337
+ derivativesSourceIntervals: "15m",
338
+ derivativesDerivedIntervals: "1h",
339
+ derivativesHourlyFallback: "stored-1h",
340
+ derivativesDataModelVersion: 2
341
+ };
290
342
  var normalizeForStableJson = (value) => {
291
343
  if (Array.isArray(value)) {
292
344
  return value.map(normalizeForStableJson);
@@ -399,6 +451,7 @@ var resolveGateFingerprint = async (projectRoot, strategyName, gitSha) => {
399
451
  const relativeCandidates = [
400
452
  `packages/strategies/src/${strategyName}/adapters/ai.ts`,
401
453
  `packages/strategies/src/${strategyName}/guardrails.ts`,
454
+ `packages/strategies/src/${strategyName}/pockets.ts`,
402
455
  `packages/strategies/src/${strategyName}/config.ts`,
403
456
  "packages/node/src/ai.ts"
404
457
  ];
@@ -445,7 +498,8 @@ var buildAiTrainLineage = async ({
445
498
  ...Object.fromEntries(
446
499
  RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
447
500
  ),
448
- ...runContext
501
+ ...runContext,
502
+ ...DERIVATIVES_CONTEXT_DATA_MODEL
449
503
  };
450
504
  return {
451
505
  gitSha,