@tradejs/cli 1.0.6 → 1.0.8

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.
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lib/runtimeParity.ts
21
+ var runtimeParity_exports = {};
22
+ __export(runtimeParity_exports, {
23
+ compareTradeParityEntries: () => compareTradeParityEntries,
24
+ dedupeRuntimeParityEntries: () => dedupeRuntimeParityEntries,
25
+ extractBacktestEntryParityEntries: () => extractBacktestEntryParityEntries,
26
+ extractRuntimeParityEntries: () => extractRuntimeParityEntries,
27
+ findRuntimeDuplicateGroups: () => findRuntimeDuplicateGroups,
28
+ summarizeMatchedParity: () => summarizeMatchedParity
29
+ });
30
+ module.exports = __toCommonJS(runtimeParity_exports);
31
+ var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
32
+ var toGroupKey = (entry) => `${entry.strategy}::${entry.symbol}::${entry.direction}`;
33
+ var toRuntimeDuplicateKey = (entry) => `${toGroupKey(entry)}::${entry.timestamp}`;
34
+ var sortTradeParityEntries = (left, right) => left.timestamp - right.timestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.direction.localeCompare(right.direction) || left.id.localeCompare(right.id);
35
+ var buildPriceDeltaPct = (leftPrice, rightPrice) => {
36
+ if (leftPrice == null || rightPrice == null || !Number.isFinite(leftPrice) || !Number.isFinite(rightPrice) || leftPrice === 0) {
37
+ return null;
38
+ }
39
+ return Math.abs((rightPrice - leftPrice) / leftPrice * 100);
40
+ };
41
+ var extractBacktestEntryParityEntries = (orderLog) => {
42
+ if (!Array.isArray(orderLog)) {
43
+ return [];
44
+ }
45
+ return orderLog.filter(
46
+ (item) => (item.type === "OPEN_LONG" || item.type === "OPEN_SHORT") && item.signal && typeof item.signal.strategy === "string" && typeof item.signal.symbol === "string" && (item.signal.direction === "LONG" || item.signal.direction === "SHORT")
47
+ ).map((item, index) => {
48
+ const signal = item.signal;
49
+ const timestamp = typeof signal.timestamp === "number" && Number.isFinite(signal.timestamp) ? signal.timestamp : item.timestamp;
50
+ return {
51
+ id: typeof signal.signalId === "string" && signal.signalId.trim() ? signal.signalId : `backtest-${index}-${timestamp}`,
52
+ source: "backtest",
53
+ strategy: signal.strategy,
54
+ symbol: signal.symbol,
55
+ direction: signal.direction,
56
+ timestamp,
57
+ price: toFiniteNumberOrNull(item.price),
58
+ signalId: typeof signal.signalId === "string" ? signal.signalId : void 0
59
+ };
60
+ }).sort((left, right) => left.timestamp - right.timestamp);
61
+ };
62
+ var extractRuntimeParityEntries = (trades) => trades.filter(
63
+ (trade) => typeof trade.strategy === "string" && typeof trade.symbol === "string" && (trade.direction === "LONG" || trade.direction === "SHORT") && typeof trade.entryTimestamp === "number" && Number.isFinite(trade.entryTimestamp)
64
+ ).map((trade) => ({
65
+ id: trade.orderId,
66
+ source: "runtime",
67
+ strategy: trade.strategy,
68
+ symbol: trade.symbol,
69
+ direction: trade.direction,
70
+ timestamp: trade.entryTimestamp,
71
+ price: toFiniteNumberOrNull(trade.entryPrice),
72
+ orderId: trade.orderId,
73
+ signalId: typeof trade.signalId === "string" ? trade.signalId : void 0
74
+ })).sort((left, right) => left.timestamp - right.timestamp);
75
+ var findRuntimeDuplicateGroups = (entries) => {
76
+ const groups = /* @__PURE__ */ new Map();
77
+ for (const entry of entries) {
78
+ if (entry.source !== "runtime") {
79
+ continue;
80
+ }
81
+ const key = toRuntimeDuplicateKey(entry);
82
+ const bucket = groups.get(key) ?? [];
83
+ bucket.push(entry);
84
+ groups.set(key, bucket);
85
+ }
86
+ return [...groups.entries()].filter(([, group]) => group.length > 1).map(([key, group]) => {
87
+ const sortedGroup = [...group].sort(sortTradeParityEntries);
88
+ const first = sortedGroup[0];
89
+ return {
90
+ key,
91
+ strategy: first.strategy,
92
+ symbol: first.symbol,
93
+ direction: first.direction,
94
+ timestamp: first.timestamp,
95
+ entries: sortedGroup
96
+ };
97
+ }).sort(
98
+ (left, right) => left.timestamp - right.timestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.direction.localeCompare(right.direction)
99
+ );
100
+ };
101
+ var dedupeRuntimeParityEntries = (entries) => {
102
+ const duplicateGroups = findRuntimeDuplicateGroups(entries);
103
+ const duplicateEntriesSet = /* @__PURE__ */ new Set();
104
+ for (const group of duplicateGroups) {
105
+ for (const duplicate of group.entries.slice(1)) {
106
+ duplicateEntriesSet.add(duplicate);
107
+ }
108
+ }
109
+ const dedupedEntries = entries.filter((entry) => !duplicateEntriesSet.has(entry)).sort(sortTradeParityEntries);
110
+ const duplicateEntries = duplicateGroups.flatMap((group) => group.entries.slice(1)).sort(sortTradeParityEntries);
111
+ return {
112
+ entries: dedupedEntries,
113
+ duplicateGroups,
114
+ duplicateEntries
115
+ };
116
+ };
117
+ var compareTradeParityEntries = ({
118
+ runtimeEntries,
119
+ backtestEntries,
120
+ toleranceMs
121
+ }) => {
122
+ const matched = [];
123
+ const runtimeOnly = [];
124
+ const backtestOnly = [];
125
+ const groupedRuntime = /* @__PURE__ */ new Map();
126
+ const groupedBacktest = /* @__PURE__ */ new Map();
127
+ for (const entry of runtimeEntries) {
128
+ const key = toGroupKey(entry);
129
+ const bucket = groupedRuntime.get(key) ?? [];
130
+ bucket.push(entry);
131
+ groupedRuntime.set(key, bucket);
132
+ }
133
+ for (const entry of backtestEntries) {
134
+ const key = toGroupKey(entry);
135
+ const bucket = groupedBacktest.get(key) ?? [];
136
+ bucket.push(entry);
137
+ groupedBacktest.set(key, bucket);
138
+ }
139
+ const groupKeys = /* @__PURE__ */ new Set([
140
+ ...groupedRuntime.keys(),
141
+ ...groupedBacktest.keys()
142
+ ]);
143
+ for (const key of groupKeys) {
144
+ const runtimeGroup = [...groupedRuntime.get(key) ?? []].sort(
145
+ (left, right) => left.timestamp - right.timestamp
146
+ );
147
+ const backtestGroup = [...groupedBacktest.get(key) ?? []].sort(
148
+ (left, right) => left.timestamp - right.timestamp
149
+ );
150
+ const unmatchedBacktest = backtestGroup.map((entry) => ({
151
+ entry,
152
+ used: false
153
+ }));
154
+ for (const runtimeEntry of runtimeGroup) {
155
+ let bestIndex = -1;
156
+ let bestDiff = Number.POSITIVE_INFINITY;
157
+ for (let index = 0; index < unmatchedBacktest.length; index += 1) {
158
+ const candidate = unmatchedBacktest[index];
159
+ if (candidate.used) {
160
+ continue;
161
+ }
162
+ const diff = Math.abs(
163
+ candidate.entry.timestamp - runtimeEntry.timestamp
164
+ );
165
+ if (diff > toleranceMs || diff >= bestDiff) {
166
+ continue;
167
+ }
168
+ bestIndex = index;
169
+ bestDiff = diff;
170
+ }
171
+ if (bestIndex < 0) {
172
+ runtimeOnly.push(runtimeEntry);
173
+ continue;
174
+ }
175
+ unmatchedBacktest[bestIndex].used = true;
176
+ const backtestEntry = unmatchedBacktest[bestIndex].entry;
177
+ matched.push({
178
+ runtime: runtimeEntry,
179
+ backtest: backtestEntry,
180
+ timestampDiffMs: bestDiff,
181
+ priceDeltaPct: buildPriceDeltaPct(
182
+ runtimeEntry.price,
183
+ backtestEntry.price
184
+ )
185
+ });
186
+ }
187
+ for (const candidate of unmatchedBacktest) {
188
+ if (!candidate.used) {
189
+ backtestOnly.push(candidate.entry);
190
+ }
191
+ }
192
+ }
193
+ matched.sort(
194
+ (left, right) => left.runtime.timestamp - right.runtime.timestamp
195
+ );
196
+ runtimeOnly.sort((left, right) => left.timestamp - right.timestamp);
197
+ backtestOnly.sort((left, right) => left.timestamp - right.timestamp);
198
+ return {
199
+ matched,
200
+ runtimeOnly,
201
+ backtestOnly
202
+ };
203
+ };
204
+ var summarizeMatchedParity = (matched) => {
205
+ if (!matched.length) {
206
+ return {
207
+ avgPriceDeltaPct: null,
208
+ maxPriceDeltaPct: null,
209
+ avgTimestampDiffMs: null,
210
+ maxTimestampDiffMs: null
211
+ };
212
+ }
213
+ const priceDeltas = matched.map((item) => item.priceDeltaPct).filter(
214
+ (value) => value != null && Number.isFinite(value)
215
+ );
216
+ const timestampDiffs = matched.map((item) => item.timestampDiffMs).filter((value) => Number.isFinite(value));
217
+ const average = (values) => values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : null;
218
+ const maximum = (values) => values.length ? Math.max(...values) : null;
219
+ return {
220
+ avgPriceDeltaPct: average(priceDeltas),
221
+ maxPriceDeltaPct: maximum(priceDeltas),
222
+ avgTimestampDiffMs: average(timestampDiffs),
223
+ maxTimestampDiffMs: maximum(timestampDiffs)
224
+ };
225
+ };
226
+ // Annotate the CommonJS export names for ESM import in node:
227
+ 0 && (module.exports = {
228
+ compareTradeParityEntries,
229
+ dedupeRuntimeParityEntries,
230
+ extractBacktestEntryParityEntries,
231
+ extractRuntimeParityEntries,
232
+ findRuntimeDuplicateGroups,
233
+ summarizeMatchedParity
234
+ });
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lib/telegramReports.ts
21
+ var telegramReports_exports = {};
22
+ __export(telegramReports_exports, {
23
+ TELEGRAM_REPORT_CHUNK_LIMIT: () => TELEGRAM_REPORT_CHUNK_LIMIT,
24
+ sendTelegramReport: () => sendTelegramReport,
25
+ splitTelegramReport: () => splitTelegramReport
26
+ });
27
+ module.exports = __toCommonJS(telegramReports_exports);
28
+ var import_cli = require("@tradejs/node/cli");
29
+ var TELEGRAM_REPORT_CHUNK_LIMIT = 3900;
30
+ var buildPartHeader = (index, total) => `\u{1F4E9} <b>Part ${index}/${total}</b>`;
31
+ var splitLongLine = (line, limit) => {
32
+ const chunks = [];
33
+ for (let index = 0; index < line.length; index += limit) {
34
+ chunks.push(line.slice(index, index + limit));
35
+ }
36
+ return chunks;
37
+ };
38
+ var splitTelegramReport = (message, limit = TELEGRAM_REPORT_CHUNK_LIMIT) => {
39
+ const normalizedLimit = Math.max(100, Math.floor(limit));
40
+ if (message.length <= normalizedLimit) {
41
+ return [message];
42
+ }
43
+ const chunks = [];
44
+ let current = "";
45
+ const flush = () => {
46
+ if (!current) {
47
+ return;
48
+ }
49
+ chunks.push(current);
50
+ current = "";
51
+ };
52
+ for (const line of message.split("\n")) {
53
+ const next = current ? `${current}
54
+ ${line}` : line;
55
+ if (next.length <= normalizedLimit) {
56
+ current = next;
57
+ continue;
58
+ }
59
+ flush();
60
+ if (line.length <= normalizedLimit) {
61
+ current = line;
62
+ continue;
63
+ }
64
+ chunks.push(...splitLongLine(line, normalizedLimit));
65
+ }
66
+ flush();
67
+ return chunks;
68
+ };
69
+ var sendTelegramReport = async (message, options = {}) => {
70
+ const chunks = splitTelegramReport(message);
71
+ if (chunks.length === 1) {
72
+ await (0, import_cli.sendTextToTG)(chunks[0], options);
73
+ return chunks.length;
74
+ }
75
+ for (const [index, chunk] of chunks.entries()) {
76
+ const header = buildPartHeader(index + 1, chunks.length);
77
+ await (0, import_cli.sendTextToTG)(`${header}
78
+
79
+ ${chunk}`, {
80
+ userName: options.userName,
81
+ markup: index === chunks.length - 1 ? options.markup : void 0
82
+ });
83
+ }
84
+ return chunks.length;
85
+ };
86
+ // Annotate the CommonJS export names for ESM import in node:
87
+ 0 && (module.exports = {
88
+ TELEGRAM_REPORT_CHUNK_LIMIT,
89
+ sendTelegramReport,
90
+ splitTelegramReport
91
+ });
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lib/timeWindow.ts
21
+ var timeWindow_exports = {};
22
+ __export(timeWindow_exports, {
23
+ resolveTimeWindow: () => resolveTimeWindow
24
+ });
25
+ module.exports = __toCommonJS(timeWindow_exports);
26
+ var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
27
+ var toEpochMs = (value) => {
28
+ if (value == null || value === "") {
29
+ return null;
30
+ }
31
+ const parsed = Number(value);
32
+ if (!Number.isFinite(parsed) || parsed <= 0) {
33
+ return null;
34
+ }
35
+ return parsed < 1e12 ? Math.trunc(parsed * 1e3) : Math.trunc(parsed);
36
+ };
37
+ var toPositiveNumber = (value) => {
38
+ if (value == null || value === "") {
39
+ return null;
40
+ }
41
+ const parsed = Number(value);
42
+ if (!Number.isFinite(parsed) || parsed <= 0) {
43
+ return null;
44
+ }
45
+ return parsed;
46
+ };
47
+ var resolveTimeWindow = ({
48
+ days,
49
+ startTime,
50
+ endTime,
51
+ nowMs = Date.now(),
52
+ defaultStartMs,
53
+ defaultEndMs
54
+ }) => {
55
+ const explicitStart = toEpochMs(startTime);
56
+ const explicitEnd = toEpochMs(endTime);
57
+ if (explicitStart != null || explicitEnd != null) {
58
+ const resolvedEnd2 = explicitEnd ?? defaultEndMs ?? nowMs;
59
+ const resolvedStart = explicitStart ?? defaultStartMs;
60
+ if (resolvedStart >= resolvedEnd2) {
61
+ throw new Error(
62
+ `Invalid time window: startTime (${resolvedStart}) must be less than endTime (${resolvedEnd2})`
63
+ );
64
+ }
65
+ return {
66
+ start: resolvedStart,
67
+ end: resolvedEnd2,
68
+ source: "explicit"
69
+ };
70
+ }
71
+ const resolvedDays = toPositiveNumber(days);
72
+ if (resolvedDays != null) {
73
+ const resolvedEnd2 = defaultEndMs ?? nowMs;
74
+ const resolvedStart = resolvedEnd2 - resolvedDays * ONE_DAY_MS;
75
+ if (resolvedStart >= resolvedEnd2) {
76
+ throw new Error(
77
+ `Invalid time window: computed start (${resolvedStart}) must be less than end (${resolvedEnd2})`
78
+ );
79
+ }
80
+ return {
81
+ start: Math.trunc(resolvedStart),
82
+ end: Math.trunc(resolvedEnd2),
83
+ source: "days"
84
+ };
85
+ }
86
+ const resolvedEnd = defaultEndMs ?? nowMs;
87
+ if (defaultStartMs >= resolvedEnd) {
88
+ throw new Error(
89
+ `Invalid default time window: start (${defaultStartMs}) must be less than end (${resolvedEnd})`
90
+ );
91
+ }
92
+ return {
93
+ start: Math.trunc(defaultStartMs),
94
+ end: Math.trunc(resolvedEnd),
95
+ source: "default"
96
+ };
97
+ };
98
+ // Annotate the CommonJS export names for ESM import in node:
99
+ 0 && (module.exports = {
100
+ resolveTimeWindow
101
+ });