@tradejs/core 1.0.9 → 1.0.11

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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/dist/backtest.d.mts +109 -9
  3. package/dist/backtest.d.ts +109 -9
  4. package/dist/backtest.js +487 -145
  5. package/dist/backtest.mjs +414 -82
  6. package/dist/{chunk-FNLPYYML.mjs → chunk-BOETNABM.mjs} +22 -1
  7. package/dist/chunk-EQEIRB6P.mjs +11847 -0
  8. package/dist/{chunk-2ORZC66W.mjs → chunk-MKCQSB4H.mjs} +65 -6
  9. package/dist/chunk-OJPHC3S2.mjs +8 -0
  10. package/dist/constants.d.mts +26 -5
  11. package/dist/constants.d.ts +26 -5
  12. package/dist/constants.js +81 -9
  13. package/dist/constants.mjs +31 -5
  14. package/dist/data.mjs +3 -5
  15. package/dist/grid.d.mts +9 -0
  16. package/dist/grid.d.ts +9 -0
  17. package/dist/grid.js +168 -0
  18. package/dist/grid.mjs +98 -0
  19. package/dist/indicators-Da_i06-8.d.mts +288 -0
  20. package/dist/indicators-Da_i06-8.d.ts +288 -0
  21. package/dist/indicators.d.mts +4 -39
  22. package/dist/indicators.d.ts +4 -39
  23. package/dist/indicators.js +10488 -423
  24. package/dist/indicators.mjs +11 -3
  25. package/dist/strategies.d.mts +31 -12
  26. package/dist/strategies.d.ts +31 -12
  27. package/dist/strategies.js +11283 -336
  28. package/dist/strategies.mjs +1246 -119
  29. package/dist/{time-BMkFD4Kd.d.mts → time-BQ3AXmxo.d.mts} +3 -1
  30. package/dist/{time-BMkFD4Kd.d.ts → time-BQ3AXmxo.d.ts} +3 -1
  31. package/dist/time.d.mts +1 -1
  32. package/dist/time.d.ts +1 -1
  33. package/dist/time.js +38 -0
  34. package/dist/time.mjs +6 -2
  35. package/dist/trade.d.mts +54 -0
  36. package/dist/trade.d.ts +54 -0
  37. package/dist/trade.js +352 -0
  38. package/dist/trade.mjs +264 -0
  39. package/package.json +19 -5
  40. package/dist/chunk-UK6VTOUX.mjs +0 -1810
  41. package/dist/indicators-B-GGjP5F.d.mts +0 -65
  42. package/dist/indicators-B-GGjP5F.d.ts +0 -65
@@ -7,6 +7,8 @@ declare const getDataTimestamp: (data: KlineChartData) => number | null;
7
7
  declare const formatUnix: (dt: number) => string;
8
8
  declare const getBacktestPreloadStart: (start: number, preloadDays?: number) => number;
9
9
  declare const getTimeline: (start?: number, end?: number, step?: number) => number[];
10
+ declare const getRuntimeStorageDayKey: (timestamp: number) => string;
11
+ declare const getRuntimeStorageDayKeys: (startTime: number, endTime: number) => string[];
10
12
  declare const compactOrderLog: (timeline: number[], orderLog: OrderLogData) => SimpleOrderLogData;
11
13
 
12
- export { getBacktestPreloadStart as a, getDataTimestamp as b, compactOrderLog as c, getItemTimestamp as d, getTimestamp as e, formatUnix as f, getTimeline as g, toMs as t };
14
+ export { getBacktestPreloadStart as a, getDataTimestamp as b, compactOrderLog as c, getItemTimestamp as d, getRuntimeStorageDayKey as e, formatUnix as f, getTimeline as g, getRuntimeStorageDayKeys as h, getTimestamp as i, toMs as t };
@@ -7,6 +7,8 @@ declare const getDataTimestamp: (data: KlineChartData) => number | null;
7
7
  declare const formatUnix: (dt: number) => string;
8
8
  declare const getBacktestPreloadStart: (start: number, preloadDays?: number) => number;
9
9
  declare const getTimeline: (start?: number, end?: number, step?: number) => number[];
10
+ declare const getRuntimeStorageDayKey: (timestamp: number) => string;
11
+ declare const getRuntimeStorageDayKeys: (startTime: number, endTime: number) => string[];
10
12
  declare const compactOrderLog: (timeline: number[], orderLog: OrderLogData) => SimpleOrderLogData;
11
13
 
12
- export { getBacktestPreloadStart as a, getDataTimestamp as b, compactOrderLog as c, getItemTimestamp as d, getTimestamp as e, formatUnix as f, getTimeline as g, toMs as t };
14
+ export { getBacktestPreloadStart as a, getDataTimestamp as b, compactOrderLog as c, getItemTimestamp as d, getRuntimeStorageDayKey as e, formatUnix as f, getTimeline as g, getRuntimeStorageDayKeys as h, getTimestamp as i, toMs as t };
package/dist/time.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- export { f as formatUnix, a as getBacktestPreloadStart, b as getDataTimestamp, d as getItemTimestamp, e as getTimestamp, t as toMs } from './time-BMkFD4Kd.mjs';
1
+ export { f as formatUnix, a as getBacktestPreloadStart, b as getDataTimestamp, d as getItemTimestamp, e as getRuntimeStorageDayKey, h as getRuntimeStorageDayKeys, i as getTimestamp, t as toMs } from './time-BQ3AXmxo.mjs';
2
2
  import '@tradejs/types';
package/dist/time.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { f as formatUnix, a as getBacktestPreloadStart, b as getDataTimestamp, d as getItemTimestamp, e as getTimestamp, t as toMs } from './time-BMkFD4Kd.js';
1
+ export { f as formatUnix, a as getBacktestPreloadStart, b as getDataTimestamp, d as getItemTimestamp, e as getRuntimeStorageDayKey, h as getRuntimeStorageDayKeys, i as getTimestamp, t as toMs } from './time-BQ3AXmxo.js';
2
2
  import '@tradejs/types';
package/dist/time.js CHANGED
@@ -24,6 +24,8 @@ __export(time_exports, {
24
24
  getBacktestPreloadStart: () => getBacktestPreloadStart,
25
25
  getDataTimestamp: () => getDataTimestamp,
26
26
  getItemTimestamp: () => getItemTimestamp,
27
+ getRuntimeStorageDayKey: () => getRuntimeStorageDayKey,
28
+ getRuntimeStorageDayKeys: () => getRuntimeStorageDayKeys,
27
29
  getTimestamp: () => getTimestamp,
28
30
  toMs: () => toMs
29
31
  });
@@ -34,10 +36,26 @@ var import_date_fns = require("date-fns");
34
36
  var import_date_fns2 = require("date-fns");
35
37
 
36
38
  // src/constants/index.ts
39
+ var BACKTEST_EXECUTION_DELAY_MS = 5 * 6e4;
37
40
  var BACKTEST_PRELOAD_DAYS = 60;
41
+ var DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS = [
42
+ "BTCUSDT",
43
+ "ETHUSDT"
44
+ ];
45
+ var DERIVATIVES_CONTEXT_DEFAULT_EXTRA_REFERENCE_SYMBOLS = [
46
+ "BNBUSDT",
47
+ "SOLUSDT",
48
+ "TRXUSDT",
49
+ "XRPUSDT"
50
+ ];
51
+ var DERIVATIVES_CONTEXT_REFERENCE_SYMBOLS = [
52
+ ...DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS,
53
+ ...DERIVATIVES_CONTEXT_DEFAULT_EXTRA_REFERENCE_SYMBOLS
54
+ ];
38
55
 
39
56
  // src/utils/timestamp.ts
40
57
  var DAY_MS = 864e5;
58
+ var RUNTIME_STORAGE_DAY_OFFSET_MS = 6 * 60 * 60 * 1e3;
41
59
  var toMs = (ts) => ts < 1e12 ? ts * 1e3 : ts;
42
60
  var getTimestamp = (days = 0) => {
43
61
  if (days > 0) {
@@ -56,12 +74,32 @@ var formatUnix = (dt) => {
56
74
  return (0, import_date_fns.format)(new Date(dt), "d MMM u HH:mm:ss");
57
75
  };
58
76
  var getBacktestPreloadStart = (start, preloadDays = BACKTEST_PRELOAD_DAYS) => Math.max(0, Math.trunc(start - preloadDays * DAY_MS));
77
+ var toIsoDayKey = (timestamp) => new Date(timestamp).toISOString().slice(0, 10);
78
+ var toRuntimeStorageDayTimestamp = (timestamp) => Math.floor((timestamp + RUNTIME_STORAGE_DAY_OFFSET_MS) / DAY_MS) * DAY_MS;
79
+ var getRuntimeStorageDayKey = (timestamp) => toIsoDayKey(toRuntimeStorageDayTimestamp(timestamp));
80
+ var getRuntimeStorageDayKeys = (startTime, endTime) => {
81
+ if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) {
82
+ return [];
83
+ }
84
+ const start = Math.min(startTime, endTime);
85
+ const endExclusive = Math.max(startTime, endTime);
86
+ const normalizedEnd = Math.max(start, endExclusive - 1);
87
+ const keys = [];
88
+ const startDay = toRuntimeStorageDayTimestamp(start);
89
+ const endDay = toRuntimeStorageDayTimestamp(normalizedEnd);
90
+ for (let current = startDay; current <= endDay; current += DAY_MS) {
91
+ keys.push(toIsoDayKey(current));
92
+ }
93
+ return keys;
94
+ };
59
95
  // Annotate the CommonJS export names for ESM import in node:
60
96
  0 && (module.exports = {
61
97
  formatUnix,
62
98
  getBacktestPreloadStart,
63
99
  getDataTimestamp,
64
100
  getItemTimestamp,
101
+ getRuntimeStorageDayKey,
102
+ getRuntimeStorageDayKeys,
65
103
  getTimestamp,
66
104
  toMs
67
105
  });
package/dist/time.mjs CHANGED
@@ -3,15 +3,19 @@ import {
3
3
  getBacktestPreloadStart,
4
4
  getDataTimestamp,
5
5
  getItemTimestamp,
6
+ getRuntimeStorageDayKey,
7
+ getRuntimeStorageDayKeys,
6
8
  getTimestamp,
7
9
  toMs
8
- } from "./chunk-FNLPYYML.mjs";
9
- import "./chunk-2ORZC66W.mjs";
10
+ } from "./chunk-BOETNABM.mjs";
11
+ import "./chunk-MKCQSB4H.mjs";
10
12
  export {
11
13
  formatUnix,
12
14
  getBacktestPreloadStart,
13
15
  getDataTimestamp,
14
16
  getItemTimestamp,
17
+ getRuntimeStorageDayKey,
18
+ getRuntimeStorageDayKeys,
15
19
  getTimestamp,
16
20
  toMs
17
21
  };
@@ -0,0 +1,54 @@
1
+ type ExecutionSlippageStage = 'entry' | 'exit';
2
+ type ExecutionSlippageDirection = 'LONG' | 'SHORT';
3
+ type ExecutionSlippageModelParams = {
4
+ baseSlippageBps?: number | null;
5
+ spreadBps?: number | null;
6
+ spreadMultiplier?: number | null;
7
+ marketImpactBps?: number | null;
8
+ delayRiskBps?: number | null;
9
+ };
10
+ type ExecutionSlippageBreakdown = {
11
+ baseSlippageBps: number;
12
+ spreadBps: number;
13
+ spreadMultiplier: number;
14
+ spreadSlippageBps: number;
15
+ marketImpactBps: number;
16
+ delayRiskBps: number;
17
+ effectiveSlippageBps: number;
18
+ };
19
+ type ExecutionDelayRiskParams = {
20
+ closes?: unknown[] | null;
21
+ candles?: unknown[] | null;
22
+ intervalMs?: number | null;
23
+ expectedDelayMs?: number | null;
24
+ lookbackCandles?: number | null;
25
+ multiplier?: number | null;
26
+ maxBps?: number | null;
27
+ };
28
+ type ApplyExecutionSlippageParams = ExecutionSlippageModelParams & {
29
+ price: number;
30
+ direction: ExecutionSlippageDirection;
31
+ stage: ExecutionSlippageStage;
32
+ };
33
+ declare const calculateEffectiveSlippageBps: ({ baseSlippageBps, spreadBps, spreadMultiplier, marketImpactBps, delayRiskBps, }?: ExecutionSlippageModelParams) => number;
34
+ declare const calculateExecutionSlippageBreakdown: ({ baseSlippageBps, spreadBps, spreadMultiplier, marketImpactBps, delayRiskBps, }?: ExecutionSlippageModelParams) => ExecutionSlippageBreakdown;
35
+ declare const calculateDelayRiskBps: ({ closes, candles, intervalMs, expectedDelayMs, lookbackCandles, multiplier, maxBps, }?: ExecutionDelayRiskParams) => number | null;
36
+ declare const slippageBpsToRate: (slippageBps: number) => number;
37
+ declare const applyExecutionSlippage: ({ price, direction, stage, ...modelParams }: ApplyExecutionSlippageParams) => number;
38
+ declare const extractExecutionSpreadBps: (signal?: {
39
+ additionalIndicators?: Record<string, unknown>;
40
+ }) => number | null;
41
+ declare const extractExecutionMarketImpactBps: (signal?: {
42
+ additionalIndicators?: Record<string, unknown>;
43
+ }) => number | null;
44
+ declare const extractExecutionDelayRiskBps: (signal?: {
45
+ interval?: unknown;
46
+ indicators?: Record<string, unknown>;
47
+ additionalIndicators?: Record<string, unknown>;
48
+ }) => number | null;
49
+
50
+ declare const normalizeStrategyOrderLinkKey: (strategyName: string | null | undefined) => string | null;
51
+ declare const createRuntimeOrderLinkPrefix: (strategyName: string | null | undefined) => string;
52
+ declare const parseStrategyOrderLinkKey: (orderLinkId: string | null | undefined) => string | null;
53
+
54
+ export { type ApplyExecutionSlippageParams, type ExecutionDelayRiskParams, type ExecutionSlippageBreakdown, type ExecutionSlippageDirection, type ExecutionSlippageModelParams, type ExecutionSlippageStage, applyExecutionSlippage, calculateDelayRiskBps, calculateEffectiveSlippageBps, calculateExecutionSlippageBreakdown, createRuntimeOrderLinkPrefix, extractExecutionDelayRiskBps, extractExecutionMarketImpactBps, extractExecutionSpreadBps, normalizeStrategyOrderLinkKey, parseStrategyOrderLinkKey, slippageBpsToRate };
@@ -0,0 +1,54 @@
1
+ type ExecutionSlippageStage = 'entry' | 'exit';
2
+ type ExecutionSlippageDirection = 'LONG' | 'SHORT';
3
+ type ExecutionSlippageModelParams = {
4
+ baseSlippageBps?: number | null;
5
+ spreadBps?: number | null;
6
+ spreadMultiplier?: number | null;
7
+ marketImpactBps?: number | null;
8
+ delayRiskBps?: number | null;
9
+ };
10
+ type ExecutionSlippageBreakdown = {
11
+ baseSlippageBps: number;
12
+ spreadBps: number;
13
+ spreadMultiplier: number;
14
+ spreadSlippageBps: number;
15
+ marketImpactBps: number;
16
+ delayRiskBps: number;
17
+ effectiveSlippageBps: number;
18
+ };
19
+ type ExecutionDelayRiskParams = {
20
+ closes?: unknown[] | null;
21
+ candles?: unknown[] | null;
22
+ intervalMs?: number | null;
23
+ expectedDelayMs?: number | null;
24
+ lookbackCandles?: number | null;
25
+ multiplier?: number | null;
26
+ maxBps?: number | null;
27
+ };
28
+ type ApplyExecutionSlippageParams = ExecutionSlippageModelParams & {
29
+ price: number;
30
+ direction: ExecutionSlippageDirection;
31
+ stage: ExecutionSlippageStage;
32
+ };
33
+ declare const calculateEffectiveSlippageBps: ({ baseSlippageBps, spreadBps, spreadMultiplier, marketImpactBps, delayRiskBps, }?: ExecutionSlippageModelParams) => number;
34
+ declare const calculateExecutionSlippageBreakdown: ({ baseSlippageBps, spreadBps, spreadMultiplier, marketImpactBps, delayRiskBps, }?: ExecutionSlippageModelParams) => ExecutionSlippageBreakdown;
35
+ declare const calculateDelayRiskBps: ({ closes, candles, intervalMs, expectedDelayMs, lookbackCandles, multiplier, maxBps, }?: ExecutionDelayRiskParams) => number | null;
36
+ declare const slippageBpsToRate: (slippageBps: number) => number;
37
+ declare const applyExecutionSlippage: ({ price, direction, stage, ...modelParams }: ApplyExecutionSlippageParams) => number;
38
+ declare const extractExecutionSpreadBps: (signal?: {
39
+ additionalIndicators?: Record<string, unknown>;
40
+ }) => number | null;
41
+ declare const extractExecutionMarketImpactBps: (signal?: {
42
+ additionalIndicators?: Record<string, unknown>;
43
+ }) => number | null;
44
+ declare const extractExecutionDelayRiskBps: (signal?: {
45
+ interval?: unknown;
46
+ indicators?: Record<string, unknown>;
47
+ additionalIndicators?: Record<string, unknown>;
48
+ }) => number | null;
49
+
50
+ declare const normalizeStrategyOrderLinkKey: (strategyName: string | null | undefined) => string | null;
51
+ declare const createRuntimeOrderLinkPrefix: (strategyName: string | null | undefined) => string;
52
+ declare const parseStrategyOrderLinkKey: (orderLinkId: string | null | undefined) => string | null;
53
+
54
+ export { type ApplyExecutionSlippageParams, type ExecutionDelayRiskParams, type ExecutionSlippageBreakdown, type ExecutionSlippageDirection, type ExecutionSlippageModelParams, type ExecutionSlippageStage, applyExecutionSlippage, calculateDelayRiskBps, calculateEffectiveSlippageBps, calculateExecutionSlippageBreakdown, createRuntimeOrderLinkPrefix, extractExecutionDelayRiskBps, extractExecutionMarketImpactBps, extractExecutionSpreadBps, normalizeStrategyOrderLinkKey, parseStrategyOrderLinkKey, slippageBpsToRate };
package/dist/trade.js ADDED
@@ -0,0 +1,352 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/trade.ts
31
+ var trade_exports = {};
32
+ __export(trade_exports, {
33
+ applyExecutionSlippage: () => applyExecutionSlippage,
34
+ calculateDelayRiskBps: () => calculateDelayRiskBps,
35
+ calculateEffectiveSlippageBps: () => calculateEffectiveSlippageBps,
36
+ calculateExecutionSlippageBreakdown: () => calculateExecutionSlippageBreakdown,
37
+ createRuntimeOrderLinkPrefix: () => createRuntimeOrderLinkPrefix,
38
+ extractExecutionDelayRiskBps: () => extractExecutionDelayRiskBps,
39
+ extractExecutionMarketImpactBps: () => extractExecutionMarketImpactBps,
40
+ extractExecutionSpreadBps: () => extractExecutionSpreadBps,
41
+ normalizeStrategyOrderLinkKey: () => normalizeStrategyOrderLinkKey,
42
+ parseStrategyOrderLinkKey: () => parseStrategyOrderLinkKey,
43
+ slippageBpsToRate: () => slippageBpsToRate
44
+ });
45
+ module.exports = __toCommonJS(trade_exports);
46
+
47
+ // src/constants/index.ts
48
+ var BACKTEST_BASE_SLIPPAGE_BPS = 10;
49
+ var BACKTEST_SPREAD_SLIPPAGE_MULTIPLIER = 1;
50
+ var BACKTEST_MARKET_IMPACT_BPS = 0;
51
+ var BACKTEST_DELAY_RISK_LOOKBACK_CANDLES = 5;
52
+ var BACKTEST_DELAY_RISK_MULTIPLIER = 0;
53
+ var BACKTEST_DELAY_RISK_MAX_BPS = 0;
54
+ var BACKTEST_EXPECTED_DELAY_MS = 0;
55
+ var BACKTEST_EXECUTION_DELAY_MS = 5 * 6e4;
56
+ var DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS = [
57
+ "BTCUSDT",
58
+ "ETHUSDT"
59
+ ];
60
+ var DERIVATIVES_CONTEXT_DEFAULT_EXTRA_REFERENCE_SYMBOLS = [
61
+ "BNBUSDT",
62
+ "SOLUSDT",
63
+ "TRXUSDT",
64
+ "XRPUSDT"
65
+ ];
66
+ var DERIVATIVES_CONTEXT_REFERENCE_SYMBOLS = [
67
+ ...DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS,
68
+ ...DERIVATIVES_CONTEXT_DEFAULT_EXTRA_REFERENCE_SYMBOLS
69
+ ];
70
+
71
+ // src/utils/array.ts
72
+ var import_lodash = __toESM(require("lodash"));
73
+ var intervalToMs = (interval) => {
74
+ const minutes = {
75
+ "1": 1,
76
+ "3": 3,
77
+ "5": 5,
78
+ "15": 15,
79
+ "30": 30,
80
+ "60": 60,
81
+ "120": 120,
82
+ "240": 240,
83
+ "360": 360,
84
+ "720": 720
85
+ };
86
+ if (interval in minutes) {
87
+ return minutes[interval] * 60 * 1e3;
88
+ }
89
+ switch (interval) {
90
+ case "D":
91
+ return 24 * 60 * 60 * 1e3;
92
+ case "W":
93
+ return 7 * 24 * 60 * 60 * 1e3;
94
+ case "M":
95
+ return 30 * 24 * 60 * 60 * 1e3;
96
+ default:
97
+ throw new Error(`Unknown interval: ${interval}`);
98
+ }
99
+ };
100
+
101
+ // src/utils/executionSlippage.ts
102
+ var toNonNegativeFiniteNumber = (value, fallback = 0) => typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
103
+ var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
104
+ var toRecord = (value) => value && typeof value === "object" ? value : null;
105
+ var getMedian = (values) => {
106
+ if (!values.length) {
107
+ return null;
108
+ }
109
+ const sorted = [...values].sort((left, right) => left - right);
110
+ const middle = Math.floor(sorted.length / 2);
111
+ if (sorted.length % 2 === 0) {
112
+ const left = sorted[middle - 1];
113
+ const right = sorted[middle];
114
+ return left == null || right == null ? null : (left + right) / 2;
115
+ }
116
+ return sorted[middle] ?? null;
117
+ };
118
+ var extractCandleClose = (value) => {
119
+ if (typeof value === "number" && Number.isFinite(value)) {
120
+ return value;
121
+ }
122
+ const record = toRecord(value);
123
+ return toFiniteNumberOrNull(record?.close);
124
+ };
125
+ var extractCloseSeries = ({
126
+ closes,
127
+ candles
128
+ }) => {
129
+ const source = Array.isArray(closes) && closes.length ? closes : candles;
130
+ if (!Array.isArray(source)) {
131
+ return [];
132
+ }
133
+ return source.map(extractCandleClose).filter((value) => value != null && value > 0);
134
+ };
135
+ var getSignalIntervalMs = (interval) => {
136
+ if (typeof interval !== "string") {
137
+ return null;
138
+ }
139
+ try {
140
+ return intervalToMs(interval);
141
+ } catch {
142
+ return null;
143
+ }
144
+ };
145
+ var getSignalCandleSeries = (signal) => {
146
+ const indicators = toRecord(signal?.indicators);
147
+ if (!indicators) {
148
+ return null;
149
+ }
150
+ const intervalKey = (() => {
151
+ switch (signal?.interval) {
152
+ case "15":
153
+ return "candles15m";
154
+ case "60":
155
+ return "candles1h";
156
+ case "240":
157
+ return "candles4h";
158
+ case "D":
159
+ return "candles1d";
160
+ default:
161
+ return null;
162
+ }
163
+ })();
164
+ const keys = [
165
+ intervalKey,
166
+ "candles15m",
167
+ "candles1h",
168
+ "candles4h",
169
+ "candles1d"
170
+ ].filter((key) => Boolean(key));
171
+ for (const key of keys) {
172
+ const value = indicators[key];
173
+ if (Array.isArray(value) && value.length > 1) {
174
+ return value;
175
+ }
176
+ }
177
+ const candle = indicators.candle;
178
+ const prevCandle = indicators.prevCandle;
179
+ return prevCandle && candle ? [prevCandle, candle] : null;
180
+ };
181
+ var calculateEffectiveSlippageBps = ({
182
+ baseSlippageBps = BACKTEST_BASE_SLIPPAGE_BPS,
183
+ spreadBps,
184
+ spreadMultiplier = BACKTEST_SPREAD_SLIPPAGE_MULTIPLIER,
185
+ marketImpactBps = BACKTEST_MARKET_IMPACT_BPS,
186
+ delayRiskBps
187
+ } = {}) => {
188
+ const base = toNonNegativeFiniteNumber(baseSlippageBps);
189
+ const spread = toNonNegativeFiniteNumber(spreadBps);
190
+ const multiplier = toNonNegativeFiniteNumber(spreadMultiplier);
191
+ const marketImpact = toNonNegativeFiniteNumber(marketImpactBps);
192
+ const delayRisk = toNonNegativeFiniteNumber(delayRiskBps);
193
+ return base + spread * multiplier + marketImpact + delayRisk;
194
+ };
195
+ var calculateExecutionSlippageBreakdown = ({
196
+ baseSlippageBps = BACKTEST_BASE_SLIPPAGE_BPS,
197
+ spreadBps,
198
+ spreadMultiplier = BACKTEST_SPREAD_SLIPPAGE_MULTIPLIER,
199
+ marketImpactBps = BACKTEST_MARKET_IMPACT_BPS,
200
+ delayRiskBps
201
+ } = {}) => {
202
+ const base = toNonNegativeFiniteNumber(baseSlippageBps);
203
+ const spread = toNonNegativeFiniteNumber(spreadBps);
204
+ const multiplier = toNonNegativeFiniteNumber(spreadMultiplier);
205
+ const spreadSlippage = spread * multiplier;
206
+ const marketImpact = toNonNegativeFiniteNumber(marketImpactBps);
207
+ const delayRisk = toNonNegativeFiniteNumber(delayRiskBps);
208
+ return {
209
+ baseSlippageBps: base,
210
+ spreadBps: spread,
211
+ spreadMultiplier: multiplier,
212
+ spreadSlippageBps: spreadSlippage,
213
+ marketImpactBps: marketImpact,
214
+ delayRiskBps: delayRisk,
215
+ effectiveSlippageBps: base + spreadSlippage + marketImpact + delayRisk
216
+ };
217
+ };
218
+ var calculateDelayRiskBps = ({
219
+ closes,
220
+ candles,
221
+ intervalMs,
222
+ expectedDelayMs = BACKTEST_EXPECTED_DELAY_MS,
223
+ lookbackCandles = BACKTEST_DELAY_RISK_LOOKBACK_CANDLES,
224
+ multiplier = BACKTEST_DELAY_RISK_MULTIPLIER,
225
+ maxBps = BACKTEST_DELAY_RISK_MAX_BPS
226
+ } = {}) => {
227
+ const normalizedLookback = Math.max(
228
+ 1,
229
+ Math.trunc(toNonNegativeFiniteNumber(lookbackCandles, 1))
230
+ );
231
+ const closeSeries = extractCloseSeries({ closes, candles }).slice(
232
+ -(normalizedLookback + 1)
233
+ );
234
+ if (closeSeries.length < 2) {
235
+ return null;
236
+ }
237
+ const moveBps = [];
238
+ for (let index = 1; index < closeSeries.length; index += 1) {
239
+ const previous = closeSeries[index - 1];
240
+ const current = closeSeries[index];
241
+ if (previous != null && current != null && previous > 0 && current > 0) {
242
+ moveBps.push(Math.abs(current / previous - 1) * 1e4);
243
+ }
244
+ }
245
+ const medianMoveBps = getMedian(moveBps);
246
+ if (medianMoveBps == null) {
247
+ return null;
248
+ }
249
+ const delayScale = typeof intervalMs === "number" && Number.isFinite(intervalMs) && intervalMs > 0 && typeof expectedDelayMs === "number" && Number.isFinite(expectedDelayMs) && expectedDelayMs > 0 ? Math.sqrt(expectedDelayMs / intervalMs) : 1;
250
+ const rawDelayRisk = medianMoveBps * delayScale * toNonNegativeFiniteNumber(multiplier);
251
+ const cappedDelayRisk = Math.min(
252
+ rawDelayRisk,
253
+ toNonNegativeFiniteNumber(maxBps, Number.POSITIVE_INFINITY)
254
+ );
255
+ return Number.isFinite(cappedDelayRisk) ? cappedDelayRisk : null;
256
+ };
257
+ var slippageBpsToRate = (slippageBps) => toNonNegativeFiniteNumber(slippageBps) / 1e4;
258
+ var applyExecutionSlippage = ({
259
+ price,
260
+ direction,
261
+ stage,
262
+ ...modelParams
263
+ }) => {
264
+ const slippageRate = slippageBpsToRate(
265
+ calculateExecutionSlippageBreakdown(modelParams).effectiveSlippageBps
266
+ );
267
+ if (!slippageRate) {
268
+ return price;
269
+ }
270
+ const sign = direction === "LONG" ? stage === "entry" ? 1 : -1 : stage === "entry" ? -1 : 1;
271
+ return price * (1 + sign * slippageRate);
272
+ };
273
+ var extractExecutionSpreadBps = (signal) => {
274
+ const additionalIndicators = toRecord(signal?.additionalIndicators);
275
+ const explicitSlippage = toRecord(additionalIndicators?.executionSlippage);
276
+ return toFiniteNumberOrNull(explicitSlippage?.spreadBps);
277
+ };
278
+ var extractExecutionMarketImpactBps = (signal) => {
279
+ const additionalIndicators = toRecord(signal?.additionalIndicators);
280
+ const explicitSlippage = toRecord(additionalIndicators?.executionSlippage);
281
+ return toFiniteNumberOrNull(explicitSlippage?.marketImpactBps);
282
+ };
283
+ var extractExecutionDelayRiskBps = (signal) => {
284
+ const additionalIndicators = toRecord(signal?.additionalIndicators);
285
+ const explicitSlippage = toRecord(additionalIndicators?.executionSlippage);
286
+ const explicitDelayRisk = toFiniteNumberOrNull(
287
+ explicitSlippage?.delayRiskBps
288
+ );
289
+ if (explicitDelayRisk != null) {
290
+ return explicitDelayRisk;
291
+ }
292
+ return calculateDelayRiskBps({
293
+ candles: getSignalCandleSeries(signal),
294
+ intervalMs: getSignalIntervalMs(signal?.interval)
295
+ });
296
+ };
297
+
298
+ // src/trade.ts
299
+ var ORDER_LINK_PREFIX = "tjs-";
300
+ var ORDER_LINK_SEPARATOR = "--";
301
+ var STRATEGY_SLUG_LENGTH = 10;
302
+ var STRATEGY_HASH_LENGTH = 5;
303
+ var toBase36Hash = (value) => {
304
+ let hash = 0;
305
+ for (let index = 0; index < value.length; index += 1) {
306
+ hash = hash * 31 + value.charCodeAt(index) >>> 0;
307
+ }
308
+ return hash.toString(36).padStart(STRATEGY_HASH_LENGTH, "0");
309
+ };
310
+ var normalizeStrategyOrderLinkKey = (strategyName) => {
311
+ const normalized = String(strategyName ?? "").trim().toLowerCase();
312
+ if (!normalized) {
313
+ return null;
314
+ }
315
+ const slug = normalized.replace(/[^a-z0-9]+/g, "").slice(0, STRATEGY_SLUG_LENGTH) || "strategy";
316
+ const hash = toBase36Hash(normalized).slice(0, STRATEGY_HASH_LENGTH);
317
+ return `${slug}-${hash}`;
318
+ };
319
+ var createRuntimeOrderLinkPrefix = (strategyName) => {
320
+ const strategyKey = normalizeStrategyOrderLinkKey(strategyName);
321
+ return strategyKey ? `${ORDER_LINK_PREFIX}${strategyKey}${ORDER_LINK_SEPARATOR}` : ORDER_LINK_PREFIX;
322
+ };
323
+ var parseStrategyOrderLinkKey = (orderLinkId) => {
324
+ const normalized = String(orderLinkId ?? "").trim().toLowerCase();
325
+ if (!normalized.startsWith(ORDER_LINK_PREFIX)) {
326
+ return null;
327
+ }
328
+ const remainder = normalized.slice(ORDER_LINK_PREFIX.length);
329
+ const separatorIndex = remainder.indexOf(ORDER_LINK_SEPARATOR);
330
+ if (separatorIndex <= 0) {
331
+ return null;
332
+ }
333
+ const strategyPart = remainder.slice(0, separatorIndex).trim();
334
+ if (!strategyPart) {
335
+ return null;
336
+ }
337
+ return strategyPart;
338
+ };
339
+ // Annotate the CommonJS export names for ESM import in node:
340
+ 0 && (module.exports = {
341
+ applyExecutionSlippage,
342
+ calculateDelayRiskBps,
343
+ calculateEffectiveSlippageBps,
344
+ calculateExecutionSlippageBreakdown,
345
+ createRuntimeOrderLinkPrefix,
346
+ extractExecutionDelayRiskBps,
347
+ extractExecutionMarketImpactBps,
348
+ extractExecutionSpreadBps,
349
+ normalizeStrategyOrderLinkKey,
350
+ parseStrategyOrderLinkKey,
351
+ slippageBpsToRate
352
+ });