@tradejs/core 3.1.29-beta.289 → 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.
- package/dist/aiFeatures.d.mts +14 -0
- package/dist/aiFeatures.d.ts +14 -0
- package/dist/aiFeatures.js +179 -0
- package/dist/aiFeatures.mjs +150 -0
- package/dist/backtest.mjs +3 -3
- package/dist/indicators.mjs +2 -2
- package/dist/portfolio.d.mts +23 -0
- package/dist/portfolio.d.ts +23 -0
- package/dist/portfolio.js +314 -0
- package/dist/portfolio.mjs +280 -0
- package/dist/strategies.mjs +2 -2
- package/dist/tickerProfiles.d.mts +17 -0
- package/dist/tickerProfiles.d.ts +17 -0
- package/dist/tickerProfiles.js +211 -0
- package/dist/tickerProfiles.mjs +182 -0
- package/package.json +17 -2
- package/dist/{chunk-OZVRWW2K.mjs → chunk-6XY6Z2DB.mjs} +3 -3
|
@@ -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.
|
|
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.
|
|
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) => {
|