@tradejs/node 3.0.0 → 3.1.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.
@@ -1,3 +1,10 @@
1
+ import {
2
+ closeOppositePositionsBeforeOpen,
3
+ createCloseAllOnGlobalProfitBeforeSignalsHook,
4
+ createCloseOppositeBeforePlaceOrderHook,
5
+ createMoveStopToBreakEvenAfterCoreDecisionHook,
6
+ createMoveStopToBreakEvenOnBarHook
7
+ } from "./chunk-LAJ7NA3Q.mjs";
1
8
  import {
2
9
  BINANCE_BREADTH_UNIVERSE_KEYS,
3
10
  buildBinanceBreadthUniverseSnapshot,
@@ -22,7 +29,7 @@ import {
22
29
  resolveHyperliquidPerpFromSignalSymbol,
23
30
  resolveStrategyConfig,
24
31
  validateEntryProtectionAtArrival
25
- } from "./chunk-OGAWBO3Z.mjs";
32
+ } from "./chunk-MKLTJQLY.mjs";
26
33
  import {
27
34
  DEFAULT_AI_MODEL,
28
35
  MAX_AI_SERIES_POINTS,
@@ -40,7 +47,9 @@ import {
40
47
  getRegisteredManifests,
41
48
  getRegisteredStrategies,
42
49
  getStrategyCreator,
50
+ getStrategyDefaults,
43
51
  getStrategyManifest,
52
+ invokeAiChat,
44
53
  isKnownStrategy,
45
54
  registerStrategyEntries,
46
55
  resetAiRuntimeCache,
@@ -49,379 +58,9 @@ import {
49
58
  runAiPromptLocal,
50
59
  strategies,
51
60
  trimSeriesDeep
52
- } from "./chunk-VRXU4E4O.mjs";
61
+ } from "./chunk-KOQSLFT2.mjs";
53
62
  import "./chunk-WS5DYEVZ.mjs";
54
63
  import "./chunk-Y6FXYEAI.mjs";
55
-
56
- // src/strategies.ts
57
- export * from "@tradejs/core/strategies";
58
-
59
- // src/strategyHooks/closeOppositePositionsBeforeOpen.ts
60
- import _ from "lodash";
61
- import { logger } from "@tradejs/infra/logger";
62
- var closeOppositePositionsBeforeOpen = async ({
63
- connector,
64
- entryContext
65
- }) => {
66
- const {
67
- symbol: currentSymbol,
68
- direction: currentDirection,
69
- timestamp,
70
- prices,
71
- strategy: strategyName
72
- } = entryContext;
73
- const price = prices.currentPrice;
74
- try {
75
- logger.log(
76
- "info",
77
- "[%s] checking open positions before open: %s %s",
78
- strategyName,
79
- currentSymbol,
80
- currentDirection
81
- );
82
- const positions = await connector.getPositions();
83
- const openPositions = (positions || []).filter(
84
- (item) => item && Number(item.qty) > 0
85
- );
86
- logger.log(
87
- "info",
88
- "[%s] open positions found: %s",
89
- strategyName,
90
- openPositions.length
91
- );
92
- const oppositePositions = openPositions.filter(
93
- (item) => item.symbol !== currentSymbol && item.direction !== currentDirection
94
- );
95
- if (_.isEmpty(oppositePositions)) {
96
- logger.log(
97
- "info",
98
- "[%s] no opposite positions to close before open: %s",
99
- strategyName,
100
- currentSymbol
101
- );
102
- return;
103
- }
104
- for (const position of oppositePositions) {
105
- logger.log(
106
- "info",
107
- "[%s] closing opposite position: %s %s qty=%s",
108
- strategyName,
109
- position.symbol,
110
- position.direction,
111
- position.qty
112
- );
113
- try {
114
- await connector.closePosition({
115
- symbol: position.symbol,
116
- price,
117
- timestamp,
118
- direction: position.direction
119
- });
120
- logger.log(
121
- "info",
122
- "[%s] opposite position closed: %s",
123
- strategyName,
124
- position.symbol
125
- );
126
- } catch (err) {
127
- logger.log(
128
- "error",
129
- "[%s] failed to close opposite position: %s %s",
130
- strategyName,
131
- position.symbol,
132
- err
133
- );
134
- }
135
- }
136
- } catch (err) {
137
- logger.log(
138
- "error",
139
- "[%s] failed to load open positions before open: %s %s",
140
- strategyName,
141
- currentSymbol,
142
- err
143
- );
144
- }
145
- };
146
- var createCloseOppositeBeforePlaceOrderHook = ({
147
- isEnabled
148
- }) => {
149
- return async ({ ctx, entry }) => {
150
- if (ctx.env === "BACKTEST") {
151
- return;
152
- }
153
- if (!isEnabled(ctx.strategyConfig)) {
154
- return;
155
- }
156
- await closeOppositePositionsBeforeOpen({
157
- connector: ctx.connector,
158
- entryContext: entry.context
159
- });
160
- };
161
- };
162
-
163
- // src/strategyHooks/shared.ts
164
- var DEFAULT_BREAK_EVEN_TRIGGER_RISK_MULTIPLIER = 0.5;
165
- var DEFAULT_BREAK_EVEN_STOP_PROFIT_MULTIPLIER = 0;
166
- var DEFAULT_GLOBAL_UNREALIZED_PNL_TRIGGER_RISK_MULTIPLIER = 4;
167
- var GLOBAL_UNREALIZED_PNL_CLOSE_ALL_CODE = "GLOBAL_UNREALIZED_PNL_TARGET_REACHED_CLOSE_ALL";
168
- var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
169
- var isOpenPosition = (position) => Boolean(
170
- position && isFiniteNumber(position.price) && isFiniteNumber(position.qty) && position.qty > 0 && (position.direction === "LONG" || position.direction === "SHORT")
171
- );
172
- var isOpenPositionPnlSnapshot = (position) => Boolean(
173
- isOpenPosition(position) && isFiniteNumber(position?.currentPrice) && isFiniteNumber(position?.unrealizedPnl)
174
- );
175
- var getStrategyMaxLossValue = (strategyConfig) => {
176
- const maxLossValue = Number(strategyConfig?.MAX_LOSS_VALUE ?? Number.NaN);
177
- return Number.isFinite(maxLossValue) && maxLossValue > 0 ? maxLossValue : null;
178
- };
179
- var getPositionStopLossPrice = (position) => {
180
- if (!position || typeof position !== "object") {
181
- return null;
182
- }
183
- const slPrice = Number(
184
- position.slPrice ?? Number.NaN
185
- );
186
- if (Number.isFinite(slPrice)) {
187
- return slPrice;
188
- }
189
- const signalStopLossPrice = Number(
190
- position.signal?.prices?.stopLossPrice ?? Number.NaN
191
- );
192
- return Number.isFinite(signalStopLossPrice) ? signalStopLossPrice : null;
193
- };
194
- var getPositionTakeProfitPrice = (position) => {
195
- if (!position || typeof position !== "object") {
196
- return null;
197
- }
198
- const directTakeProfitPrice = Number(
199
- position.tpPrice ?? position.takeProfitPrice ?? Number.NaN
200
- );
201
- if (Number.isFinite(directTakeProfitPrice)) {
202
- return directTakeProfitPrice;
203
- }
204
- const signalTakeProfitPrice = Number(
205
- position.signal?.prices?.takeProfitPrice ?? Number.NaN
206
- );
207
- return Number.isFinite(signalTakeProfitPrice) ? signalTakeProfitPrice : null;
208
- };
209
- var getBreakEvenStopPrice = ({
210
- direction,
211
- entryPrice,
212
- takeProfitPrice,
213
- stopProfitMultiplier
214
- }) => {
215
- if (!Number.isFinite(entryPrice)) {
216
- return null;
217
- }
218
- const normalizedStopProfitMultiplier = Number.isFinite(stopProfitMultiplier) ? Math.min(Math.max(stopProfitMultiplier, 0), 1) : DEFAULT_BREAK_EVEN_STOP_PROFIT_MULTIPLIER;
219
- if (takeProfitPrice == null || !Number.isFinite(takeProfitPrice) || direction === "LONG" && takeProfitPrice <= entryPrice || direction === "SHORT" && takeProfitPrice >= entryPrice) {
220
- return entryPrice;
221
- }
222
- const distanceToTakeProfit = takeProfitPrice - entryPrice;
223
- return entryPrice + distanceToTakeProfit * normalizedStopProfitMultiplier;
224
- };
225
- var getFavorableMovePct = ({
226
- direction,
227
- entryPrice,
228
- currentPrice
229
- }) => {
230
- if (!Number.isFinite(entryPrice) || !Number.isFinite(currentPrice) || entryPrice <= 0) {
231
- return null;
232
- }
233
- return direction === "LONG" ? (currentPrice - entryPrice) / entryPrice * 100 : (entryPrice - currentPrice) / entryPrice * 100;
234
- };
235
- var getPositionRiskPct = ({
236
- direction,
237
- entryPrice,
238
- stopLossPrice
239
- }) => {
240
- if (stopLossPrice == null || !Number.isFinite(entryPrice) || !Number.isFinite(stopLossPrice) || entryPrice <= 0) {
241
- return null;
242
- }
243
- return direction === "LONG" ? (entryPrice - stopLossPrice) / entryPrice * 100 : (stopLossPrice - entryPrice) / entryPrice * 100;
244
- };
245
- var isBreakEvenStopAlreadyApplied = ({
246
- direction,
247
- entryPrice,
248
- stopLossPrice
249
- }) => {
250
- if (stopLossPrice == null || !Number.isFinite(entryPrice) || !Number.isFinite(stopLossPrice)) {
251
- return false;
252
- }
253
- return direction === "LONG" ? stopLossPrice >= entryPrice : stopLossPrice <= entryPrice;
254
- };
255
- var getConfiguredDirectionRiskPct = ({
256
- strategyConfig,
257
- direction
258
- }) => {
259
- if (!strategyConfig || typeof strategyConfig !== "object") {
260
- return null;
261
- }
262
- const directSideConfig = strategyConfig[direction];
263
- const directSideRiskPct = Number(directSideConfig?.SL ?? Number.NaN);
264
- if (Number.isFinite(directSideRiskPct)) {
265
- return directSideRiskPct;
266
- }
267
- for (const candidate of Object.values(strategyConfig)) {
268
- if (!candidate || typeof candidate !== "object") {
269
- continue;
270
- }
271
- const candidateDirection = candidate.direction;
272
- const candidateRiskPct = Number(
273
- candidate.SL ?? Number.NaN
274
- );
275
- if (candidateDirection === direction && Number.isFinite(candidateRiskPct)) {
276
- return candidateRiskPct;
277
- }
278
- }
279
- return null;
280
- };
281
- var toStrategyCodePrefix = (strategyName) => strategyName === "TrendLine" ? "TRENDLINE" : strategyName.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").toUpperCase();
282
-
283
- // src/strategyHooks/moveStopToBreakEvenAfterCoreDecision.ts
284
- var createMoveStopToBreakEvenOnBarHook = ({
285
- isEnabled = () => true,
286
- triggerRiskMultiplier = DEFAULT_BREAK_EVEN_TRIGGER_RISK_MULTIPLIER,
287
- stopProfitMultiplier = DEFAULT_BREAK_EVEN_STOP_PROFIT_MULTIPLIER
288
- } = {}) => {
289
- return async ({ ctx, market }) => {
290
- if (!isEnabled(ctx.strategyConfig)) {
291
- return;
292
- }
293
- const currentPosition = await ctx.connector.getPosition(ctx.symbol);
294
- if (!isOpenPosition(currentPosition)) {
295
- return;
296
- }
297
- const currentPrice = Number(market.candle.close ?? Number.NaN);
298
- if (!Number.isFinite(currentPrice)) {
299
- return;
300
- }
301
- const currentStopLossPrice = getPositionStopLossPrice(currentPosition);
302
- if (isBreakEvenStopAlreadyApplied({
303
- direction: currentPosition.direction,
304
- entryPrice: currentPosition.price,
305
- stopLossPrice: currentStopLossPrice
306
- })) {
307
- return;
308
- }
309
- const favorableMovePct = getFavorableMovePct({
310
- direction: currentPosition.direction,
311
- entryPrice: currentPosition.price,
312
- currentPrice
313
- });
314
- const currentPositionRiskPct = getPositionRiskPct({
315
- direction: currentPosition.direction,
316
- entryPrice: currentPosition.price,
317
- stopLossPrice: currentStopLossPrice
318
- });
319
- const configuredRiskPct = getConfiguredDirectionRiskPct({
320
- strategyConfig: ctx.strategyConfig,
321
- direction: currentPosition.direction
322
- });
323
- const triggerRiskPct = currentPositionRiskPct ?? configuredRiskPct;
324
- if (favorableMovePct == null || triggerRiskPct == null || favorableMovePct < triggerRiskPct * triggerRiskMultiplier) {
325
- return;
326
- }
327
- const stopLossPrice = getBreakEvenStopPrice({
328
- direction: currentPosition.direction,
329
- entryPrice: currentPosition.price,
330
- takeProfitPrice: getPositionTakeProfitPrice(currentPosition),
331
- stopProfitMultiplier
332
- });
333
- if (stopLossPrice == null) {
334
- return;
335
- }
336
- return {
337
- kind: "protect",
338
- code: `${toStrategyCodePrefix(ctx.strategyName)}_MOVE_STOP_TO_BREAK_EVEN`,
339
- protectPlan: {
340
- direction: currentPosition.direction,
341
- stopLossPrice
342
- }
343
- };
344
- };
345
- };
346
- var createMoveStopToBreakEvenAfterCoreDecisionHook = createMoveStopToBreakEvenOnBarHook;
347
-
348
- // src/signalsHooks/closeAllPositionsOnGlobalProfitBeforeSignals.ts
349
- import { logger as logger2 } from "@tradejs/infra/logger";
350
- var createCloseAllOnGlobalProfitBeforeSignalsHook = ({
351
- getStrategyDefaultConfig = () => void 0,
352
- profitRiskMultiplier = DEFAULT_GLOBAL_UNREALIZED_PNL_TRIGGER_RISK_MULTIPLIER
353
- } = {}) => {
354
- return async ({ connector, runtimeStrategies }) => {
355
- if (typeof connector.getOpenPositionPnl !== "function") {
356
- return;
357
- }
358
- const openPositions = (await connector.getOpenPositionPnl()).filter(
359
- isOpenPositionPnlSnapshot
360
- );
361
- if (!openPositions.length) {
362
- return;
363
- }
364
- const totalUnrealizedPnl = openPositions.reduce(
365
- (sum, position) => sum + position.unrealizedPnl,
366
- 0
367
- );
368
- if (!Number.isFinite(totalUnrealizedPnl) || totalUnrealizedPnl <= 0) {
369
- return;
370
- }
371
- const maxLossValues = runtimeStrategies.flatMap(
372
- ({ strategyName, strategyConfig }) => {
373
- const maxLossValue = getStrategyMaxLossValue({
374
- ...getStrategyDefaultConfig(strategyName) ?? {},
375
- ...strategyConfig ?? {}
376
- });
377
- return maxLossValue == null ? [] : [maxLossValue];
378
- }
379
- );
380
- if (!maxLossValues.length) {
381
- return;
382
- }
383
- const averageMaxLossValue = maxLossValues.reduce((sum, value) => sum + value, 0) / maxLossValues.length;
384
- const unrealizedPnlThreshold = averageMaxLossValue * profitRiskMultiplier;
385
- if (!Number.isFinite(unrealizedPnlThreshold) || unrealizedPnlThreshold <= 0 || totalUnrealizedPnl < unrealizedPnlThreshold) {
386
- return;
387
- }
388
- logger2.info(
389
- "closing all positions before signals by global unrealized pnl threshold: totalPnl=%s threshold=%s positions=%s",
390
- totalUnrealizedPnl,
391
- unrealizedPnlThreshold,
392
- openPositions.length
393
- );
394
- const closeTimestamp = Date.now();
395
- const closeResults = await Promise.allSettled(
396
- openPositions.map(
397
- (position) => connector.closePosition({
398
- symbol: position.symbol,
399
- direction: position.direction,
400
- price: position.currentPrice,
401
- timestamp: closeTimestamp
402
- })
403
- )
404
- );
405
- const failedClosures = closeResults.flatMap((result, index) => {
406
- if (result.status === "fulfilled" && result.value === true) {
407
- return [];
408
- }
409
- return [
410
- `${openPositions[index]?.symbol}:${openPositions[index]?.direction ?? "UNKNOWN"}`
411
- ];
412
- });
413
- if (failedClosures.length) {
414
- logger2.warn(
415
- "close-all before signals hook could not confirm closures for %s",
416
- failedClosures.join(", ")
417
- );
418
- }
419
- return {
420
- abort: true,
421
- reason: GLOBAL_UNREALIZED_PNL_CLOSE_ALL_CODE
422
- };
423
- };
424
- };
425
64
  export {
426
65
  BINANCE_BREADTH_UNIVERSE_KEYS,
427
66
  DEFAULT_AI_MODEL,
@@ -462,7 +101,9 @@ export {
462
101
  getRegisteredManifests,
463
102
  getRegisteredStrategies,
464
103
  getStrategyCreator,
104
+ getStrategyDefaults,
465
105
  getStrategyManifest,
106
+ invokeAiChat,
466
107
  isKnownStrategy,
467
108
  isTrackedHyperliquidPerp,
468
109
  isTrackedHyperliquidWhale,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/node",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Node-only runtime for the TradeJS TypeScript framework: strategies, backtests, Pine strategy loading, and plugin registries.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -58,6 +58,16 @@
58
58
  "import": "./dist/registry.mjs",
59
59
  "require": "./dist/registry.js"
60
60
  },
61
+ "./runtimeTrades": {
62
+ "types": "./dist/runtimeTrades.d.ts",
63
+ "import": "./dist/runtimeTrades.mjs",
64
+ "require": "./dist/runtimeTrades.js"
65
+ },
66
+ "./runtimeDashboard": {
67
+ "types": "./dist/runtimeDashboard.d.ts",
68
+ "import": "./dist/runtimeDashboard.mjs",
69
+ "require": "./dist/runtimeDashboard.js"
70
+ },
61
71
  "./strategies": {
62
72
  "types": "./dist/strategies.d.ts",
63
73
  "import": "./dist/strategies.mjs",
@@ -67,9 +77,9 @@
67
77
  "dependencies": {
68
78
  "@langchain/core": "^1.2.3",
69
79
  "@langchain/openai": "^1.5.5",
70
- "@tradejs/core": "^3.0.0",
71
- "@tradejs/infra": "^3.0.0",
72
- "@tradejs/types": "^3.0.0",
80
+ "@tradejs/core": "^3.1.0",
81
+ "@tradejs/infra": "^3.1.0",
82
+ "@tradejs/types": "^3.1.0",
73
83
  "chalk": "4.1.2",
74
84
  "ioredis": "5.11.1",
75
85
  "lodash": "^4.18.1",
@@ -80,7 +90,10 @@
80
90
  "tsconfig-paths": "^4.2.0"
81
91
  },
82
92
  "devDependencies": {
83
- "@tradejs/strategies": "^3.0.0",
93
+ "@tradejs/strategy-adaptive-momentum-ribbon": "^3.0.0",
94
+ "@tradejs/strategy-hyperliquid-consensus": "^3.0.0",
95
+ "@tradejs/strategy-trend-line": "^3.0.0",
96
+ "@tradejs/strategy-volume-divergence": "^3.0.0",
84
97
  "@types/node": "^24",
85
98
  "tsup": "^8.5.1",
86
99
  "typescript": "^5.9"