@tradejs/core 2.0.18 → 2.0.20
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/backtest.js +2 -3
- package/dist/backtest.mjs +1 -1
- package/dist/{chunk-NN7Z3GIK.mjs → chunk-Q3OFPRVV.mjs} +115 -125
- package/dist/{chunk-BOETNABM.mjs → chunk-S4KHOAXM.mjs} +1 -2
- package/dist/chunk-U3GDILUZ.mjs +13 -0
- package/dist/grid.d.mts +3 -2
- package/dist/grid.d.ts +3 -2
- package/dist/grid.js +13 -11
- package/dist/grid.mjs +6 -6
- package/dist/{indicators-C4AaiFrs.d.ts → indicatorControllerContracts-DdSuUzb2.d.mts} +26 -92
- package/dist/{indicators-C4AaiFrs.d.mts → indicatorControllerContracts-DdSuUzb2.d.ts} +26 -92
- package/dist/indicators.d.mts +70 -3
- package/dist/indicators.d.ts +70 -3
- package/dist/indicators.js +114 -125
- package/dist/indicators.mjs +2 -2
- package/dist/strategies.d.mts +7 -4
- package/dist/strategies.d.ts +7 -4
- package/dist/strategies.js +184 -149
- package/dist/strategies.mjs +60 -17
- package/dist/time.js +2 -3
- package/dist/time.mjs +1 -1
- package/dist/uuid-DMoErO_1.d.mts +3 -0
- package/dist/uuid-DMoErO_1.d.ts +3 -0
- package/package.json +2 -2
- package/dist/chunk-AJK4NS7Y.mjs +0 -10
package/dist/strategies.js
CHANGED
|
@@ -6407,7 +6407,7 @@ var calculateRecentRangeExpansionSeries = (candles, lookback) => calculateRecent
|
|
|
6407
6407
|
(index) => calculateRangeExpansionAt(candles, index)
|
|
6408
6408
|
);
|
|
6409
6409
|
|
|
6410
|
-
// src/utils/
|
|
6410
|
+
// src/utils/indicatorBaseContextRelative.ts
|
|
6411
6411
|
var SESSION_WINDOWS = [
|
|
6412
6412
|
{ name: "asia", startMinuteUtc: 0, endMinuteUtc: 8 * 60 },
|
|
6413
6413
|
{ name: "europe", startMinuteUtc: 7 * 60, endMinuteUtc: 16 * 60 },
|
|
@@ -6416,28 +6416,24 @@ var SESSION_WINDOWS = [
|
|
|
6416
6416
|
var FUNDING_WINDOW_STEP_MINUTES = 8 * 60;
|
|
6417
6417
|
var FUNDING_WINDOW_NEARBY_MINUTES = 60;
|
|
6418
6418
|
var SESSION_WINDOW_EDGE_MINUTES = 60;
|
|
6419
|
-
var BASE_INTERVAL_MS = 15 * 6e4;
|
|
6420
6419
|
var PSYCHOLOGICAL_LEVEL_WINDOWS = {
|
|
6421
|
-
m15:
|
|
6420
|
+
m15: 15 * 6e4,
|
|
6422
6421
|
h1: 60 * 6e4,
|
|
6423
6422
|
h4: 4 * 60 * 6e4
|
|
6424
6423
|
};
|
|
6425
|
-
var isInsideSession = (minuteUtc, startMinuteUtc, endMinuteUtc) => startMinuteUtc <= endMinuteUtc ? minuteUtc >= startMinuteUtc && minuteUtc < endMinuteUtc : minuteUtc >= startMinuteUtc || minuteUtc < endMinuteUtc;
|
|
6426
6424
|
var buildSessionContext = (timestamp) => {
|
|
6427
6425
|
const date = new Date(timestamp);
|
|
6428
|
-
const
|
|
6429
|
-
const utcMinute = date.getUTCMinutes();
|
|
6430
|
-
const minuteUtc = utcHour * 60 + utcMinute;
|
|
6426
|
+
const minuteUtc = date.getUTCHours() * 60 + date.getUTCMinutes();
|
|
6431
6427
|
const dayOfWeekUtc = date.getUTCDay() || 7;
|
|
6432
6428
|
const activeSessions = SESSION_WINDOWS.filter(
|
|
6433
|
-
(
|
|
6434
|
-
).map((
|
|
6429
|
+
({ startMinuteUtc, endMinuteUtc }) => startMinuteUtc <= endMinuteUtc ? minuteUtc >= startMinuteUtc && minuteUtc < endMinuteUtc : minuteUtc >= startMinuteUtc || minuteUtc < endMinuteUtc
|
|
6430
|
+
).map(({ name }) => name);
|
|
6435
6431
|
const sessionPhase = activeSessions.includes("us") ? "us" : activeSessions.includes("europe") ? "europe" : activeSessions.includes("asia") ? "asia" : "off_hours";
|
|
6436
6432
|
const primaryWindow = SESSION_WINDOWS.find(
|
|
6437
|
-
(
|
|
6433
|
+
({ name }) => name === sessionPhase
|
|
6438
6434
|
);
|
|
6439
|
-
const minutesFromSessionOpen = primaryWindow
|
|
6440
|
-
const minutesToSessionClose = primaryWindow
|
|
6435
|
+
const minutesFromSessionOpen = primaryWindow ? minuteUtc - primaryWindow.startMinuteUtc : null;
|
|
6436
|
+
const minutesToSessionClose = primaryWindow ? primaryWindow.endMinuteUtc - minuteUtc : null;
|
|
6441
6437
|
const sessionWindowPhase = primaryWindow == null || minutesFromSessionOpen == null ? "off_hours" : minutesFromSessionOpen < SESSION_WINDOW_EDGE_MINUTES ? "opening" : minutesToSessionClose != null && minutesToSessionClose <= SESSION_WINDOW_EDGE_MINUTES ? "closing" : "active";
|
|
6442
6438
|
const minutesToFundingWindow = (FUNDING_WINDOW_STEP_MINUTES - minuteUtc % FUNDING_WINDOW_STEP_MINUTES) % FUNDING_WINDOW_STEP_MINUTES;
|
|
6443
6439
|
return {
|
|
@@ -6453,7 +6449,7 @@ var buildSessionContext = (timestamp) => {
|
|
|
6453
6449
|
isWeekendUtc: dayOfWeekUtc >= 6
|
|
6454
6450
|
};
|
|
6455
6451
|
};
|
|
6456
|
-
var
|
|
6452
|
+
var unavailablePsychologicalLevelWindow = () => ({
|
|
6457
6453
|
crossed: null,
|
|
6458
6454
|
direction: "unknown",
|
|
6459
6455
|
level: null,
|
|
@@ -6462,7 +6458,7 @@ var buildUnavailablePsychologicalLevelWindow = () => ({
|
|
|
6462
6458
|
});
|
|
6463
6459
|
var buildPsychologicalLevelWindow = (startPrice, endPrice, stepUsd) => {
|
|
6464
6460
|
if (!Number.isFinite(startPrice) || !Number.isFinite(endPrice) || !Number.isFinite(stepUsd) || startPrice <= 0 || endPrice <= 0 || stepUsd <= 0) {
|
|
6465
|
-
return
|
|
6461
|
+
return unavailablePsychologicalLevelWindow();
|
|
6466
6462
|
}
|
|
6467
6463
|
if (endPrice === startPrice) {
|
|
6468
6464
|
return {
|
|
@@ -6486,21 +6482,17 @@ var buildPsychologicalLevelWindow = (startPrice, endPrice, stepUsd) => {
|
|
|
6486
6482
|
distanceBeyondLevelBps: null
|
|
6487
6483
|
};
|
|
6488
6484
|
}
|
|
6489
|
-
const levelsCrossed = Math.round(Math.abs(lastCrossedLevel - firstCrossedLevel) / stepUsd) + 1;
|
|
6490
|
-
const distanceBeyondLevelBps = Math.abs(endPrice - lastCrossedLevel) / lastCrossedLevel * 1e4;
|
|
6491
6485
|
return {
|
|
6492
6486
|
crossed: true,
|
|
6493
6487
|
direction: movingUp ? "up" : "down",
|
|
6494
6488
|
level: lastCrossedLevel,
|
|
6495
|
-
levelsCrossed,
|
|
6496
|
-
distanceBeyondLevelBps
|
|
6489
|
+
levelsCrossed: Math.round(Math.abs(lastCrossedLevel - firstCrossedLevel) / stepUsd) + 1,
|
|
6490
|
+
distanceBeyondLevelBps: Math.abs(endPrice - lastCrossedLevel) / lastCrossedLevel * 1e4
|
|
6497
6491
|
};
|
|
6498
6492
|
};
|
|
6499
6493
|
var buildPsychologicalLevelAssetContext = (candles, stepUsd) => {
|
|
6500
6494
|
const endCandle = candles[candles.length - 1];
|
|
6501
|
-
if (!endCandle)
|
|
6502
|
-
return null;
|
|
6503
|
-
}
|
|
6495
|
+
if (!endCandle) return null;
|
|
6504
6496
|
const candlesByTimestamp = new Map(
|
|
6505
6497
|
candles.map((item) => [item.timestamp, item])
|
|
6506
6498
|
);
|
|
@@ -6515,104 +6507,70 @@ var buildPsychologicalLevelAssetContext = (candles, stepUsd) => {
|
|
|
6515
6507
|
startCandle.close,
|
|
6516
6508
|
endCandle.close,
|
|
6517
6509
|
stepUsd
|
|
6518
|
-
) :
|
|
6510
|
+
) : unavailablePsychologicalLevelWindow()
|
|
6519
6511
|
];
|
|
6520
6512
|
})
|
|
6521
6513
|
);
|
|
6522
|
-
return {
|
|
6523
|
-
source: "aligned_15m_ohlcv",
|
|
6524
|
-
stepUsd,
|
|
6525
|
-
windows
|
|
6526
|
-
};
|
|
6514
|
+
return { source: "aligned_15m_ohlcv", stepUsd, windows };
|
|
6527
6515
|
};
|
|
6528
|
-
var
|
|
6529
|
-
var BASE_CONTEXT_CANDLE_WINDOW = 256;
|
|
6530
|
-
var PIVOT_LEFT_RIGHT = 2;
|
|
6531
|
-
var ZONE_ATR_FACTOR = 0.5;
|
|
6532
|
-
var PROFILE_BIN_COUNT = 24;
|
|
6533
|
-
var SR_ZONE_PIVOT_PERIOD = 9;
|
|
6534
|
-
var SR_ZONE_MIN_STRENGTH = 2;
|
|
6535
|
-
var SR_ZONE_MAX_PIVOTS = 15;
|
|
6536
|
-
var SR_ZONE_CHANNEL_WIDTH_PCT = 8;
|
|
6537
|
-
var SR_ZONE_MAX_LEVELS = 6;
|
|
6538
|
-
var VOLUME_STRUCTURE_CALC_BARS = 180;
|
|
6539
|
-
var VOLUME_STRUCTURE_ROW_COUNT = 20;
|
|
6540
|
-
var LIQUIDITY_ZONE_LOOKBACK = 15;
|
|
6541
|
-
var LIQUIDITY_ZONE_MAX_AGE = 120;
|
|
6542
|
-
var LIQUIDITY_TAIL_ATR_LENGTH = 14;
|
|
6543
|
-
var LIQUIDITY_TAIL_ATR_MULT = 0.8;
|
|
6544
|
-
var LIQUIDITY_TAIL_MIN_WICK_RATIO = 1.3;
|
|
6545
|
-
var LIQUIDITY_TAIL_WICK_DOMINANCE = 1.2;
|
|
6546
|
-
var LIQUIDITY_TAIL_MIN_GAP = 5;
|
|
6547
|
-
var LIQUIDITY_TAIL_MAX_AGE = 120;
|
|
6548
|
-
var TREND_FOLLOW_PIVOT_LENGTH = 10;
|
|
6549
|
-
var TREND_FOLLOW_ATR_LENGTH = 14;
|
|
6550
|
-
var TREND_FOLLOW_ATR_MULT = 4;
|
|
6551
|
-
var ADAPTIVE_CHANNEL_REGRESSION_BARS = 7;
|
|
6552
|
-
var ADAPTIVE_CHANNEL_ENVELOPE_BARS = 2;
|
|
6553
|
-
var ADAPTIVE_CHANNEL_ATR_STRETCH = 2;
|
|
6554
|
-
var ADAPTIVE_CHANNEL_VOLATILITY_LOOKBACK = 100;
|
|
6555
|
-
var ADAPTIVE_CHANNEL_CALC_LOOKBACK = 220;
|
|
6556
|
-
var STRUCTURE_ZONES_ZONE_WIDTH_ATR = 0.5;
|
|
6557
|
-
var STRUCTURE_ZONES_ACCEPT_BARS = 2;
|
|
6558
|
-
var calculateReturnPctFromCandles = (candles) => {
|
|
6516
|
+
var returnPct = (candles) => {
|
|
6559
6517
|
if (candles.length < 2) return null;
|
|
6560
|
-
const previous = candles[candles.length - 2];
|
|
6561
|
-
const current = candles[candles.length - 1];
|
|
6562
|
-
return percentChange(current.close, previous.close);
|
|
6563
|
-
};
|
|
6564
|
-
var calculateRatioReturnPct = (coinCandles, btcCandles) => {
|
|
6565
|
-
if (coinCandles.length < 2 || btcCandles.length < 2) return null;
|
|
6566
|
-
const currentCoin = coinCandles[coinCandles.length - 1];
|
|
6567
|
-
const previousCoin = coinCandles[coinCandles.length - 2];
|
|
6568
|
-
const currentBtc = btcCandles[btcCandles.length - 1];
|
|
6569
|
-
const previousBtc = btcCandles[btcCandles.length - 2];
|
|
6570
|
-
if (currentBtc.close <= 0 || previousBtc.close <= 0) return null;
|
|
6571
6518
|
return percentChange(
|
|
6572
|
-
|
|
6573
|
-
|
|
6519
|
+
candles[candles.length - 1].close,
|
|
6520
|
+
candles[candles.length - 2].close
|
|
6574
6521
|
);
|
|
6575
6522
|
};
|
|
6576
|
-
var
|
|
6577
|
-
|
|
6578
|
-
const
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6523
|
+
var ratioReturnPct = (target, reference) => {
|
|
6524
|
+
if (target.length < 2 || reference.length < 2) return null;
|
|
6525
|
+
const targetCurrent = target[target.length - 1];
|
|
6526
|
+
const targetPrevious = target[target.length - 2];
|
|
6527
|
+
const referenceCurrent = reference[reference.length - 1];
|
|
6528
|
+
const referencePrevious = reference[reference.length - 2];
|
|
6529
|
+
if (referenceCurrent.close <= 0 || referencePrevious.close <= 0) return null;
|
|
6530
|
+
return percentChange(
|
|
6531
|
+
targetCurrent.close / referenceCurrent.close,
|
|
6532
|
+
targetPrevious.close / referencePrevious.close
|
|
6533
|
+
);
|
|
6534
|
+
};
|
|
6535
|
+
var alignRecentCandles = (target, reference) => {
|
|
6536
|
+
const alignedTarget = [];
|
|
6537
|
+
const alignedReference = [];
|
|
6538
|
+
let targetIndex = Math.max(0, target.length - 80);
|
|
6539
|
+
let referenceIndex = Math.max(0, reference.length - 80);
|
|
6540
|
+
while (targetIndex < target.length && referenceIndex < reference.length) {
|
|
6541
|
+
const targetTimestamp = target[targetIndex].timestamp;
|
|
6542
|
+
const referenceTimestamp = reference[referenceIndex].timestamp;
|
|
6543
|
+
if (targetTimestamp === referenceTimestamp) {
|
|
6544
|
+
alignedTarget.push(target[targetIndex]);
|
|
6545
|
+
alignedReference.push(reference[referenceIndex]);
|
|
6546
|
+
targetIndex += 1;
|
|
6547
|
+
referenceIndex += 1;
|
|
6548
|
+
} else if (targetTimestamp < referenceTimestamp) {
|
|
6549
|
+
targetIndex += 1;
|
|
6591
6550
|
} else {
|
|
6592
|
-
|
|
6551
|
+
referenceIndex += 1;
|
|
6593
6552
|
}
|
|
6594
6553
|
}
|
|
6595
|
-
return {
|
|
6554
|
+
return { alignedTarget, alignedReference };
|
|
6596
6555
|
};
|
|
6597
|
-
var
|
|
6556
|
+
var returnSeries = (candles, limit) => {
|
|
6598
6557
|
const returns = [];
|
|
6599
|
-
|
|
6600
|
-
for (let index = startIndex; index < candles.length; index += 1) {
|
|
6558
|
+
for (let index = Math.max(1, candles.length - limit); index < candles.length; index += 1) {
|
|
6601
6559
|
const previous = candles[index - 1];
|
|
6602
6560
|
const current = candles[index];
|
|
6603
6561
|
if (previous.close <= 0) continue;
|
|
6604
|
-
const
|
|
6605
|
-
if (
|
|
6562
|
+
const value = percentChange(current.close, previous.close);
|
|
6563
|
+
if (value != null && Number.isFinite(value)) returns.push(value);
|
|
6606
6564
|
}
|
|
6607
6565
|
return returns;
|
|
6608
6566
|
};
|
|
6609
|
-
var
|
|
6567
|
+
var beta = (targetReturns, referenceReturns) => {
|
|
6610
6568
|
const length = Math.min(targetReturns.length, referenceReturns.length);
|
|
6611
6569
|
if (length < 2) return null;
|
|
6612
6570
|
const target = targetReturns.slice(-length);
|
|
6613
6571
|
const reference = referenceReturns.slice(-length);
|
|
6614
|
-
const referenceMean = reference.reduce((sum2, value) => sum2 + value, 0) /
|
|
6615
|
-
const targetMean = target.reduce((sum2, value) => sum2 + value, 0) /
|
|
6572
|
+
const referenceMean = reference.reduce((sum2, value) => sum2 + value, 0) / length;
|
|
6573
|
+
const targetMean = target.reduce((sum2, value) => sum2 + value, 0) / length;
|
|
6616
6574
|
let covariance = 0;
|
|
6617
6575
|
let referenceVariance = 0;
|
|
6618
6576
|
for (let index = 0; index < length; index += 1) {
|
|
@@ -6629,16 +6587,6 @@ var classifyRatioTrend = (ratioReturn24h, ratioReturn4h) => {
|
|
|
6629
6587
|
if (value < -0.15) return "down";
|
|
6630
6588
|
return "flat";
|
|
6631
6589
|
};
|
|
6632
|
-
var hasDistinctReferenceCandles = (coinCandles, referenceCandles) => {
|
|
6633
|
-
const coinLast = coinCandles[coinCandles.length - 1];
|
|
6634
|
-
const referenceLast = referenceCandles[referenceCandles.length - 1];
|
|
6635
|
-
const coinPrev = coinCandles[coinCandles.length - 2];
|
|
6636
|
-
const referencePrev = referenceCandles[referenceCandles.length - 2];
|
|
6637
|
-
if (!coinLast || !referenceLast || !coinPrev || !referencePrev) {
|
|
6638
|
-
return false;
|
|
6639
|
-
}
|
|
6640
|
-
return coinLast.timestamp !== referenceLast.timestamp || coinLast.close !== referenceLast.close || coinPrev.timestamp !== referencePrev.timestamp || coinPrev.close !== referencePrev.close;
|
|
6641
|
-
};
|
|
6642
6590
|
var buildTargetVsBtcContext = ({
|
|
6643
6591
|
coin1h,
|
|
6644
6592
|
btc1h,
|
|
@@ -6649,19 +6597,21 @@ var buildTargetVsBtcContext = ({
|
|
|
6649
6597
|
coinCandles,
|
|
6650
6598
|
btcCandles
|
|
6651
6599
|
}) => {
|
|
6652
|
-
const ratioReturn1h =
|
|
6653
|
-
const ratioReturn4h =
|
|
6654
|
-
const ratioReturn24h =
|
|
6655
|
-
const coinReturn1h =
|
|
6656
|
-
const coinReturn4h =
|
|
6657
|
-
const coinReturn24h =
|
|
6658
|
-
const btcReturn1h =
|
|
6659
|
-
const btcReturn4h =
|
|
6660
|
-
const btcReturn24h =
|
|
6661
|
-
const {
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6600
|
+
const ratioReturn1h = ratioReturnPct(coin1h, btc1h);
|
|
6601
|
+
const ratioReturn4h = ratioReturnPct(coin4h, btc4h);
|
|
6602
|
+
const ratioReturn24h = ratioReturnPct(coin1d, btc1d);
|
|
6603
|
+
const coinReturn1h = returnPct(coin1h);
|
|
6604
|
+
const coinReturn4h = returnPct(coin4h);
|
|
6605
|
+
const coinReturn24h = returnPct(coin1d);
|
|
6606
|
+
const btcReturn1h = returnPct(btc1h);
|
|
6607
|
+
const btcReturn4h = returnPct(btc4h);
|
|
6608
|
+
const btcReturn24h = returnPct(btc1d);
|
|
6609
|
+
const { alignedTarget, alignedReference } = alignRecentCandles(
|
|
6610
|
+
coinCandles,
|
|
6611
|
+
btcCandles
|
|
6612
|
+
);
|
|
6613
|
+
const coinReturns20 = returnSeries(alignedTarget, 21).slice(-20);
|
|
6614
|
+
const btcReturns20 = returnSeries(alignedReference, 21).slice(-20);
|
|
6665
6615
|
return {
|
|
6666
6616
|
source: "aligned_ohlcv",
|
|
6667
6617
|
ratioReturn1h,
|
|
@@ -6670,11 +6620,21 @@ var buildTargetVsBtcContext = ({
|
|
|
6670
6620
|
alphaVsBtc1h: coinReturn1h == null || btcReturn1h == null ? null : coinReturn1h - btcReturn1h,
|
|
6671
6621
|
alphaVsBtc4h: coinReturn4h == null || btcReturn4h == null ? null : coinReturn4h - btcReturn4h,
|
|
6672
6622
|
alphaVsBtc24h: coinReturn24h == null || btcReturn24h == null ? null : coinReturn24h - btcReturn24h,
|
|
6673
|
-
betaToBtc20:
|
|
6674
|
-
correlationToBtc20,
|
|
6623
|
+
betaToBtc20: beta(coinReturns20, btcReturns20),
|
|
6624
|
+
correlationToBtc20: coinReturns20.length === btcReturns20.length && coinReturns20.length >= 2 ? calculatePearsonCorrelation(coinReturns20, btcReturns20) : null,
|
|
6675
6625
|
ratioTrend: classifyRatioTrend(ratioReturn24h, ratioReturn4h)
|
|
6676
6626
|
};
|
|
6677
6627
|
};
|
|
6628
|
+
var hasDistinctReferenceCandles = (target, reference) => {
|
|
6629
|
+
const targetLast = target[target.length - 1];
|
|
6630
|
+
const referenceLast = reference[reference.length - 1];
|
|
6631
|
+
const targetPrevious = target[target.length - 2];
|
|
6632
|
+
const referencePrevious = reference[reference.length - 2];
|
|
6633
|
+
if (!targetLast || !referenceLast || !targetPrevious || !referencePrevious) {
|
|
6634
|
+
return false;
|
|
6635
|
+
}
|
|
6636
|
+
return targetLast.timestamp !== referenceLast.timestamp || targetLast.close !== referenceLast.close || targetPrevious.timestamp !== referencePrevious.timestamp || targetPrevious.close !== referencePrevious.close;
|
|
6637
|
+
};
|
|
6678
6638
|
var buildTargetVsEthContext = ({
|
|
6679
6639
|
coin1h,
|
|
6680
6640
|
eth1h,
|
|
@@ -6685,9 +6645,7 @@ var buildTargetVsEthContext = ({
|
|
|
6685
6645
|
coinCandles,
|
|
6686
6646
|
ethCandles
|
|
6687
6647
|
}) => {
|
|
6688
|
-
if (!hasDistinctReferenceCandles(coinCandles, ethCandles))
|
|
6689
|
-
return null;
|
|
6690
|
-
}
|
|
6648
|
+
if (!hasDistinctReferenceCandles(coinCandles, ethCandles)) return null;
|
|
6691
6649
|
const context = buildTargetVsBtcContext({
|
|
6692
6650
|
coin1h,
|
|
6693
6651
|
btc1h: eth1h,
|
|
@@ -6711,6 +6669,38 @@ var buildTargetVsEthContext = ({
|
|
|
6711
6669
|
ratioTrend: context.ratioTrend
|
|
6712
6670
|
};
|
|
6713
6671
|
};
|
|
6672
|
+
|
|
6673
|
+
// src/utils/indicatorBaseContext.ts
|
|
6674
|
+
var STRUCTURE_LOOKBACK = 80;
|
|
6675
|
+
var BASE_CONTEXT_CANDLE_WINDOW = 256;
|
|
6676
|
+
var PIVOT_LEFT_RIGHT = 2;
|
|
6677
|
+
var ZONE_ATR_FACTOR = 0.5;
|
|
6678
|
+
var PROFILE_BIN_COUNT = 24;
|
|
6679
|
+
var SR_ZONE_PIVOT_PERIOD = 9;
|
|
6680
|
+
var SR_ZONE_MIN_STRENGTH = 2;
|
|
6681
|
+
var SR_ZONE_MAX_PIVOTS = 15;
|
|
6682
|
+
var SR_ZONE_CHANNEL_WIDTH_PCT = 8;
|
|
6683
|
+
var SR_ZONE_MAX_LEVELS = 6;
|
|
6684
|
+
var VOLUME_STRUCTURE_CALC_BARS = 180;
|
|
6685
|
+
var VOLUME_STRUCTURE_ROW_COUNT = 20;
|
|
6686
|
+
var LIQUIDITY_ZONE_LOOKBACK = 15;
|
|
6687
|
+
var LIQUIDITY_ZONE_MAX_AGE = 120;
|
|
6688
|
+
var LIQUIDITY_TAIL_ATR_LENGTH = 14;
|
|
6689
|
+
var LIQUIDITY_TAIL_ATR_MULT = 0.8;
|
|
6690
|
+
var LIQUIDITY_TAIL_MIN_WICK_RATIO = 1.3;
|
|
6691
|
+
var LIQUIDITY_TAIL_WICK_DOMINANCE = 1.2;
|
|
6692
|
+
var LIQUIDITY_TAIL_MIN_GAP = 5;
|
|
6693
|
+
var LIQUIDITY_TAIL_MAX_AGE = 120;
|
|
6694
|
+
var TREND_FOLLOW_PIVOT_LENGTH = 10;
|
|
6695
|
+
var TREND_FOLLOW_ATR_LENGTH = 14;
|
|
6696
|
+
var TREND_FOLLOW_ATR_MULT = 4;
|
|
6697
|
+
var ADAPTIVE_CHANNEL_REGRESSION_BARS = 7;
|
|
6698
|
+
var ADAPTIVE_CHANNEL_ENVELOPE_BARS = 2;
|
|
6699
|
+
var ADAPTIVE_CHANNEL_ATR_STRETCH = 2;
|
|
6700
|
+
var ADAPTIVE_CHANNEL_VOLATILITY_LOOKBACK = 100;
|
|
6701
|
+
var ADAPTIVE_CHANNEL_CALC_LOOKBACK = 220;
|
|
6702
|
+
var STRUCTURE_ZONES_ZONE_WIDTH_ATR = 0.5;
|
|
6703
|
+
var STRUCTURE_ZONES_ACCEPT_BARS = 2;
|
|
6714
6704
|
var BASE_CONTEXT_MA_LAYER_PERIODS = [
|
|
6715
6705
|
[5, 12],
|
|
6716
6706
|
[9, 13],
|
|
@@ -10701,13 +10691,12 @@ var buildIndicatorSeriesByTimeframes = (candles, periods, sourcePrefix = "") =>
|
|
|
10701
10691
|
|
|
10702
10692
|
// src/utils/timestamp.ts
|
|
10703
10693
|
var import_date_fns = require("date-fns");
|
|
10704
|
-
var import_date_fns2 = require("date-fns");
|
|
10705
10694
|
var RUNTIME_STORAGE_DAY_OFFSET_MS = 6 * 60 * 60 * 1e3;
|
|
10706
10695
|
var getTimestamp = (days = 0) => {
|
|
10707
10696
|
if (days > 0) {
|
|
10708
|
-
return (0,
|
|
10697
|
+
return (0, import_date_fns.getUnixTime)((0, import_date_fns.subDays)(/* @__PURE__ */ new Date(), days)) * 1e3;
|
|
10709
10698
|
}
|
|
10710
|
-
return (0,
|
|
10699
|
+
return (0, import_date_fns.getUnixTime)(/* @__PURE__ */ new Date()) * 1e3;
|
|
10711
10700
|
};
|
|
10712
10701
|
|
|
10713
10702
|
// src/utils/strategyHelpers/indicators.ts
|
|
@@ -11076,23 +11065,46 @@ var getDirectionalTpSlPrices = ({
|
|
|
11076
11065
|
};
|
|
11077
11066
|
|
|
11078
11067
|
// src/utils/strategyHelpers/state.ts
|
|
11079
|
-
var
|
|
11068
|
+
var resolveLastTradeControllerParams = ({
|
|
11080
11069
|
env,
|
|
11081
11070
|
enabled = env ? env === "BACKTEST" : true,
|
|
11082
11071
|
cooldownMs = 864e5
|
|
11072
|
+
}) => ({
|
|
11073
|
+
enabled,
|
|
11074
|
+
cooldownMs
|
|
11075
|
+
});
|
|
11076
|
+
var createStateBackedLastTradeController = ({
|
|
11077
|
+
state,
|
|
11078
|
+
enabled,
|
|
11079
|
+
cooldownMs
|
|
11083
11080
|
}) => {
|
|
11084
|
-
|
|
11081
|
+
const getLastTradeTimestamp = () => state.get().lastTradeTimestamp;
|
|
11085
11082
|
return {
|
|
11086
|
-
isInCooldown: (timestamp) =>
|
|
11087
|
-
|
|
11088
|
-
|
|
11083
|
+
isInCooldown: (timestamp) => {
|
|
11084
|
+
const lastTradeTimestamp = getLastTradeTimestamp();
|
|
11085
|
+
return Boolean(
|
|
11086
|
+
enabled && lastTradeTimestamp != null && timestamp <= lastTradeTimestamp + cooldownMs
|
|
11087
|
+
);
|
|
11088
|
+
},
|
|
11089
11089
|
markTrade: (timestamp) => {
|
|
11090
11090
|
if (!enabled) return;
|
|
11091
|
-
|
|
11091
|
+
state.update((current) => {
|
|
11092
|
+
current.lastTradeTimestamp = timestamp;
|
|
11093
|
+
});
|
|
11092
11094
|
},
|
|
11093
|
-
getLastTradeTimestamp
|
|
11095
|
+
getLastTradeTimestamp
|
|
11094
11096
|
};
|
|
11095
11097
|
};
|
|
11098
|
+
var createLastTradeController = (params) => {
|
|
11099
|
+
const state = { lastTradeTimestamp: null };
|
|
11100
|
+
return createStateBackedLastTradeController({
|
|
11101
|
+
...resolveLastTradeControllerParams(params),
|
|
11102
|
+
state: {
|
|
11103
|
+
get: () => state,
|
|
11104
|
+
update: (fn) => fn(state)
|
|
11105
|
+
}
|
|
11106
|
+
});
|
|
11107
|
+
};
|
|
11096
11108
|
var stableStringify = (value, seen = /* @__PURE__ */ new WeakSet()) => {
|
|
11097
11109
|
if (value == null) {
|
|
11098
11110
|
return String(value);
|
|
@@ -11234,11 +11246,14 @@ var createStrategyStateControllerFactory = ({
|
|
|
11234
11246
|
};
|
|
11235
11247
|
|
|
11236
11248
|
// src/utils/uuid.ts
|
|
11237
|
-
var
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11249
|
+
var randomUuid = () => {
|
|
11250
|
+
if (!globalThis.crypto?.randomUUID) {
|
|
11251
|
+
throw new Error("crypto.randomUUID is not available in this runtime");
|
|
11252
|
+
}
|
|
11253
|
+
return globalThis.crypto.randomUUID();
|
|
11241
11254
|
};
|
|
11255
|
+
var createIdGenerator = (generateUuid = randomUuid) => (length = 12) => generateUuid().slice(-length);
|
|
11256
|
+
var uuid = createIdGenerator();
|
|
11242
11257
|
|
|
11243
11258
|
// src/utils/strategyHelpers/signalBuilders.ts
|
|
11244
11259
|
var COMPACT_MTF_CANDLE_LIMIT = 3;
|
|
@@ -11922,13 +11937,14 @@ var buildEntrySignalDecision = ({
|
|
|
11922
11937
|
additionalIndicators,
|
|
11923
11938
|
signalId,
|
|
11924
11939
|
orderPlan,
|
|
11925
|
-
runtime
|
|
11940
|
+
runtime,
|
|
11941
|
+
generateId = uuid
|
|
11926
11942
|
}) => ({
|
|
11927
11943
|
kind: "entry",
|
|
11928
11944
|
code,
|
|
11929
11945
|
entryContext,
|
|
11930
11946
|
signal: buildStrategySignal({
|
|
11931
|
-
signalId: signalId ??
|
|
11947
|
+
signalId: signalId ?? generateId(),
|
|
11932
11948
|
strategy: entryContext.strategy,
|
|
11933
11949
|
symbol: entryContext.symbol,
|
|
11934
11950
|
interval: entryContext.interval,
|
|
@@ -11943,7 +11959,7 @@ var buildEntrySignalDecision = ({
|
|
|
11943
11959
|
orderPlan,
|
|
11944
11960
|
runtime
|
|
11945
11961
|
});
|
|
11946
|
-
var
|
|
11962
|
+
var isFiniteNumber2 = (value) => typeof value === "number" && Number.isFinite(value);
|
|
11947
11963
|
var toDefaultEntryCode = (strategy, direction) => `${strategy.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").toUpperCase()}_${direction}_ENTRY`;
|
|
11948
11964
|
var toDefaultExitCode = (strategy, direction) => `${strategy.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").toUpperCase()}_${direction}_EXIT`;
|
|
11949
11965
|
var toDefaultProtectCode = (strategy, direction) => `${strategy.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").toUpperCase()}_${direction}_PROTECT`;
|
|
@@ -11954,7 +11970,7 @@ var resolveTakeProfitPrice = ({
|
|
|
11954
11970
|
if (!Array.isArray(takeProfits) || takeProfits.length === 0) {
|
|
11955
11971
|
throw new Error("strategyApi.entry requires at least one takeProfit");
|
|
11956
11972
|
}
|
|
11957
|
-
const prices = takeProfits.map((tp) => tp?.price).filter((price) =>
|
|
11973
|
+
const prices = takeProfits.map((tp) => tp?.price).filter((price) => isFiniteNumber2(price));
|
|
11958
11974
|
if (prices.length === 0) {
|
|
11959
11975
|
throw new Error("strategyApi.entry requires finite takeProfit prices");
|
|
11960
11976
|
}
|
|
@@ -11971,6 +11987,7 @@ var createStrategyAPI = ({
|
|
|
11971
11987
|
isConfigFromBacktest,
|
|
11972
11988
|
sharedReplayKey,
|
|
11973
11989
|
getSharedReplayState,
|
|
11990
|
+
generateId,
|
|
11974
11991
|
loadDecisionBaseContext
|
|
11975
11992
|
}) => {
|
|
11976
11993
|
const isBacktestEnv = env === "BACKTEST";
|
|
@@ -12028,6 +12045,7 @@ var createStrategyAPI = ({
|
|
|
12028
12045
|
sharedReplayKey,
|
|
12029
12046
|
getSharedReplayState
|
|
12030
12047
|
});
|
|
12048
|
+
let lastTradeControllerIndex = 0;
|
|
12031
12049
|
const getBaseContextFromIndicators = (indicators) => isRecordLike(indicators) && isRecordLike(indicators.baseContext) ? indicators.baseContext : void 0;
|
|
12032
12050
|
const getCurrentIndicatorsContext = () => {
|
|
12033
12051
|
ensureBarCache();
|
|
@@ -12088,7 +12106,7 @@ var createStrategyAPI = ({
|
|
|
12088
12106
|
};
|
|
12089
12107
|
const resolveDecisionPriceContext = () => {
|
|
12090
12108
|
const candle = cachedData[cachedData.length - 1];
|
|
12091
|
-
if (!candle || !
|
|
12109
|
+
if (!candle || !isFiniteNumber2(candle.timestamp) || !isFiniteNumber2(candle.close)) {
|
|
12092
12110
|
throw new Error(
|
|
12093
12111
|
"strategyApi.getDecisionPriceContext requires a current closed candle"
|
|
12094
12112
|
);
|
|
@@ -12120,7 +12138,7 @@ var createStrategyAPI = ({
|
|
|
12120
12138
|
direction,
|
|
12121
12139
|
takeProfits: orderPlan.takeProfits
|
|
12122
12140
|
});
|
|
12123
|
-
if (!
|
|
12141
|
+
if (!isFiniteNumber2(stopLossPrice)) {
|
|
12124
12142
|
throw new Error(
|
|
12125
12143
|
"strategyApi.entry requires finite orderPlan.stopLossPrice"
|
|
12126
12144
|
);
|
|
@@ -12153,7 +12171,8 @@ var createStrategyAPI = ({
|
|
|
12153
12171
|
additionalIndicators,
|
|
12154
12172
|
signalId,
|
|
12155
12173
|
orderPlan,
|
|
12156
|
-
runtime
|
|
12174
|
+
runtime,
|
|
12175
|
+
generateId
|
|
12157
12176
|
});
|
|
12158
12177
|
},
|
|
12159
12178
|
exit: async ({ code, direction }) => {
|
|
@@ -12179,10 +12198,26 @@ var createStrategyAPI = ({
|
|
|
12179
12198
|
getDecisionPriceContext,
|
|
12180
12199
|
getCurrentPosition,
|
|
12181
12200
|
getDirectionalTpSlPrices: (params) => getDirectionalTpSlPrices(params),
|
|
12182
|
-
createLastTradeController: (params) =>
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12201
|
+
createLastTradeController: (params) => {
|
|
12202
|
+
const resolvedParams = resolveLastTradeControllerParams({
|
|
12203
|
+
env,
|
|
12204
|
+
...params
|
|
12205
|
+
});
|
|
12206
|
+
const controllerIndex = lastTradeControllerIndex;
|
|
12207
|
+
lastTradeControllerIndex += 1;
|
|
12208
|
+
const state = createStateController(
|
|
12209
|
+
`__tradejs:last-trade:${controllerIndex}`,
|
|
12210
|
+
() => ({ lastTradeTimestamp: null }),
|
|
12211
|
+
{
|
|
12212
|
+
configKey: JSON.stringify(resolvedParams),
|
|
12213
|
+
sharedReplay: env === "CRON"
|
|
12214
|
+
}
|
|
12215
|
+
);
|
|
12216
|
+
return createStateBackedLastTradeController({
|
|
12217
|
+
...resolvedParams,
|
|
12218
|
+
state
|
|
12219
|
+
});
|
|
12220
|
+
},
|
|
12186
12221
|
createStateController
|
|
12187
12222
|
};
|
|
12188
12223
|
};
|