@tradejs/core 1.0.0
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/README.md +60 -0
- package/dist/api.d.mts +7 -0
- package/dist/api.d.ts +7 -0
- package/dist/api.js +64 -0
- package/dist/api.mjs +39 -0
- package/dist/async.d.mts +4 -0
- package/dist/async.d.ts +4 -0
- package/dist/async.js +48 -0
- package/dist/async.mjs +20 -0
- package/dist/backtest.d.mts +45 -0
- package/dist/backtest.d.ts +45 -0
- package/dist/backtest.js +574 -0
- package/dist/backtest.mjs +355 -0
- package/dist/chunk-AYC2QVKI.mjs +35 -0
- package/dist/chunk-JG2QPVAV.mjs +190 -0
- package/dist/chunk-LIGD3WWX.mjs +1545 -0
- package/dist/chunk-M7QGVZ3J.mjs +61 -0
- package/dist/chunk-NQ7D3T4E.mjs +10 -0
- package/dist/chunk-PXLXXXLA.mjs +67 -0
- package/dist/config.d.mts +14 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +49 -0
- package/dist/config.mjs +21 -0
- package/dist/constants.d.mts +41 -0
- package/dist/constants.d.ts +41 -0
- package/dist/constants.js +238 -0
- package/dist/constants.mjs +50 -0
- package/dist/data.d.mts +9 -0
- package/dist/data.d.ts +9 -0
- package/dist/data.js +100 -0
- package/dist/data.mjs +12 -0
- package/dist/figures.d.mts +103 -0
- package/dist/figures.d.ts +103 -0
- package/dist/figures.js +274 -0
- package/dist/figures.mjs +239 -0
- package/dist/indicators-x3xKl3_W.d.mts +90 -0
- package/dist/indicators-x3xKl3_W.d.ts +90 -0
- package/dist/indicators.d.mts +124 -0
- package/dist/indicators.d.ts +124 -0
- package/dist/indicators.js +1631 -0
- package/dist/indicators.mjs +66 -0
- package/dist/json.d.mts +3 -0
- package/dist/json.d.ts +3 -0
- package/dist/json.js +34 -0
- package/dist/json.mjs +7 -0
- package/dist/math.d.mts +35 -0
- package/dist/math.d.ts +35 -0
- package/dist/math.js +98 -0
- package/dist/math.mjs +38 -0
- package/dist/pine.d.mts +29 -0
- package/dist/pine.d.ts +29 -0
- package/dist/pine.js +59 -0
- package/dist/pine.mjs +29 -0
- package/dist/strategies.d.mts +104 -0
- package/dist/strategies.d.ts +104 -0
- package/dist/strategies.js +1080 -0
- package/dist/strategies.mjs +390 -0
- package/dist/tickers.d.mts +7 -0
- package/dist/tickers.d.ts +7 -0
- package/dist/tickers.js +166 -0
- package/dist/tickers.mjs +125 -0
- package/dist/time-DEyFa2vI.d.mts +11 -0
- package/dist/time-DEyFa2vI.d.ts +11 -0
- package/dist/time.d.mts +2 -0
- package/dist/time.d.ts +2 -0
- package/dist/time.js +58 -0
- package/dist/time.mjs +15 -0
- package/package.json +99 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import {
|
|
2
|
+
uuid
|
|
3
|
+
} from "./chunk-NQ7D3T4E.mjs";
|
|
4
|
+
import {
|
|
5
|
+
createIndicators
|
|
6
|
+
} from "./chunk-LIGD3WWX.mjs";
|
|
7
|
+
import "./chunk-AYC2QVKI.mjs";
|
|
8
|
+
import {
|
|
9
|
+
getTimestamp
|
|
10
|
+
} from "./chunk-PXLXXXLA.mjs";
|
|
11
|
+
import {
|
|
12
|
+
FEE_PERCENT
|
|
13
|
+
} from "./chunk-JG2QPVAV.mjs";
|
|
14
|
+
import "./chunk-M7QGVZ3J.mjs";
|
|
15
|
+
|
|
16
|
+
// src/utils/strategyHelpers/indicators.ts
|
|
17
|
+
var buildDefaultIndicatorPeriods = (config) => ({
|
|
18
|
+
maFast: config.MA_FAST,
|
|
19
|
+
maMedium: config.MA_MEDIUM,
|
|
20
|
+
maSlow: config.MA_SLOW,
|
|
21
|
+
obvSma: config.OBV_SMA,
|
|
22
|
+
atr: config.ATR,
|
|
23
|
+
atrPctShort: config.ATR_PCT_SHORT,
|
|
24
|
+
atrPctLong: config.ATR_PCT_LONG,
|
|
25
|
+
bb: config.BB,
|
|
26
|
+
bbStd: config.BB_STD,
|
|
27
|
+
macdFast: config.MACD_FAST,
|
|
28
|
+
macdSlow: config.MACD_SLOW,
|
|
29
|
+
macdSignal: config.MACD_SIGNAL,
|
|
30
|
+
levelLookback: config.LEVEL_LOOKBACK,
|
|
31
|
+
levelDelay: config.LEVEL_DELAY
|
|
32
|
+
});
|
|
33
|
+
var createStrategyIndicatorsState = ({
|
|
34
|
+
env,
|
|
35
|
+
data,
|
|
36
|
+
btcData,
|
|
37
|
+
btcBinanceData,
|
|
38
|
+
btcCoinbaseData,
|
|
39
|
+
periods
|
|
40
|
+
}) => {
|
|
41
|
+
let controller = env === "BACKTEST" ? createIndicators(data, btcData, {
|
|
42
|
+
periods,
|
|
43
|
+
btcBinanceData,
|
|
44
|
+
btcCoinbaseData
|
|
45
|
+
}) : null;
|
|
46
|
+
let currentBarPair;
|
|
47
|
+
const withSnapshot = (value) => Object.assign(value, {
|
|
48
|
+
snapshot: () => value.result()
|
|
49
|
+
});
|
|
50
|
+
const applyBar = (candle, btcCandle) => {
|
|
51
|
+
if (!controller) return;
|
|
52
|
+
controller.next(candle, btcCandle);
|
|
53
|
+
};
|
|
54
|
+
const ensureControllerInitialized = () => {
|
|
55
|
+
if (controller) return withSnapshot(controller);
|
|
56
|
+
controller = createIndicators(data.slice(0, -1), btcData.slice(0, -1), {
|
|
57
|
+
periods,
|
|
58
|
+
btcBinanceData,
|
|
59
|
+
btcCoinbaseData
|
|
60
|
+
});
|
|
61
|
+
const lastCandle = data[data.length - 1];
|
|
62
|
+
const lastBtcCandle = btcData[btcData.length - 1];
|
|
63
|
+
if (lastCandle && lastBtcCandle) {
|
|
64
|
+
controller.next(lastCandle, lastBtcCandle);
|
|
65
|
+
}
|
|
66
|
+
return withSnapshot(controller);
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
isInitialized: () => controller != null,
|
|
70
|
+
setCurrentBar: (candle, btcCandle) => {
|
|
71
|
+
currentBarPair = { candle, btcCandle };
|
|
72
|
+
},
|
|
73
|
+
onBar: (candle, btcCandle) => {
|
|
74
|
+
const resolvedCandle = candle ?? currentBarPair?.candle;
|
|
75
|
+
const resolvedBtcCandle = btcCandle ?? currentBarPair?.btcCandle;
|
|
76
|
+
if (!resolvedCandle || !resolvedBtcCandle) return;
|
|
77
|
+
applyBar(resolvedCandle, resolvedBtcCandle);
|
|
78
|
+
},
|
|
79
|
+
next: (candle, btcCandle) => {
|
|
80
|
+
if (!controller) return void 0;
|
|
81
|
+
return controller.next(candle, btcCandle);
|
|
82
|
+
},
|
|
83
|
+
// Lazy bootstrap for live mode: initialize on history before current bar and then apply current bar once.
|
|
84
|
+
ensureInitializedWithCurrentBar: ensureControllerInitialized,
|
|
85
|
+
snapshot: () => ensureControllerInitialized().snapshot(),
|
|
86
|
+
latestNumber: (key) => {
|
|
87
|
+
const snapshot = ensureControllerInitialized().snapshot();
|
|
88
|
+
const value = snapshot?.[key];
|
|
89
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
90
|
+
return void 0;
|
|
91
|
+
}
|
|
92
|
+
const last = value[value.length - 1];
|
|
93
|
+
return typeof last === "number" ? last : void 0;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// src/utils/strategyHelpers/market.ts
|
|
99
|
+
var getStrategyMarketSnapshot = async ({
|
|
100
|
+
env,
|
|
101
|
+
connector,
|
|
102
|
+
symbol,
|
|
103
|
+
interval,
|
|
104
|
+
cachedData,
|
|
105
|
+
preloadStart,
|
|
106
|
+
backtestPriceMode = "mid"
|
|
107
|
+
}) => {
|
|
108
|
+
const fullData = env === "BACKTEST" ? cachedData : await connector.kline({
|
|
109
|
+
symbol,
|
|
110
|
+
start: preloadStart,
|
|
111
|
+
end: getTimestamp(),
|
|
112
|
+
cacheOnly: false,
|
|
113
|
+
interval
|
|
114
|
+
});
|
|
115
|
+
const lastCandle = fullData[fullData.length - 1];
|
|
116
|
+
let currentPrice = lastCandle.close;
|
|
117
|
+
if (env === "BACKTEST") {
|
|
118
|
+
if (backtestPriceMode === "mid") {
|
|
119
|
+
currentPrice = (lastCandle.open + lastCandle.close) / 2;
|
|
120
|
+
} else if (backtestPriceMode === "open") {
|
|
121
|
+
currentPrice = lastCandle.open;
|
|
122
|
+
} else if (backtestPriceMode === "rand") {
|
|
123
|
+
const min = Math.min(lastCandle.low, lastCandle.high);
|
|
124
|
+
const max = Math.max(lastCandle.low, lastCandle.high);
|
|
125
|
+
currentPrice = min + Math.random() * (max - min);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
fullData,
|
|
130
|
+
lastCandle,
|
|
131
|
+
timestamp: lastCandle.timestamp,
|
|
132
|
+
currentPrice
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
var calculateRiskRatio = ({
|
|
136
|
+
direction,
|
|
137
|
+
currentPrice,
|
|
138
|
+
takeProfitPrice,
|
|
139
|
+
stopLossPrice
|
|
140
|
+
}) => {
|
|
141
|
+
const isLong = direction === "LONG";
|
|
142
|
+
const reward = isLong ? takeProfitPrice - currentPrice : currentPrice - takeProfitPrice;
|
|
143
|
+
const risk = isLong ? currentPrice - stopLossPrice : stopLossPrice - currentPrice;
|
|
144
|
+
return risk > 0 ? reward / risk : 0;
|
|
145
|
+
};
|
|
146
|
+
var getDirectionalTpSlPrices = ({
|
|
147
|
+
price,
|
|
148
|
+
direction,
|
|
149
|
+
takeProfitDelta,
|
|
150
|
+
stopLossDelta,
|
|
151
|
+
unit = "percent",
|
|
152
|
+
maxLossValue,
|
|
153
|
+
feePercent = FEE_PERCENT
|
|
154
|
+
}) => {
|
|
155
|
+
const deltaFactor = unit === "percent" ? 100 : 1;
|
|
156
|
+
const tp = takeProfitDelta / deltaFactor;
|
|
157
|
+
const sl = stopLossDelta / deltaFactor;
|
|
158
|
+
const isLong = direction === "LONG";
|
|
159
|
+
const stopLossPrice = isLong ? price * (1 - sl) : price * (1 + sl);
|
|
160
|
+
const takeProfitPrice = isLong ? price * (1 + tp) : price * (1 - tp);
|
|
161
|
+
const riskRatio = calculateRiskRatio({
|
|
162
|
+
direction,
|
|
163
|
+
currentPrice: price,
|
|
164
|
+
takeProfitPrice,
|
|
165
|
+
stopLossPrice
|
|
166
|
+
});
|
|
167
|
+
const slPercent = unit === "percent" ? stopLossDelta : stopLossDelta * 100;
|
|
168
|
+
const qty = typeof maxLossValue === "number" && Number.isFinite(maxLossValue) && maxLossValue > 0 ? maxLossValue / (price * (slPercent + feePercent) / 100) : void 0;
|
|
169
|
+
return {
|
|
170
|
+
stopLossPrice,
|
|
171
|
+
takeProfitPrice,
|
|
172
|
+
riskRatio,
|
|
173
|
+
qty
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/utils/strategyHelpers/state.ts
|
|
178
|
+
var createLastTradeController = ({
|
|
179
|
+
env,
|
|
180
|
+
enabled = env ? env === "BACKTEST" : true,
|
|
181
|
+
cooldownMs = 864e5
|
|
182
|
+
}) => {
|
|
183
|
+
let lastTradeTimestamp = null;
|
|
184
|
+
return {
|
|
185
|
+
isInCooldown: (timestamp) => Boolean(
|
|
186
|
+
enabled && lastTradeTimestamp != null && timestamp <= lastTradeTimestamp + cooldownMs
|
|
187
|
+
),
|
|
188
|
+
markTrade: (timestamp) => {
|
|
189
|
+
if (!enabled) return;
|
|
190
|
+
lastTradeTimestamp = timestamp;
|
|
191
|
+
},
|
|
192
|
+
getLastTradeTimestamp: () => lastTradeTimestamp
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// src/utils/strategyHelpers/signalBuilders.ts
|
|
197
|
+
var mapAiRuntimeFromConfig = (config, overrides = {}) => ({
|
|
198
|
+
enabled: Boolean(config.AI_ENABLED ?? true),
|
|
199
|
+
minQuality: Number(config.MIN_AI_QUALITY ?? 4),
|
|
200
|
+
...overrides
|
|
201
|
+
});
|
|
202
|
+
var mapMlRuntimeFromConfig = (config, overrides = {}) => ({
|
|
203
|
+
enabled: Boolean(config.ML_ENABLED ?? true),
|
|
204
|
+
mlThreshold: Number(config.ML_THRESHOLD ?? 0),
|
|
205
|
+
...overrides
|
|
206
|
+
});
|
|
207
|
+
var buildStrategySignal = ({
|
|
208
|
+
signalId,
|
|
209
|
+
strategy,
|
|
210
|
+
symbol,
|
|
211
|
+
interval,
|
|
212
|
+
direction,
|
|
213
|
+
timestamp,
|
|
214
|
+
prices,
|
|
215
|
+
figures = {},
|
|
216
|
+
indicators = {},
|
|
217
|
+
additionalIndicators,
|
|
218
|
+
isConfigFromBacktest
|
|
219
|
+
}) => ({
|
|
220
|
+
signalId,
|
|
221
|
+
strategy,
|
|
222
|
+
symbol,
|
|
223
|
+
interval,
|
|
224
|
+
direction,
|
|
225
|
+
timestamp,
|
|
226
|
+
figures,
|
|
227
|
+
prices,
|
|
228
|
+
indicators,
|
|
229
|
+
additionalIndicators,
|
|
230
|
+
isConfigFromBacktest
|
|
231
|
+
});
|
|
232
|
+
var buildEntrySignalDecision = ({
|
|
233
|
+
code,
|
|
234
|
+
entryContext,
|
|
235
|
+
figures,
|
|
236
|
+
indicators,
|
|
237
|
+
additionalIndicators,
|
|
238
|
+
signalId,
|
|
239
|
+
orderPlan,
|
|
240
|
+
runtime
|
|
241
|
+
}) => ({
|
|
242
|
+
kind: "entry",
|
|
243
|
+
code,
|
|
244
|
+
entryContext,
|
|
245
|
+
signal: buildStrategySignal({
|
|
246
|
+
signalId: signalId ?? uuid(),
|
|
247
|
+
strategy: entryContext.strategy,
|
|
248
|
+
symbol: entryContext.symbol,
|
|
249
|
+
interval: entryContext.interval,
|
|
250
|
+
direction: entryContext.direction,
|
|
251
|
+
timestamp: entryContext.timestamp,
|
|
252
|
+
prices: entryContext.prices,
|
|
253
|
+
figures,
|
|
254
|
+
indicators,
|
|
255
|
+
additionalIndicators,
|
|
256
|
+
isConfigFromBacktest: entryContext.isConfigFromBacktest
|
|
257
|
+
}),
|
|
258
|
+
orderPlan,
|
|
259
|
+
runtime
|
|
260
|
+
});
|
|
261
|
+
var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
262
|
+
var toDefaultEntryCode = (strategy, direction) => `${strategy.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").toUpperCase()}_${direction}_ENTRY`;
|
|
263
|
+
var resolveTakeProfitPrice = ({
|
|
264
|
+
direction,
|
|
265
|
+
takeProfits
|
|
266
|
+
}) => {
|
|
267
|
+
if (!Array.isArray(takeProfits) || takeProfits.length === 0) {
|
|
268
|
+
throw new Error("strategyApi.entry requires at least one takeProfit");
|
|
269
|
+
}
|
|
270
|
+
const prices = takeProfits.map((tp) => tp?.price).filter((price) => isFiniteNumber(price));
|
|
271
|
+
if (prices.length === 0) {
|
|
272
|
+
throw new Error("strategyApi.entry requires finite takeProfit prices");
|
|
273
|
+
}
|
|
274
|
+
return direction === "LONG" ? Math.max(...prices) : Math.min(...prices);
|
|
275
|
+
};
|
|
276
|
+
var createStrategyAPI = ({
|
|
277
|
+
strategy,
|
|
278
|
+
symbol,
|
|
279
|
+
interval,
|
|
280
|
+
env,
|
|
281
|
+
connector,
|
|
282
|
+
cachedData,
|
|
283
|
+
indicatorsState,
|
|
284
|
+
preloadStart,
|
|
285
|
+
backtestPriceMode,
|
|
286
|
+
isConfigFromBacktest
|
|
287
|
+
}) => {
|
|
288
|
+
const getCurrentPosition = () => connector.getPosition(symbol);
|
|
289
|
+
const isPositionExists = async () => {
|
|
290
|
+
const position = await getCurrentPosition();
|
|
291
|
+
return Boolean(
|
|
292
|
+
position && typeof position.qty === "number" && position.qty > 0
|
|
293
|
+
);
|
|
294
|
+
};
|
|
295
|
+
const getMarketData = async (params = {}) => {
|
|
296
|
+
const resolvedPreloadStart = params.preloadStart ?? preloadStart;
|
|
297
|
+
if (typeof resolvedPreloadStart !== "number") {
|
|
298
|
+
throw new Error("strategyApi.getMarketData requires preloadStart");
|
|
299
|
+
}
|
|
300
|
+
const snapshot = await getStrategyMarketSnapshot({
|
|
301
|
+
env,
|
|
302
|
+
connector,
|
|
303
|
+
symbol,
|
|
304
|
+
interval,
|
|
305
|
+
cachedData,
|
|
306
|
+
preloadStart: resolvedPreloadStart,
|
|
307
|
+
backtestPriceMode: params.backtestPriceMode ?? backtestPriceMode
|
|
308
|
+
});
|
|
309
|
+
return snapshot;
|
|
310
|
+
};
|
|
311
|
+
return {
|
|
312
|
+
skip: (code) => ({ kind: "skip", code }),
|
|
313
|
+
entry: async ({
|
|
314
|
+
code,
|
|
315
|
+
direction,
|
|
316
|
+
figures,
|
|
317
|
+
indicators,
|
|
318
|
+
additionalIndicators,
|
|
319
|
+
signalId,
|
|
320
|
+
orderPlan,
|
|
321
|
+
runtime
|
|
322
|
+
}) => {
|
|
323
|
+
const marketData = await getMarketData();
|
|
324
|
+
const currentPrice = marketData.currentPrice;
|
|
325
|
+
const timestamp = marketData.timestamp;
|
|
326
|
+
const stopLossPrice = orderPlan.stopLossPrice;
|
|
327
|
+
const takeProfitPrice = resolveTakeProfitPrice({
|
|
328
|
+
direction,
|
|
329
|
+
takeProfits: orderPlan.takeProfits
|
|
330
|
+
});
|
|
331
|
+
if (!isFiniteNumber(stopLossPrice)) {
|
|
332
|
+
throw new Error(
|
|
333
|
+
"strategyApi.entry requires finite orderPlan.stopLossPrice"
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
const resolvedCode = code ?? toDefaultEntryCode(String(strategy), direction);
|
|
337
|
+
const riskRatio = calculateRiskRatio({
|
|
338
|
+
direction,
|
|
339
|
+
currentPrice,
|
|
340
|
+
takeProfitPrice,
|
|
341
|
+
stopLossPrice
|
|
342
|
+
});
|
|
343
|
+
return buildEntrySignalDecision({
|
|
344
|
+
code: resolvedCode,
|
|
345
|
+
entryContext: {
|
|
346
|
+
strategy,
|
|
347
|
+
symbol,
|
|
348
|
+
interval,
|
|
349
|
+
direction,
|
|
350
|
+
timestamp,
|
|
351
|
+
prices: {
|
|
352
|
+
currentPrice,
|
|
353
|
+
takeProfitPrice,
|
|
354
|
+
stopLossPrice,
|
|
355
|
+
riskRatio
|
|
356
|
+
},
|
|
357
|
+
isConfigFromBacktest
|
|
358
|
+
},
|
|
359
|
+
figures,
|
|
360
|
+
indicators,
|
|
361
|
+
additionalIndicators,
|
|
362
|
+
signalId,
|
|
363
|
+
orderPlan,
|
|
364
|
+
runtime
|
|
365
|
+
});
|
|
366
|
+
},
|
|
367
|
+
getMarketData,
|
|
368
|
+
nextIndicators: (candle, btcCandle) => indicatorsState?.next(candle, btcCandle),
|
|
369
|
+
getCurrentPosition,
|
|
370
|
+
isCurrentPositionExists: isPositionExists,
|
|
371
|
+
getDirectionalTpSlPrices: (params) => getDirectionalTpSlPrices(params),
|
|
372
|
+
createLastTradeController: (params) => createLastTradeController({
|
|
373
|
+
env,
|
|
374
|
+
...params
|
|
375
|
+
})
|
|
376
|
+
};
|
|
377
|
+
};
|
|
378
|
+
export {
|
|
379
|
+
buildDefaultIndicatorPeriods,
|
|
380
|
+
buildEntrySignalDecision,
|
|
381
|
+
buildStrategySignal,
|
|
382
|
+
calculateRiskRatio,
|
|
383
|
+
createLastTradeController,
|
|
384
|
+
createStrategyAPI,
|
|
385
|
+
createStrategyIndicatorsState,
|
|
386
|
+
getDirectionalTpSlPrices,
|
|
387
|
+
getStrategyMarketSnapshot,
|
|
388
|
+
mapAiRuntimeFromConfig,
|
|
389
|
+
mapMlRuntimeFromConfig
|
|
390
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Ticker, Item } from '@tradejs/types';
|
|
2
|
+
|
|
3
|
+
declare const getVolatilityTickers: (data: Ticker[]) => Item[];
|
|
4
|
+
declare const getTopTickers: (data: Ticker[], topN?: number) => Item<Ticker>[];
|
|
5
|
+
declare const normalizeTickerData: (raw: Record<string, string>) => Ticker;
|
|
6
|
+
|
|
7
|
+
export { getTopTickers, getVolatilityTickers, normalizeTickerData };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Ticker, Item } from '@tradejs/types';
|
|
2
|
+
|
|
3
|
+
declare const getVolatilityTickers: (data: Ticker[]) => Item[];
|
|
4
|
+
declare const getTopTickers: (data: Ticker[], topN?: number) => Item<Ticker>[];
|
|
5
|
+
declare const normalizeTickerData: (raw: Record<string, string>) => Ticker;
|
|
6
|
+
|
|
7
|
+
export { getTopTickers, getVolatilityTickers, normalizeTickerData };
|
package/dist/tickers.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
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/tickers.ts
|
|
31
|
+
var tickers_exports = {};
|
|
32
|
+
__export(tickers_exports, {
|
|
33
|
+
getTopTickers: () => getTopTickers,
|
|
34
|
+
getVolatilityTickers: () => getVolatilityTickers,
|
|
35
|
+
normalizeTickerData: () => normalizeTickerData
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(tickers_exports);
|
|
38
|
+
|
|
39
|
+
// src/utils/tickers.ts
|
|
40
|
+
var import_lodash2 = __toESM(require("lodash"));
|
|
41
|
+
|
|
42
|
+
// src/utils/math.ts
|
|
43
|
+
var import_lodash = __toESM(require("lodash"));
|
|
44
|
+
var round = (value, precision = 2) => precision > 0 ? Math.round(value * 10 ** precision) / 10 ** precision : Math.round(value);
|
|
45
|
+
|
|
46
|
+
// src/utils/tickers.ts
|
|
47
|
+
var EXCLUDE_TICKERS = [
|
|
48
|
+
"USDEUSDT",
|
|
49
|
+
"USDCUSDT",
|
|
50
|
+
"USDTUSDT",
|
|
51
|
+
"RLUSDUSDT",
|
|
52
|
+
"PAXGUSDT",
|
|
53
|
+
"XAUTUSDT"
|
|
54
|
+
];
|
|
55
|
+
var getVolatilityTickers = (data) => {
|
|
56
|
+
const result = [];
|
|
57
|
+
const selected = /* @__PURE__ */ new Set();
|
|
58
|
+
const getBaseSymbol = (symbol) => symbol.replace(/(USDT)$/i, "");
|
|
59
|
+
const byVol24h = [...data].map((coin) => ({
|
|
60
|
+
...coin,
|
|
61
|
+
volatility24h: Math.abs(coin.price24hPcnt)
|
|
62
|
+
})).sort((a, b) => b.volatility24h - a.volatility24h);
|
|
63
|
+
const byVol1h = [...data].map((coin) => {
|
|
64
|
+
const prev1h = coin.prevPrice1h;
|
|
65
|
+
const last = coin.lastPrice;
|
|
66
|
+
const volatility1h = prev1h ? Math.abs((last - prev1h) / prev1h) : 0;
|
|
67
|
+
return {
|
|
68
|
+
...coin,
|
|
69
|
+
volatility1h
|
|
70
|
+
};
|
|
71
|
+
}).sort((a, b) => b.volatility1h - a.volatility1h);
|
|
72
|
+
const byVolume = [...data].map((coin) => ({
|
|
73
|
+
...coin,
|
|
74
|
+
volume24hNum: coin.volume24h
|
|
75
|
+
})).sort((a, b) => b.volume24hNum - a.volume24hNum);
|
|
76
|
+
const addTop = (list, category, limit) => {
|
|
77
|
+
for (const coin of list) {
|
|
78
|
+
if (!selected.has(coin.symbol)) {
|
|
79
|
+
selected.add(coin.symbol);
|
|
80
|
+
result.push({
|
|
81
|
+
label: `${getBaseSymbol(coin.symbol)}`,
|
|
82
|
+
value: coin.symbol,
|
|
83
|
+
category
|
|
84
|
+
});
|
|
85
|
+
if (result.length >= limit) break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
addTop(byVol24h, "volatility24h", 10);
|
|
90
|
+
addTop(byVol1h, "volatility1h", 20);
|
|
91
|
+
addTop(byVolume, "volume", 30);
|
|
92
|
+
return result.slice(0, 30).sort(
|
|
93
|
+
(a, b) => a.category === b.category ? a.value.localeCompare(b.value) : a.category.localeCompare(b.category)
|
|
94
|
+
).map(({ value, label, category }) => ({
|
|
95
|
+
label,
|
|
96
|
+
value,
|
|
97
|
+
description: category
|
|
98
|
+
}));
|
|
99
|
+
};
|
|
100
|
+
var getTopTickers = (data, topN) => {
|
|
101
|
+
const scores = data.filter(({ symbol }) => {
|
|
102
|
+
if (symbol.includes("BTC") && symbol !== "BTCUSDT") {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (symbol.startsWith("USD") || !symbol.endsWith("USDT")) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
if (EXCLUDE_TICKERS.includes(symbol)) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
112
|
+
}).map((coin) => {
|
|
113
|
+
const volumeMln = coin.volume24h / 1e6;
|
|
114
|
+
return {
|
|
115
|
+
...coin,
|
|
116
|
+
symbol: coin.symbol,
|
|
117
|
+
volume24h: volumeMln
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
const sortedByVlolume = import_lodash2.default.sortBy(scores, (item) => -item.volume24h);
|
|
121
|
+
const items = sortedByVlolume.map(({ symbol, volume24h, ...coin }, i) => ({
|
|
122
|
+
label: symbol.replace(/(USDT)$/i, ""),
|
|
123
|
+
value: symbol,
|
|
124
|
+
description: `volume: ${round(volume24h, 2)}m (#${i + 1})`,
|
|
125
|
+
data: {
|
|
126
|
+
...coin,
|
|
127
|
+
symbol,
|
|
128
|
+
volume24h
|
|
129
|
+
}
|
|
130
|
+
}));
|
|
131
|
+
return import_lodash2.default.sortBy(topN ? items.slice(0, topN) : items, "label");
|
|
132
|
+
};
|
|
133
|
+
var normalizeTickerData = (raw) => ({
|
|
134
|
+
symbol: raw.symbol,
|
|
135
|
+
lastPrice: parseFloat(raw.lastPrice),
|
|
136
|
+
indexPrice: parseFloat(raw.indexPrice),
|
|
137
|
+
markPrice: parseFloat(raw.markPrice),
|
|
138
|
+
prevPrice24h: parseFloat(raw.prevPrice24h),
|
|
139
|
+
price24hPcnt: parseFloat(raw.price24hPcnt),
|
|
140
|
+
highPrice24h: parseFloat(raw.highPrice24h),
|
|
141
|
+
lowPrice24h: parseFloat(raw.lowPrice24h),
|
|
142
|
+
prevPrice1h: parseFloat(raw.prevPrice1h),
|
|
143
|
+
openInterest: parseFloat(raw.openInterest),
|
|
144
|
+
openInterestValue: parseFloat(raw.openInterestValue),
|
|
145
|
+
turnover24h: parseFloat(raw.turnover24h),
|
|
146
|
+
volume24h: parseFloat(raw.volume24h),
|
|
147
|
+
fundingRate: parseFloat(raw.fundingRate),
|
|
148
|
+
nextFundingTime: parseInt(raw.nextFundingTime),
|
|
149
|
+
predictedDeliveryPrice: raw.predictedDeliveryPrice,
|
|
150
|
+
basisRate: raw.basisRate,
|
|
151
|
+
deliveryFeeRate: raw.deliveryFeeRate,
|
|
152
|
+
deliveryTime: parseInt(raw.deliveryTime),
|
|
153
|
+
ask1Size: parseFloat(raw.ask1Size),
|
|
154
|
+
bid1Price: parseFloat(raw.bid1Price),
|
|
155
|
+
ask1Price: parseFloat(raw.ask1Price),
|
|
156
|
+
bid1Size: parseFloat(raw.bid1Size),
|
|
157
|
+
basis: raw.basis,
|
|
158
|
+
preOpenPrice: raw.preOpenPrice,
|
|
159
|
+
preQty: raw.preQty
|
|
160
|
+
});
|
|
161
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
162
|
+
0 && (module.exports = {
|
|
163
|
+
getTopTickers,
|
|
164
|
+
getVolatilityTickers,
|
|
165
|
+
normalizeTickerData
|
|
166
|
+
});
|
package/dist/tickers.mjs
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
round
|
|
3
|
+
} from "./chunk-AYC2QVKI.mjs";
|
|
4
|
+
|
|
5
|
+
// src/utils/tickers.ts
|
|
6
|
+
import _ from "lodash";
|
|
7
|
+
var EXCLUDE_TICKERS = [
|
|
8
|
+
"USDEUSDT",
|
|
9
|
+
"USDCUSDT",
|
|
10
|
+
"USDTUSDT",
|
|
11
|
+
"RLUSDUSDT",
|
|
12
|
+
"PAXGUSDT",
|
|
13
|
+
"XAUTUSDT"
|
|
14
|
+
];
|
|
15
|
+
var getVolatilityTickers = (data) => {
|
|
16
|
+
const result = [];
|
|
17
|
+
const selected = /* @__PURE__ */ new Set();
|
|
18
|
+
const getBaseSymbol = (symbol) => symbol.replace(/(USDT)$/i, "");
|
|
19
|
+
const byVol24h = [...data].map((coin) => ({
|
|
20
|
+
...coin,
|
|
21
|
+
volatility24h: Math.abs(coin.price24hPcnt)
|
|
22
|
+
})).sort((a, b) => b.volatility24h - a.volatility24h);
|
|
23
|
+
const byVol1h = [...data].map((coin) => {
|
|
24
|
+
const prev1h = coin.prevPrice1h;
|
|
25
|
+
const last = coin.lastPrice;
|
|
26
|
+
const volatility1h = prev1h ? Math.abs((last - prev1h) / prev1h) : 0;
|
|
27
|
+
return {
|
|
28
|
+
...coin,
|
|
29
|
+
volatility1h
|
|
30
|
+
};
|
|
31
|
+
}).sort((a, b) => b.volatility1h - a.volatility1h);
|
|
32
|
+
const byVolume = [...data].map((coin) => ({
|
|
33
|
+
...coin,
|
|
34
|
+
volume24hNum: coin.volume24h
|
|
35
|
+
})).sort((a, b) => b.volume24hNum - a.volume24hNum);
|
|
36
|
+
const addTop = (list, category, limit) => {
|
|
37
|
+
for (const coin of list) {
|
|
38
|
+
if (!selected.has(coin.symbol)) {
|
|
39
|
+
selected.add(coin.symbol);
|
|
40
|
+
result.push({
|
|
41
|
+
label: `${getBaseSymbol(coin.symbol)}`,
|
|
42
|
+
value: coin.symbol,
|
|
43
|
+
category
|
|
44
|
+
});
|
|
45
|
+
if (result.length >= limit) break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
addTop(byVol24h, "volatility24h", 10);
|
|
50
|
+
addTop(byVol1h, "volatility1h", 20);
|
|
51
|
+
addTop(byVolume, "volume", 30);
|
|
52
|
+
return result.slice(0, 30).sort(
|
|
53
|
+
(a, b) => a.category === b.category ? a.value.localeCompare(b.value) : a.category.localeCompare(b.category)
|
|
54
|
+
).map(({ value, label, category }) => ({
|
|
55
|
+
label,
|
|
56
|
+
value,
|
|
57
|
+
description: category
|
|
58
|
+
}));
|
|
59
|
+
};
|
|
60
|
+
var getTopTickers = (data, topN) => {
|
|
61
|
+
const scores = data.filter(({ symbol }) => {
|
|
62
|
+
if (symbol.includes("BTC") && symbol !== "BTCUSDT") {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (symbol.startsWith("USD") || !symbol.endsWith("USDT")) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (EXCLUDE_TICKERS.includes(symbol)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}).map((coin) => {
|
|
73
|
+
const volumeMln = coin.volume24h / 1e6;
|
|
74
|
+
return {
|
|
75
|
+
...coin,
|
|
76
|
+
symbol: coin.symbol,
|
|
77
|
+
volume24h: volumeMln
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
const sortedByVlolume = _.sortBy(scores, (item) => -item.volume24h);
|
|
81
|
+
const items = sortedByVlolume.map(({ symbol, volume24h, ...coin }, i) => ({
|
|
82
|
+
label: symbol.replace(/(USDT)$/i, ""),
|
|
83
|
+
value: symbol,
|
|
84
|
+
description: `volume: ${round(volume24h, 2)}m (#${i + 1})`,
|
|
85
|
+
data: {
|
|
86
|
+
...coin,
|
|
87
|
+
symbol,
|
|
88
|
+
volume24h
|
|
89
|
+
}
|
|
90
|
+
}));
|
|
91
|
+
return _.sortBy(topN ? items.slice(0, topN) : items, "label");
|
|
92
|
+
};
|
|
93
|
+
var normalizeTickerData = (raw) => ({
|
|
94
|
+
symbol: raw.symbol,
|
|
95
|
+
lastPrice: parseFloat(raw.lastPrice),
|
|
96
|
+
indexPrice: parseFloat(raw.indexPrice),
|
|
97
|
+
markPrice: parseFloat(raw.markPrice),
|
|
98
|
+
prevPrice24h: parseFloat(raw.prevPrice24h),
|
|
99
|
+
price24hPcnt: parseFloat(raw.price24hPcnt),
|
|
100
|
+
highPrice24h: parseFloat(raw.highPrice24h),
|
|
101
|
+
lowPrice24h: parseFloat(raw.lowPrice24h),
|
|
102
|
+
prevPrice1h: parseFloat(raw.prevPrice1h),
|
|
103
|
+
openInterest: parseFloat(raw.openInterest),
|
|
104
|
+
openInterestValue: parseFloat(raw.openInterestValue),
|
|
105
|
+
turnover24h: parseFloat(raw.turnover24h),
|
|
106
|
+
volume24h: parseFloat(raw.volume24h),
|
|
107
|
+
fundingRate: parseFloat(raw.fundingRate),
|
|
108
|
+
nextFundingTime: parseInt(raw.nextFundingTime),
|
|
109
|
+
predictedDeliveryPrice: raw.predictedDeliveryPrice,
|
|
110
|
+
basisRate: raw.basisRate,
|
|
111
|
+
deliveryFeeRate: raw.deliveryFeeRate,
|
|
112
|
+
deliveryTime: parseInt(raw.deliveryTime),
|
|
113
|
+
ask1Size: parseFloat(raw.ask1Size),
|
|
114
|
+
bid1Price: parseFloat(raw.bid1Price),
|
|
115
|
+
ask1Price: parseFloat(raw.ask1Price),
|
|
116
|
+
bid1Size: parseFloat(raw.bid1Size),
|
|
117
|
+
basis: raw.basis,
|
|
118
|
+
preOpenPrice: raw.preOpenPrice,
|
|
119
|
+
preQty: raw.preQty
|
|
120
|
+
});
|
|
121
|
+
export {
|
|
122
|
+
getTopTickers,
|
|
123
|
+
getVolatilityTickers,
|
|
124
|
+
normalizeTickerData
|
|
125
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { OrderLogData, SimpleOrderLogData, KlineChartData, KlineChartItem } from '@tradejs/types';
|
|
2
|
+
|
|
3
|
+
declare const toMs: (ts: number) => number;
|
|
4
|
+
declare const getTimestamp: (days?: number) => number;
|
|
5
|
+
declare const getItemTimestamp: (item: KlineChartItem) => number;
|
|
6
|
+
declare const getDataTimestamp: (data: KlineChartData) => number | null;
|
|
7
|
+
declare const formatUnix: (dt: number) => string;
|
|
8
|
+
declare const getTimeline: (start?: number, end?: number, step?: number) => number[];
|
|
9
|
+
declare const compactOrderLog: (timeline: number[], orderLog: OrderLogData) => SimpleOrderLogData;
|
|
10
|
+
|
|
11
|
+
export { getDataTimestamp as a, getItemTimestamp as b, compactOrderLog as c, getTimestamp as d, formatUnix as f, getTimeline as g, toMs as t };
|