@tradejs/cli 2.0.12 → 2.0.14
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/cli.js +1543 -531
- package/dist/lib/aiTrainResearch.js +18 -3
- package/dist/lib/hyperliquidRateLimiter.js +75 -0
- package/dist/lib/hyperliquidWhaleBackfill.js +435 -0
- package/dist/lib/hyperliquidWhaleData.js +307 -0
- package/dist/lib/hyperliquidWhaleSelection.js +116 -0
- package/dist/lib/hyperliquidWhaleStream.js +326 -0
- package/dist/lib/marketContextPrepare.js +436 -2
- package/dist/lib/runtimeLineage.js +60 -4
- package/dist/scripts/aiTrain.js +18 -3
- package/dist/scripts/backtest.js +438 -0
- package/dist/scripts/hyperliquidWhaleBackfill.js +476 -0
- package/dist/scripts/hyperliquidWhaleIngest.js +333 -0
- package/dist/scripts/hyperliquidWhalesUpdate.js +369 -0
- package/dist/scripts/replay.js +500 -11
- package/dist/scripts/replayRunner.js +500 -11
- package/dist/scripts/runtimeParity.js +433 -0
- package/dist/scripts/signals.js +500 -13
- package/dist/scripts/signalsDaemon.js +500 -13
- package/package.json +12 -9
|
@@ -1639,7 +1639,8 @@ __export(marketContextPrepare_exports, {
|
|
|
1639
1639
|
prepareMarketContextForRun: () => prepareMarketContextForRun,
|
|
1640
1640
|
shouldPrepareBinanceMarketContextForRun: () => shouldPrepareBinanceMarketContextForRun,
|
|
1641
1641
|
shouldPrepareCoinMarketCapContextForRun: () => shouldPrepareCoinMarketCapContextForRun,
|
|
1642
|
-
shouldPrepareDerivativesContextForRun: () => shouldPrepareDerivativesContextForRun
|
|
1642
|
+
shouldPrepareDerivativesContextForRun: () => shouldPrepareDerivativesContextForRun,
|
|
1643
|
+
shouldPrepareHyperliquidWhaleContextForRun: () => shouldPrepareHyperliquidWhaleContextForRun
|
|
1643
1644
|
});
|
|
1644
1645
|
module.exports = __toCommonJS(marketContextPrepare_exports);
|
|
1645
1646
|
var import_chalk4 = __toESM(require("chalk"));
|
|
@@ -4496,6 +4497,412 @@ var timeOperation = async (label, operation, log = console.log) => {
|
|
|
4496
4497
|
}
|
|
4497
4498
|
};
|
|
4498
4499
|
|
|
4500
|
+
// src/lib/hyperliquidWhaleBackfill.ts
|
|
4501
|
+
var import_timescale4 = require("@tradejs/infra/timescale");
|
|
4502
|
+
var import_strategies2 = require("@tradejs/node/strategies");
|
|
4503
|
+
|
|
4504
|
+
// src/lib/hyperliquidWhaleData.ts
|
|
4505
|
+
var HYPERLIQUID_WHALE_BUCKET_MS = 6e4;
|
|
4506
|
+
var finiteNumber = (value) => {
|
|
4507
|
+
const numeric = typeof value === "number" ? value : Number(value);
|
|
4508
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
4509
|
+
};
|
|
4510
|
+
var POSITION_EPSILON = 1e-12;
|
|
4511
|
+
var normalizePosition = (value) => Math.abs(value) <= POSITION_EPSILON ? 0 : value;
|
|
4512
|
+
var classifyHyperliquidPositionChange = (params) => {
|
|
4513
|
+
const startValue = finiteNumber(params.startPosition);
|
|
4514
|
+
const size = finiteNumber(params.size);
|
|
4515
|
+
const side = String(params.side ?? "").toUpperCase();
|
|
4516
|
+
if (startValue == null || size == null || size <= 0) return null;
|
|
4517
|
+
if (side !== "B" && side !== "A") return null;
|
|
4518
|
+
const startPosition = normalizePosition(startValue);
|
|
4519
|
+
const endPosition = normalizePosition(
|
|
4520
|
+
startPosition + (side === "B" ? size : -size)
|
|
4521
|
+
);
|
|
4522
|
+
const startLong = Math.max(startPosition, 0);
|
|
4523
|
+
const endLong = Math.max(endPosition, 0);
|
|
4524
|
+
const startShort = Math.max(-startPosition, 0);
|
|
4525
|
+
const endShort = Math.max(-endPosition, 0);
|
|
4526
|
+
const longEntrySize = Math.max(0, endLong - startLong);
|
|
4527
|
+
const shortEntrySize = Math.max(0, endShort - startShort);
|
|
4528
|
+
const longExitSize = Math.max(0, startLong - endLong);
|
|
4529
|
+
const shortExitSize = Math.max(0, startShort - endShort);
|
|
4530
|
+
const flipped = startPosition * endPosition < 0;
|
|
4531
|
+
const opened = startPosition === 0;
|
|
4532
|
+
const closed = endPosition === 0;
|
|
4533
|
+
const increased = Math.sign(startPosition) === Math.sign(endPosition) && Math.abs(endPosition) > Math.abs(startPosition);
|
|
4534
|
+
return {
|
|
4535
|
+
startPosition,
|
|
4536
|
+
endPosition,
|
|
4537
|
+
action: flipped ? "flip" : opened ? "open" : closed ? "close" : increased ? "increase" : "reduce",
|
|
4538
|
+
longEntrySize,
|
|
4539
|
+
shortEntrySize,
|
|
4540
|
+
longExitSize,
|
|
4541
|
+
shortExitSize
|
|
4542
|
+
};
|
|
4543
|
+
};
|
|
4544
|
+
var normalizeAddress = (value) => typeof value === "string" && /^0x[0-9a-fA-F]{40}$/.test(value) ? value.toLowerCase() : null;
|
|
4545
|
+
var normalizeTradeFields = (value) => {
|
|
4546
|
+
const symbol = typeof value.coin === "string" ? value.coin.trim() : "";
|
|
4547
|
+
const price = finiteNumber(value.px);
|
|
4548
|
+
const size = finiteNumber(value.sz);
|
|
4549
|
+
const timestamp = finiteNumber(value.time);
|
|
4550
|
+
const tid = typeof value.tid === "string" || typeof value.tid === "number" ? String(value.tid) : "";
|
|
4551
|
+
if (!symbol || price == null || size == null || timestamp == null || !tid || price <= 0 || size <= 0) {
|
|
4552
|
+
return null;
|
|
4553
|
+
}
|
|
4554
|
+
return {
|
|
4555
|
+
symbol,
|
|
4556
|
+
price,
|
|
4557
|
+
size,
|
|
4558
|
+
timestamp,
|
|
4559
|
+
tid,
|
|
4560
|
+
notionalUsd: price * size
|
|
4561
|
+
};
|
|
4562
|
+
};
|
|
4563
|
+
var normalizeHyperliquidUserFill = (params) => {
|
|
4564
|
+
const normalized = normalizeTradeFields(params.fill);
|
|
4565
|
+
if (!normalized || !params.trackedSymbols.has(normalized.symbol)) return null;
|
|
4566
|
+
const address = normalizeAddress(params.address);
|
|
4567
|
+
if (!address) return null;
|
|
4568
|
+
const side = String(params.fill.side ?? "").toUpperCase();
|
|
4569
|
+
if (side !== "B" && side !== "A") return null;
|
|
4570
|
+
const positionChange = classifyHyperliquidPositionChange({
|
|
4571
|
+
startPosition: params.fill.startPosition,
|
|
4572
|
+
side,
|
|
4573
|
+
size: normalized.size
|
|
4574
|
+
});
|
|
4575
|
+
const closedPnl = finiteNumber(params.fill.closedPnl);
|
|
4576
|
+
const liquidation = params.fill.liquidation != null && typeof params.fill.liquidation === "object";
|
|
4577
|
+
const positionFields = positionChange ? side === "B" ? {
|
|
4578
|
+
buyerStartPosition: positionChange.startPosition,
|
|
4579
|
+
buyerEndPosition: positionChange.endPosition,
|
|
4580
|
+
buyerPositionAction: positionChange.action,
|
|
4581
|
+
buyerClosedPnl: closedPnl,
|
|
4582
|
+
buyerLiquidation: liquidation
|
|
4583
|
+
} : {
|
|
4584
|
+
sellerStartPosition: positionChange.startPosition,
|
|
4585
|
+
sellerEndPosition: positionChange.endPosition,
|
|
4586
|
+
sellerPositionAction: positionChange.action,
|
|
4587
|
+
sellerClosedPnl: closedPnl,
|
|
4588
|
+
sellerLiquidation: liquidation
|
|
4589
|
+
} : {};
|
|
4590
|
+
return {
|
|
4591
|
+
symbol: normalized.symbol,
|
|
4592
|
+
ts: new Date(normalized.timestamp),
|
|
4593
|
+
tid: normalized.tid,
|
|
4594
|
+
price: normalized.price,
|
|
4595
|
+
size: normalized.size,
|
|
4596
|
+
notionalUsd: normalized.notionalUsd,
|
|
4597
|
+
buyerAddress: side === "B" ? address : null,
|
|
4598
|
+
sellerAddress: side === "A" ? address : null,
|
|
4599
|
+
buyerTracked: side === "B",
|
|
4600
|
+
sellerTracked: side === "A",
|
|
4601
|
+
...positionFields,
|
|
4602
|
+
...params.identity,
|
|
4603
|
+
source: "hyperliquid_user_fills"
|
|
4604
|
+
};
|
|
4605
|
+
};
|
|
4606
|
+
|
|
4607
|
+
// src/lib/hyperliquidRateLimiter.ts
|
|
4608
|
+
var DEFAULT_WEIGHT_PER_MINUTE = 1e3;
|
|
4609
|
+
var WINDOW_MS = 6e4;
|
|
4610
|
+
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
4611
|
+
var HyperliquidInfoRateLimiter = class {
|
|
4612
|
+
constructor(capacity = DEFAULT_WEIGHT_PER_MINUTE, now = Date.now, wait = sleep2) {
|
|
4613
|
+
this.capacity = capacity;
|
|
4614
|
+
this.now = now;
|
|
4615
|
+
this.wait = wait;
|
|
4616
|
+
this.reservations = [];
|
|
4617
|
+
this.queue = Promise.resolve();
|
|
4618
|
+
}
|
|
4619
|
+
reserve(weight) {
|
|
4620
|
+
const normalizedWeight = Math.max(1, Math.ceil(weight));
|
|
4621
|
+
const pending = this.queue.then(
|
|
4622
|
+
() => this.reserveInternal(normalizedWeight)
|
|
4623
|
+
);
|
|
4624
|
+
this.queue = pending.catch(() => void 0);
|
|
4625
|
+
return pending;
|
|
4626
|
+
}
|
|
4627
|
+
async reserveInternal(weight) {
|
|
4628
|
+
if (weight > this.capacity) {
|
|
4629
|
+
throw new Error(
|
|
4630
|
+
`Hyperliquid request weight ${weight} exceeds limiter capacity ${this.capacity}`
|
|
4631
|
+
);
|
|
4632
|
+
}
|
|
4633
|
+
for (; ; ) {
|
|
4634
|
+
const nowMs = this.now();
|
|
4635
|
+
this.reservations = this.reservations.filter(
|
|
4636
|
+
(reservation) => reservation.atMs > nowMs - WINDOW_MS
|
|
4637
|
+
);
|
|
4638
|
+
const used = this.reservations.reduce(
|
|
4639
|
+
(sum, reservation) => sum + reservation.weight,
|
|
4640
|
+
0
|
|
4641
|
+
);
|
|
4642
|
+
if (used + weight <= this.capacity) {
|
|
4643
|
+
this.reservations.push({ atMs: nowMs, weight });
|
|
4644
|
+
return;
|
|
4645
|
+
}
|
|
4646
|
+
const oldest = this.reservations[0];
|
|
4647
|
+
await this.wait(Math.max(1, oldest.atMs + WINDOW_MS - nowMs));
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
};
|
|
4651
|
+
var hyperliquidInfoResponseWeight = (rows) => Math.max(0, Math.ceil(Math.max(0, rows) / 20));
|
|
4652
|
+
|
|
4653
|
+
// src/lib/hyperliquidWhaleBackfill.ts
|
|
4654
|
+
var DEFAULT_BASE_URL = "https://api.hyperliquid.xyz";
|
|
4655
|
+
var DEFAULT_CONCURRENCY = 2;
|
|
4656
|
+
var DEFAULT_RATE_LIMIT_WEIGHT = 1e3;
|
|
4657
|
+
var MAX_PAGE_SIZE = 2e3;
|
|
4658
|
+
var MAX_PAGES_PER_WHALE = 5;
|
|
4659
|
+
var REQUEST_WEIGHT = 20;
|
|
4660
|
+
var sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
4661
|
+
var positiveInt = (value, fallback) => {
|
|
4662
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
4663
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
4664
|
+
};
|
|
4665
|
+
var minuteCeil = (value) => Math.ceil(value / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS;
|
|
4666
|
+
var nextMinute = (value) => (Math.floor(value / HYPERLIQUID_WHALE_BUCKET_MS) + 1) * HYPERLIQUID_WHALE_BUCKET_MS;
|
|
4667
|
+
var fetchHyperliquidUserFillsByTime = async (params) => {
|
|
4668
|
+
const fetchImpl = params.fetchImpl ?? fetch;
|
|
4669
|
+
const limiter = params.rateLimiter ?? new HyperliquidInfoRateLimiter(
|
|
4670
|
+
positiveInt(
|
|
4671
|
+
process.env.HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT,
|
|
4672
|
+
DEFAULT_RATE_LIMIT_WEIGHT
|
|
4673
|
+
),
|
|
4674
|
+
Date.now,
|
|
4675
|
+
params.wait ?? sleep3
|
|
4676
|
+
);
|
|
4677
|
+
const fills = [];
|
|
4678
|
+
let cursor = params.startTime;
|
|
4679
|
+
for (let page = 0; page < (params.maxPages ?? MAX_PAGES_PER_WHALE) && cursor <= params.endTime; page += 1) {
|
|
4680
|
+
await limiter.reserve(REQUEST_WEIGHT);
|
|
4681
|
+
const response = await fetchImpl(
|
|
4682
|
+
`${process.env.HYPERLIQUID_API_BASE_URL?.trim() || DEFAULT_BASE_URL}/info`,
|
|
4683
|
+
{
|
|
4684
|
+
method: "POST",
|
|
4685
|
+
headers: { "content-type": "application/json" },
|
|
4686
|
+
body: JSON.stringify({
|
|
4687
|
+
type: "userFillsByTime",
|
|
4688
|
+
user: params.address,
|
|
4689
|
+
startTime: cursor,
|
|
4690
|
+
endTime: params.endTime,
|
|
4691
|
+
aggregateByTime: true
|
|
4692
|
+
})
|
|
4693
|
+
}
|
|
4694
|
+
);
|
|
4695
|
+
if (!response.ok) {
|
|
4696
|
+
throw new Error(
|
|
4697
|
+
`Hyperliquid userFillsByTime ${response.status}: ${await response.text()}`
|
|
4698
|
+
);
|
|
4699
|
+
}
|
|
4700
|
+
const raw = await response.json();
|
|
4701
|
+
const pageRows = Array.isArray(raw) ? raw : [];
|
|
4702
|
+
const responseWeight = hyperliquidInfoResponseWeight(pageRows.length);
|
|
4703
|
+
if (responseWeight > 0) await limiter.reserve(responseWeight);
|
|
4704
|
+
fills.push(...pageRows);
|
|
4705
|
+
if (pageRows.length < MAX_PAGE_SIZE) {
|
|
4706
|
+
return { fills, truncated: false, coveredFromMs: params.startTime };
|
|
4707
|
+
}
|
|
4708
|
+
const lastTime = Math.max(
|
|
4709
|
+
...pageRows.map((row) => Number(row.time)).filter(Number.isFinite)
|
|
4710
|
+
);
|
|
4711
|
+
if (!Number.isFinite(lastTime) || lastTime < cursor) {
|
|
4712
|
+
throw new Error(
|
|
4713
|
+
`Hyperliquid fills pagination stalled for ${params.address}`
|
|
4714
|
+
);
|
|
4715
|
+
}
|
|
4716
|
+
cursor = lastTime + 1;
|
|
4717
|
+
}
|
|
4718
|
+
if (cursor > params.endTime) {
|
|
4719
|
+
return { fills, truncated: false, coveredFromMs: params.startTime };
|
|
4720
|
+
}
|
|
4721
|
+
const firstAvailableTime = Math.min(
|
|
4722
|
+
...fills.map((fill) => Number(fill.time)).filter(Number.isFinite)
|
|
4723
|
+
);
|
|
4724
|
+
return {
|
|
4725
|
+
fills,
|
|
4726
|
+
truncated: true,
|
|
4727
|
+
coveredFromMs: Math.min(params.endTime + 1, nextMinute(firstAvailableTime))
|
|
4728
|
+
};
|
|
4729
|
+
};
|
|
4730
|
+
var runConcurrent = async (params) => {
|
|
4731
|
+
let nextIndex = 0;
|
|
4732
|
+
await Promise.all(
|
|
4733
|
+
Array.from(
|
|
4734
|
+
{ length: Math.min(params.concurrency, params.items.length) },
|
|
4735
|
+
async () => {
|
|
4736
|
+
for (; ; ) {
|
|
4737
|
+
const index = nextIndex;
|
|
4738
|
+
nextIndex += 1;
|
|
4739
|
+
if (index >= params.items.length) return;
|
|
4740
|
+
await params.worker(params.items[index], index);
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4743
|
+
)
|
|
4744
|
+
);
|
|
4745
|
+
};
|
|
4746
|
+
var backfillHyperliquidWhaleContext = async (params) => {
|
|
4747
|
+
const universe = (0, import_strategies2.getHyperliquidPerpUniverseSnapshot)();
|
|
4748
|
+
const whales = (0, import_strategies2.getHyperliquidWhaleRegistrySnapshot)();
|
|
4749
|
+
const trackedSymbols = new Set(universe.symbols);
|
|
4750
|
+
const identity = {
|
|
4751
|
+
universeFingerprint: universe.fingerprint,
|
|
4752
|
+
whaleRegistryFingerprint: whales.fingerprint
|
|
4753
|
+
};
|
|
4754
|
+
const fromMs = Math.floor(params.startMs / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS;
|
|
4755
|
+
const toMs = minuteCeil(params.endMs);
|
|
4756
|
+
const cached = await (0, import_timescale4.hasHyperliquidWhaleBackfillCoverage)({
|
|
4757
|
+
fromMs,
|
|
4758
|
+
toMs,
|
|
4759
|
+
...identity
|
|
4760
|
+
});
|
|
4761
|
+
const emptyResult = () => ({
|
|
4762
|
+
cached,
|
|
4763
|
+
cacheOnly: params.cacheOnly,
|
|
4764
|
+
fills: 0,
|
|
4765
|
+
events: 0,
|
|
4766
|
+
buckets: 0,
|
|
4767
|
+
coverageBuckets: 0,
|
|
4768
|
+
complete: cached,
|
|
4769
|
+
walletsProcessed: 0,
|
|
4770
|
+
walletsCached: 0,
|
|
4771
|
+
completeWallets: 0,
|
|
4772
|
+
truncatedWallets: 0,
|
|
4773
|
+
failedWallets: 0,
|
|
4774
|
+
fromMs,
|
|
4775
|
+
toMs
|
|
4776
|
+
});
|
|
4777
|
+
if (cached || params.cacheOnly) return emptyResult();
|
|
4778
|
+
const limiter = new HyperliquidInfoRateLimiter(
|
|
4779
|
+
positiveInt(
|
|
4780
|
+
process.env.HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT,
|
|
4781
|
+
DEFAULT_RATE_LIMIT_WEIGHT
|
|
4782
|
+
),
|
|
4783
|
+
Date.now,
|
|
4784
|
+
params.wait ?? sleep3
|
|
4785
|
+
);
|
|
4786
|
+
const concurrency = Math.min(
|
|
4787
|
+
4,
|
|
4788
|
+
positiveInt(
|
|
4789
|
+
params.concurrency ?? process.env.HYPERLIQUID_WHALE_CONCURRENCY,
|
|
4790
|
+
DEFAULT_CONCURRENCY
|
|
4791
|
+
)
|
|
4792
|
+
);
|
|
4793
|
+
let fills = 0;
|
|
4794
|
+
let events = 0;
|
|
4795
|
+
let walletsProcessed = 0;
|
|
4796
|
+
let walletsCached = 0;
|
|
4797
|
+
let completeWallets = 0;
|
|
4798
|
+
let truncatedWallets = 0;
|
|
4799
|
+
let failedWallets = 0;
|
|
4800
|
+
const failures = [];
|
|
4801
|
+
await runConcurrent({
|
|
4802
|
+
items: whales.addresses,
|
|
4803
|
+
concurrency,
|
|
4804
|
+
worker: async (address) => {
|
|
4805
|
+
const previous = await (0, import_timescale4.getHyperliquidWhaleWalletCoverage)({
|
|
4806
|
+
address,
|
|
4807
|
+
fromMs,
|
|
4808
|
+
toMs,
|
|
4809
|
+
...identity
|
|
4810
|
+
});
|
|
4811
|
+
if (previous && previous.status !== "failed") {
|
|
4812
|
+
walletsCached += 1;
|
|
4813
|
+
if (previous.status === "complete") completeWallets += 1;
|
|
4814
|
+
else truncatedWallets += 1;
|
|
4815
|
+
} else {
|
|
4816
|
+
try {
|
|
4817
|
+
const result2 = await fetchHyperliquidUserFillsByTime({
|
|
4818
|
+
address,
|
|
4819
|
+
startTime: fromMs,
|
|
4820
|
+
endTime: toMs - 1,
|
|
4821
|
+
fetchImpl: params.fetchImpl,
|
|
4822
|
+
rateLimiter: limiter
|
|
4823
|
+
});
|
|
4824
|
+
const addressEvents = result2.fills.map(
|
|
4825
|
+
(fill) => normalizeHyperliquidUserFill({
|
|
4826
|
+
fill,
|
|
4827
|
+
address,
|
|
4828
|
+
trackedSymbols,
|
|
4829
|
+
identity
|
|
4830
|
+
})
|
|
4831
|
+
).filter((row) => row != null);
|
|
4832
|
+
await (0, import_timescale4.upsertHyperliquidWhaleTradeEvents)(addressEvents);
|
|
4833
|
+
await (0, import_timescale4.upsertHyperliquidWhaleWalletCoverage)({
|
|
4834
|
+
address,
|
|
4835
|
+
fromMs,
|
|
4836
|
+
toMs,
|
|
4837
|
+
coveredFromMs: result2.coveredFromMs,
|
|
4838
|
+
coveredToMs: toMs,
|
|
4839
|
+
status: result2.truncated ? "truncated" : "complete",
|
|
4840
|
+
fillsCount: result2.fills.length,
|
|
4841
|
+
...identity
|
|
4842
|
+
});
|
|
4843
|
+
fills += result2.fills.length;
|
|
4844
|
+
events += addressEvents.length;
|
|
4845
|
+
if (result2.truncated) truncatedWallets += 1;
|
|
4846
|
+
else completeWallets += 1;
|
|
4847
|
+
} catch (error) {
|
|
4848
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
4849
|
+
failures.push(normalizedError);
|
|
4850
|
+
failedWallets += 1;
|
|
4851
|
+
await (0, import_timescale4.upsertHyperliquidWhaleWalletCoverage)({
|
|
4852
|
+
address,
|
|
4853
|
+
fromMs,
|
|
4854
|
+
toMs,
|
|
4855
|
+
coveredFromMs: null,
|
|
4856
|
+
coveredToMs: null,
|
|
4857
|
+
status: "failed",
|
|
4858
|
+
fillsCount: 0,
|
|
4859
|
+
error: normalizedError.message,
|
|
4860
|
+
...identity
|
|
4861
|
+
});
|
|
4862
|
+
}
|
|
4863
|
+
}
|
|
4864
|
+
walletsProcessed += 1;
|
|
4865
|
+
params.log?.(
|
|
4866
|
+
`Hyperliquid whales ${walletsProcessed}/${whales.addresses.length}: complete=${completeWallets}, truncated=${truncatedWallets}, failed=${failedWallets}, fills=${fills}, events=${events}`
|
|
4867
|
+
);
|
|
4868
|
+
}
|
|
4869
|
+
});
|
|
4870
|
+
const buckets = await (0, import_timescale4.rebuildHyperliquidWhaleFlowRows)({
|
|
4871
|
+
fromMs,
|
|
4872
|
+
toMs,
|
|
4873
|
+
...identity
|
|
4874
|
+
});
|
|
4875
|
+
const coverageBuckets = await (0, import_timescale4.rebuildHyperliquidWhaleCoverageRows)({
|
|
4876
|
+
fromMs,
|
|
4877
|
+
toMs,
|
|
4878
|
+
expectedWhales: whales.addresses.length,
|
|
4879
|
+
...identity
|
|
4880
|
+
});
|
|
4881
|
+
const result = {
|
|
4882
|
+
cached: false,
|
|
4883
|
+
cacheOnly: false,
|
|
4884
|
+
fills,
|
|
4885
|
+
events,
|
|
4886
|
+
buckets,
|
|
4887
|
+
coverageBuckets,
|
|
4888
|
+
complete: truncatedWallets === 0 && failedWallets === 0,
|
|
4889
|
+
walletsProcessed,
|
|
4890
|
+
walletsCached,
|
|
4891
|
+
completeWallets,
|
|
4892
|
+
truncatedWallets,
|
|
4893
|
+
failedWallets,
|
|
4894
|
+
fromMs,
|
|
4895
|
+
toMs
|
|
4896
|
+
};
|
|
4897
|
+
if (params.strict !== false && failures.length) {
|
|
4898
|
+
throw new AggregateError(
|
|
4899
|
+
failures,
|
|
4900
|
+
`Hyperliquid whale backfill failed for ${failures.length}/${whales.addresses.length} wallets`
|
|
4901
|
+
);
|
|
4902
|
+
}
|
|
4903
|
+
return result;
|
|
4904
|
+
};
|
|
4905
|
+
|
|
4499
4906
|
// src/lib/marketContextPrepare.ts
|
|
4500
4907
|
var shouldPrepareDerivativesContextForRun = (params) => {
|
|
4501
4908
|
if (params.universe === "tradfi") return false;
|
|
@@ -4546,6 +4953,20 @@ var shouldPrepareCoinMarketCapContextForRun = (params) => {
|
|
|
4546
4953
|
cacheOnly: params.cacheOnly
|
|
4547
4954
|
});
|
|
4548
4955
|
};
|
|
4956
|
+
var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
|
|
4957
|
+
const strategyRequiresContext = params.strategyNames?.includes(
|
|
4958
|
+
"HyperliquidConsensus"
|
|
4959
|
+
);
|
|
4960
|
+
if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
|
|
4961
|
+
return false;
|
|
4962
|
+
}
|
|
4963
|
+
const configured = String(
|
|
4964
|
+
process.env.HYPERLIQUID_WHALE_BACKFILL_ENABLED ?? ""
|
|
4965
|
+
).trim().toLowerCase();
|
|
4966
|
+
if (["0", "false", "no", "off"].includes(configured)) return false;
|
|
4967
|
+
if (strategyRequiresContext) return true;
|
|
4968
|
+
return ["1", "true", "yes", "on"].includes(configured);
|
|
4969
|
+
};
|
|
4549
4970
|
var resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
|
|
4550
4971
|
var buildCoinMarketCapBackfillParams = (params) => ({
|
|
4551
4972
|
userName: params.userName,
|
|
@@ -4582,6 +5003,18 @@ var prepareMarketContextForRun = async (params) => {
|
|
|
4582
5003
|
)
|
|
4583
5004
|
);
|
|
4584
5005
|
}
|
|
5006
|
+
if (shouldPrepareHyperliquidWhaleContextForRun(params)) {
|
|
5007
|
+
await timeOperation2(
|
|
5008
|
+
"hyperliquid whale context backfill",
|
|
5009
|
+
() => backfillHyperliquidWhaleContext({
|
|
5010
|
+
startMs: params.preloadStartMs ?? params.startMs,
|
|
5011
|
+
endMs: params.endMs,
|
|
5012
|
+
cacheOnly: params.cacheOnly,
|
|
5013
|
+
strict: false,
|
|
5014
|
+
log
|
|
5015
|
+
})
|
|
5016
|
+
);
|
|
5017
|
+
}
|
|
4585
5018
|
if (shouldPrepareBinanceMarketContextForRun(params)) {
|
|
4586
5019
|
await timeOperation2("binance market context backfill", () => {
|
|
4587
5020
|
const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
|
|
@@ -4602,5 +5035,6 @@ var prepareMarketContextForRun = async (params) => {
|
|
|
4602
5035
|
prepareMarketContextForRun,
|
|
4603
5036
|
shouldPrepareBinanceMarketContextForRun,
|
|
4604
5037
|
shouldPrepareCoinMarketCapContextForRun,
|
|
4605
|
-
shouldPrepareDerivativesContextForRun
|
|
5038
|
+
shouldPrepareDerivativesContextForRun,
|
|
5039
|
+
shouldPrepareHyperliquidWhaleContextForRun
|
|
4606
5040
|
});
|
|
@@ -41,6 +41,8 @@ var import_node_child_process = require("child_process");
|
|
|
41
41
|
var import_node_crypto = require("crypto");
|
|
42
42
|
var import_promises = __toESM(require("fs/promises"));
|
|
43
43
|
var import_node_path = __toESM(require("path"));
|
|
44
|
+
var import_types = require("@tradejs/types");
|
|
45
|
+
var import_strategies = require("@tradejs/node/strategies");
|
|
44
46
|
var RUNTIME_CONTEXT_ENV_KEYS = [
|
|
45
47
|
"AI_MODE",
|
|
46
48
|
"MIN_AI_QUALITY",
|
|
@@ -58,7 +60,12 @@ var RUNTIME_CONTEXT_ENV_KEYS = [
|
|
|
58
60
|
"COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS",
|
|
59
61
|
"COINMARKETCAP_CONTEXT_BACKFILL_ENABLED",
|
|
60
62
|
"COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
|
|
61
|
-
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
|
|
63
|
+
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS",
|
|
64
|
+
"HYPERLIQUID_WHALE_CONTEXT_ENABLED",
|
|
65
|
+
"HYPERLIQUID_WHALE_BACKFILL_ENABLED",
|
|
66
|
+
"HYPERLIQUID_WHALE_MIN_COVERAGE_PCT",
|
|
67
|
+
"HYPERLIQUID_WHALE_CONCURRENCY",
|
|
68
|
+
"HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT"
|
|
62
69
|
];
|
|
63
70
|
var RUNTIME_CONTEXT_DATA_MODEL = {
|
|
64
71
|
derivativesSourceIntervals: "15m",
|
|
@@ -68,7 +75,11 @@ var RUNTIME_CONTEXT_DATA_MODEL = {
|
|
|
68
75
|
coinMarketCapAvailabilityModel: "closed-interval-all-daily",
|
|
69
76
|
coinMarketCapDataModelVersion: 3,
|
|
70
77
|
binanceBreadthUniverseModel: "fixed-versioned-top5-top10-top30-top50-top100",
|
|
71
|
-
binanceBreadthDataModelVersion: 2
|
|
78
|
+
binanceBreadthDataModelVersion: 2,
|
|
79
|
+
hyperliquidWhaleCanonicalInterval: "1m",
|
|
80
|
+
hyperliquidWhaleGateMinNotionalUsd: 5e4,
|
|
81
|
+
hyperliquidWhaleMinCoveragePct: 0.8,
|
|
82
|
+
hyperliquidWhaleDataModelVersion: import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
72
83
|
};
|
|
73
84
|
var normalizeForStableJson = (value) => {
|
|
74
85
|
if (Array.isArray(value)) {
|
|
@@ -81,10 +92,24 @@ var normalizeForStableJson = (value) => {
|
|
|
81
92
|
}
|
|
82
93
|
return value;
|
|
83
94
|
};
|
|
95
|
+
var resolveRuntimeMaxLossValue = (config) => {
|
|
96
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const strategyConfig = config.strategyConfig;
|
|
100
|
+
if (!strategyConfig || typeof strategyConfig !== "object" || Array.isArray(strategyConfig)) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const maxLossValue = Number(
|
|
104
|
+
strategyConfig.MAX_LOSS_VALUE
|
|
105
|
+
);
|
|
106
|
+
return Number.isFinite(maxLossValue) ? maxLossValue : null;
|
|
107
|
+
};
|
|
84
108
|
var fingerprintRuntimeValue = (value) => (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
|
|
85
109
|
var gitLineageCache = /* @__PURE__ */ new Map();
|
|
86
110
|
var gateFingerprintCache = /* @__PURE__ */ new Map();
|
|
87
111
|
var binanceBreadthFingerprintCache = /* @__PURE__ */ new Map();
|
|
112
|
+
var snapshotFingerprintCache = /* @__PURE__ */ new Map();
|
|
88
113
|
var resolveGitLineage = (projectRoot) => {
|
|
89
114
|
const cached = gitLineageCache.get(projectRoot);
|
|
90
115
|
if (cached) {
|
|
@@ -158,6 +183,24 @@ var resolveBinanceBreadthFingerprint = async (projectRoot) => {
|
|
|
158
183
|
binanceBreadthFingerprintCache.set(projectRoot, result);
|
|
159
184
|
return result;
|
|
160
185
|
};
|
|
186
|
+
var resolveSnapshotFingerprint = async (projectRoot, relativePath) => {
|
|
187
|
+
const cacheKey = `${projectRoot}:${relativePath}`;
|
|
188
|
+
if (snapshotFingerprintCache.has(cacheKey)) {
|
|
189
|
+
return snapshotFingerprintCache.get(cacheKey) ?? null;
|
|
190
|
+
}
|
|
191
|
+
const content = await readOptionalFile(import_node_path.default.join(projectRoot, relativePath));
|
|
192
|
+
let result = null;
|
|
193
|
+
if (content) {
|
|
194
|
+
try {
|
|
195
|
+
const parsed = JSON.parse(content.toString("utf8"));
|
|
196
|
+
result = typeof parsed.fingerprint === "string" && parsed.fingerprint.trim() ? parsed.fingerprint.trim() : null;
|
|
197
|
+
} catch {
|
|
198
|
+
result = null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
snapshotFingerprintCache.set(cacheKey, result);
|
|
202
|
+
return result;
|
|
203
|
+
};
|
|
161
204
|
var resolveGateFingerprint = async ({
|
|
162
205
|
projectRoot,
|
|
163
206
|
strategyName,
|
|
@@ -173,7 +216,8 @@ var resolveGateFingerprint = async ({
|
|
|
173
216
|
`packages/strategies/src/${strategyName}/guardrails.ts`,
|
|
174
217
|
`packages/strategies/src/${strategyName}/pockets.ts`,
|
|
175
218
|
`packages/strategies/src/${strategyName}/config.ts`,
|
|
176
|
-
"packages/node/src/ai.ts"
|
|
219
|
+
"packages/node/src/ai.ts",
|
|
220
|
+
"packages/core/src/utils/strategyHelpers/signalBuilders.ts"
|
|
177
221
|
];
|
|
178
222
|
const optionalEntries = await Promise.all(
|
|
179
223
|
relativeCandidates.map(async (relativePath) => {
|
|
@@ -211,12 +255,22 @@ var buildRuntimeLineage = async ({
|
|
|
211
255
|
env = process.env
|
|
212
256
|
}) => {
|
|
213
257
|
const git = resolveGitLineage(projectRoot);
|
|
258
|
+
const hyperliquidPerpUniverseFingerprint = typeof import_strategies.getHyperliquidPerpUniverseSnapshot === "function" ? (0, import_strategies.getHyperliquidPerpUniverseSnapshot)().fingerprint : await resolveSnapshotFingerprint(
|
|
259
|
+
projectRoot,
|
|
260
|
+
"packages/node/src/config/hyperliquidPerpUniverse.json"
|
|
261
|
+
);
|
|
262
|
+
const hyperliquidWhaleRegistryFingerprint = typeof import_strategies.getHyperliquidWhaleRegistrySnapshot === "function" ? (0, import_strategies.getHyperliquidWhaleRegistrySnapshot)().fingerprint : await resolveSnapshotFingerprint(
|
|
263
|
+
projectRoot,
|
|
264
|
+
"packages/node/src/config/hyperliquidWhales.json"
|
|
265
|
+
);
|
|
214
266
|
const context = {
|
|
215
267
|
...Object.fromEntries(
|
|
216
268
|
RUNTIME_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
|
|
217
269
|
),
|
|
218
270
|
...RUNTIME_CONTEXT_DATA_MODEL,
|
|
219
271
|
binanceBreadthUniverseFingerprint: await resolveBinanceBreadthFingerprint(projectRoot),
|
|
272
|
+
hyperliquidPerpUniverseFingerprint,
|
|
273
|
+
hyperliquidWhaleRegistryFingerprint,
|
|
220
274
|
...runContext
|
|
221
275
|
};
|
|
222
276
|
return {
|
|
@@ -228,7 +282,8 @@ var buildRuntimeLineage = async ({
|
|
|
228
282
|
gitSha: git.gitSha
|
|
229
283
|
}),
|
|
230
284
|
configFingerprint: fingerprintRuntimeValue(config),
|
|
231
|
-
contextFingerprint: fingerprintRuntimeValue(context)
|
|
285
|
+
contextFingerprint: fingerprintRuntimeValue(context),
|
|
286
|
+
maxLossValue: resolveRuntimeMaxLossValue(config)
|
|
232
287
|
};
|
|
233
288
|
};
|
|
234
289
|
var runtimeLineageKey = (lineage) => [
|
|
@@ -244,6 +299,7 @@ var resetRuntimeLineageCachesForTests = () => {
|
|
|
244
299
|
gitLineageCache.clear();
|
|
245
300
|
gateFingerprintCache.clear();
|
|
246
301
|
binanceBreadthFingerprintCache.clear();
|
|
302
|
+
snapshotFingerprintCache.clear();
|
|
247
303
|
};
|
|
248
304
|
// Annotate the CommonJS export names for ESM import in node:
|
|
249
305
|
0 && (module.exports = {
|
package/dist/scripts/aiTrain.js
CHANGED
|
@@ -2557,6 +2557,8 @@ var import_node_child_process = require("child_process");
|
|
|
2557
2557
|
var import_node_crypto = require("crypto");
|
|
2558
2558
|
var import_promises = __toESM(require("fs/promises"));
|
|
2559
2559
|
var import_node_path = __toESM(require("path"));
|
|
2560
|
+
var import_strategies = require("@tradejs/node/strategies");
|
|
2561
|
+
var import_types = require("@tradejs/types");
|
|
2560
2562
|
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
2561
2563
|
var RESEARCH_CONTEXT_ENV_KEYS = [
|
|
2562
2564
|
"AI_MODE",
|
|
@@ -2575,13 +2577,22 @@ var RESEARCH_CONTEXT_ENV_KEYS = [
|
|
|
2575
2577
|
"COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS",
|
|
2576
2578
|
"COINMARKETCAP_CONTEXT_BACKFILL_ENABLED",
|
|
2577
2579
|
"COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
|
|
2578
|
-
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
|
|
2580
|
+
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS",
|
|
2581
|
+
"HYPERLIQUID_WHALE_CONTEXT_ENABLED",
|
|
2582
|
+
"HYPERLIQUID_WHALE_BACKFILL_ENABLED",
|
|
2583
|
+
"HYPERLIQUID_WHALE_MIN_COVERAGE_PCT",
|
|
2584
|
+
"HYPERLIQUID_WHALE_CONCURRENCY",
|
|
2585
|
+
"HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT"
|
|
2579
2586
|
];
|
|
2580
2587
|
var DERIVATIVES_CONTEXT_DATA_MODEL = {
|
|
2581
2588
|
derivativesSourceIntervals: "15m",
|
|
2582
2589
|
derivativesDerivedIntervals: "1h",
|
|
2583
2590
|
derivativesHourlyFallback: "stored-1h",
|
|
2584
|
-
derivativesDataModelVersion: 2
|
|
2591
|
+
derivativesDataModelVersion: 2,
|
|
2592
|
+
hyperliquidWhaleCanonicalInterval: "1m",
|
|
2593
|
+
hyperliquidWhaleGateMinNotionalUsd: 5e4,
|
|
2594
|
+
hyperliquidWhaleMinCoveragePct: 0.8,
|
|
2595
|
+
hyperliquidWhaleDataModelVersion: import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2585
2596
|
};
|
|
2586
2597
|
var normalizeForStableJson = (value) => {
|
|
2587
2598
|
if (Array.isArray(value)) {
|
|
@@ -2738,12 +2749,16 @@ var buildAiTrainLineage = async ({
|
|
|
2738
2749
|
const { gitSha, gitDirty } = getGitLineage(projectRoot);
|
|
2739
2750
|
const gate = await resolveGateFingerprint(projectRoot, strategyName, gitSha);
|
|
2740
2751
|
const normalizedConfigIds = [...new Set(configIds.filter(Boolean))].sort();
|
|
2752
|
+
const hyperliquidPerpUniverseFingerprint = typeof import_strategies.getHyperliquidPerpUniverseSnapshot === "function" ? (0, import_strategies.getHyperliquidPerpUniverseSnapshot)().fingerprint : null;
|
|
2753
|
+
const hyperliquidWhaleRegistryFingerprint = typeof import_strategies.getHyperliquidWhaleRegistrySnapshot === "function" ? (0, import_strategies.getHyperliquidWhaleRegistrySnapshot)().fingerprint : null;
|
|
2741
2754
|
const context = {
|
|
2742
2755
|
...Object.fromEntries(
|
|
2743
2756
|
RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
|
|
2744
2757
|
),
|
|
2745
2758
|
...runContext,
|
|
2746
|
-
...DERIVATIVES_CONTEXT_DATA_MODEL
|
|
2759
|
+
...DERIVATIVES_CONTEXT_DATA_MODEL,
|
|
2760
|
+
hyperliquidPerpUniverseFingerprint,
|
|
2761
|
+
hyperliquidWhaleRegistryFingerprint
|
|
2747
2762
|
};
|
|
2748
2763
|
return {
|
|
2749
2764
|
gitSha,
|