@pear-protocol/market-sdk 0.0.1-preview.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.
Files changed (82) hide show
  1. package/README.md +319 -0
  2. package/dist/chart/cache/index.d.ts +21 -0
  3. package/dist/chart/cache/index.js +101 -0
  4. package/dist/chart/chart.d.ts +25 -0
  5. package/dist/chart/chart.js +141 -0
  6. package/dist/chart/collector/binance.d.ts +10 -0
  7. package/dist/chart/collector/binance.js +27 -0
  8. package/dist/chart/collector/bybit.d.ts +10 -0
  9. package/dist/chart/collector/bybit.js +39 -0
  10. package/dist/chart/collector/helpers.d.ts +10 -0
  11. package/dist/chart/collector/helpers.js +21 -0
  12. package/dist/chart/collector/hyperliquid.d.ts +10 -0
  13. package/dist/chart/collector/hyperliquid.js +15 -0
  14. package/dist/chart/collector/index.d.ts +41 -0
  15. package/dist/chart/collector/index.js +202 -0
  16. package/dist/chart/collector/okx.d.ts +10 -0
  17. package/dist/chart/collector/okx.js +38 -0
  18. package/dist/chart/compute/asset.d.ts +11 -0
  19. package/dist/chart/compute/asset.js +24 -0
  20. package/dist/chart/compute/index.d.ts +10 -0
  21. package/dist/chart/compute/index.js +4 -0
  22. package/dist/chart/compute/performance.d.ts +11 -0
  23. package/dist/chart/compute/performance.js +81 -0
  24. package/dist/chart/compute/price-ratio.d.ts +11 -0
  25. package/dist/chart/compute/price-ratio.js +107 -0
  26. package/dist/chart/compute/weighted-ratio.d.ts +11 -0
  27. package/dist/chart/compute/weighted-ratio.js +109 -0
  28. package/dist/chart/types.d.ts +55 -0
  29. package/dist/chart/types.js +1 -0
  30. package/dist/chart/utils.d.ts +14 -0
  31. package/dist/chart/utils.js +91 -0
  32. package/dist/chart/ws/base-candle.d.ts +29 -0
  33. package/dist/chart/ws/base-candle.js +71 -0
  34. package/dist/chart/ws/binance.d.ts +19 -0
  35. package/dist/chart/ws/binance.js +43 -0
  36. package/dist/chart/ws/bybit.d.ts +18 -0
  37. package/dist/chart/ws/bybit.js +63 -0
  38. package/dist/chart/ws/hyperliquid.d.ts +31 -0
  39. package/dist/chart/ws/hyperliquid.js +40 -0
  40. package/dist/chart/ws/index.d.ts +11 -0
  41. package/dist/chart/ws/index.js +24 -0
  42. package/dist/chart/ws/okx.d.ts +18 -0
  43. package/dist/chart/ws/okx.js +60 -0
  44. package/dist/index.d.ts +10 -0
  45. package/dist/index.js +4 -0
  46. package/dist/orderbook/book/aggregate.d.ts +26 -0
  47. package/dist/orderbook/book/aggregate.js +38 -0
  48. package/dist/orderbook/book/local-book.d.ts +37 -0
  49. package/dist/orderbook/book/local-book.js +90 -0
  50. package/dist/orderbook/orderbook.d.ts +48 -0
  51. package/dist/orderbook/orderbook.js +111 -0
  52. package/dist/orderbook/types.d.ts +67 -0
  53. package/dist/orderbook/types.js +4 -0
  54. package/dist/orderbook/utils.d.ts +12 -0
  55. package/dist/orderbook/utils.js +35 -0
  56. package/dist/orderbook/ws/base-depth.d.ts +41 -0
  57. package/dist/orderbook/ws/base-depth.js +89 -0
  58. package/dist/orderbook/ws/binance.d.ts +23 -0
  59. package/dist/orderbook/ws/binance.js +126 -0
  60. package/dist/orderbook/ws/bybit.d.ts +15 -0
  61. package/dist/orderbook/ws/bybit.js +40 -0
  62. package/dist/orderbook/ws/hyperliquid.d.ts +20 -0
  63. package/dist/orderbook/ws/hyperliquid.js +87 -0
  64. package/dist/orderbook/ws/index.d.ts +11 -0
  65. package/dist/orderbook/ws/index.js +24 -0
  66. package/dist/orderbook/ws/okx.d.ts +15 -0
  67. package/dist/orderbook/ws/okx.js +33 -0
  68. package/dist/shared/types.d.ts +6 -0
  69. package/dist/shared/types.js +1 -0
  70. package/dist/transport/base-transport.d.ts +28 -0
  71. package/dist/transport/base-transport.js +95 -0
  72. package/dist/transport/binance.d.ts +13 -0
  73. package/dist/transport/binance.js +16 -0
  74. package/dist/transport/bybit.d.ts +13 -0
  75. package/dist/transport/bybit.js +16 -0
  76. package/dist/transport/hyperliquid.d.ts +13 -0
  77. package/dist/transport/hyperliquid.js +16 -0
  78. package/dist/transport/index.d.ts +10 -0
  79. package/dist/transport/index.js +24 -0
  80. package/dist/transport/okx.d.ts +13 -0
  81. package/dist/transport/okx.js +16 -0
  82. package/package.json +37 -0
@@ -0,0 +1,10 @@
1
+ import { Connector } from '@pear-protocol/types';
2
+ import { CandleInterval, CandleData } from '../types.js';
3
+ import '../../transport/index.js';
4
+ import '../../transport/base-transport.js';
5
+ import 'partysocket';
6
+ import '../../shared/types.js';
7
+
8
+ declare function fetchHistoricalCandles(connector: Connector, symbol: string, startTime: number, endTime: number, interval: CandleInterval): Promise<CandleData[]>;
9
+
10
+ export { fetchHistoricalCandles };
@@ -0,0 +1,21 @@
1
+ import { fetchHistoricalCandles as fetchHistoricalCandles$3 } from './binance';
2
+ import { fetchHistoricalCandles as fetchHistoricalCandles$2 } from './bybit';
3
+ import { fetchHistoricalCandles as fetchHistoricalCandles$4 } from './hyperliquid';
4
+ import { fetchHistoricalCandles as fetchHistoricalCandles$1 } from './okx';
5
+
6
+ async function fetchHistoricalCandles(connector, symbol, startTime, endTime, interval) {
7
+ switch (connector) {
8
+ case "hyperliquid":
9
+ return fetchHistoricalCandles$4(symbol, startTime, endTime, interval);
10
+ case "binance":
11
+ return fetchHistoricalCandles$3(symbol, startTime, endTime, interval);
12
+ case "bybit":
13
+ return fetchHistoricalCandles$2(symbol, startTime, endTime, interval);
14
+ case "okx":
15
+ return fetchHistoricalCandles$1(symbol, startTime, endTime, interval);
16
+ default:
17
+ throw new Error(`Unsupported connector: ${connector}`);
18
+ }
19
+ }
20
+
21
+ export { fetchHistoricalCandles };
@@ -0,0 +1,10 @@
1
+ import { CandleInterval, CandleData } from '../types.js';
2
+ import '../../transport/index.js';
3
+ import '@pear-protocol/types';
4
+ import '../../transport/base-transport.js';
5
+ import 'partysocket';
6
+ import '../../shared/types.js';
7
+
8
+ declare function fetchHistoricalCandles(symbol: string, startTime: number, endTime: number, interval: CandleInterval): Promise<CandleData[]>;
9
+
10
+ export { fetchHistoricalCandles };
@@ -0,0 +1,15 @@
1
+ async function fetchHistoricalCandles(symbol, startTime, endTime, interval) {
2
+ const request = {
3
+ req: { coin: symbol, startTime, endTime, interval },
4
+ type: "candleSnapshot"
5
+ };
6
+ const response = await fetch("https://api.hyperliquid.xyz/info", {
7
+ method: "POST",
8
+ headers: { "Content-Type": "application/json" },
9
+ body: JSON.stringify(request)
10
+ });
11
+ if (!response.ok) throw new Error(`Hyperliquid API error: ${response.status} ${response.statusText}`);
12
+ return response.json();
13
+ }
14
+
15
+ export { fetchHistoricalCandles };
@@ -0,0 +1,41 @@
1
+ import { ChartConfig, TokenSelection, CandleInterval, RealtimeCandleCallback, CandleData } from '../types.js';
2
+ import '../../transport/index.js';
3
+ import '@pear-protocol/types';
4
+ import '../../transport/base-transport.js';
5
+ import 'partysocket';
6
+ import '../../shared/types.js';
7
+
8
+ declare class DataCollector {
9
+ private transport;
10
+ private longTokens;
11
+ private shortTokens;
12
+ private candleInterval;
13
+ private prevTokenSymbols;
14
+ private prevInterval;
15
+ private candleWs;
16
+ private latestCandles;
17
+ private realtimeListeners;
18
+ private cache;
19
+ constructor(config: ChartConfig);
20
+ setTokens(longTokens: TokenSelection[], shortTokens: TokenSelection[]): void;
21
+ setCandleInterval(interval: CandleInterval): void;
22
+ attachCandleWs(): void;
23
+ detachCandleWs(): void;
24
+ get isWsAttached(): boolean;
25
+ addRealtimeListener(id: string, cb: RealtimeCandleCallback): void;
26
+ removeRealtimeListener(id: string): void;
27
+ getLatestCandles(): Record<string, CandleData> | null;
28
+ getLongTokens(): TokenSelection[];
29
+ getShortTokens(): TokenSelection[];
30
+ getCandleInterval(): CandleInterval;
31
+ getEffectiveDataBoundary(interval: CandleInterval): number | null;
32
+ clearCache(): void;
33
+ fetchHistoricalPriceData(startTime: number, endTime: number, interval: CandleInterval, callback?: (data: Record<string, CandleData[]>) => void): Promise<Record<string, CandleData[]>>;
34
+ destroy(): void;
35
+ private getAllTokens;
36
+ private syncTokenChanges;
37
+ private getHistoricalPriceData;
38
+ private syncWsSubscriptions;
39
+ }
40
+
41
+ export { DataCollector };
@@ -0,0 +1,202 @@
1
+ import { CandleCache } from '../cache';
2
+ import { createCandleWs } from '../ws';
3
+ import { fetchHistoricalCandles } from './helpers';
4
+
5
+ class DataCollector {
6
+ transport;
7
+ longTokens;
8
+ shortTokens;
9
+ candleInterval;
10
+ prevTokenSymbols = /* @__PURE__ */ new Set();
11
+ prevInterval = null;
12
+ candleWs = null;
13
+ latestCandles = /* @__PURE__ */ new Map();
14
+ realtimeListeners = /* @__PURE__ */ new Map();
15
+ cache = new CandleCache();
16
+ constructor(config) {
17
+ this.transport = config.transport;
18
+ this.longTokens = config.longTokens ?? [];
19
+ this.shortTokens = config.shortTokens ?? [];
20
+ this.candleInterval = config.candleInterval ?? "1h";
21
+ this.syncTokenChanges();
22
+ }
23
+ setTokens(longTokens, shortTokens) {
24
+ this.longTokens = longTokens;
25
+ this.shortTokens = shortTokens;
26
+ this.syncWsSubscriptions();
27
+ this.syncTokenChanges();
28
+ }
29
+ setCandleInterval(interval) {
30
+ const prevInterval = this.candleInterval;
31
+ this.candleInterval = interval;
32
+ this.syncTokenChanges();
33
+ if (this.candleWs && prevInterval !== interval) {
34
+ for (const symbol of this.prevTokenSymbols) {
35
+ this.candleWs.unsubscribe(symbol, prevInterval);
36
+ }
37
+ this.latestCandles.clear();
38
+ for (const symbol of this.prevTokenSymbols) {
39
+ this.candleWs.subscribe(symbol, interval);
40
+ }
41
+ }
42
+ }
43
+ attachCandleWs() {
44
+ if (this.candleWs) return;
45
+ this.candleWs = createCandleWs(this.transport, this.transport.connector, (symbol, candle) => {
46
+ this.latestCandles.set(symbol, candle);
47
+ for (const cb of this.realtimeListeners.values()) {
48
+ try {
49
+ cb(symbol, candle);
50
+ } catch (error) {
51
+ console.warn("Error in realtime listener callback", error);
52
+ }
53
+ }
54
+ });
55
+ this.candleWs.start();
56
+ for (const token of this.getAllTokens()) {
57
+ this.candleWs.subscribe(token.symbol, this.candleInterval);
58
+ }
59
+ }
60
+ detachCandleWs() {
61
+ if (this.candleWs) {
62
+ this.candleWs.stop();
63
+ this.candleWs = null;
64
+ }
65
+ }
66
+ get isWsAttached() {
67
+ return this.candleWs !== null;
68
+ }
69
+ addRealtimeListener(id, cb) {
70
+ this.realtimeListeners.set(id, cb);
71
+ }
72
+ removeRealtimeListener(id) {
73
+ this.realtimeListeners.delete(id);
74
+ }
75
+ getLatestCandles() {
76
+ const allTokens = this.getAllTokens();
77
+ if (allTokens.length === 0) return null;
78
+ const result = {};
79
+ for (const token of allTokens) {
80
+ const candle = this.latestCandles.get(token.symbol);
81
+ if (!candle) return null;
82
+ result[token.symbol] = candle;
83
+ }
84
+ return result;
85
+ }
86
+ getLongTokens() {
87
+ return this.longTokens;
88
+ }
89
+ getShortTokens() {
90
+ return this.shortTokens;
91
+ }
92
+ getCandleInterval() {
93
+ return this.candleInterval;
94
+ }
95
+ getEffectiveDataBoundary(interval) {
96
+ const symbols = this.getAllTokens().map((t) => t.symbol);
97
+ return this.cache.getEffectiveBoundary(symbols, interval);
98
+ }
99
+ clearCache() {
100
+ this.cache.clear();
101
+ this.latestCandles.clear();
102
+ }
103
+ async fetchHistoricalPriceData(startTime, endTime, interval, callback) {
104
+ const allTokens = this.getAllTokens();
105
+ if (allTokens.length === 0) {
106
+ const emptyResult = {};
107
+ callback?.(emptyResult);
108
+ return emptyResult;
109
+ }
110
+ const tokensToFetch = allTokens.filter((token) => !this.cache.hasData(token.symbol, interval, startTime, endTime));
111
+ if (tokensToFetch.length === 0) {
112
+ const cachedData = this.getHistoricalPriceData(startTime, endTime, interval);
113
+ callback?.(cachedData);
114
+ return cachedData;
115
+ }
116
+ for (const token of tokensToFetch) {
117
+ this.cache.addLoadingToken(token.symbol);
118
+ }
119
+ try {
120
+ const fetchPromises = tokensToFetch.map(async (token) => {
121
+ try {
122
+ const candles = await fetchHistoricalCandles(
123
+ this.transport.connector,
124
+ token.symbol,
125
+ startTime,
126
+ endTime,
127
+ interval
128
+ );
129
+ this.cache.addHistoricalPriceData(token.symbol, interval, candles, { start: startTime, end: endTime });
130
+ return { symbol: token.symbol, candles, success: true };
131
+ } catch (error) {
132
+ console.warn(`Failed to fetch historical data for ${token.symbol}:`, error);
133
+ return { symbol: token.symbol, candles: [], success: false };
134
+ }
135
+ });
136
+ await Promise.all(fetchPromises);
137
+ const allData = this.getHistoricalPriceData(startTime, endTime, interval);
138
+ callback?.(allData);
139
+ return allData;
140
+ } catch (error) {
141
+ console.error("Failed to fetch historical data:", error);
142
+ throw error;
143
+ } finally {
144
+ for (const token of tokensToFetch) {
145
+ this.cache.removeLoadingToken(token.symbol);
146
+ }
147
+ }
148
+ }
149
+ destroy() {
150
+ this.detachCandleWs();
151
+ this.realtimeListeners.clear();
152
+ this.latestCandles.clear();
153
+ this.cache.clear();
154
+ }
155
+ getAllTokens() {
156
+ return [...this.longTokens, ...this.shortTokens];
157
+ }
158
+ syncTokenChanges() {
159
+ const currentSymbols = new Set(this.getAllTokens().map((t) => t.symbol));
160
+ if (this.prevInterval !== null && this.prevInterval !== this.candleInterval) {
161
+ this.cache.clear();
162
+ this.prevTokenSymbols = currentSymbols;
163
+ this.prevInterval = this.candleInterval;
164
+ return;
165
+ }
166
+ for (const symbol of this.prevTokenSymbols) {
167
+ if (!currentSymbols.has(symbol)) {
168
+ this.cache.removeToken(symbol, this.candleInterval);
169
+ }
170
+ }
171
+ this.prevTokenSymbols = currentSymbols;
172
+ this.prevInterval = this.candleInterval;
173
+ }
174
+ getHistoricalPriceData(startTime, endTime, interval) {
175
+ const allTokens = this.getAllTokens();
176
+ const result = {};
177
+ for (const token of allTokens) {
178
+ result[token.symbol] = this.cache.getData(token.symbol, interval, startTime, endTime);
179
+ }
180
+ return result;
181
+ }
182
+ syncWsSubscriptions() {
183
+ if (!this.candleWs) return;
184
+ const currentSymbols = new Set(this.getAllTokens().map((t) => t.symbol));
185
+ for (const symbol of this.prevTokenSymbols) {
186
+ if (!currentSymbols.has(symbol)) {
187
+ this.candleWs.unsubscribe(symbol, this.candleInterval);
188
+ this.latestCandles.delete(symbol);
189
+ }
190
+ }
191
+ if (currentSymbols.size === 0) {
192
+ this.detachCandleWs();
193
+ this.latestCandles.clear();
194
+ return;
195
+ }
196
+ for (const symbol of currentSymbols) {
197
+ this.candleWs.subscribe(symbol, this.candleInterval);
198
+ }
199
+ }
200
+ }
201
+
202
+ export { DataCollector };
@@ -0,0 +1,10 @@
1
+ import { CandleInterval, CandleData } from '../types.js';
2
+ import '../../transport/index.js';
3
+ import '@pear-protocol/types';
4
+ import '../../transport/base-transport.js';
5
+ import 'partysocket';
6
+ import '../../shared/types.js';
7
+
8
+ declare function fetchHistoricalCandles(instId: string, startTime: number, endTime: number, interval: CandleInterval): Promise<CandleData[]>;
9
+
10
+ export { fetchHistoricalCandles };
@@ -0,0 +1,38 @@
1
+ const OKX_URL = "https://www.okx.com/api/v5/market/candles";
2
+ const MAX_LIMIT = 300;
3
+ const intervalMap = {
4
+ "1m": "1m",
5
+ "3m": "3m",
6
+ "5m": "5m",
7
+ "15m": "15m",
8
+ "30m": "30m",
9
+ "1h": "1H",
10
+ "2h": "2H",
11
+ "4h": "4H",
12
+ "8h": "8H",
13
+ "12h": "12H",
14
+ "1d": "1D",
15
+ "3d": "3D",
16
+ "1w": "1W",
17
+ "1M": "1M"
18
+ };
19
+ function toCandleData(item) {
20
+ const startMs = Number(item[0]);
21
+ return { t: startMs, T: startMs, o: Number(item[1]), h: Number(item[2]), l: Number(item[3]), c: Number(item[4]) };
22
+ }
23
+ async function fetchHistoricalCandles(instId, startTime, endTime, interval) {
24
+ const params = new URLSearchParams({
25
+ instId,
26
+ bar: intervalMap[interval],
27
+ before: String(startTime),
28
+ after: String(endTime),
29
+ limit: String(MAX_LIMIT)
30
+ });
31
+ const response = await fetch(`${OKX_URL}?${params}`);
32
+ if (!response.ok) throw new Error(`OKX API error: ${response.status} ${response.statusText}`);
33
+ const data = await response.json();
34
+ if (data.code !== "0") throw new Error(`OKX API error: ${data.msg}`);
35
+ return data.data.reverse().map(toCandleData);
36
+ }
37
+
38
+ export { fetchHistoricalCandles };
@@ -0,0 +1,11 @@
1
+ import { CandleData, Bar } from '../types.js';
2
+ import '../../transport/index.js';
3
+ import '@pear-protocol/types';
4
+ import '../../transport/base-transport.js';
5
+ import 'partysocket';
6
+ import '../../shared/types.js';
7
+
8
+ declare function computeAssetBars(tokenCandles: Record<string, CandleData[]>, symbol: string): Bar[];
9
+ declare function computeRealtimeAssetBar(snapshot: Record<string, CandleData>, symbol: string): Bar | null;
10
+
11
+ export { computeAssetBars, computeRealtimeAssetBar };
@@ -0,0 +1,24 @@
1
+ function computeAssetBars(tokenCandles, symbol) {
2
+ const candles = tokenCandles[symbol];
3
+ if (!candles) return [];
4
+ return candles.map((c) => ({
5
+ time: c.t,
6
+ open: c.o,
7
+ high: c.h,
8
+ low: c.l,
9
+ close: c.c
10
+ }));
11
+ }
12
+ function computeRealtimeAssetBar(snapshot, symbol) {
13
+ const candle = snapshot[symbol];
14
+ if (!candle) return null;
15
+ return {
16
+ time: candle.t,
17
+ open: candle.o,
18
+ high: candle.h,
19
+ low: candle.l,
20
+ close: candle.c
21
+ };
22
+ }
23
+
24
+ export { computeAssetBars, computeRealtimeAssetBar };
@@ -0,0 +1,10 @@
1
+ export { computeAssetBars, computeRealtimeAssetBar } from './asset.js';
2
+ export { computePerformanceCandles, computeRealtimePerformanceBar } from './performance.js';
3
+ export { computePriceRatioCandles, computeRealtimePriceRatioBar } from './price-ratio.js';
4
+ export { computeRealtimeWeightedRatioBar, computeWeightedRatioCandles } from './weighted-ratio.js';
5
+ import '../types.js';
6
+ import '../../transport/index.js';
7
+ import '@pear-protocol/types';
8
+ import '../../transport/base-transport.js';
9
+ import 'partysocket';
10
+ import '../../shared/types.js';
@@ -0,0 +1,4 @@
1
+ export { computeAssetBars, computeRealtimeAssetBar } from './asset';
2
+ export { computePerformanceCandles, computeRealtimePerformanceBar } from './performance';
3
+ export { computePriceRatioCandles, computeRealtimePriceRatioBar } from './price-ratio';
4
+ export { computeRealtimeWeightedRatioBar, computeWeightedRatioCandles } from './weighted-ratio';
@@ -0,0 +1,11 @@
1
+ import { TokenSelection, CandleData, PerformanceResult, Bar } from '../types.js';
2
+ import '../../transport/index.js';
3
+ import '@pear-protocol/types';
4
+ import '../../transport/base-transport.js';
5
+ import 'partysocket';
6
+ import '../../shared/types.js';
7
+
8
+ declare function computePerformanceCandles(longTokens: TokenSelection[], shortTokens: TokenSelection[], tokenCandles: Record<string, CandleData[]>): PerformanceResult;
9
+ declare function computeRealtimePerformanceBar(longTokens: TokenSelection[], shortTokens: TokenSelection[], snapshot: Record<string, CandleData>, baselinePrices: Record<string, number>): Bar | null;
10
+
11
+ export { computePerformanceCandles, computeRealtimePerformanceBar };
@@ -0,0 +1,81 @@
1
+ import { createCandleLookups, getCompleteTimestamps } from '../utils';
2
+
3
+ function computePerformanceCandles(longTokens, shortTokens, tokenCandles) {
4
+ const empty = { bars: [], baselinePrices: {} };
5
+ const allTokens = [...longTokens, ...shortTokens];
6
+ if (allTokens.length === 0) return empty;
7
+ const candleLookups = createCandleLookups(tokenCandles);
8
+ const allSymbols = allTokens.map((t) => t.symbol);
9
+ const completeTimestamps = getCompleteTimestamps(candleLookups, allSymbols);
10
+ if (completeTimestamps.length === 0) return empty;
11
+ const baselinePrices = {};
12
+ const firstTimestamp = completeTimestamps[0];
13
+ for (const token of allTokens) {
14
+ const candle = candleLookups[token.symbol]?.get(firstTimestamp);
15
+ if (!candle || candle.o <= 0) return empty;
16
+ baselinePrices[token.symbol] = candle.o;
17
+ }
18
+ const initialPortfolioValue = 1e3;
19
+ const longSet = new Set(longTokens.map((t) => t.symbol));
20
+ const bars = [];
21
+ for (const timestamp of completeTimestamps) {
22
+ let portfolioValue = 0;
23
+ let missing = false;
24
+ for (const token of allTokens) {
25
+ const candle = candleLookups[token.symbol]?.get(timestamp);
26
+ if (!candle) {
27
+ missing = true;
28
+ break;
29
+ }
30
+ const baselinePrice = baselinePrices[token.symbol];
31
+ const allocation = token.weight / 100 * initialPortfolioValue;
32
+ const priceRatio = candle.c / baselinePrice;
33
+ if (longSet.has(token.symbol)) {
34
+ portfolioValue += allocation * priceRatio;
35
+ } else {
36
+ portfolioValue += allocation / priceRatio;
37
+ }
38
+ }
39
+ if (missing) continue;
40
+ bars.push({
41
+ time: timestamp,
42
+ open: portfolioValue,
43
+ high: portfolioValue,
44
+ low: portfolioValue,
45
+ close: portfolioValue
46
+ });
47
+ }
48
+ return { bars, baselinePrices };
49
+ }
50
+ function computeRealtimePerformanceBar(longTokens, shortTokens, snapshot, baselinePrices) {
51
+ const allTokens = [...longTokens, ...shortTokens];
52
+ if (allTokens.length === 0) return null;
53
+ const initialPortfolioValue = 1e3;
54
+ const longSet = new Set(longTokens.map((t) => t.symbol));
55
+ let portfolioValue = 0;
56
+ let time = null;
57
+ for (const token of allTokens) {
58
+ const c = snapshot[token.symbol];
59
+ if (!c) return null;
60
+ const baselinePrice = baselinePrices[token.symbol];
61
+ if (!baselinePrice || baselinePrice <= 0) return null;
62
+ const allocation = token.weight / 100 * initialPortfolioValue;
63
+ const priceRatio = c.c / baselinePrice;
64
+ if (longSet.has(token.symbol)) {
65
+ portfolioValue += allocation * priceRatio;
66
+ } else {
67
+ portfolioValue += allocation / priceRatio;
68
+ }
69
+ if (time === null) time = c.t;
70
+ }
71
+ if (time === null) return null;
72
+ return {
73
+ time,
74
+ open: portfolioValue,
75
+ high: portfolioValue,
76
+ low: portfolioValue,
77
+ close: portfolioValue
78
+ };
79
+ }
80
+
81
+ export { computePerformanceCandles, computeRealtimePerformanceBar };
@@ -0,0 +1,11 @@
1
+ import { TokenSelection, CandleData, Bar } from '../types.js';
2
+ import '../../transport/index.js';
3
+ import '@pear-protocol/types';
4
+ import '../../transport/base-transport.js';
5
+ import 'partysocket';
6
+ import '../../shared/types.js';
7
+
8
+ declare function computePriceRatioCandles(longTokens: TokenSelection[], shortTokens: TokenSelection[], tokenCandles: Record<string, CandleData[]>): Bar[];
9
+ declare function computeRealtimePriceRatioBar(longTokens: TokenSelection[], shortTokens: TokenSelection[], snapshot: Record<string, CandleData>): Bar | null;
10
+
11
+ export { computePriceRatioCandles, computeRealtimePriceRatioBar };
@@ -0,0 +1,107 @@
1
+ import { createCandleLookups, getCompleteTimestamps } from '../utils';
2
+
3
+ function computePriceRatioCandles(longTokens, shortTokens, tokenCandles) {
4
+ if (longTokens.length === 0 && shortTokens.length === 0) return [];
5
+ const candleLookups = createCandleLookups(tokenCandles);
6
+ const allSymbols = [...longTokens, ...shortTokens].map((t) => t.symbol);
7
+ const completeTimestamps = getCompleteTimestamps(candleLookups, allSymbols);
8
+ const bars = [];
9
+ for (const timestamp of completeTimestamps) {
10
+ let longSumO = 0;
11
+ let longSumH = 0;
12
+ let longSumL = 0;
13
+ let longSumC = 0;
14
+ let shortSumO = 0;
15
+ let shortSumH = 0;
16
+ let shortSumL = 0;
17
+ let shortSumC = 0;
18
+ let missing = false;
19
+ for (const token of longTokens) {
20
+ const candle = candleLookups[token.symbol]?.get(timestamp);
21
+ if (!candle) {
22
+ missing = true;
23
+ break;
24
+ }
25
+ if (!(candle.o > 0 && candle.h > 0 && candle.l > 0 && candle.c > 0)) {
26
+ missing = true;
27
+ break;
28
+ }
29
+ const w = token.weight / 100;
30
+ longSumO += w * candle.o;
31
+ longSumH += w * candle.h;
32
+ longSumL += w * candle.l;
33
+ longSumC += w * candle.c;
34
+ }
35
+ if (missing) continue;
36
+ for (const token of shortTokens) {
37
+ const candle = candleLookups[token.symbol]?.get(timestamp);
38
+ if (!candle) {
39
+ missing = true;
40
+ break;
41
+ }
42
+ if (!(candle.o > 0 && candle.h > 0 && candle.l > 0 && candle.c > 0)) {
43
+ missing = true;
44
+ break;
45
+ }
46
+ const w = token.weight / 100;
47
+ shortSumO += w * candle.o;
48
+ shortSumH += w * candle.h;
49
+ shortSumL += w * candle.l;
50
+ shortSumC += w * candle.c;
51
+ }
52
+ if (missing) continue;
53
+ if (shortSumO <= 0 || shortSumH <= 0 || shortSumL <= 0 || shortSumC <= 0) continue;
54
+ bars.push({
55
+ time: timestamp,
56
+ open: longSumO / shortSumO,
57
+ high: longSumH / shortSumH,
58
+ low: longSumL / shortSumL,
59
+ close: longSumC / shortSumC
60
+ });
61
+ }
62
+ return bars;
63
+ }
64
+ function computeRealtimePriceRatioBar(longTokens, shortTokens, snapshot) {
65
+ let longSumO = 0;
66
+ let longSumH = 0;
67
+ let longSumL = 0;
68
+ let longSumC = 0;
69
+ let shortSumO = 0;
70
+ let shortSumH = 0;
71
+ let shortSumL = 0;
72
+ let shortSumC = 0;
73
+ let time = null;
74
+ for (const token of longTokens) {
75
+ const c = snapshot[token.symbol];
76
+ if (!c) return null;
77
+ if (!(c.o > 0 && c.h > 0 && c.l > 0 && c.c > 0)) return null;
78
+ const w = token.weight / 100;
79
+ longSumO += w * c.o;
80
+ longSumH += w * c.h;
81
+ longSumL += w * c.l;
82
+ longSumC += w * c.c;
83
+ if (time === null) time = c.t;
84
+ }
85
+ for (const token of shortTokens) {
86
+ const c = snapshot[token.symbol];
87
+ if (!c) return null;
88
+ if (!(c.o > 0 && c.h > 0 && c.l > 0 && c.c > 0)) return null;
89
+ const w = token.weight / 100;
90
+ shortSumO += w * c.o;
91
+ shortSumH += w * c.h;
92
+ shortSumL += w * c.l;
93
+ shortSumC += w * c.c;
94
+ if (time === null) time = c.t;
95
+ }
96
+ if (time === null) return null;
97
+ if (shortSumO <= 0 || shortSumH <= 0 || shortSumL <= 0 || shortSumC <= 0) return null;
98
+ return {
99
+ time,
100
+ open: longSumO / shortSumO,
101
+ high: longSumH / shortSumH,
102
+ low: longSumL / shortSumL,
103
+ close: longSumC / shortSumC
104
+ };
105
+ }
106
+
107
+ export { computePriceRatioCandles, computeRealtimePriceRatioBar };
@@ -0,0 +1,11 @@
1
+ import { TokenSelection, CandleData, Bar } from '../types.js';
2
+ import '../../transport/index.js';
3
+ import '@pear-protocol/types';
4
+ import '../../transport/base-transport.js';
5
+ import 'partysocket';
6
+ import '../../shared/types.js';
7
+
8
+ declare function computeWeightedRatioCandles(longTokens: TokenSelection[], shortTokens: TokenSelection[], tokenCandles: Record<string, CandleData[]>): Bar[];
9
+ declare function computeRealtimeWeightedRatioBar(longTokens: TokenSelection[], shortTokens: TokenSelection[], snapshot: Record<string, CandleData>): Bar | null;
10
+
11
+ export { computeRealtimeWeightedRatioBar, computeWeightedRatioCandles };