@tradejs/core 3.1.29-beta.290 → 3.1.29-beta.291

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,211 @@
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/tickerProfiles.ts
21
+ var tickerProfiles_exports = {};
22
+ __export(tickerProfiles_exports, {
23
+ TICKER_PROFILE_DEFINITION: () => TICKER_PROFILE_DEFINITION,
24
+ buildTickerProfileSnapshot: () => buildTickerProfileSnapshot,
25
+ isTickerProfileSnapshot: () => isTickerProfileSnapshot,
26
+ tickerProfileMonth: () => tickerProfileMonth,
27
+ tickerProfileNextMonth: () => tickerProfileNextMonth
28
+ });
29
+ module.exports = __toCommonJS(tickerProfiles_exports);
30
+ var HOUR = 36e5;
31
+ var WINDOW = 720 * HOUR;
32
+ var MIN_HOURS = 576;
33
+ var TICKER_PROFILE_DEFINITION = "hourly-30d-monthly-terciles/v1";
34
+ var tickerProfileMonth = (timestamp) => {
35
+ if (!Number.isSafeInteger(timestamp) || timestamp < 0)
36
+ throw new Error("Invalid profile timestamp");
37
+ const date = new Date(timestamp);
38
+ return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1);
39
+ };
40
+ var tickerProfileNextMonth = (timestamp) => {
41
+ const date = new Date(timestamp);
42
+ return Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1);
43
+ };
44
+ var prepare = (candles, asOf) => {
45
+ const eligible = candles.filter(
46
+ (c) => c.timestamp >= asOf - WINDOW - HOUR && c.timestamp + HOUR <= asOf
47
+ );
48
+ let previous = -1;
49
+ for (const candle of eligible) {
50
+ if (!Number.isSafeInteger(candle.timestamp) || candle.timestamp % HOUR !== 0 || candle.timestamp <= previous || ![candle.high, candle.low, candle.close].every(
51
+ (v) => Number.isFinite(v) && v > 0
52
+ ) || !Number.isFinite(candle.volume) || candle.volume < 0)
53
+ throw new Error("Invalid or unordered hourly profile candles");
54
+ previous = candle.timestamp;
55
+ }
56
+ const returns = /* @__PURE__ */ new Map();
57
+ for (let i = 1; i < eligible.length; i++) {
58
+ const a = eligible[i - 1], b = eligible[i];
59
+ if (b.timestamp - a.timestamp === HOUR && b.timestamp >= asOf - WINDOW)
60
+ returns.set(b.timestamp, Math.log(b.close / a.close));
61
+ }
62
+ return {
63
+ candles: eligible.filter((c) => c.timestamp >= asOf - WINDOW),
64
+ returns
65
+ };
66
+ };
67
+ var correlate = (target, reference) => {
68
+ const pairs = [...target].filter(([t]) => reference.has(t)).map(([t, r]) => [r, reference.get(t) ?? 0]);
69
+ if (pairs.length < MIN_HOURS) return { value: null, count: pairs.length };
70
+ const mx = pairs.reduce((s, p) => s + p[0], 0) / pairs.length;
71
+ const my = pairs.reduce((s, p) => s + p[1], 0) / pairs.length;
72
+ let xx = 0, yy = 0, xy = 0;
73
+ for (const [x, y] of pairs) {
74
+ xx += (x - mx) ** 2;
75
+ yy += (y - my) ** 2;
76
+ xy += (x - mx) * (y - my);
77
+ }
78
+ return {
79
+ value: xx > 0 && yy > 0 ? Math.max(-1, Math.min(1, xy / Math.sqrt(xx * yy))) : null,
80
+ count: pairs.length
81
+ };
82
+ };
83
+ var correlationGroup = (v) => v === null ? "unknown" : v < 0.3 ? "weak" : v < 0.7 ? "medium" : "strong";
84
+ var cuts = (values) => {
85
+ const sorted = values.filter((v) => v !== null).sort((a, b) => a - b);
86
+ return [
87
+ sorted[Math.floor((sorted.length - 1) / 3)] ?? null,
88
+ sorted[Math.floor((sorted.length - 1) * 2 / 3)] ?? null
89
+ ];
90
+ };
91
+ var group = (v, thresholds) => v === null || thresholds[0] === null || thresholds[1] === null ? "unknown" : v <= thresholds[0] ? "low" : v <= thresholds[1] ? "middle" : "high";
92
+ var buildTickerProfileSnapshot = ({
93
+ asOf,
94
+ provider,
95
+ universeId,
96
+ membership,
97
+ candlesBySymbol
98
+ }) => {
99
+ if (tickerProfileMonth(asOf) !== asOf)
100
+ throw new Error("Profiles must start at a UTC month boundary");
101
+ const symbols = Object.keys(candlesBySymbol).sort();
102
+ if (!symbols.length || symbols.some((s) => !/^[A-Z0-9]+USDT$/.test(s)))
103
+ throw new Error("Profile universe requires explicit USDT symbols");
104
+ const btc = prepare(candlesBySymbol.BTCUSDT ?? [], asOf).returns;
105
+ const eth = prepare(candlesBySymbol.ETHUSDT ?? [], asOf).returns;
106
+ const validUntil = tickerProfileNextMonth(asOf);
107
+ const profiles = symbols.map((symbol) => {
108
+ const { candles, returns } = prepare(candlesBySymbol[symbol], asOf);
109
+ const quoteCount = candles.filter(
110
+ (c) => Number.isFinite(c.turnover) && Number(c.turnover) >= 0
111
+ ).length;
112
+ const ready = candles.length >= MIN_HOURS;
113
+ const dailyTurnoverUSDT = ready ? candles.reduce(
114
+ (s, c) => s + (Number.isFinite(c.turnover) && Number(c.turnover) >= 0 ? Number(c.turnover) : c.volume * (c.high + c.low + c.close) / 3),
115
+ 0
116
+ ) / 30 : null;
117
+ const rr = [...returns.values()], mean = rr.reduce((a, b) => a + b, 0) / (rr.length || 1);
118
+ const dailyVolatilityPct = rr.length >= MIN_HOURS ? Math.sqrt(
119
+ rr.reduce((s, v) => s + (v - mean) ** 2, 0) / (rr.length - 1)
120
+ ) * Math.sqrt(24) * 100 : null;
121
+ const cb = correlate(returns, btc), ce = correlate(returns, eth);
122
+ return {
123
+ symbol,
124
+ asOf,
125
+ validUntil,
126
+ windowStart: asOf - WINDOW,
127
+ hours: candles.length,
128
+ returnHours: rr.length,
129
+ btcAlignedHours: cb.count,
130
+ ethAlignedHours: ce.count,
131
+ turnoverSource: !ready ? "unknown" : quoteCount === candles.length ? "quote" : quoteCount === 0 ? "estimated" : "mixed",
132
+ dailyTurnoverUSDT,
133
+ dailyVolatilityPct,
134
+ correlationBTC: cb.value,
135
+ correlationETH: ce.value,
136
+ moneyGroup: "unknown",
137
+ volatilityGroup: "unknown",
138
+ btcCorrelationGroup: correlationGroup(cb.value),
139
+ ethCorrelationGroup: correlationGroup(ce.value)
140
+ };
141
+ });
142
+ const thresholds = {
143
+ money: cuts(profiles.map((p) => p.dailyTurnoverUSDT)),
144
+ volatility: cuts(profiles.map((p) => p.dailyVolatilityPct))
145
+ };
146
+ for (const p of profiles) {
147
+ p.moneyGroup = group(p.dailyTurnoverUSDT, thresholds.money);
148
+ p.volatilityGroup = group(p.dailyVolatilityPct, thresholds.volatility);
149
+ }
150
+ return {
151
+ schema: "tradejs-ticker-profiles/v1",
152
+ definition: TICKER_PROFILE_DEFINITION,
153
+ provider,
154
+ universeId,
155
+ membership,
156
+ asOf,
157
+ validUntil,
158
+ thresholds,
159
+ profiles
160
+ };
161
+ };
162
+ var record = (v) => Boolean(v) && typeof v === "object" && !Array.isArray(v);
163
+ var nullableNumber = (v) => v === null || typeof v === "number" && Number.isFinite(v);
164
+ var isTickerProfileSnapshot = (value) => {
165
+ if (!record(value) || value.schema !== "tradejs-ticker-profiles/v1" || value.definition !== TICKER_PROFILE_DEFINITION || typeof value.provider !== "string" || typeof value.universeId !== "string" || !["frozen-list", "point-in-time"].includes(String(value.membership)) || typeof value.asOf !== "number" || !Number.isSafeInteger(value.asOf) || value.asOf < 0 || tickerProfileMonth(value.asOf) !== value.asOf || value.validUntil !== tickerProfileNextMonth(value.asOf) || !record(value.thresholds) || !Array.isArray(value.profiles))
166
+ return false;
167
+ for (const key of ["money", "volatility"]) {
168
+ const cut = value.thresholds[key];
169
+ if (!Array.isArray(cut) || cut.length !== 2 || !cut.every(nullableNumber))
170
+ return false;
171
+ }
172
+ const symbols = /* @__PURE__ */ new Set();
173
+ for (const p of value.profiles) {
174
+ if (!record(p) || typeof p.symbol !== "string" || !/^[A-Z0-9]+USDT$/.test(p.symbol) || symbols.has(p.symbol) || p.asOf !== value.asOf || p.validUntil !== value.validUntil || p.windowStart !== value.asOf - WINDOW)
175
+ return false;
176
+ symbols.add(p.symbol);
177
+ for (const k of [
178
+ "hours",
179
+ "returnHours",
180
+ "btcAlignedHours",
181
+ "ethAlignedHours"
182
+ ])
183
+ if (typeof p[k] !== "number" || !Number.isInteger(p[k]) || p[k] < 0 || p[k] > 720)
184
+ return false;
185
+ for (const k of ["dailyTurnoverUSDT", "dailyVolatilityPct"])
186
+ if (!nullableNumber(p[k]) || typeof p[k] === "number" && p[k] < 0)
187
+ return false;
188
+ for (const k of ["correlationBTC", "correlationETH"])
189
+ if (!nullableNumber(p[k]) || typeof p[k] === "number" && Math.abs(p[k]) > 1)
190
+ return false;
191
+ if (!["quote", "estimated", "mixed", "unknown"].includes(
192
+ String(p.turnoverSource)
193
+ ))
194
+ return false;
195
+ for (const k of ["moneyGroup", "volatilityGroup"])
196
+ if (!["low", "middle", "high", "unknown"].includes(String(p[k])))
197
+ return false;
198
+ for (const k of ["btcCorrelationGroup", "ethCorrelationGroup"])
199
+ if (!["weak", "medium", "strong", "unknown"].includes(String(p[k])))
200
+ return false;
201
+ }
202
+ return symbols.size > 0;
203
+ };
204
+ // Annotate the CommonJS export names for ESM import in node:
205
+ 0 && (module.exports = {
206
+ TICKER_PROFILE_DEFINITION,
207
+ buildTickerProfileSnapshot,
208
+ isTickerProfileSnapshot,
209
+ tickerProfileMonth,
210
+ tickerProfileNextMonth
211
+ });
@@ -0,0 +1,182 @@
1
+ // src/tickerProfiles.ts
2
+ var HOUR = 36e5;
3
+ var WINDOW = 720 * HOUR;
4
+ var MIN_HOURS = 576;
5
+ var TICKER_PROFILE_DEFINITION = "hourly-30d-monthly-terciles/v1";
6
+ var tickerProfileMonth = (timestamp) => {
7
+ if (!Number.isSafeInteger(timestamp) || timestamp < 0)
8
+ throw new Error("Invalid profile timestamp");
9
+ const date = new Date(timestamp);
10
+ return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1);
11
+ };
12
+ var tickerProfileNextMonth = (timestamp) => {
13
+ const date = new Date(timestamp);
14
+ return Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1);
15
+ };
16
+ var prepare = (candles, asOf) => {
17
+ const eligible = candles.filter(
18
+ (c) => c.timestamp >= asOf - WINDOW - HOUR && c.timestamp + HOUR <= asOf
19
+ );
20
+ let previous = -1;
21
+ for (const candle of eligible) {
22
+ if (!Number.isSafeInteger(candle.timestamp) || candle.timestamp % HOUR !== 0 || candle.timestamp <= previous || ![candle.high, candle.low, candle.close].every(
23
+ (v) => Number.isFinite(v) && v > 0
24
+ ) || !Number.isFinite(candle.volume) || candle.volume < 0)
25
+ throw new Error("Invalid or unordered hourly profile candles");
26
+ previous = candle.timestamp;
27
+ }
28
+ const returns = /* @__PURE__ */ new Map();
29
+ for (let i = 1; i < eligible.length; i++) {
30
+ const a = eligible[i - 1], b = eligible[i];
31
+ if (b.timestamp - a.timestamp === HOUR && b.timestamp >= asOf - WINDOW)
32
+ returns.set(b.timestamp, Math.log(b.close / a.close));
33
+ }
34
+ return {
35
+ candles: eligible.filter((c) => c.timestamp >= asOf - WINDOW),
36
+ returns
37
+ };
38
+ };
39
+ var correlate = (target, reference) => {
40
+ const pairs = [...target].filter(([t]) => reference.has(t)).map(([t, r]) => [r, reference.get(t) ?? 0]);
41
+ if (pairs.length < MIN_HOURS) return { value: null, count: pairs.length };
42
+ const mx = pairs.reduce((s, p) => s + p[0], 0) / pairs.length;
43
+ const my = pairs.reduce((s, p) => s + p[1], 0) / pairs.length;
44
+ let xx = 0, yy = 0, xy = 0;
45
+ for (const [x, y] of pairs) {
46
+ xx += (x - mx) ** 2;
47
+ yy += (y - my) ** 2;
48
+ xy += (x - mx) * (y - my);
49
+ }
50
+ return {
51
+ value: xx > 0 && yy > 0 ? Math.max(-1, Math.min(1, xy / Math.sqrt(xx * yy))) : null,
52
+ count: pairs.length
53
+ };
54
+ };
55
+ var correlationGroup = (v) => v === null ? "unknown" : v < 0.3 ? "weak" : v < 0.7 ? "medium" : "strong";
56
+ var cuts = (values) => {
57
+ const sorted = values.filter((v) => v !== null).sort((a, b) => a - b);
58
+ return [
59
+ sorted[Math.floor((sorted.length - 1) / 3)] ?? null,
60
+ sorted[Math.floor((sorted.length - 1) * 2 / 3)] ?? null
61
+ ];
62
+ };
63
+ var group = (v, thresholds) => v === null || thresholds[0] === null || thresholds[1] === null ? "unknown" : v <= thresholds[0] ? "low" : v <= thresholds[1] ? "middle" : "high";
64
+ var buildTickerProfileSnapshot = ({
65
+ asOf,
66
+ provider,
67
+ universeId,
68
+ membership,
69
+ candlesBySymbol
70
+ }) => {
71
+ if (tickerProfileMonth(asOf) !== asOf)
72
+ throw new Error("Profiles must start at a UTC month boundary");
73
+ const symbols = Object.keys(candlesBySymbol).sort();
74
+ if (!symbols.length || symbols.some((s) => !/^[A-Z0-9]+USDT$/.test(s)))
75
+ throw new Error("Profile universe requires explicit USDT symbols");
76
+ const btc = prepare(candlesBySymbol.BTCUSDT ?? [], asOf).returns;
77
+ const eth = prepare(candlesBySymbol.ETHUSDT ?? [], asOf).returns;
78
+ const validUntil = tickerProfileNextMonth(asOf);
79
+ const profiles = symbols.map((symbol) => {
80
+ const { candles, returns } = prepare(candlesBySymbol[symbol], asOf);
81
+ const quoteCount = candles.filter(
82
+ (c) => Number.isFinite(c.turnover) && Number(c.turnover) >= 0
83
+ ).length;
84
+ const ready = candles.length >= MIN_HOURS;
85
+ const dailyTurnoverUSDT = ready ? candles.reduce(
86
+ (s, c) => s + (Number.isFinite(c.turnover) && Number(c.turnover) >= 0 ? Number(c.turnover) : c.volume * (c.high + c.low + c.close) / 3),
87
+ 0
88
+ ) / 30 : null;
89
+ const rr = [...returns.values()], mean = rr.reduce((a, b) => a + b, 0) / (rr.length || 1);
90
+ const dailyVolatilityPct = rr.length >= MIN_HOURS ? Math.sqrt(
91
+ rr.reduce((s, v) => s + (v - mean) ** 2, 0) / (rr.length - 1)
92
+ ) * Math.sqrt(24) * 100 : null;
93
+ const cb = correlate(returns, btc), ce = correlate(returns, eth);
94
+ return {
95
+ symbol,
96
+ asOf,
97
+ validUntil,
98
+ windowStart: asOf - WINDOW,
99
+ hours: candles.length,
100
+ returnHours: rr.length,
101
+ btcAlignedHours: cb.count,
102
+ ethAlignedHours: ce.count,
103
+ turnoverSource: !ready ? "unknown" : quoteCount === candles.length ? "quote" : quoteCount === 0 ? "estimated" : "mixed",
104
+ dailyTurnoverUSDT,
105
+ dailyVolatilityPct,
106
+ correlationBTC: cb.value,
107
+ correlationETH: ce.value,
108
+ moneyGroup: "unknown",
109
+ volatilityGroup: "unknown",
110
+ btcCorrelationGroup: correlationGroup(cb.value),
111
+ ethCorrelationGroup: correlationGroup(ce.value)
112
+ };
113
+ });
114
+ const thresholds = {
115
+ money: cuts(profiles.map((p) => p.dailyTurnoverUSDT)),
116
+ volatility: cuts(profiles.map((p) => p.dailyVolatilityPct))
117
+ };
118
+ for (const p of profiles) {
119
+ p.moneyGroup = group(p.dailyTurnoverUSDT, thresholds.money);
120
+ p.volatilityGroup = group(p.dailyVolatilityPct, thresholds.volatility);
121
+ }
122
+ return {
123
+ schema: "tradejs-ticker-profiles/v1",
124
+ definition: TICKER_PROFILE_DEFINITION,
125
+ provider,
126
+ universeId,
127
+ membership,
128
+ asOf,
129
+ validUntil,
130
+ thresholds,
131
+ profiles
132
+ };
133
+ };
134
+ var record = (v) => Boolean(v) && typeof v === "object" && !Array.isArray(v);
135
+ var nullableNumber = (v) => v === null || typeof v === "number" && Number.isFinite(v);
136
+ var isTickerProfileSnapshot = (value) => {
137
+ if (!record(value) || value.schema !== "tradejs-ticker-profiles/v1" || value.definition !== TICKER_PROFILE_DEFINITION || typeof value.provider !== "string" || typeof value.universeId !== "string" || !["frozen-list", "point-in-time"].includes(String(value.membership)) || typeof value.asOf !== "number" || !Number.isSafeInteger(value.asOf) || value.asOf < 0 || tickerProfileMonth(value.asOf) !== value.asOf || value.validUntil !== tickerProfileNextMonth(value.asOf) || !record(value.thresholds) || !Array.isArray(value.profiles))
138
+ return false;
139
+ for (const key of ["money", "volatility"]) {
140
+ const cut = value.thresholds[key];
141
+ if (!Array.isArray(cut) || cut.length !== 2 || !cut.every(nullableNumber))
142
+ return false;
143
+ }
144
+ const symbols = /* @__PURE__ */ new Set();
145
+ for (const p of value.profiles) {
146
+ if (!record(p) || typeof p.symbol !== "string" || !/^[A-Z0-9]+USDT$/.test(p.symbol) || symbols.has(p.symbol) || p.asOf !== value.asOf || p.validUntil !== value.validUntil || p.windowStart !== value.asOf - WINDOW)
147
+ return false;
148
+ symbols.add(p.symbol);
149
+ for (const k of [
150
+ "hours",
151
+ "returnHours",
152
+ "btcAlignedHours",
153
+ "ethAlignedHours"
154
+ ])
155
+ if (typeof p[k] !== "number" || !Number.isInteger(p[k]) || p[k] < 0 || p[k] > 720)
156
+ return false;
157
+ for (const k of ["dailyTurnoverUSDT", "dailyVolatilityPct"])
158
+ if (!nullableNumber(p[k]) || typeof p[k] === "number" && p[k] < 0)
159
+ return false;
160
+ for (const k of ["correlationBTC", "correlationETH"])
161
+ if (!nullableNumber(p[k]) || typeof p[k] === "number" && Math.abs(p[k]) > 1)
162
+ return false;
163
+ if (!["quote", "estimated", "mixed", "unknown"].includes(
164
+ String(p.turnoverSource)
165
+ ))
166
+ return false;
167
+ for (const k of ["moneyGroup", "volatilityGroup"])
168
+ if (!["low", "middle", "high", "unknown"].includes(String(p[k])))
169
+ return false;
170
+ for (const k of ["btcCorrelationGroup", "ethCorrelationGroup"])
171
+ if (!["weak", "medium", "strong", "unknown"].includes(String(p[k])))
172
+ return false;
173
+ }
174
+ return symbols.size > 0;
175
+ };
176
+ export {
177
+ TICKER_PROFILE_DEFINITION,
178
+ buildTickerProfileSnapshot,
179
+ isTickerProfileSnapshot,
180
+ tickerProfileMonth,
181
+ tickerProfileNextMonth
182
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/core",
3
- "version": "3.1.29-beta.290",
3
+ "version": "3.1.29-beta.291",
4
4
  "description": "MIT-licensed browser-safe API for TradeJS config, strategy authoring, figures, and shared helpers.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -135,10 +135,25 @@
135
135
  "types": "./dist/jev.d.ts",
136
136
  "import": "./dist/jev.mjs",
137
137
  "require": "./dist/jev.js"
138
+ },
139
+ "./tickerProfiles": {
140
+ "types": "./dist/tickerProfiles.d.ts",
141
+ "import": "./dist/tickerProfiles.mjs",
142
+ "require": "./dist/tickerProfiles.js"
143
+ },
144
+ "./aiFeatures": {
145
+ "types": "./dist/aiFeatures.d.ts",
146
+ "import": "./dist/aiFeatures.mjs",
147
+ "require": "./dist/aiFeatures.js"
148
+ },
149
+ "./portfolio": {
150
+ "types": "./dist/portfolio.d.ts",
151
+ "import": "./dist/portfolio.mjs",
152
+ "require": "./dist/portfolio.js"
138
153
  }
139
154
  },
140
155
  "dependencies": {
141
- "@tradejs/types": "^3.1.29-beta.290",
156
+ "@tradejs/types": "^3.1.29-beta.291",
142
157
  "date-fns": "^3.6.0",
143
158
  "fast-technical-indicators": "^1.1.4",
144
159
  "klinecharts": "10.0.0-alpha9",
@@ -4,14 +4,14 @@ import {
4
4
  import {
5
5
  toMs
6
6
  } from "./chunk-S4KHOAXM.mjs";
7
- import {
8
- round
9
- } from "./chunk-LI6FPPKB.mjs";
10
7
  import {
11
8
  CORRELATION_WINDOW,
12
9
  ML_BASE_CANDLES_WINDOW,
13
10
  TRENDLINE_DEFAULTS
14
11
  } from "./chunk-MKCQSB4H.mjs";
12
+ import {
13
+ round
14
+ } from "./chunk-LI6FPPKB.mjs";
15
15
 
16
16
  // src/utils/correlation.ts
17
17
  var alignSortedCandlesByTimestamp = (coinCandles, btcCandles) => {