@tradejs/infra 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/ai.js +39 -74
- package/dist/ai.mjs +37 -74
- package/dist/chunk-2CZREG43.mjs +112 -0
- package/dist/chunk-DFMKDB2R.mjs +1285 -0
- package/dist/chunk-I2J6YDBD.mjs +910 -0
- package/dist/chunk-NWXFWTWU.mjs +1114 -0
- package/dist/chunk-SZQB7ER5.mjs +492 -0
- package/dist/chunk-YVIHTUV5.mjs +286 -0
- package/dist/coreResearch.d.mts +20 -0
- package/dist/coreResearch.d.ts +20 -0
- package/dist/coreResearch.js +89 -0
- package/dist/coreResearch.mjs +48 -0
- package/dist/internal-2coHaaos.d.mts +26 -0
- package/dist/internal-2coHaaos.d.ts +26 -0
- package/dist/ml.mjs +3 -3
- package/dist/runtimeDeployments.d.mts +10 -0
- package/dist/runtimeDeployments.d.ts +10 -0
- package/dist/runtimeDeployments.js +447 -0
- package/dist/runtimeDeployments.mjs +81 -0
- package/dist/runtimeStrategyConfigs.d.mts +28 -0
- package/dist/runtimeStrategyConfigs.d.ts +28 -0
- package/dist/runtimeStrategyConfigs.js +425 -0
- package/dist/runtimeStrategyConfigs.mjs +89 -0
- package/dist/strategyReleaseEvidence.d.mts +12 -0
- package/dist/strategyReleaseEvidence.d.ts +12 -0
- package/dist/strategyReleaseEvidence.js +120 -0
- package/dist/strategyReleaseEvidence.mjs +90 -0
- package/dist/timescale/candles.d.mts +38 -0
- package/dist/timescale/candles.d.ts +38 -0
- package/dist/timescale/candles.js +408 -0
- package/dist/timescale/candles.mjs +21 -0
- package/dist/timescale/client.d.mts +4 -0
- package/dist/timescale/client.d.ts +4 -0
- package/dist/timescale/client.js +109 -0
- package/dist/timescale/client.mjs +12 -0
- package/dist/timescale/derivatives.d.mts +90 -0
- package/dist/timescale/derivatives.d.ts +90 -0
- package/dist/timescale/derivatives.js +1270 -0
- package/dist/timescale/derivatives.mjs +26 -0
- package/dist/timescale/hyperliquidWhales.d.mts +149 -0
- package/dist/timescale/hyperliquidWhales.d.ts +149 -0
- package/dist/timescale/hyperliquidWhales.js +1893 -0
- package/dist/timescale/hyperliquidWhales.mjs +30 -0
- package/dist/timescale/marketContext.d.mts +188 -0
- package/dist/timescale/marketContext.d.ts +188 -0
- package/dist/timescale/marketContext.js +2091 -0
- package/dist/timescale/marketContext.mjs +60 -0
- package/dist/timescale/spread.d.mts +11 -0
- package/dist/timescale/spread.d.ts +11 -0
- package/dist/timescale/spread.js +215 -0
- package/dist/timescale/spread.mjs +11 -0
- package/dist/timescale.d.mts +9 -476
- package/dist/timescale.d.ts +9 -476
- package/dist/timescale.js +2121 -2112
- package/dist/timescale.mjs +73 -4070
- package/dist/tradingAccounts.d.mts +2 -8
- package/dist/tradingAccounts.d.ts +2 -8
- package/dist/tradingAccounts.js +0 -71
- package/dist/tradingAccounts.mjs +0 -65
- package/dist/values-BrvcmnfM.d.mts +6 -0
- package/dist/values-BrvcmnfM.d.ts +6 -0
- package/package.json +53 -2
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// src/strategyReleaseEvidence.ts
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
import { createReadStream } from "fs";
|
|
4
|
+
import {
|
|
5
|
+
STRATEGY_EVIDENCE_MARKERS_SCHEMA
|
|
6
|
+
} from "@tradejs/types";
|
|
7
|
+
var SHA256_RE = /^[a-f0-9]{64}$/;
|
|
8
|
+
var LINEAGE_FINGERPRINT_RE = /^[a-f0-9]{16}$/;
|
|
9
|
+
var MARKER_TYPES = /* @__PURE__ */ new Set([
|
|
10
|
+
"G",
|
|
11
|
+
"L",
|
|
12
|
+
"E",
|
|
13
|
+
"D",
|
|
14
|
+
"P",
|
|
15
|
+
"R"
|
|
16
|
+
]);
|
|
17
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
18
|
+
var isString = (value) => typeof value === "string" && value.trim().length > 0;
|
|
19
|
+
var isNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
20
|
+
var canonicalStrategyEvidenceJson = (value) => {
|
|
21
|
+
const normalize = (current) => {
|
|
22
|
+
if (Array.isArray(current)) return current.map(normalize);
|
|
23
|
+
const record = asRecord(current);
|
|
24
|
+
if (!record) return current;
|
|
25
|
+
return Object.fromEntries(
|
|
26
|
+
Object.entries(record).filter(([, entry]) => entry !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalize(entry)])
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
return JSON.stringify(normalize(value));
|
|
30
|
+
};
|
|
31
|
+
var strategyEvidenceSha256 = (value) => createHash("sha256").update(canonicalStrategyEvidenceJson(value)).digest("hex");
|
|
32
|
+
var strategyEvidenceFingerprint = (value) => strategyEvidenceSha256(value).slice(0, 16);
|
|
33
|
+
var strategyEvidenceFileSha256 = async (filePath) => {
|
|
34
|
+
const hash = createHash("sha256");
|
|
35
|
+
for await (const chunk of createReadStream(filePath)) hash.update(chunk);
|
|
36
|
+
return hash.digest("hex");
|
|
37
|
+
};
|
|
38
|
+
var safeStrategyEvidenceSegment = (value, fallback = "strategy") => {
|
|
39
|
+
const safe = value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
40
|
+
return safe && safe !== "." && safe !== ".." ? safe : fallback;
|
|
41
|
+
};
|
|
42
|
+
var compactStrategyEvidenceTimestamp = (timestamp) => new Date(timestamp).toISOString().replace(/[-:]/g, "").replace(/\.000Z$/, "Z");
|
|
43
|
+
var createStrategyEvidenceMarkerEnvelope = (payload) => {
|
|
44
|
+
const payloadSha256 = strategyEvidenceSha256(payload);
|
|
45
|
+
return {
|
|
46
|
+
schema: STRATEGY_EVIDENCE_MARKERS_SCHEMA,
|
|
47
|
+
artifactId: `${safeStrategyEvidenceSegment(payload.strategy)}_${compactStrategyEvidenceTimestamp(payload.createdAt)}_${payloadSha256.slice(0, 16)}`,
|
|
48
|
+
payloadSha256,
|
|
49
|
+
payload
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
var verifyStrategyEvidenceMarkerEnvelope = (value) => {
|
|
53
|
+
const envelope = asRecord(value);
|
|
54
|
+
const payload = asRecord(envelope?.payload);
|
|
55
|
+
if (envelope?.schema !== STRATEGY_EVIDENCE_MARKERS_SCHEMA || !isString(envelope.artifactId) || !isString(envelope.payloadSha256) || !SHA256_RE.test(envelope.payloadSha256) || !payload || !isString(payload.strategy) || !isNumber(payload.createdAt) || !Array.isArray(payload.markers) || !Array.isArray(payload.sourceArtifacts)) {
|
|
56
|
+
throw new Error("Invalid strategy evidence marker envelope");
|
|
57
|
+
}
|
|
58
|
+
for (const value2 of payload.markers) {
|
|
59
|
+
const marker = asRecord(value2);
|
|
60
|
+
const coverage = asRecord(marker?.coverage);
|
|
61
|
+
if (!marker || !isString(marker.id) || !isString(marker.type) || !MARKER_TYPES.has(marker.type) || !isNumber(marker.timestamp) || !isString(marker.label) || !isString(marker.summary) || !isString(marker.artifactId) || !isString(marker.artifactSha256) || !SHA256_RE.test(marker.artifactSha256) || marker.gitSha !== void 0 && !isString(marker.gitSha) || marker.gateFingerprint !== void 0 && (!isString(marker.gateFingerprint) || !LINEAGE_FINGERPRINT_RE.test(marker.gateFingerprint)) || marker.configFingerprint !== void 0 && (!isString(marker.configFingerprint) || !LINEAGE_FINGERPRINT_RE.test(marker.configFingerprint)) || marker.contextFingerprint !== void 0 && (!isString(marker.contextFingerprint) || !LINEAGE_FINGERPRINT_RE.test(marker.contextFingerprint)) || marker.maxLossValue !== void 0 && !isNumber(marker.maxLossValue) || marker.coverage !== void 0 && (!coverage || !isNumber(coverage.startTime) || !isNumber(coverage.endTime) || coverage.startTime > coverage.endTime)) {
|
|
62
|
+
throw new Error("Invalid strategy evidence marker");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const value2 of payload.sourceArtifacts) {
|
|
66
|
+
const artifact = asRecord(value2);
|
|
67
|
+
if (!artifact || !isString(artifact.artifactId) || !isString(artifact.sha256) || !SHA256_RE.test(artifact.sha256) || artifact.path !== void 0 && typeof artifact.path !== "string") {
|
|
68
|
+
throw new Error("Invalid strategy evidence source artifact");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const payloadSha256 = strategyEvidenceSha256(payload);
|
|
72
|
+
if (payloadSha256 !== envelope.payloadSha256) {
|
|
73
|
+
throw new Error("Strategy evidence marker checksum mismatch");
|
|
74
|
+
}
|
|
75
|
+
const expectedId = `${safeStrategyEvidenceSegment(payload.strategy)}_${compactStrategyEvidenceTimestamp(payload.createdAt)}_${payloadSha256.slice(0, 16)}`;
|
|
76
|
+
if (expectedId !== envelope.artifactId) {
|
|
77
|
+
throw new Error("Strategy evidence marker artifact identity mismatch");
|
|
78
|
+
}
|
|
79
|
+
return value;
|
|
80
|
+
};
|
|
81
|
+
export {
|
|
82
|
+
canonicalStrategyEvidenceJson,
|
|
83
|
+
compactStrategyEvidenceTimestamp,
|
|
84
|
+
createStrategyEvidenceMarkerEnvelope,
|
|
85
|
+
safeStrategyEvidenceSegment,
|
|
86
|
+
strategyEvidenceFileSha256,
|
|
87
|
+
strategyEvidenceFingerprint,
|
|
88
|
+
strategyEvidenceSha256,
|
|
89
|
+
verifyStrategyEvidenceMarkerEnvelope
|
|
90
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { KlineChartData } from '@tradejs/types';
|
|
2
|
+
|
|
3
|
+
type CandleRow = {
|
|
4
|
+
provider: string;
|
|
5
|
+
symbol: string;
|
|
6
|
+
interval: number;
|
|
7
|
+
ts: Date;
|
|
8
|
+
open: number;
|
|
9
|
+
high: number;
|
|
10
|
+
low: number;
|
|
11
|
+
close: number;
|
|
12
|
+
volume?: number | null;
|
|
13
|
+
turnover?: number | null;
|
|
14
|
+
takerBuyBaseVolume?: number | null;
|
|
15
|
+
takerBuyQuoteVolume?: number | null;
|
|
16
|
+
takerSellBaseVolume?: number | null;
|
|
17
|
+
takerSellQuoteVolume?: number | null;
|
|
18
|
+
};
|
|
19
|
+
declare const toRows: (provider: string, symbol: string, interval: number, data: KlineChartData) => CandleRow[];
|
|
20
|
+
declare function upsertCandles(rows: CandleRow[]): Promise<void>;
|
|
21
|
+
declare function getCandlesRange(provider: string, symbol: string, interval: number, startMs: number, endMs: number): Promise<any[]>;
|
|
22
|
+
declare function getDataEdges(provider: string, symbol: string, interval: number): Promise<{
|
|
23
|
+
min: number | undefined;
|
|
24
|
+
max: number | undefined;
|
|
25
|
+
}>;
|
|
26
|
+
declare function getDataEdgesForSymbols(provider: string, symbols: string[], interval: number): Promise<Map<string, {
|
|
27
|
+
min?: number;
|
|
28
|
+
max?: number;
|
|
29
|
+
}>>;
|
|
30
|
+
declare function waitForDbReady(attempts?: number, delayMs?: number): Promise<void>;
|
|
31
|
+
declare function deleteCandles(provider: string, symbol: string, interval: number): Promise<void>;
|
|
32
|
+
declare function findContinuityGap(provider: string, symbol: string, interval: number): Promise<{
|
|
33
|
+
ts: number;
|
|
34
|
+
prevTs: number;
|
|
35
|
+
diffSeconds: number;
|
|
36
|
+
} | null>;
|
|
37
|
+
|
|
38
|
+
export { type CandleRow, deleteCandles, findContinuityGap, getCandlesRange, getDataEdges, getDataEdgesForSymbols, toRows, upsertCandles, waitForDbReady };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { KlineChartData } from '@tradejs/types';
|
|
2
|
+
|
|
3
|
+
type CandleRow = {
|
|
4
|
+
provider: string;
|
|
5
|
+
symbol: string;
|
|
6
|
+
interval: number;
|
|
7
|
+
ts: Date;
|
|
8
|
+
open: number;
|
|
9
|
+
high: number;
|
|
10
|
+
low: number;
|
|
11
|
+
close: number;
|
|
12
|
+
volume?: number | null;
|
|
13
|
+
turnover?: number | null;
|
|
14
|
+
takerBuyBaseVolume?: number | null;
|
|
15
|
+
takerBuyQuoteVolume?: number | null;
|
|
16
|
+
takerSellBaseVolume?: number | null;
|
|
17
|
+
takerSellQuoteVolume?: number | null;
|
|
18
|
+
};
|
|
19
|
+
declare const toRows: (provider: string, symbol: string, interval: number, data: KlineChartData) => CandleRow[];
|
|
20
|
+
declare function upsertCandles(rows: CandleRow[]): Promise<void>;
|
|
21
|
+
declare function getCandlesRange(provider: string, symbol: string, interval: number, startMs: number, endMs: number): Promise<any[]>;
|
|
22
|
+
declare function getDataEdges(provider: string, symbol: string, interval: number): Promise<{
|
|
23
|
+
min: number | undefined;
|
|
24
|
+
max: number | undefined;
|
|
25
|
+
}>;
|
|
26
|
+
declare function getDataEdgesForSymbols(provider: string, symbols: string[], interval: number): Promise<Map<string, {
|
|
27
|
+
min?: number;
|
|
28
|
+
max?: number;
|
|
29
|
+
}>>;
|
|
30
|
+
declare function waitForDbReady(attempts?: number, delayMs?: number): Promise<void>;
|
|
31
|
+
declare function deleteCandles(provider: string, symbol: string, interval: number): Promise<void>;
|
|
32
|
+
declare function findContinuityGap(provider: string, symbol: string, interval: number): Promise<{
|
|
33
|
+
ts: number;
|
|
34
|
+
prevTs: number;
|
|
35
|
+
diffSeconds: number;
|
|
36
|
+
} | null>;
|
|
37
|
+
|
|
38
|
+
export { type CandleRow, deleteCandles, findContinuityGap, getCandlesRange, getDataEdges, getDataEdgesForSymbols, toRows, upsertCandles, waitForDbReady };
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/timescale/candles.ts
|
|
21
|
+
var candles_exports = {};
|
|
22
|
+
__export(candles_exports, {
|
|
23
|
+
deleteCandles: () => deleteCandles,
|
|
24
|
+
findContinuityGap: () => findContinuityGap,
|
|
25
|
+
getCandlesRange: () => getCandlesRange,
|
|
26
|
+
getDataEdges: () => getDataEdges,
|
|
27
|
+
getDataEdgesForSymbols: () => getDataEdgesForSymbols,
|
|
28
|
+
toRows: () => toRows,
|
|
29
|
+
upsertCandles: () => upsertCandles,
|
|
30
|
+
waitForDbReady: () => waitForDbReady
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(candles_exports);
|
|
33
|
+
|
|
34
|
+
// src/timescale/pool.ts
|
|
35
|
+
var import_pg = require("pg");
|
|
36
|
+
var getPool = () => {
|
|
37
|
+
if (!global.__pgPool__) {
|
|
38
|
+
const max = Number(process.env.PG_POOL_MAX ?? 10);
|
|
39
|
+
const connectionTimeoutMillis = Number(
|
|
40
|
+
process.env.PG_CONNECTION_TIMEOUT_MS ?? 3e4
|
|
41
|
+
);
|
|
42
|
+
global.__pgPool__ = new import_pg.Pool({
|
|
43
|
+
host: process.env.PG_HOST || "127.0.0.1",
|
|
44
|
+
port: Number(process.env.PG_PORT ?? 5432),
|
|
45
|
+
user: process.env.PG_USER || "app",
|
|
46
|
+
password: String(process.env.PG_PASSWORD ?? "app"),
|
|
47
|
+
database: process.env.PG_DATABASE || process.env.PG_DB || "app",
|
|
48
|
+
max: Number.isFinite(max) && max > 0 ? Math.floor(max) : 10,
|
|
49
|
+
idleTimeoutMillis: 3e4,
|
|
50
|
+
connectionTimeoutMillis: Number.isFinite(connectionTimeoutMillis) && connectionTimeoutMillis > 0 ? Math.floor(connectionTimeoutMillis) : 3e4
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return global.__pgPool__;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/timescale/values.ts
|
|
57
|
+
var normalizeCandleProvider = (provider) => String(provider || "").trim().toLowerCase();
|
|
58
|
+
var normalizeCandleSymbol = (symbol) => String(symbol || "").trim().toUpperCase();
|
|
59
|
+
|
|
60
|
+
// src/timescale/internal.ts
|
|
61
|
+
var candlesSchemaReady = false;
|
|
62
|
+
var candlesSchemaReadyPromise = null;
|
|
63
|
+
var CANDLES_SCHEMA_LOCK_KEY = 61e4;
|
|
64
|
+
var withSchemaLock = async (lockKey, work) => {
|
|
65
|
+
const pool = getPool();
|
|
66
|
+
await pool.query("SELECT pg_advisory_lock($1)", [lockKey]);
|
|
67
|
+
try {
|
|
68
|
+
await work();
|
|
69
|
+
} finally {
|
|
70
|
+
await pool.query("SELECT pg_advisory_unlock($1)", [lockKey]);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var ensureCandlesSchema = async () => {
|
|
74
|
+
if (candlesSchemaReady) return;
|
|
75
|
+
if (candlesSchemaReadyPromise) {
|
|
76
|
+
await candlesSchemaReadyPromise;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
candlesSchemaReadyPromise = withSchemaLock(
|
|
80
|
+
CANDLES_SCHEMA_LOCK_KEY,
|
|
81
|
+
async () => {
|
|
82
|
+
const pool = getPool();
|
|
83
|
+
await pool.query("CREATE EXTENSION IF NOT EXISTS timescaledb");
|
|
84
|
+
await pool.query(`
|
|
85
|
+
CREATE TABLE IF NOT EXISTS candles (
|
|
86
|
+
provider text NOT NULL DEFAULT 'bybit',
|
|
87
|
+
symbol text NOT NULL,
|
|
88
|
+
interval integer NOT NULL,
|
|
89
|
+
ts timestamptz NOT NULL,
|
|
90
|
+
open double precision NOT NULL,
|
|
91
|
+
high double precision NOT NULL,
|
|
92
|
+
low double precision NOT NULL,
|
|
93
|
+
close double precision NOT NULL,
|
|
94
|
+
volume double precision,
|
|
95
|
+
turnover double precision,
|
|
96
|
+
taker_buy_base_volume double precision,
|
|
97
|
+
taker_buy_quote_volume double precision,
|
|
98
|
+
taker_sell_base_volume double precision,
|
|
99
|
+
taker_sell_quote_volume double precision,
|
|
100
|
+
PRIMARY KEY (provider, symbol, interval, ts)
|
|
101
|
+
)
|
|
102
|
+
`);
|
|
103
|
+
await pool.query(`
|
|
104
|
+
SELECT create_hypertable(
|
|
105
|
+
'candles',
|
|
106
|
+
'ts',
|
|
107
|
+
if_not_exists => TRUE,
|
|
108
|
+
chunk_time_interval => interval '7 days'
|
|
109
|
+
)
|
|
110
|
+
`);
|
|
111
|
+
await pool.query(`
|
|
112
|
+
CREATE INDEX IF NOT EXISTS candles_provider_symbol_interval_ts_idx
|
|
113
|
+
ON candles (provider, symbol, interval, ts DESC)
|
|
114
|
+
`);
|
|
115
|
+
await pool.query(`
|
|
116
|
+
ALTER TABLE candles
|
|
117
|
+
ADD COLUMN IF NOT EXISTS taker_buy_base_volume double precision,
|
|
118
|
+
ADD COLUMN IF NOT EXISTS taker_buy_quote_volume double precision,
|
|
119
|
+
ADD COLUMN IF NOT EXISTS taker_sell_base_volume double precision,
|
|
120
|
+
ADD COLUMN IF NOT EXISTS taker_sell_quote_volume double precision
|
|
121
|
+
`);
|
|
122
|
+
candlesSchemaReady = true;
|
|
123
|
+
}
|
|
124
|
+
).finally(() => {
|
|
125
|
+
candlesSchemaReadyPromise = null;
|
|
126
|
+
});
|
|
127
|
+
await candlesSchemaReadyPromise;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// src/timescale/candles.ts
|
|
131
|
+
var toRows = (provider, symbol, interval, data) => {
|
|
132
|
+
const normalizedProvider = normalizeCandleProvider(provider);
|
|
133
|
+
if (!normalizedProvider) {
|
|
134
|
+
throw new Error("Candle provider is required");
|
|
135
|
+
}
|
|
136
|
+
const normalizedSymbol = normalizeCandleSymbol(symbol);
|
|
137
|
+
return data.map((i) => ({
|
|
138
|
+
provider: normalizedProvider,
|
|
139
|
+
symbol: normalizedSymbol,
|
|
140
|
+
interval,
|
|
141
|
+
ts: new Date(i.timestamp),
|
|
142
|
+
// ms -> Date
|
|
143
|
+
open: i.open,
|
|
144
|
+
high: i.high,
|
|
145
|
+
low: i.low,
|
|
146
|
+
close: i.close,
|
|
147
|
+
volume: i.volume ?? null,
|
|
148
|
+
turnover: i.turnover ?? null,
|
|
149
|
+
takerBuyBaseVolume: i.takerBuyBaseVolume ?? null,
|
|
150
|
+
takerBuyQuoteVolume: i.takerBuyQuoteVolume ?? null,
|
|
151
|
+
takerSellBaseVolume: i.takerSellBaseVolume ?? null,
|
|
152
|
+
takerSellQuoteVolume: i.takerSellQuoteVolume ?? null
|
|
153
|
+
}));
|
|
154
|
+
};
|
|
155
|
+
async function upsertCandles(rows) {
|
|
156
|
+
if (!rows.length) return;
|
|
157
|
+
await ensureCandlesSchema();
|
|
158
|
+
const pool = getPool();
|
|
159
|
+
const cols = [
|
|
160
|
+
"provider",
|
|
161
|
+
"symbol",
|
|
162
|
+
"interval",
|
|
163
|
+
"ts",
|
|
164
|
+
"open",
|
|
165
|
+
"high",
|
|
166
|
+
"low",
|
|
167
|
+
"close",
|
|
168
|
+
"volume",
|
|
169
|
+
"turnover",
|
|
170
|
+
"taker_buy_base_volume",
|
|
171
|
+
"taker_buy_quote_volume",
|
|
172
|
+
"taker_sell_base_volume",
|
|
173
|
+
"taker_sell_quote_volume"
|
|
174
|
+
];
|
|
175
|
+
const maxRows = Math.floor(65535 / cols.length);
|
|
176
|
+
if (rows.length > maxRows) {
|
|
177
|
+
for (let i = 0; i < rows.length; i += maxRows) {
|
|
178
|
+
await upsertCandles(rows.slice(i, i + maxRows));
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const valuesSql = rows.map(
|
|
183
|
+
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
184
|
+
).join(",");
|
|
185
|
+
const flat = rows.flatMap((r) => [
|
|
186
|
+
normalizeCandleProvider(r.provider),
|
|
187
|
+
normalizeCandleSymbol(r.symbol),
|
|
188
|
+
r.interval,
|
|
189
|
+
r.ts,
|
|
190
|
+
r.open,
|
|
191
|
+
r.high,
|
|
192
|
+
r.low,
|
|
193
|
+
r.close,
|
|
194
|
+
r.volume ?? null,
|
|
195
|
+
r.turnover ?? null,
|
|
196
|
+
r.takerBuyBaseVolume ?? null,
|
|
197
|
+
r.takerBuyQuoteVolume ?? null,
|
|
198
|
+
r.takerSellBaseVolume ?? null,
|
|
199
|
+
r.takerSellQuoteVolume ?? null
|
|
200
|
+
]);
|
|
201
|
+
const sql = `
|
|
202
|
+
INSERT INTO candles (${cols.join(",")})
|
|
203
|
+
VALUES ${valuesSql}
|
|
204
|
+
ON CONFLICT (provider, symbol, interval, ts) DO UPDATE SET
|
|
205
|
+
open = EXCLUDED.open,
|
|
206
|
+
high = EXCLUDED.high,
|
|
207
|
+
low = EXCLUDED.low,
|
|
208
|
+
close = EXCLUDED.close,
|
|
209
|
+
volume = COALESCE(EXCLUDED.volume, candles.volume),
|
|
210
|
+
turnover = COALESCE(EXCLUDED.turnover, candles.turnover),
|
|
211
|
+
taker_buy_base_volume = COALESCE(EXCLUDED.taker_buy_base_volume, candles.taker_buy_base_volume),
|
|
212
|
+
taker_buy_quote_volume = COALESCE(EXCLUDED.taker_buy_quote_volume, candles.taker_buy_quote_volume),
|
|
213
|
+
taker_sell_base_volume = COALESCE(EXCLUDED.taker_sell_base_volume, candles.taker_sell_base_volume),
|
|
214
|
+
taker_sell_quote_volume = COALESCE(EXCLUDED.taker_sell_quote_volume, candles.taker_sell_quote_volume)
|
|
215
|
+
`;
|
|
216
|
+
const client = await pool.connect();
|
|
217
|
+
try {
|
|
218
|
+
await client.query("BEGIN");
|
|
219
|
+
await client.query(sql, flat);
|
|
220
|
+
await client.query("COMMIT");
|
|
221
|
+
} catch (e) {
|
|
222
|
+
await client.query("ROLLBACK");
|
|
223
|
+
throw e;
|
|
224
|
+
} finally {
|
|
225
|
+
client.release();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function getCandlesRange(provider, symbol, interval, startMs, endMs) {
|
|
229
|
+
await ensureCandlesSchema();
|
|
230
|
+
const pool = getPool();
|
|
231
|
+
const normalizedProvider = normalizeCandleProvider(provider);
|
|
232
|
+
const normalizedSymbol = normalizeCandleSymbol(symbol);
|
|
233
|
+
const sql = `
|
|
234
|
+
SELECT symbol, interval, ts,
|
|
235
|
+
open, high, low, close, volume, turnover,
|
|
236
|
+
taker_buy_base_volume AS "takerBuyBaseVolume",
|
|
237
|
+
taker_buy_quote_volume AS "takerBuyQuoteVolume",
|
|
238
|
+
taker_sell_base_volume AS "takerSellBaseVolume",
|
|
239
|
+
taker_sell_quote_volume AS "takerSellQuoteVolume"
|
|
240
|
+
FROM candles
|
|
241
|
+
WHERE provider = $1 AND symbol = $2 AND interval = $3
|
|
242
|
+
AND ts >= to_timestamp($4/1000.0)
|
|
243
|
+
AND ts <= to_timestamp($5/1000.0)
|
|
244
|
+
ORDER BY ts ASC
|
|
245
|
+
`;
|
|
246
|
+
const res = await pool.query(sql, [
|
|
247
|
+
normalizedProvider,
|
|
248
|
+
normalizedSymbol,
|
|
249
|
+
interval,
|
|
250
|
+
startMs,
|
|
251
|
+
endMs
|
|
252
|
+
]);
|
|
253
|
+
return res.rows;
|
|
254
|
+
}
|
|
255
|
+
async function getDataEdges(provider, symbol, interval) {
|
|
256
|
+
await ensureCandlesSchema();
|
|
257
|
+
const pool = getPool();
|
|
258
|
+
const normalizedProvider = normalizeCandleProvider(provider);
|
|
259
|
+
const normalizedSymbol = normalizeCandleSymbol(symbol);
|
|
260
|
+
const sqlMin = `
|
|
261
|
+
SELECT extract(epoch from ts)*1000 AS ms
|
|
262
|
+
FROM candles
|
|
263
|
+
WHERE provider=$1 AND symbol=$2 AND interval=$3
|
|
264
|
+
ORDER BY ts ASC
|
|
265
|
+
LIMIT 1
|
|
266
|
+
`;
|
|
267
|
+
const sqlMax = `
|
|
268
|
+
SELECT extract(epoch from ts)*1000 AS ms
|
|
269
|
+
FROM candles
|
|
270
|
+
WHERE provider=$1 AND symbol=$2 AND interval=$3
|
|
271
|
+
ORDER BY ts DESC
|
|
272
|
+
LIMIT 1
|
|
273
|
+
`;
|
|
274
|
+
const [minQ, maxQ] = await Promise.all([
|
|
275
|
+
pool.query(sqlMin, [normalizedProvider, normalizedSymbol, interval]),
|
|
276
|
+
pool.query(sqlMax, [normalizedProvider, normalizedSymbol, interval])
|
|
277
|
+
]);
|
|
278
|
+
const minRaw = minQ.rows[0]?.ms;
|
|
279
|
+
const maxRaw = maxQ.rows[0]?.ms;
|
|
280
|
+
const min = Number.isFinite(Number(minRaw)) ? Number(minRaw) : void 0;
|
|
281
|
+
const max = Number.isFinite(Number(maxRaw)) ? Number(maxRaw) : void 0;
|
|
282
|
+
return { min, max };
|
|
283
|
+
}
|
|
284
|
+
async function getDataEdgesForSymbols(provider, symbols, interval) {
|
|
285
|
+
const normalizedSymbols = [
|
|
286
|
+
...new Set(symbols.map(normalizeCandleSymbol).filter(Boolean))
|
|
287
|
+
];
|
|
288
|
+
const result = /* @__PURE__ */ new Map();
|
|
289
|
+
for (const symbol of normalizedSymbols) {
|
|
290
|
+
result.set(symbol, {});
|
|
291
|
+
}
|
|
292
|
+
if (!normalizedSymbols.length) {
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
await ensureCandlesSchema();
|
|
296
|
+
const pool = getPool();
|
|
297
|
+
const normalizedProvider = normalizeCandleProvider(provider);
|
|
298
|
+
const sql = `
|
|
299
|
+
WITH requested(symbol) AS (
|
|
300
|
+
SELECT unnest($2::text[])
|
|
301
|
+
)
|
|
302
|
+
SELECT
|
|
303
|
+
r.symbol,
|
|
304
|
+
(
|
|
305
|
+
SELECT extract(epoch from c.ts)*1000
|
|
306
|
+
FROM candles c
|
|
307
|
+
WHERE c.provider = $1 AND c.symbol = r.symbol AND c.interval = $3
|
|
308
|
+
ORDER BY c.ts ASC
|
|
309
|
+
LIMIT 1
|
|
310
|
+
) AS min_ms,
|
|
311
|
+
(
|
|
312
|
+
SELECT extract(epoch from c.ts)*1000
|
|
313
|
+
FROM candles c
|
|
314
|
+
WHERE c.provider = $1 AND c.symbol = r.symbol AND c.interval = $3
|
|
315
|
+
ORDER BY c.ts DESC
|
|
316
|
+
LIMIT 1
|
|
317
|
+
) AS max_ms
|
|
318
|
+
FROM requested r
|
|
319
|
+
`;
|
|
320
|
+
const response = await pool.query(sql, [
|
|
321
|
+
normalizedProvider,
|
|
322
|
+
normalizedSymbols,
|
|
323
|
+
interval
|
|
324
|
+
]);
|
|
325
|
+
for (const row of response.rows) {
|
|
326
|
+
const symbol = normalizeCandleSymbol(String(row.symbol || ""));
|
|
327
|
+
if (!symbol) continue;
|
|
328
|
+
const min = row.min_ms == null ? NaN : Number(row.min_ms);
|
|
329
|
+
const max = row.max_ms == null ? NaN : Number(row.max_ms);
|
|
330
|
+
result.set(symbol, {
|
|
331
|
+
...Number.isFinite(min) ? { min } : {},
|
|
332
|
+
...Number.isFinite(max) ? { max } : {}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
async function waitForDbReady(attempts = 20, delayMs = 1e3) {
|
|
338
|
+
const pool = getPool();
|
|
339
|
+
let lastError;
|
|
340
|
+
for (let i = 0; i < attempts; i++) {
|
|
341
|
+
try {
|
|
342
|
+
await pool.query("SELECT 1");
|
|
343
|
+
return;
|
|
344
|
+
} catch (e) {
|
|
345
|
+
lastError = e;
|
|
346
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
throw lastError;
|
|
350
|
+
}
|
|
351
|
+
async function deleteCandles(provider, symbol, interval) {
|
|
352
|
+
const pool = getPool();
|
|
353
|
+
const normalizedProvider = normalizeCandleProvider(provider);
|
|
354
|
+
const normalizedSymbol = normalizeCandleSymbol(symbol);
|
|
355
|
+
const sql = `
|
|
356
|
+
DELETE FROM candles
|
|
357
|
+
WHERE provider = $1 AND symbol = $2 AND interval = $3
|
|
358
|
+
`;
|
|
359
|
+
await pool.query(sql, [normalizedProvider, normalizedSymbol, interval]);
|
|
360
|
+
}
|
|
361
|
+
async function findContinuityGap(provider, symbol, interval) {
|
|
362
|
+
const pool = getPool();
|
|
363
|
+
const normalizedProvider = normalizeCandleProvider(provider);
|
|
364
|
+
const normalizedSymbol = normalizeCandleSymbol(symbol);
|
|
365
|
+
const expectedSeconds = interval * 60;
|
|
366
|
+
const sql = `
|
|
367
|
+
WITH ordered AS (
|
|
368
|
+
SELECT
|
|
369
|
+
ts,
|
|
370
|
+
LAG(ts) OVER (ORDER BY ts) AS prev_ts
|
|
371
|
+
FROM candles
|
|
372
|
+
WHERE provider = $1 AND symbol = $2 AND interval = $3
|
|
373
|
+
)
|
|
374
|
+
SELECT
|
|
375
|
+
ts,
|
|
376
|
+
prev_ts,
|
|
377
|
+
EXTRACT(EPOCH FROM (ts - prev_ts))::int AS diff_seconds
|
|
378
|
+
FROM ordered
|
|
379
|
+
WHERE prev_ts IS NOT NULL
|
|
380
|
+
AND EXTRACT(EPOCH FROM (ts - prev_ts))::int <> $4
|
|
381
|
+
ORDER BY ts ASC
|
|
382
|
+
LIMIT 1
|
|
383
|
+
`;
|
|
384
|
+
const res = await pool.query(sql, [
|
|
385
|
+
normalizedProvider,
|
|
386
|
+
normalizedSymbol,
|
|
387
|
+
interval,
|
|
388
|
+
expectedSeconds
|
|
389
|
+
]);
|
|
390
|
+
const row = res.rows[0];
|
|
391
|
+
if (!row) return null;
|
|
392
|
+
return {
|
|
393
|
+
ts: new Date(row.ts).getTime(),
|
|
394
|
+
prevTs: new Date(row.prev_ts).getTime(),
|
|
395
|
+
diffSeconds: row.diff_seconds
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
399
|
+
0 && (module.exports = {
|
|
400
|
+
deleteCandles,
|
|
401
|
+
findContinuityGap,
|
|
402
|
+
getCandlesRange,
|
|
403
|
+
getDataEdges,
|
|
404
|
+
getDataEdgesForSymbols,
|
|
405
|
+
toRows,
|
|
406
|
+
upsertCandles,
|
|
407
|
+
waitForDbReady
|
|
408
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
deleteCandles,
|
|
3
|
+
findContinuityGap,
|
|
4
|
+
getCandlesRange,
|
|
5
|
+
getDataEdges,
|
|
6
|
+
getDataEdgesForSymbols,
|
|
7
|
+
toRows,
|
|
8
|
+
upsertCandles,
|
|
9
|
+
waitForDbReady
|
|
10
|
+
} from "../chunk-YVIHTUV5.mjs";
|
|
11
|
+
import "../chunk-I2J6YDBD.mjs";
|
|
12
|
+
export {
|
|
13
|
+
deleteCandles,
|
|
14
|
+
findContinuityGap,
|
|
15
|
+
getCandlesRange,
|
|
16
|
+
getDataEdges,
|
|
17
|
+
getDataEdgesForSymbols,
|
|
18
|
+
toRows,
|
|
19
|
+
upsertCandles,
|
|
20
|
+
waitForDbReady
|
|
21
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { T as TimescaleMarketContextQueryOptions, a as TimescaleMarketContextSource, c as closeTimescalePool, b as configureTimescaleMarketContextSchemaMode } from '../internal-2coHaaos.mjs';
|
|
2
|
+
export { waitForDbReady } from './candles.mjs';
|
|
3
|
+
import 'pg';
|
|
4
|
+
import '@tradejs/types';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { T as TimescaleMarketContextQueryOptions, a as TimescaleMarketContextSource, c as closeTimescalePool, b as configureTimescaleMarketContextSchemaMode } from '../internal-2coHaaos.js';
|
|
2
|
+
export { waitForDbReady } from './candles.js';
|
|
3
|
+
import 'pg';
|
|
4
|
+
import '@tradejs/types';
|