@pear-protocol/utils 0.5.1 → 0.5.3
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/index.d.ts +1 -0
- package/dist/index.js +530 -186
- package/dist/sync/applicables/matching.d.ts +2 -2
- package/dist/xauth/index.d.ts +78 -0
- package/package.json +3 -2
- package/dist/asset/compute.test.d.ts +0 -1
- package/dist/basket/derive-basket-key.test.d.ts +0 -1
- package/dist/fills/compute.test.d.ts +0 -1
- package/dist/math/decimal.test.d.ts +0 -1
- package/dist/position/compute.test.d.ts +0 -1
- package/dist/proxy.test.d.ts +0 -1
- package/dist/sync/applicables/basket.test.d.ts +0 -1
- package/dist/sync/applicables/exposure.test.d.ts +0 -1
- package/dist/sync/applicables/fills.test.d.ts +0 -1
- package/dist/sync/applicables/index.test.d.ts +0 -1
- package/dist/sync/applicables/matching.test.d.ts +0 -1
- package/dist/sync/applicables/position.test.d.ts +0 -1
- package/dist/sync/applicables/synthetics.test.d.ts +0 -1
- package/dist/sync/compute.test.d.ts +0 -1
- package/dist/sync/cutoff.test.d.ts +0 -1
package/dist/index.js
CHANGED
|
@@ -1,67 +1,88 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { sha256 } from '@noble/hashes/
|
|
3
|
-
import { bytesToHex } from '@noble/hashes/utils';
|
|
1
|
+
import BigNumber7 from 'bignumber.js';
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha2';
|
|
3
|
+
import { bytesToHex, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
|
4
4
|
import { WATERMARK_FUTURE_SKEW_MS } from '@pear-protocol/types';
|
|
5
|
+
import { base64urlnopad } from '@scure/base';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
9
|
function parse(value) {
|
|
8
|
-
return value instanceof
|
|
10
|
+
return value instanceof BigNumber7 ? value : new BigNumber7(value);
|
|
9
11
|
}
|
|
12
|
+
__name(parse, "parse");
|
|
10
13
|
function toDecimalString(value) {
|
|
11
14
|
return parse(value).toFixed();
|
|
12
15
|
}
|
|
16
|
+
__name(toDecimalString, "toDecimalString");
|
|
13
17
|
function signed(value) {
|
|
14
18
|
const bn = parse(value);
|
|
15
19
|
return bn.isNegative() && !bn.isZero() ? bn.toFixed() : `+${bn.toFixed()}`;
|
|
16
20
|
}
|
|
21
|
+
__name(signed, "signed");
|
|
17
22
|
function add(a, b) {
|
|
18
23
|
return parse(a).plus(parse(b));
|
|
19
24
|
}
|
|
25
|
+
__name(add, "add");
|
|
20
26
|
function subtract(a, b) {
|
|
21
27
|
return parse(a).minus(parse(b));
|
|
22
28
|
}
|
|
29
|
+
__name(subtract, "subtract");
|
|
23
30
|
function multiply(a, b) {
|
|
24
31
|
return parse(a).times(parse(b));
|
|
25
32
|
}
|
|
33
|
+
__name(multiply, "multiply");
|
|
26
34
|
function divide(a, b) {
|
|
27
35
|
return parse(a).div(parse(b));
|
|
28
36
|
}
|
|
37
|
+
__name(divide, "divide");
|
|
29
38
|
function negate(value) {
|
|
30
39
|
return parse(value).negated();
|
|
31
40
|
}
|
|
41
|
+
__name(negate, "negate");
|
|
32
42
|
function abs(value) {
|
|
33
43
|
return parse(value).abs();
|
|
34
44
|
}
|
|
45
|
+
__name(abs, "abs");
|
|
35
46
|
function min(a, b) {
|
|
36
|
-
return
|
|
47
|
+
return BigNumber7.min(parse(a), parse(b));
|
|
37
48
|
}
|
|
49
|
+
__name(min, "min");
|
|
38
50
|
function isZero(value) {
|
|
39
51
|
return parse(value).isZero();
|
|
40
52
|
}
|
|
53
|
+
__name(isZero, "isZero");
|
|
41
54
|
function isPositive(value) {
|
|
42
55
|
return parse(value).isPositive();
|
|
43
56
|
}
|
|
57
|
+
__name(isPositive, "isPositive");
|
|
44
58
|
function isNegative(value) {
|
|
45
59
|
return parse(value).isNegative();
|
|
46
60
|
}
|
|
61
|
+
__name(isNegative, "isNegative");
|
|
47
62
|
function isFiniteDecimal(value) {
|
|
48
63
|
return parse(value).isFinite();
|
|
49
64
|
}
|
|
65
|
+
__name(isFiniteDecimal, "isFiniteDecimal");
|
|
50
66
|
function isEqualTo(a, b) {
|
|
51
67
|
return parse(a).isEqualTo(parse(b));
|
|
52
68
|
}
|
|
69
|
+
__name(isEqualTo, "isEqualTo");
|
|
53
70
|
function isGreaterThan(a, b) {
|
|
54
71
|
return parse(a).isGreaterThan(parse(b));
|
|
55
72
|
}
|
|
73
|
+
__name(isGreaterThan, "isGreaterThan");
|
|
56
74
|
function isGreaterThanOrEqual(a, b) {
|
|
57
75
|
return parse(a).isGreaterThanOrEqualTo(parse(b));
|
|
58
76
|
}
|
|
77
|
+
__name(isGreaterThanOrEqual, "isGreaterThanOrEqual");
|
|
59
78
|
function isLessThan(a, b) {
|
|
60
79
|
return parse(a).isLessThan(parse(b));
|
|
61
80
|
}
|
|
81
|
+
__name(isLessThan, "isLessThan");
|
|
62
82
|
function isLessThanOrEqual(a, b) {
|
|
63
83
|
return parse(a).isLessThanOrEqualTo(parse(b));
|
|
64
84
|
}
|
|
85
|
+
__name(isLessThanOrEqual, "isLessThanOrEqual");
|
|
65
86
|
function sideSign(side) {
|
|
66
87
|
switch (side) {
|
|
67
88
|
case "BUY":
|
|
@@ -70,36 +91,42 @@ function sideSign(side) {
|
|
|
70
91
|
return -1;
|
|
71
92
|
}
|
|
72
93
|
}
|
|
94
|
+
__name(sideSign, "sideSign");
|
|
73
95
|
function signedBySide(value, side) {
|
|
74
96
|
return parse(value).times(sideSign(side));
|
|
75
97
|
}
|
|
98
|
+
__name(signedBySide, "signedBySide");
|
|
76
99
|
|
|
77
100
|
// src/math/index.ts
|
|
78
|
-
var ZERO = new
|
|
79
|
-
var BIPS = new
|
|
101
|
+
var ZERO = new BigNumber7(0);
|
|
102
|
+
var BIPS = new BigNumber7(1e4);
|
|
80
103
|
function exponentiate(value) {
|
|
81
104
|
const result = Math.exp(value);
|
|
82
|
-
return new
|
|
105
|
+
return new BigNumber7(result);
|
|
83
106
|
}
|
|
107
|
+
__name(exponentiate, "exponentiate");
|
|
84
108
|
function sign(value) {
|
|
85
109
|
if (value.isZero()) return 0;
|
|
86
110
|
return value.isPositive() ? 1 : -1;
|
|
87
111
|
}
|
|
112
|
+
__name(sign, "sign");
|
|
88
113
|
function countDecimals(value) {
|
|
89
114
|
const parts = value.split(".");
|
|
90
115
|
return parts[1]?.length ?? 0;
|
|
91
116
|
}
|
|
117
|
+
__name(countDecimals, "countDecimals");
|
|
92
118
|
function compareValue(track, value, threshold) {
|
|
93
119
|
return track === "PEAK" ? value.isGreaterThanOrEqualTo(threshold) : value.isLessThan(threshold);
|
|
94
120
|
}
|
|
121
|
+
__name(compareValue, "compareValue");
|
|
95
122
|
|
|
96
123
|
// src/fills/compute.ts
|
|
97
124
|
function computeEntryPriceForAsset(fills) {
|
|
98
125
|
let entryPrice = ZERO;
|
|
99
126
|
let positionSize = ZERO;
|
|
100
127
|
for (const fill of fills) {
|
|
101
|
-
const price = new
|
|
102
|
-
const quantity = new
|
|
128
|
+
const price = new BigNumber7(fill.price);
|
|
129
|
+
const quantity = new BigNumber7(fill.quantity);
|
|
103
130
|
const signed2 = fill.side === "BUY" ? quantity : quantity.negated();
|
|
104
131
|
const newPositionSize = positionSize.plus(signed2);
|
|
105
132
|
const isSameDirection = !positionSize.isZero() && positionSize.isPositive() === signed2.isPositive();
|
|
@@ -113,6 +140,7 @@ function computeEntryPriceForAsset(fills) {
|
|
|
113
140
|
}
|
|
114
141
|
return entryPrice;
|
|
115
142
|
}
|
|
143
|
+
__name(computeEntryPriceForAsset, "computeEntryPriceForAsset");
|
|
116
144
|
|
|
117
145
|
// src/asset/compute.ts
|
|
118
146
|
function computeAssetEntryNotional(signedQuantity, fills) {
|
|
@@ -126,6 +154,7 @@ function computeAssetEntryNotional(signedQuantity, fills) {
|
|
|
126
154
|
const unsignedQuantity = signedQuantity.abs();
|
|
127
155
|
return unsignedQuantity.multipliedBy(entryPrice);
|
|
128
156
|
}
|
|
157
|
+
__name(computeAssetEntryNotional, "computeAssetEntryNotional");
|
|
129
158
|
function computeAssetUnrealizedPnL(signedQuantity, currentPrice, fills) {
|
|
130
159
|
if (signedQuantity.isZero()) {
|
|
131
160
|
return ZERO;
|
|
@@ -137,9 +166,11 @@ function computeAssetUnrealizedPnL(signedQuantity, currentPrice, fills) {
|
|
|
137
166
|
const deltaPrice = currentPrice.minus(entryPrice);
|
|
138
167
|
return signedQuantity.multipliedBy(deltaPrice);
|
|
139
168
|
}
|
|
169
|
+
__name(computeAssetUnrealizedPnL, "computeAssetUnrealizedPnL");
|
|
140
170
|
function computeAssetGrossRealizedPnL(fills) {
|
|
141
171
|
return walkAssetFills(fills).gross;
|
|
142
172
|
}
|
|
173
|
+
__name(computeAssetGrossRealizedPnL, "computeAssetGrossRealizedPnL");
|
|
143
174
|
function computeAssetUndeterminedSizes(fills) {
|
|
144
175
|
const result = {};
|
|
145
176
|
for (const asset of computeAssetRealizedPnLs(fills)) {
|
|
@@ -149,6 +180,7 @@ function computeAssetUndeterminedSizes(fills) {
|
|
|
149
180
|
}
|
|
150
181
|
return result;
|
|
151
182
|
}
|
|
183
|
+
__name(computeAssetUndeterminedSizes, "computeAssetUndeterminedSizes");
|
|
152
184
|
function computeAssetUnrealizedPnLs(exposureMap, priceMap, fills) {
|
|
153
185
|
const result = [];
|
|
154
186
|
for (const [id, signedQtyString] of Object.entries(exposureMap)) {
|
|
@@ -156,11 +188,11 @@ function computeAssetUnrealizedPnLs(exposureMap, priceMap, fills) {
|
|
|
156
188
|
if (!priceString) {
|
|
157
189
|
continue;
|
|
158
190
|
}
|
|
159
|
-
const signedQuantity = new
|
|
191
|
+
const signedQuantity = new BigNumber7(signedQtyString);
|
|
160
192
|
if (signedQuantity.isZero()) {
|
|
161
193
|
continue;
|
|
162
194
|
}
|
|
163
|
-
const currentPrice = new
|
|
195
|
+
const currentPrice = new BigNumber7(priceString);
|
|
164
196
|
const assetFills = fills.filter((fill) => fill.symbol === id);
|
|
165
197
|
const entryPrice = computeEntryPriceForAsset(assetFills);
|
|
166
198
|
const upnl = signedQuantity.multipliedBy(currentPrice.minus(entryPrice));
|
|
@@ -174,6 +206,7 @@ function computeAssetUnrealizedPnLs(exposureMap, priceMap, fills) {
|
|
|
174
206
|
}
|
|
175
207
|
return result;
|
|
176
208
|
}
|
|
209
|
+
__name(computeAssetUnrealizedPnLs, "computeAssetUnrealizedPnLs");
|
|
177
210
|
function computeAssetRealizedPnLs(fills) {
|
|
178
211
|
const bySymbol = /* @__PURE__ */ new Map();
|
|
179
212
|
for (const fill of fills) {
|
|
@@ -183,8 +216,8 @@ function computeAssetRealizedPnLs(fills) {
|
|
|
183
216
|
}
|
|
184
217
|
const result = [];
|
|
185
218
|
for (const [id, assetFills] of bySymbol) {
|
|
186
|
-
const tradeFees = assetFills.reduce((acc, fill) => acc.plus(new
|
|
187
|
-
const pearFees = assetFills.reduce((acc, fill) => acc.plus(new
|
|
219
|
+
const tradeFees = assetFills.reduce((acc, fill) => acc.plus(new BigNumber7(fill.tradeFee ?? "0")), ZERO);
|
|
220
|
+
const pearFees = assetFills.reduce((acc, fill) => acc.plus(new BigNumber7(fill.pearFee ?? "0")), ZERO);
|
|
188
221
|
const totalFees = tradeFees.plus(pearFees);
|
|
189
222
|
const { gross: grossPnl, undeterminedSize, closures, exitQuantity, exitNotional } = walkAssetFills(assetFills);
|
|
190
223
|
const netPnl = grossPnl.minus(totalFees);
|
|
@@ -205,6 +238,7 @@ function computeAssetRealizedPnLs(fills) {
|
|
|
205
238
|
}
|
|
206
239
|
return result;
|
|
207
240
|
}
|
|
241
|
+
__name(computeAssetRealizedPnLs, "computeAssetRealizedPnLs");
|
|
208
242
|
function walkAssetFills(fills) {
|
|
209
243
|
let entryPrice = ZERO;
|
|
210
244
|
let positionSize = ZERO;
|
|
@@ -262,6 +296,9 @@ function walkAssetFills(fills) {
|
|
|
262
296
|
exitNotional
|
|
263
297
|
};
|
|
264
298
|
}
|
|
299
|
+
__name(walkAssetFills, "walkAssetFills");
|
|
300
|
+
|
|
301
|
+
// src/basket/compute.ts
|
|
265
302
|
function computeBasketWeightedRatioV1(basket, priceMap) {
|
|
266
303
|
let logSum = 0;
|
|
267
304
|
for (const leg of basket) {
|
|
@@ -278,28 +315,39 @@ function computeBasketWeightedRatioV1(basket, priceMap) {
|
|
|
278
315
|
}
|
|
279
316
|
return exponentiate(logSum);
|
|
280
317
|
}
|
|
318
|
+
__name(computeBasketWeightedRatioV1, "computeBasketWeightedRatioV1");
|
|
281
319
|
function deriveBasketKey(longInstrumentIds, shortInstrumentIds) {
|
|
282
320
|
const payload = JSON.stringify({
|
|
283
|
-
long: [
|
|
284
|
-
|
|
321
|
+
long: [
|
|
322
|
+
...longInstrumentIds
|
|
323
|
+
].sort((a, b) => a.localeCompare(b)),
|
|
324
|
+
short: [
|
|
325
|
+
...shortInstrumentIds
|
|
326
|
+
].sort((a, b) => a.localeCompare(b))
|
|
285
327
|
});
|
|
286
328
|
return bytesToHex(sha256(new TextEncoder().encode(payload)));
|
|
287
329
|
}
|
|
330
|
+
__name(deriveBasketKey, "deriveBasketKey");
|
|
288
331
|
function contractsToBase(contracts, contractSize) {
|
|
289
|
-
return new
|
|
332
|
+
return new BigNumber7(contracts).times(contractSize);
|
|
290
333
|
}
|
|
334
|
+
__name(contractsToBase, "contractsToBase");
|
|
291
335
|
function baseToContracts(base, contractSize) {
|
|
292
|
-
return new
|
|
336
|
+
return new BigNumber7(base).div(contractSize);
|
|
293
337
|
}
|
|
338
|
+
__name(baseToContracts, "baseToContracts");
|
|
294
339
|
function computeRealizedPnlBySymbol(fills) {
|
|
295
340
|
const bySymbol = groupFillsBySymbol(fills);
|
|
296
341
|
const result = /* @__PURE__ */ new Map();
|
|
297
342
|
for (const [symbol, symbolFills] of bySymbol) {
|
|
298
|
-
const sorted = [
|
|
343
|
+
const sorted = [
|
|
344
|
+
...symbolFills
|
|
345
|
+
].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
299
346
|
result.set(symbol, computeRealizedForSymbol(sorted));
|
|
300
347
|
}
|
|
301
348
|
return result;
|
|
302
349
|
}
|
|
350
|
+
__name(computeRealizedPnlBySymbol, "computeRealizedPnlBySymbol");
|
|
303
351
|
function computeRealizedPnlFromFills(fills) {
|
|
304
352
|
const byPnl = computeRealizedPnlBySymbol(fills);
|
|
305
353
|
let total = ZERO;
|
|
@@ -308,6 +356,7 @@ function computeRealizedPnlFromFills(fills) {
|
|
|
308
356
|
}
|
|
309
357
|
return total;
|
|
310
358
|
}
|
|
359
|
+
__name(computeRealizedPnlFromFills, "computeRealizedPnlFromFills");
|
|
311
360
|
function groupFillsBySymbol(fills) {
|
|
312
361
|
const bySymbol = /* @__PURE__ */ new Map();
|
|
313
362
|
for (const fill of fills) {
|
|
@@ -317,17 +366,18 @@ function groupFillsBySymbol(fills) {
|
|
|
317
366
|
}
|
|
318
367
|
return bySymbol;
|
|
319
368
|
}
|
|
369
|
+
__name(groupFillsBySymbol, "groupFillsBySymbol");
|
|
320
370
|
function computeRealizedForSymbol(fills) {
|
|
321
371
|
let entryPrice = ZERO;
|
|
322
372
|
let positionSize = ZERO;
|
|
323
373
|
let realized = ZERO;
|
|
324
374
|
for (const fill of fills) {
|
|
325
|
-
const price = new
|
|
326
|
-
const quantity = new
|
|
375
|
+
const price = new BigNumber7(fill.price);
|
|
376
|
+
const quantity = new BigNumber7(fill.quantity);
|
|
327
377
|
const signed2 = fill.side === "BUY" ? quantity : quantity.negated();
|
|
328
378
|
const newPositionSize = positionSize.plus(signed2);
|
|
329
379
|
if (!positionSize.isZero() && positionSize.isPositive() !== signed2.isPositive()) {
|
|
330
|
-
const closedQty =
|
|
380
|
+
const closedQty = BigNumber7.min(positionSize.abs(), signed2.abs());
|
|
331
381
|
if (positionSize.isPositive()) {
|
|
332
382
|
realized = realized.plus(closedQty.multipliedBy(price.minus(entryPrice)));
|
|
333
383
|
} else {
|
|
@@ -345,6 +395,9 @@ function computeRealizedForSymbol(fills) {
|
|
|
345
395
|
}
|
|
346
396
|
return realized;
|
|
347
397
|
}
|
|
398
|
+
__name(computeRealizedForSymbol, "computeRealizedForSymbol");
|
|
399
|
+
|
|
400
|
+
// src/position/compute.ts
|
|
348
401
|
function computeUnrealizedPositionState(exposureMap, priceMap, fills) {
|
|
349
402
|
const assetUPnLs = computeAssetUnrealizedPnLs(exposureMap, priceMap, fills);
|
|
350
403
|
const entryPrice = computePositionEntryPrice(assetUPnLs);
|
|
@@ -363,9 +416,11 @@ function computeUnrealizedPositionState(exposureMap, priceMap, fills) {
|
|
|
363
416
|
upnlBips
|
|
364
417
|
};
|
|
365
418
|
}
|
|
419
|
+
__name(computeUnrealizedPositionState, "computeUnrealizedPositionState");
|
|
366
420
|
function computeRealizedPositionState(fills) {
|
|
367
421
|
return aggregateRealizedPositionState(computeAssetRealizedPnLs(fills));
|
|
368
422
|
}
|
|
423
|
+
__name(computeRealizedPositionState, "computeRealizedPositionState");
|
|
369
424
|
function aggregateRealizedPositionState(assetPnLs) {
|
|
370
425
|
const grossPnl = computePositionGrossRealizedPnL(assetPnLs);
|
|
371
426
|
const netPnl = computePositionNetRealizedPnL(assetPnLs);
|
|
@@ -380,25 +435,24 @@ function aggregateRealizedPositionState(assetPnLs) {
|
|
|
380
435
|
pearFees
|
|
381
436
|
};
|
|
382
437
|
}
|
|
438
|
+
__name(aggregateRealizedPositionState, "aggregateRealizedPositionState");
|
|
383
439
|
function computePositionEntryPrice(assetUPnLs) {
|
|
384
|
-
const { totalWeightedPrice, totalWeight } = assetUPnLs.reduce(
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
totalWeight: ZERO
|
|
395
|
-
}
|
|
396
|
-
);
|
|
440
|
+
const { totalWeightedPrice, totalWeight } = assetUPnLs.reduce((acc, { signedQuantity, entryPrice }) => {
|
|
441
|
+
const absQuantity = signedQuantity.abs();
|
|
442
|
+
return {
|
|
443
|
+
totalWeightedPrice: acc.totalWeightedPrice.plus(absQuantity.multipliedBy(entryPrice)),
|
|
444
|
+
totalWeight: acc.totalWeight.plus(absQuantity)
|
|
445
|
+
};
|
|
446
|
+
}, {
|
|
447
|
+
totalWeightedPrice: ZERO,
|
|
448
|
+
totalWeight: ZERO
|
|
449
|
+
});
|
|
397
450
|
if (totalWeight.isZero()) {
|
|
398
451
|
return null;
|
|
399
452
|
}
|
|
400
453
|
return totalWeightedPrice.dividedBy(totalWeight);
|
|
401
454
|
}
|
|
455
|
+
__name(computePositionEntryPrice, "computePositionEntryPrice");
|
|
402
456
|
function computePositionEntryNotional(assetUPnLs) {
|
|
403
457
|
return assetUPnLs.reduce((acc, asset) => {
|
|
404
458
|
if (asset.signedQuantity.isZero()) {
|
|
@@ -407,25 +461,24 @@ function computePositionEntryNotional(assetUPnLs) {
|
|
|
407
461
|
return acc.plus(asset.signedQuantity.abs().multipliedBy(asset.entryPrice));
|
|
408
462
|
}, ZERO);
|
|
409
463
|
}
|
|
464
|
+
__name(computePositionEntryNotional, "computePositionEntryNotional");
|
|
410
465
|
function computePositionCurrentPrice(assetUPnLs) {
|
|
411
|
-
const { totalWeightedPrice, totalWeight } = assetUPnLs.reduce(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
totalWeight: ZERO
|
|
422
|
-
}
|
|
423
|
-
);
|
|
466
|
+
const { totalWeightedPrice, totalWeight } = assetUPnLs.reduce((acc, { signedQuantity, currentPrice }) => {
|
|
467
|
+
const absQuantity = signedQuantity.abs();
|
|
468
|
+
return {
|
|
469
|
+
totalWeightedPrice: acc.totalWeightedPrice.plus(absQuantity.multipliedBy(currentPrice)),
|
|
470
|
+
totalWeight: acc.totalWeight.plus(absQuantity)
|
|
471
|
+
};
|
|
472
|
+
}, {
|
|
473
|
+
totalWeightedPrice: ZERO,
|
|
474
|
+
totalWeight: ZERO
|
|
475
|
+
});
|
|
424
476
|
if (totalWeight.isZero()) {
|
|
425
477
|
return null;
|
|
426
478
|
}
|
|
427
479
|
return totalWeightedPrice.dividedBy(totalWeight);
|
|
428
480
|
}
|
|
481
|
+
__name(computePositionCurrentPrice, "computePositionCurrentPrice");
|
|
429
482
|
function computePositionMarkNotional(assetUPnLs) {
|
|
430
483
|
return assetUPnLs.reduce((acc, asset) => {
|
|
431
484
|
if (asset.signedQuantity.isZero()) {
|
|
@@ -434,21 +487,26 @@ function computePositionMarkNotional(assetUPnLs) {
|
|
|
434
487
|
return acc.plus(asset.signedQuantity.abs().multipliedBy(asset.currentPrice));
|
|
435
488
|
}, ZERO);
|
|
436
489
|
}
|
|
490
|
+
__name(computePositionMarkNotional, "computePositionMarkNotional");
|
|
437
491
|
function computePositionUnrealizedPnL(assetUPnLs) {
|
|
438
492
|
return assetUPnLs.reduce((acc, asset) => acc.plus(asset.upnl), ZERO);
|
|
439
493
|
}
|
|
494
|
+
__name(computePositionUnrealizedPnL, "computePositionUnrealizedPnL");
|
|
440
495
|
function computePositionUnrealizedPnLBips(upnl, entryNotional) {
|
|
441
496
|
if (upnl.isZero() || entryNotional.isZero()) {
|
|
442
497
|
return ZERO;
|
|
443
498
|
}
|
|
444
499
|
return upnl.div(entryNotional).multipliedBy(BIPS);
|
|
445
500
|
}
|
|
501
|
+
__name(computePositionUnrealizedPnLBips, "computePositionUnrealizedPnLBips");
|
|
446
502
|
function computePositionGrossRealizedPnL(assetPnLs) {
|
|
447
503
|
return assetPnLs.reduce((acc, asset) => acc.plus(asset.grossPnl), ZERO);
|
|
448
504
|
}
|
|
505
|
+
__name(computePositionGrossRealizedPnL, "computePositionGrossRealizedPnL");
|
|
449
506
|
function computePositionNetRealizedPnL(assetPnLs) {
|
|
450
507
|
return assetPnLs.reduce((acc, asset) => acc.plus(asset.netPnl), ZERO);
|
|
451
508
|
}
|
|
509
|
+
__name(computePositionNetRealizedPnL, "computePositionNetRealizedPnL");
|
|
452
510
|
function computePositionEntryPrices(exposureMap, fills) {
|
|
453
511
|
const entryPrices = {};
|
|
454
512
|
for (const instrumentId of Object.keys(exposureMap)) {
|
|
@@ -459,6 +517,7 @@ function computePositionEntryPrices(exposureMap, fills) {
|
|
|
459
517
|
}
|
|
460
518
|
return entryPrices;
|
|
461
519
|
}
|
|
520
|
+
__name(computePositionEntryPrices, "computePositionEntryPrices");
|
|
462
521
|
function computeUnrealizedPositionStateFromEntryPrices(exposureMap, entryPrices, priceMap) {
|
|
463
522
|
const assetUPnLs = [];
|
|
464
523
|
for (const [id, signedQuantityString] of Object.entries(exposureMap)) {
|
|
@@ -470,7 +529,13 @@ function computeUnrealizedPositionStateFromEntryPrices(exposureMap, entryPrices,
|
|
|
470
529
|
const currentPrice = parse(currentPriceString);
|
|
471
530
|
const entryPrice2 = parse(entryPriceString);
|
|
472
531
|
const upnl2 = multiply(signedQuantity, subtract(currentPrice, entryPrice2));
|
|
473
|
-
assetUPnLs.push({
|
|
532
|
+
assetUPnLs.push({
|
|
533
|
+
id,
|
|
534
|
+
signedQuantity,
|
|
535
|
+
entryPrice: entryPrice2,
|
|
536
|
+
currentPrice,
|
|
537
|
+
upnl: upnl2
|
|
538
|
+
});
|
|
474
539
|
}
|
|
475
540
|
const entryPrice = computePositionEntryPrice(assetUPnLs);
|
|
476
541
|
const entryNotional = computePositionEntryNotional(assetUPnLs);
|
|
@@ -478,19 +543,31 @@ function computeUnrealizedPositionStateFromEntryPrices(exposureMap, entryPrices,
|
|
|
478
543
|
const markNotional = computePositionMarkNotional(assetUPnLs);
|
|
479
544
|
const upnl = computePositionUnrealizedPnL(assetUPnLs);
|
|
480
545
|
const upnlBips = computePositionUnrealizedPnLBips(upnl, entryNotional);
|
|
481
|
-
return {
|
|
546
|
+
return {
|
|
547
|
+
assetUPnLs,
|
|
548
|
+
entryPrice,
|
|
549
|
+
entryNotional,
|
|
550
|
+
markPrice,
|
|
551
|
+
markNotional,
|
|
552
|
+
upnl,
|
|
553
|
+
upnlBips
|
|
554
|
+
};
|
|
482
555
|
}
|
|
556
|
+
__name(computeUnrealizedPositionStateFromEntryPrices, "computeUnrealizedPositionStateFromEntryPrices");
|
|
483
557
|
function generateDeterministicPositionKey(input) {
|
|
484
|
-
const symbols = [
|
|
558
|
+
const symbols = [
|
|
559
|
+
...input.symbols
|
|
560
|
+
].sort((a, b) => a.localeCompare(b));
|
|
485
561
|
const payload = JSON.stringify({
|
|
486
562
|
tradeAccountId: input.tradeAccountId.toLowerCase(),
|
|
487
563
|
symbols
|
|
488
564
|
});
|
|
489
565
|
return bytesToHex(sha256(new TextEncoder().encode(payload)));
|
|
490
566
|
}
|
|
491
|
-
|
|
567
|
+
__name(generateDeterministicPositionKey, "generateDeterministicPositionKey");
|
|
568
|
+
BigNumber7.config({
|
|
492
569
|
DECIMAL_PLACES: 30,
|
|
493
|
-
ROUNDING_MODE:
|
|
570
|
+
ROUNDING_MODE: BigNumber7.ROUND_DOWN
|
|
494
571
|
});
|
|
495
572
|
function precisePrice(connector, raw, precision, side = "BUY", slippageToleranceBps = 0) {
|
|
496
573
|
const normalized = normalizePrice(raw);
|
|
@@ -505,34 +582,38 @@ function precisePrice(connector, raw, precision, side = "BUY", slippageTolerance
|
|
|
505
582
|
return preciseTickGridPrice(effectiveRaw, precision, side);
|
|
506
583
|
}
|
|
507
584
|
}
|
|
585
|
+
__name(precisePrice, "precisePrice");
|
|
508
586
|
function withSlippageTolerance(normalized, slippageToleranceBps, side) {
|
|
509
587
|
if (slippageToleranceBps === 0) {
|
|
510
588
|
return normalized;
|
|
511
589
|
}
|
|
512
|
-
const bps = new
|
|
590
|
+
const bps = new BigNumber7(slippageToleranceBps);
|
|
513
591
|
const factor = bps.div(1e4);
|
|
514
|
-
return side === "BUY" ? normalized.times(new
|
|
592
|
+
return side === "BUY" ? normalized.times(new BigNumber7(1).plus(factor)) : normalized.times(new BigNumber7(1).minus(factor));
|
|
515
593
|
}
|
|
594
|
+
__name(withSlippageTolerance, "withSlippageTolerance");
|
|
516
595
|
function preciseHyperliquidPrice(price, precision, side) {
|
|
517
596
|
const maxDecimals = precision.price.decimals;
|
|
518
597
|
const maxSigFigs = precision.price.sigFigs ?? 5;
|
|
519
|
-
const rounding = side === "BUY" ?
|
|
598
|
+
const rounding = side === "BUY" ? BigNumber7.ROUND_FLOOR : BigNumber7.ROUND_CEIL;
|
|
520
599
|
price = price.decimalPlaces(maxDecimals, rounding);
|
|
521
600
|
price = clampSigFigs(price, maxSigFigs, maxDecimals, rounding);
|
|
522
601
|
return price.toFixed();
|
|
523
602
|
}
|
|
603
|
+
__name(preciseHyperliquidPrice, "preciseHyperliquidPrice");
|
|
524
604
|
function preciseTickGridPrice(price, precision, side) {
|
|
525
605
|
const tick = precision.price.tick ?? 1e-4;
|
|
526
|
-
const rounding = side === "BUY" ?
|
|
527
|
-
const tickBN = new
|
|
606
|
+
const rounding = side === "BUY" ? BigNumber7.ROUND_FLOOR : BigNumber7.ROUND_CEIL;
|
|
607
|
+
const tickBN = new BigNumber7(tick);
|
|
528
608
|
const scaled = price.div(tickBN);
|
|
529
609
|
const snapped = scaled.integerValue(rounding).times(tickBN);
|
|
530
610
|
const maxDecimals = precision.price.decimals;
|
|
531
611
|
const final = snapped.decimalPlaces(maxDecimals, rounding);
|
|
532
612
|
return final.toFixed();
|
|
533
613
|
}
|
|
614
|
+
__name(preciseTickGridPrice, "preciseTickGridPrice");
|
|
534
615
|
function clampSigFigs(price, maxSigFigs, maxDecimals, rounding) {
|
|
535
|
-
const countSigFigs = (value) => value.toFixed().replace(".", "").replace(/^0+/, "").length;
|
|
616
|
+
const countSigFigs = /* @__PURE__ */ __name((value) => value.toFixed().replace(".", "").replace(/^0+/, "").length, "countSigFigs");
|
|
536
617
|
if (countSigFigs(price) <= maxSigFigs) {
|
|
537
618
|
return price;
|
|
538
619
|
}
|
|
@@ -544,13 +625,15 @@ function clampSigFigs(price, maxSigFigs, maxDecimals, rounding) {
|
|
|
544
625
|
}
|
|
545
626
|
return price;
|
|
546
627
|
}
|
|
628
|
+
__name(clampSigFigs, "clampSigFigs");
|
|
547
629
|
function normalizePrice(value) {
|
|
548
|
-
const parsed = new
|
|
549
|
-
return new
|
|
630
|
+
const parsed = new BigNumber7(value);
|
|
631
|
+
return new BigNumber7(parsed.toFixed());
|
|
550
632
|
}
|
|
551
|
-
|
|
633
|
+
__name(normalizePrice, "normalizePrice");
|
|
634
|
+
BigNumber7.config({
|
|
552
635
|
DECIMAL_PLACES: 30,
|
|
553
|
-
ROUNDING_MODE:
|
|
636
|
+
ROUNDING_MODE: BigNumber7.ROUND_DOWN
|
|
554
637
|
});
|
|
555
638
|
function preciseQuantity(connector, raw, precision, side) {
|
|
556
639
|
switch (connector) {
|
|
@@ -563,23 +646,26 @@ function preciseQuantity(connector, raw, precision, side) {
|
|
|
563
646
|
return preciseStepGridQuantity(raw, precision, side);
|
|
564
647
|
}
|
|
565
648
|
}
|
|
649
|
+
__name(preciseQuantity, "preciseQuantity");
|
|
566
650
|
function preciseHyperliquidQuantity(raw, precision, side) {
|
|
567
651
|
const decimals = precision.quantity.decimals;
|
|
568
|
-
const quantity = new
|
|
569
|
-
const rounded = quantity.decimalPlaces(decimals, side === "BUY" ?
|
|
652
|
+
const quantity = new BigNumber7(raw);
|
|
653
|
+
const rounded = quantity.decimalPlaces(decimals, side === "BUY" ? BigNumber7.ROUND_FLOOR : BigNumber7.ROUND_CEIL);
|
|
570
654
|
return rounded.toFixed();
|
|
571
655
|
}
|
|
656
|
+
__name(preciseHyperliquidQuantity, "preciseHyperliquidQuantity");
|
|
572
657
|
function preciseStepGridQuantity(raw, precision, side) {
|
|
573
658
|
const step = precision.quantity.step;
|
|
574
659
|
const contractQuantity = baseToContracts(raw, precision.quantity.contractSize);
|
|
575
|
-
const stepBN = new
|
|
660
|
+
const stepBN = new BigNumber7(step);
|
|
576
661
|
const scaled = contractQuantity.div(stepBN);
|
|
577
|
-
const rounded = side === "BUY" ? scaled.integerValue(
|
|
662
|
+
const rounded = side === "BUY" ? scaled.integerValue(BigNumber7.ROUND_FLOOR) : scaled.integerValue(BigNumber7.ROUND_CEIL);
|
|
578
663
|
const result = rounded.times(stepBN);
|
|
579
664
|
const maxDecimals = precision.quantity.decimals;
|
|
580
|
-
const final = result.decimalPlaces(maxDecimals, side === "BUY" ?
|
|
665
|
+
const final = result.decimalPlaces(maxDecimals, side === "BUY" ? BigNumber7.ROUND_FLOOR : BigNumber7.ROUND_CEIL);
|
|
581
666
|
return final.toFixed();
|
|
582
667
|
}
|
|
668
|
+
__name(preciseStepGridQuantity, "preciseStepGridQuantity");
|
|
583
669
|
|
|
584
670
|
// src/proxy.ts
|
|
585
671
|
var activeOrigin = null;
|
|
@@ -594,12 +680,14 @@ function configureProxy(config) {
|
|
|
594
680
|
activeOrigin = null;
|
|
595
681
|
}
|
|
596
682
|
}
|
|
683
|
+
__name(configureProxy, "configureProxy");
|
|
597
684
|
function toProxyUrl(target, origin) {
|
|
598
685
|
const proxied = new URL(origin);
|
|
599
686
|
proxied.pathname = target.pathname;
|
|
600
687
|
proxied.search = target.search;
|
|
601
688
|
return proxied.toString();
|
|
602
689
|
}
|
|
690
|
+
__name(toProxyUrl, "toProxyUrl");
|
|
603
691
|
async function withProxyFallback(proxied, direct) {
|
|
604
692
|
try {
|
|
605
693
|
const response = await proxied();
|
|
@@ -608,17 +696,19 @@ async function withProxyFallback(proxied, direct) {
|
|
|
608
696
|
}
|
|
609
697
|
return direct();
|
|
610
698
|
}
|
|
699
|
+
__name(withProxyFallback, "withProxyFallback");
|
|
611
700
|
function proxyFetch(input, init) {
|
|
612
701
|
const origin = activeOrigin;
|
|
613
702
|
if (!origin) return fetch(input, init);
|
|
614
703
|
const target = new URL(input);
|
|
615
704
|
const headers = new Headers(init?.headers);
|
|
616
705
|
headers.set("X-Proxy-Target", target.host);
|
|
617
|
-
return withProxyFallback(
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
);
|
|
706
|
+
return withProxyFallback(() => fetch(toProxyUrl(target, origin), {
|
|
707
|
+
...init,
|
|
708
|
+
headers
|
|
709
|
+
}), () => fetch(input, init));
|
|
621
710
|
}
|
|
711
|
+
__name(proxyFetch, "proxyFetch");
|
|
622
712
|
|
|
623
713
|
// src/sync/applicables/constants.ts
|
|
624
714
|
var DUST = parse("0.00000001");
|
|
@@ -626,8 +716,8 @@ var MISSED_HISTORY_FILL_PREFIX = "opening-synthetic-";
|
|
|
626
716
|
var BASKET_RULES = [
|
|
627
717
|
// V2 Basket Hyperliquid — legs of one Pear basket order share the order's cloid.
|
|
628
718
|
{
|
|
629
|
-
matches: (fill) => fill.connector === "hyperliquid" && isPearCloid(fill.connector, fill.cloid),
|
|
630
|
-
key: (fill) => `v2-basket-hl ${fill.cloid}
|
|
719
|
+
matches: /* @__PURE__ */ __name((fill) => fill.connector === "hyperliquid" && isPearCloid(fill.connector, fill.cloid), "matches"),
|
|
720
|
+
key: /* @__PURE__ */ __name((fill) => `v2-basket-hl ${fill.cloid}`, "key")
|
|
631
721
|
}
|
|
632
722
|
];
|
|
633
723
|
function isPearCloid(connector, cloid) {
|
|
@@ -636,17 +726,21 @@ function isPearCloid(connector, cloid) {
|
|
|
636
726
|
}
|
|
637
727
|
return PEAR_CLOID_CHECK[connector]?.(cloid) ?? false;
|
|
638
728
|
}
|
|
729
|
+
__name(isPearCloid, "isPearCloid");
|
|
639
730
|
var PEAR_CLOID_CHECK = {
|
|
640
|
-
hyperliquid: (cloid) => cloid.startsWith("0x50454152"),
|
|
641
|
-
lighter: (cloid) => cloid.length === 15 && cloid.startsWith("10")
|
|
731
|
+
hyperliquid: /* @__PURE__ */ __name((cloid) => cloid.startsWith("0x50454152"), "hyperliquid"),
|
|
732
|
+
lighter: /* @__PURE__ */ __name((cloid) => cloid.length === 15 && cloid.startsWith("10"), "lighter")
|
|
642
733
|
};
|
|
643
734
|
|
|
644
735
|
// src/sync/applicables/basket.ts
|
|
645
736
|
var Basket = class _Basket {
|
|
737
|
+
static {
|
|
738
|
+
__name(this, "Basket");
|
|
739
|
+
}
|
|
740
|
+
legs;
|
|
646
741
|
constructor(legs) {
|
|
647
742
|
this.legs = legs;
|
|
648
743
|
}
|
|
649
|
-
legs;
|
|
650
744
|
/** Detect baskets from a fill stream, returned in ascending time order. */
|
|
651
745
|
static detect(fills) {
|
|
652
746
|
return groupFillsIntoBaskets(fills).map((group) => new _Basket(combineFillsBySymbolAndSide(group)));
|
|
@@ -661,26 +755,32 @@ var Basket = class _Basket {
|
|
|
661
755
|
}
|
|
662
756
|
};
|
|
663
757
|
var CombinedLeg = class {
|
|
664
|
-
|
|
665
|
-
this
|
|
666
|
-
this.side = side;
|
|
758
|
+
static {
|
|
759
|
+
__name(this, "CombinedLeg");
|
|
667
760
|
}
|
|
668
761
|
symbol;
|
|
669
762
|
side;
|
|
670
763
|
_totalSize = ZERO;
|
|
671
764
|
queue = [];
|
|
765
|
+
constructor(symbol, side) {
|
|
766
|
+
this.symbol = symbol;
|
|
767
|
+
this.side = side;
|
|
768
|
+
}
|
|
672
769
|
get totalSize() {
|
|
673
770
|
return this._totalSize;
|
|
674
771
|
}
|
|
675
772
|
/** Append a fill to this leg's queue and grow the total size. */
|
|
676
773
|
addFill(fill) {
|
|
677
774
|
this._totalSize = this._totalSize.plus(fill.quantity);
|
|
678
|
-
this.queue.push({
|
|
775
|
+
this.queue.push({
|
|
776
|
+
fill,
|
|
777
|
+
remaining: parse(fill.quantity)
|
|
778
|
+
});
|
|
679
779
|
}
|
|
680
780
|
/**
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
781
|
+
* Take `qty` of fill slices from this leg (FIFO across source fills, consumes the queue).
|
|
782
|
+
* Returns the parts taken, which are consumed from the queue.
|
|
783
|
+
*/
|
|
684
784
|
take(qty) {
|
|
685
785
|
const parts = [];
|
|
686
786
|
let need = qty;
|
|
@@ -692,7 +792,10 @@ var CombinedLeg = class {
|
|
|
692
792
|
continue;
|
|
693
793
|
}
|
|
694
794
|
const taken = min(entry.remaining, need);
|
|
695
|
-
parts.push({
|
|
795
|
+
parts.push({
|
|
796
|
+
source: entry.fill,
|
|
797
|
+
quantity: taken.toFixed()
|
|
798
|
+
});
|
|
696
799
|
entry.remaining = entry.remaining.minus(taken);
|
|
697
800
|
need = need.minus(taken);
|
|
698
801
|
}
|
|
@@ -704,14 +807,20 @@ function groupFillsIntoBaskets(fills) {
|
|
|
704
807
|
for (const fill of fills) {
|
|
705
808
|
const rule = BASKET_RULES.find((r) => r.matches(fill));
|
|
706
809
|
const key = rule ? rule.key(fill) : `id ${fill.exchangeFillId}`;
|
|
707
|
-
const bucket = buckets.get(key) ?? {
|
|
810
|
+
const bucket = buckets.get(key) ?? {
|
|
811
|
+
timestamp: fill.timestamp,
|
|
812
|
+
fills: []
|
|
813
|
+
};
|
|
708
814
|
bucket.timestamp = Math.min(bucket.timestamp, fill.timestamp);
|
|
709
815
|
bucket.fills.push(fill);
|
|
710
816
|
buckets.set(key, bucket);
|
|
711
817
|
}
|
|
712
|
-
const sorted = [
|
|
818
|
+
const sorted = [
|
|
819
|
+
...buckets.values()
|
|
820
|
+
].sort((a, b) => a.timestamp - b.timestamp);
|
|
713
821
|
return sorted.map((bucket) => bucket.fills);
|
|
714
822
|
}
|
|
823
|
+
__name(groupFillsIntoBaskets, "groupFillsIntoBaskets");
|
|
715
824
|
function combineFillsBySymbolAndSide(group) {
|
|
716
825
|
const map = /* @__PURE__ */ new Map();
|
|
717
826
|
for (const fill of group) {
|
|
@@ -723,15 +832,21 @@ function combineFillsBySymbolAndSide(group) {
|
|
|
723
832
|
}
|
|
724
833
|
combined.addFill(fill);
|
|
725
834
|
}
|
|
726
|
-
return [
|
|
835
|
+
return [
|
|
836
|
+
...map.values()
|
|
837
|
+
];
|
|
727
838
|
}
|
|
839
|
+
__name(combineFillsBySymbolAndSide, "combineFillsBySymbolAndSide");
|
|
728
840
|
|
|
729
841
|
// src/sync/applicables/exposure.ts
|
|
730
842
|
var Exposure = class _Exposure {
|
|
843
|
+
static {
|
|
844
|
+
__name(this, "Exposure");
|
|
845
|
+
}
|
|
846
|
+
legs;
|
|
731
847
|
constructor(legs) {
|
|
732
848
|
this.legs = legs;
|
|
733
849
|
}
|
|
734
|
-
legs;
|
|
735
850
|
/** Create an exposure with no legs — the starting state for a new position. */
|
|
736
851
|
static empty() {
|
|
737
852
|
return new _Exposure(/* @__PURE__ */ new Map());
|
|
@@ -784,7 +899,10 @@ var Exposure = class _Exposure {
|
|
|
784
899
|
break;
|
|
785
900
|
}
|
|
786
901
|
}
|
|
787
|
-
return {
|
|
902
|
+
return {
|
|
903
|
+
longs,
|
|
904
|
+
shorts
|
|
905
|
+
};
|
|
788
906
|
}
|
|
789
907
|
/** True once every leg sits within DUST of zero (effectively closed). */
|
|
790
908
|
isFlat() {
|
|
@@ -805,10 +923,10 @@ var Exposure = class _Exposure {
|
|
|
805
923
|
return true;
|
|
806
924
|
}
|
|
807
925
|
/**
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
926
|
+
* Snap every non-zero leg to exactly zero. Called once a position is flat so it
|
|
927
|
+
* reads CLOSED (all-zero exposure) and never lingers in the open list with stray
|
|
928
|
+
* dust that a later reopen of the same pair could merge into.
|
|
929
|
+
*/
|
|
812
930
|
snapToZero() {
|
|
813
931
|
for (const [symbol, qty] of this.legs) {
|
|
814
932
|
if (!qty.isZero()) {
|
|
@@ -818,7 +936,9 @@ var Exposure = class _Exposure {
|
|
|
818
936
|
}
|
|
819
937
|
/** All symbols with a leg, including zeroed ones (a closed leg still carries cost basis). */
|
|
820
938
|
symbols() {
|
|
821
|
-
return [
|
|
939
|
+
return [
|
|
940
|
+
...this.legs.keys()
|
|
941
|
+
];
|
|
822
942
|
}
|
|
823
943
|
/** Canonical signed-string view for the wire (`+0` for zero). */
|
|
824
944
|
toSigned() {
|
|
@@ -833,29 +953,43 @@ var Exposure = class _Exposure {
|
|
|
833
953
|
// src/sync/applicables/matching.ts
|
|
834
954
|
function matchPositions(args) {
|
|
835
955
|
const { tradeAccountId, orphanFills, pear } = args;
|
|
836
|
-
const book = WorkingBook.load({
|
|
956
|
+
const book = WorkingBook.load({
|
|
957
|
+
tradeAccountId,
|
|
958
|
+
openPositions: pear.positions()
|
|
959
|
+
});
|
|
837
960
|
for (const basket of Basket.detect(orphanFills)) {
|
|
838
961
|
const exactCloseTarget = findExactCloseTarget(book.open(), basket.longs, basket.shorts);
|
|
839
|
-
const leftover = applyCloses({
|
|
962
|
+
const leftover = applyCloses({
|
|
963
|
+
legs: basket.legs,
|
|
964
|
+
book,
|
|
965
|
+
exactCloseTarget
|
|
966
|
+
});
|
|
840
967
|
if (leftover.length === 0) {
|
|
841
968
|
continue;
|
|
842
969
|
}
|
|
843
|
-
const target = resolveLeftoverTarget({
|
|
970
|
+
const target = resolveLeftoverTarget({
|
|
971
|
+
book,
|
|
972
|
+
leftover
|
|
973
|
+
});
|
|
844
974
|
applyLeftover(target, leftover);
|
|
845
975
|
}
|
|
846
976
|
return book.toMatched();
|
|
847
977
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
978
|
+
__name(matchPositions, "matchPositions");
|
|
979
|
+
var WorkingBook = class WorkingBook2 {
|
|
980
|
+
static {
|
|
981
|
+
__name(this, "WorkingBook");
|
|
851
982
|
}
|
|
852
983
|
tradeAccountId;
|
|
853
984
|
all = [];
|
|
854
985
|
openByKey = /* @__PURE__ */ new Map();
|
|
986
|
+
constructor(tradeAccountId) {
|
|
987
|
+
this.tradeAccountId = tradeAccountId;
|
|
988
|
+
}
|
|
855
989
|
/** Seed the book from existing open DB positions (they arrive pre-filtered by connector). */
|
|
856
990
|
static load(args) {
|
|
857
991
|
const { tradeAccountId, openPositions } = args;
|
|
858
|
-
const book = new
|
|
992
|
+
const book = new WorkingBook2(tradeAccountId);
|
|
859
993
|
for (const p of openPositions) {
|
|
860
994
|
const position = {
|
|
861
995
|
// Trust the position's own stored key — never recompute it from exposure.
|
|
@@ -874,7 +1008,10 @@ var WorkingBook = class _WorkingBook {
|
|
|
874
1008
|
}
|
|
875
1009
|
/** Deterministic position key for a symbol set under this book's trade account. */
|
|
876
1010
|
keyFor(symbols) {
|
|
877
|
-
return generateDeterministicPositionKey({
|
|
1011
|
+
return generateDeterministicPositionKey({
|
|
1012
|
+
tradeAccountId: this.tradeAccountId,
|
|
1013
|
+
symbols
|
|
1014
|
+
});
|
|
878
1015
|
}
|
|
879
1016
|
/** Currently open working positions, in insertion order. */
|
|
880
1017
|
open() {
|
|
@@ -922,7 +1059,10 @@ function findExactCloseTarget(openList, basketLongs, basketShorts) {
|
|
|
922
1059
|
return setEq(longs, basketShorts) && setEq(shorts, basketLongs);
|
|
923
1060
|
});
|
|
924
1061
|
}
|
|
925
|
-
|
|
1062
|
+
__name(findExactCloseTarget, "findExactCloseTarget");
|
|
1063
|
+
var setEq = /* @__PURE__ */ __name((a, b) => a.size === b.size && [
|
|
1064
|
+
...a
|
|
1065
|
+
].every((x) => b.has(x)), "setEq");
|
|
926
1066
|
function applyCloses(args) {
|
|
927
1067
|
const { legs, book, exactCloseTarget } = args;
|
|
928
1068
|
const openList = book.open();
|
|
@@ -952,48 +1092,75 @@ function applyCloses(args) {
|
|
|
952
1092
|
book.markClosedIfFlat(position);
|
|
953
1093
|
}
|
|
954
1094
|
if (isGreaterThanOrEqual(remaining, DUST)) {
|
|
955
|
-
leftover.push({
|
|
1095
|
+
leftover.push({
|
|
1096
|
+
side: leg.side,
|
|
1097
|
+
qty: remaining,
|
|
1098
|
+
leg
|
|
1099
|
+
});
|
|
956
1100
|
}
|
|
957
1101
|
}
|
|
958
1102
|
return leftover;
|
|
959
1103
|
}
|
|
1104
|
+
__name(applyCloses, "applyCloses");
|
|
960
1105
|
function resolveLeftoverTarget(args) {
|
|
961
1106
|
const { book, leftover } = args;
|
|
962
1107
|
const leftoverLongs = new Set(leftover.filter((entry) => entry.side === "BUY").map((entry) => entry.leg.symbol));
|
|
963
1108
|
const leftoverShorts = new Set(leftover.filter((entry) => entry.side === "SELL").map((entry) => entry.leg.symbol));
|
|
964
1109
|
const target = book.open().find((position) => {
|
|
965
1110
|
const { longs, shorts } = position.exposure.activeLegs();
|
|
966
|
-
const
|
|
967
|
-
const
|
|
968
|
-
return
|
|
1111
|
+
const sameActiveShape = setEq(leftoverLongs, longs) && setEq(leftoverShorts, shorts);
|
|
1112
|
+
const noDormantLegs = position.exposure.symbols().length === longs.size + shorts.size;
|
|
1113
|
+
return sameActiveShape && noDormantLegs;
|
|
969
1114
|
});
|
|
970
1115
|
if (target) {
|
|
971
1116
|
return target;
|
|
972
1117
|
}
|
|
973
|
-
const symbols = [
|
|
1118
|
+
const symbols = [
|
|
1119
|
+
.../* @__PURE__ */ new Set([
|
|
1120
|
+
...leftoverLongs,
|
|
1121
|
+
...leftoverShorts
|
|
1122
|
+
])
|
|
1123
|
+
];
|
|
974
1124
|
const positionKey = book.keyFor(symbols);
|
|
975
1125
|
return book.findOpenByKey(positionKey) ?? book.create(positionKey);
|
|
976
1126
|
}
|
|
1127
|
+
__name(resolveLeftoverTarget, "resolveLeftoverTarget");
|
|
977
1128
|
function applyLeftover(target, leftover) {
|
|
978
1129
|
for (const entry of leftover) {
|
|
979
1130
|
target.parts.push(...entry.leg.take(entry.qty));
|
|
980
1131
|
target.exposure.addSigned(entry.leg.symbol, entry.qty.toFixed(), entry.side);
|
|
981
1132
|
}
|
|
982
1133
|
}
|
|
1134
|
+
__name(applyLeftover, "applyLeftover");
|
|
983
1135
|
|
|
984
1136
|
// src/sync/applicables/synthetics.ts
|
|
985
1137
|
function isMissedHistoryFill(fill) {
|
|
986
1138
|
return fill.exchangeFillId.startsWith(MISSED_HISTORY_FILL_PREFIX);
|
|
987
1139
|
}
|
|
1140
|
+
__name(isMissedHistoryFill, "isMissedHistoryFill");
|
|
988
1141
|
function addFillsForMissedHistory(args) {
|
|
989
1142
|
const missedHistoryFills = buildMissedHistoryFills(args);
|
|
990
|
-
return [
|
|
1143
|
+
return [
|
|
1144
|
+
...missedHistoryFills,
|
|
1145
|
+
...args.orphanFills
|
|
1146
|
+
].sort((a, b) => a.timestamp - b.timestamp);
|
|
991
1147
|
}
|
|
1148
|
+
__name(addFillsForMissedHistory, "addFillsForMissedHistory");
|
|
992
1149
|
function alignPositionsWithExchange(args) {
|
|
993
1150
|
const { positions, pear, snapshot, tradeAccountId } = args;
|
|
994
|
-
const neededSynthetics = findNeededSynthetics({
|
|
995
|
-
|
|
1151
|
+
const neededSynthetics = findNeededSynthetics({
|
|
1152
|
+
positions,
|
|
1153
|
+
pear,
|
|
1154
|
+
snapshot
|
|
1155
|
+
});
|
|
1156
|
+
return assignSyntheticsToPositions({
|
|
1157
|
+
neededSynthetics,
|
|
1158
|
+
positions,
|
|
1159
|
+
pear,
|
|
1160
|
+
tradeAccountId
|
|
1161
|
+
});
|
|
996
1162
|
}
|
|
1163
|
+
__name(alignPositionsWithExchange, "alignPositionsWithExchange");
|
|
997
1164
|
function buildSyntheticFill(args) {
|
|
998
1165
|
return {
|
|
999
1166
|
// Content-addressed id — no wall-clock, no positionId, so re-syncs collide
|
|
@@ -1020,6 +1187,7 @@ function buildSyntheticFill(args) {
|
|
|
1020
1187
|
liquidation: null
|
|
1021
1188
|
};
|
|
1022
1189
|
}
|
|
1190
|
+
__name(buildSyntheticFill, "buildSyntheticFill");
|
|
1023
1191
|
function buildMissedHistoryFills(args) {
|
|
1024
1192
|
const { orphanFills, pear, connector, entryBySymbol, snapshot } = args;
|
|
1025
1193
|
const fillTotals = /* @__PURE__ */ new Map();
|
|
@@ -1030,13 +1198,16 @@ function buildMissedHistoryFills(args) {
|
|
|
1030
1198
|
orphanNotional.set(fill.symbol, add(orphanNotional.get(fill.symbol) ?? ZERO, notional));
|
|
1031
1199
|
}
|
|
1032
1200
|
const pearTotals = pear.totalBySymbol();
|
|
1033
|
-
const assets = /* @__PURE__ */ new Set([
|
|
1201
|
+
const assets = /* @__PURE__ */ new Set([
|
|
1202
|
+
...orphanFills.map((fill) => fill.symbol),
|
|
1203
|
+
...snapshot.symbols()
|
|
1204
|
+
]);
|
|
1034
1205
|
const earliestTs = orphanFills.reduce((min2, fill) => Math.min(min2, fill.timestamp), Infinity);
|
|
1035
1206
|
if (!Number.isFinite(earliestTs)) {
|
|
1036
1207
|
return [];
|
|
1037
1208
|
}
|
|
1038
1209
|
const watermark = Math.max(1, earliestTs - 1);
|
|
1039
|
-
const haveBySymbol = (symbol) => add(pearTotals.get(symbol) ?? ZERO, fillTotals.get(symbol) ?? ZERO);
|
|
1210
|
+
const haveBySymbol = /* @__PURE__ */ __name((symbol) => add(pearTotals.get(symbol) ?? ZERO, fillTotals.get(symbol) ?? ZERO), "haveBySymbol");
|
|
1040
1211
|
const result = [];
|
|
1041
1212
|
for (const gap of gapsToTarget({
|
|
1042
1213
|
assets,
|
|
@@ -1068,6 +1239,7 @@ function buildMissedHistoryFills(args) {
|
|
|
1068
1239
|
}
|
|
1069
1240
|
return result;
|
|
1070
1241
|
}
|
|
1242
|
+
__name(buildMissedHistoryFills, "buildMissedHistoryFills");
|
|
1071
1243
|
function priceForMissedHistoryFill(args) {
|
|
1072
1244
|
const { exchangePosition, syntheticQty, pearQty, pearEntry, orphanNotional } = args;
|
|
1073
1245
|
const fallback = exchangePosition?.entryPrice ?? "0";
|
|
@@ -1089,6 +1261,7 @@ function priceForMissedHistoryFill(args) {
|
|
|
1089
1261
|
const derived = divide(subtract(targetNotional, realNotional), synthQty);
|
|
1090
1262
|
return derived.isFinite() && derived.isPositive() ? derived.toFixed() : fallback;
|
|
1091
1263
|
}
|
|
1264
|
+
__name(priceForMissedHistoryFill, "priceForMissedHistoryFill");
|
|
1092
1265
|
function findNeededSynthetics(args) {
|
|
1093
1266
|
const { positions, pear, snapshot } = args;
|
|
1094
1267
|
const pearTotals = pear.totalBySymbol();
|
|
@@ -1104,13 +1277,13 @@ function findNeededSynthetics(args) {
|
|
|
1104
1277
|
for (const symbol of pearTotals.keys()) {
|
|
1105
1278
|
assets.add(symbol);
|
|
1106
1279
|
}
|
|
1107
|
-
const haveBySymbol = (symbol) => {
|
|
1280
|
+
const haveBySymbol = /* @__PURE__ */ __name((symbol) => {
|
|
1108
1281
|
let totalFills = ZERO;
|
|
1109
1282
|
for (const pos of positions) {
|
|
1110
1283
|
totalFills = add(totalFills, Exposure.netFromParts(pos.parts, symbol));
|
|
1111
1284
|
}
|
|
1112
1285
|
return add(pearTotals.get(symbol) ?? ZERO, totalFills);
|
|
1113
|
-
};
|
|
1286
|
+
}, "haveBySymbol");
|
|
1114
1287
|
const result = /* @__PURE__ */ new Map();
|
|
1115
1288
|
for (const gap of gapsToTarget({
|
|
1116
1289
|
assets,
|
|
@@ -1137,9 +1310,13 @@ function findNeededSynthetics(args) {
|
|
|
1137
1310
|
}
|
|
1138
1311
|
return result;
|
|
1139
1312
|
}
|
|
1313
|
+
__name(findNeededSynthetics, "findNeededSynthetics");
|
|
1140
1314
|
function assignSyntheticsToPositions(args) {
|
|
1141
1315
|
const { neededSynthetics, positions, pear, tradeAccountId } = args;
|
|
1142
|
-
const keyOf = (symbols) => generateDeterministicPositionKey({
|
|
1316
|
+
const keyOf = /* @__PURE__ */ __name((symbols) => generateDeterministicPositionKey({
|
|
1317
|
+
tradeAccountId,
|
|
1318
|
+
symbols
|
|
1319
|
+
}), "keyOf");
|
|
1143
1320
|
const existingBySymbol = pear.positionBySymbol();
|
|
1144
1321
|
const created = [];
|
|
1145
1322
|
const byPosition = /* @__PURE__ */ new Map();
|
|
@@ -1147,11 +1324,12 @@ function assignSyntheticsToPositions(args) {
|
|
|
1147
1324
|
let target = positions.find((pos) => !pos.closed && pos.parts.some((part) => part.source.symbol === symbol));
|
|
1148
1325
|
if (!target) {
|
|
1149
1326
|
const existing = existingBySymbol.get(symbol);
|
|
1150
|
-
const positionKey = existing ? existing.positionKey : keyOf([
|
|
1327
|
+
const positionKey = existing ? existing.positionKey : keyOf([
|
|
1328
|
+
symbol
|
|
1329
|
+
]);
|
|
1151
1330
|
target = positions.find((pos) => pos.positionKey === positionKey) ?? created.find((pos) => pos.positionKey === positionKey);
|
|
1152
1331
|
if (!target) {
|
|
1153
1332
|
target = existing ? (
|
|
1154
|
-
// Stand-in for an existing DB position untouched by this batch's fills:
|
|
1155
1333
|
// seed its exposure and origin so folding the synthetic yields the true
|
|
1156
1334
|
// final state (a gone-close nets to zero -> CLOSED/NETTED, a gap writes
|
|
1157
1335
|
// the full total, not just the delta).
|
|
@@ -1175,8 +1353,15 @@ function assignSyntheticsToPositions(args) {
|
|
|
1175
1353
|
list.push(spec);
|
|
1176
1354
|
byPosition.set(target, list);
|
|
1177
1355
|
}
|
|
1178
|
-
return {
|
|
1356
|
+
return {
|
|
1357
|
+
positions: [
|
|
1358
|
+
...positions,
|
|
1359
|
+
...created
|
|
1360
|
+
],
|
|
1361
|
+
synthByPosition: byPosition
|
|
1362
|
+
};
|
|
1179
1363
|
}
|
|
1364
|
+
__name(assignSyntheticsToPositions, "assignSyntheticsToPositions");
|
|
1180
1365
|
function gapsToTarget(args) {
|
|
1181
1366
|
const { assets, haveBySymbol, snapshot } = args;
|
|
1182
1367
|
const gaps = [];
|
|
@@ -1192,6 +1377,7 @@ function gapsToTarget(args) {
|
|
|
1192
1377
|
}
|
|
1193
1378
|
return gaps;
|
|
1194
1379
|
}
|
|
1380
|
+
__name(gapsToTarget, "gapsToTarget");
|
|
1195
1381
|
function findGapToTarget(args) {
|
|
1196
1382
|
const { symbol, have, target } = args;
|
|
1197
1383
|
if (isZero(target)) {
|
|
@@ -1216,6 +1402,7 @@ function findGapToTarget(args) {
|
|
|
1216
1402
|
}
|
|
1217
1403
|
return null;
|
|
1218
1404
|
}
|
|
1405
|
+
__name(findGapToTarget, "findGapToTarget");
|
|
1219
1406
|
|
|
1220
1407
|
// src/sync/applicables/fills.ts
|
|
1221
1408
|
function assignPartIds(positions) {
|
|
@@ -1231,13 +1418,18 @@ function assignPartIds(positions) {
|
|
|
1231
1418
|
for (const [sourceId, parts] of bySource) {
|
|
1232
1419
|
const single = parts.length === 1 ? parts[0] : void 0;
|
|
1233
1420
|
if (single && isEqualTo(single.quantity, single.source.quantity)) {
|
|
1234
|
-
resolution.set(single, {
|
|
1421
|
+
resolution.set(single, {
|
|
1422
|
+
exchangeFillId: sourceId
|
|
1423
|
+
});
|
|
1235
1424
|
continue;
|
|
1236
1425
|
}
|
|
1237
1426
|
parts.forEach((part, i) => {
|
|
1238
1427
|
resolution.set(part, {
|
|
1239
1428
|
exchangeFillId: `${sourceId}-p${i}`,
|
|
1240
|
-
original: {
|
|
1429
|
+
original: {
|
|
1430
|
+
exchangeFillId: sourceId,
|
|
1431
|
+
quantity: part.source.quantity
|
|
1432
|
+
}
|
|
1241
1433
|
});
|
|
1242
1434
|
});
|
|
1243
1435
|
}
|
|
@@ -1249,6 +1441,7 @@ function assignPartIds(positions) {
|
|
|
1249
1441
|
return id;
|
|
1250
1442
|
};
|
|
1251
1443
|
}
|
|
1444
|
+
__name(assignPartIds, "assignPartIds");
|
|
1252
1445
|
function partToApplicableFill(part, id, connector) {
|
|
1253
1446
|
const fill = part.source;
|
|
1254
1447
|
const base = {
|
|
@@ -1283,9 +1476,12 @@ function partToApplicableFill(part, id, connector) {
|
|
|
1283
1476
|
tradeFee: feeShareForPart(fill.tradeFee, part.quantity, fill.quantity),
|
|
1284
1477
|
builderFee: feeShareForPart(fill.builderFee, part.quantity, fill.quantity),
|
|
1285
1478
|
liquidation: fill.liquidation,
|
|
1286
|
-
...id.original ? {
|
|
1479
|
+
...id.original ? {
|
|
1480
|
+
original: id.original
|
|
1481
|
+
} : {}
|
|
1287
1482
|
};
|
|
1288
1483
|
}
|
|
1484
|
+
__name(partToApplicableFill, "partToApplicableFill");
|
|
1289
1485
|
function feeShareForPart(totalFee, partQty, sourceQty) {
|
|
1290
1486
|
if (isEqualTo(partQty, sourceQty)) {
|
|
1291
1487
|
return totalFee;
|
|
@@ -1296,6 +1492,7 @@ function feeShareForPart(totalFee, partQty, sourceQty) {
|
|
|
1296
1492
|
const feeShare = divide(multiply(totalFee, partQty), sourceQty);
|
|
1297
1493
|
return feeShare.toFixed();
|
|
1298
1494
|
}
|
|
1495
|
+
__name(feeShareForPart, "feeShareForPart");
|
|
1299
1496
|
|
|
1300
1497
|
// src/sync/applicables/position.ts
|
|
1301
1498
|
function assembleApplicables(args) {
|
|
@@ -1310,17 +1507,19 @@ function assembleApplicables(args) {
|
|
|
1310
1507
|
}
|
|
1311
1508
|
return applicables;
|
|
1312
1509
|
}
|
|
1510
|
+
__name(assembleApplicables, "assembleApplicables");
|
|
1313
1511
|
function assembleOneApplicable(pos, connector, partIdFor, assignedSynthetics, now) {
|
|
1314
1512
|
const realFills = pos.parts.map((part) => partToApplicableFill(part, partIdFor(part), connector)).sort((a, b) => a.timestamp - b.timestamp);
|
|
1315
|
-
const syntheticFills = assignedSynthetics.map(
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1513
|
+
const syntheticFills = assignedSynthetics.map((synthetic) => buildSyntheticFill({
|
|
1514
|
+
positionKey: pos.positionKey,
|
|
1515
|
+
connector,
|
|
1516
|
+
timestamp: anchorSyntheticTimestamp(realFills, synthetic.symbol, now),
|
|
1517
|
+
...synthetic
|
|
1518
|
+
}));
|
|
1519
|
+
const fills = [
|
|
1520
|
+
...realFills,
|
|
1521
|
+
...syntheticFills
|
|
1522
|
+
].sort((a, b) => a.timestamp - b.timestamp);
|
|
1324
1523
|
if (fills.length === 0) {
|
|
1325
1524
|
return null;
|
|
1326
1525
|
}
|
|
@@ -1333,11 +1532,14 @@ function assembleOneApplicable(pos, connector, partIdFor, assignedSynthetics, no
|
|
|
1333
1532
|
exposure: exposure.toSigned(),
|
|
1334
1533
|
origin,
|
|
1335
1534
|
status,
|
|
1336
|
-
...closed ? {
|
|
1535
|
+
...closed ? {
|
|
1536
|
+
closedReason: "NETTED"
|
|
1537
|
+
} : {},
|
|
1337
1538
|
fills,
|
|
1338
1539
|
metadata: buildMetadata(new Set(exposure.symbols()), realFills)
|
|
1339
1540
|
};
|
|
1340
1541
|
}
|
|
1542
|
+
__name(assembleOneApplicable, "assembleOneApplicable");
|
|
1341
1543
|
function finalExposure(pos, synthetics) {
|
|
1342
1544
|
const exposure = pos.exposure.clone();
|
|
1343
1545
|
for (const synthetic of synthetics) {
|
|
@@ -1345,6 +1547,7 @@ function finalExposure(pos, synthetics) {
|
|
|
1345
1547
|
}
|
|
1346
1548
|
return exposure;
|
|
1347
1549
|
}
|
|
1550
|
+
__name(finalExposure, "finalExposure");
|
|
1348
1551
|
function anchorSyntheticTimestamp(realFills, symbol, now) {
|
|
1349
1552
|
const oldestForSymbol = realFills.find((fill) => fill.symbol === symbol)?.timestamp;
|
|
1350
1553
|
if (oldestForSymbol !== void 0) {
|
|
@@ -1353,12 +1556,16 @@ function anchorSyntheticTimestamp(realFills, symbol, now) {
|
|
|
1353
1556
|
const oldestRealTs = realFills[0]?.timestamp;
|
|
1354
1557
|
return oldestRealTs !== void 0 ? Math.max(1, oldestRealTs - 1) : now;
|
|
1355
1558
|
}
|
|
1559
|
+
__name(anchorSyntheticTimestamp, "anchorSyntheticTimestamp");
|
|
1356
1560
|
function buildMetadata(symbols, realFills) {
|
|
1357
1561
|
const initialExposureUSD = {};
|
|
1358
1562
|
for (const symbol of symbols) {
|
|
1359
|
-
const legFills = realFills.flatMap(
|
|
1360
|
-
|
|
1361
|
-
|
|
1563
|
+
const legFills = realFills.flatMap((fill) => fill.symbol === symbol && fill.usd != null ? [
|
|
1564
|
+
{
|
|
1565
|
+
side: fill.side,
|
|
1566
|
+
usd: fill.usd
|
|
1567
|
+
}
|
|
1568
|
+
] : []);
|
|
1362
1569
|
const opener = legFills[0];
|
|
1363
1570
|
if (!opener) {
|
|
1364
1571
|
continue;
|
|
@@ -1367,8 +1574,11 @@ function buildMetadata(symbols, realFills) {
|
|
|
1367
1574
|
const signedNotional = signed(opener.side === "BUY" ? notional : notional.negated());
|
|
1368
1575
|
initialExposureUSD[symbol] = signedNotional;
|
|
1369
1576
|
}
|
|
1370
|
-
return {
|
|
1577
|
+
return {
|
|
1578
|
+
initialExposureUSD
|
|
1579
|
+
};
|
|
1371
1580
|
}
|
|
1581
|
+
__name(buildMetadata, "buildMetadata");
|
|
1372
1582
|
function mergeOrigin(existing, fills) {
|
|
1373
1583
|
let anyExternal = false;
|
|
1374
1584
|
let anyPear = false;
|
|
@@ -1396,16 +1606,23 @@ function mergeOrigin(existing, fills) {
|
|
|
1396
1606
|
}
|
|
1397
1607
|
return anyPear ? "PEAR" : "EXTERNAL";
|
|
1398
1608
|
}
|
|
1609
|
+
__name(mergeOrigin, "mergeOrigin");
|
|
1399
1610
|
|
|
1400
1611
|
// src/sync/applicables/snapshot.ts
|
|
1401
1612
|
var ExchangeSnapshot = class _ExchangeSnapshot {
|
|
1613
|
+
static {
|
|
1614
|
+
__name(this, "ExchangeSnapshot");
|
|
1615
|
+
}
|
|
1616
|
+
bySymbol;
|
|
1402
1617
|
constructor(bySymbol) {
|
|
1403
1618
|
this.bySymbol = bySymbol;
|
|
1404
1619
|
}
|
|
1405
|
-
bySymbol;
|
|
1406
1620
|
/** Index `positions` by symbol for O(1) lookup. */
|
|
1407
1621
|
static from(positions) {
|
|
1408
|
-
return new _ExchangeSnapshot(new Map(positions.map((position) => [
|
|
1622
|
+
return new _ExchangeSnapshot(new Map(positions.map((position) => [
|
|
1623
|
+
position.symbol,
|
|
1624
|
+
position
|
|
1625
|
+
])));
|
|
1409
1626
|
}
|
|
1410
1627
|
/** Raw exchange position for `symbol`, or `undefined` when the exchange holds nothing. */
|
|
1411
1628
|
get(symbol) {
|
|
@@ -1427,14 +1644,19 @@ var ExchangeSnapshot = class _ExchangeSnapshot {
|
|
|
1427
1644
|
}
|
|
1428
1645
|
/** All symbols the exchange currently reports a position for. */
|
|
1429
1646
|
symbols() {
|
|
1430
|
-
return [
|
|
1647
|
+
return [
|
|
1648
|
+
...this.bySymbol.keys()
|
|
1649
|
+
];
|
|
1431
1650
|
}
|
|
1432
1651
|
};
|
|
1433
1652
|
var PearHoldings = class _PearHoldings {
|
|
1653
|
+
static {
|
|
1654
|
+
__name(this, "PearHoldings");
|
|
1655
|
+
}
|
|
1656
|
+
filtered;
|
|
1434
1657
|
constructor(filtered) {
|
|
1435
1658
|
this.filtered = filtered;
|
|
1436
1659
|
}
|
|
1437
|
-
filtered;
|
|
1438
1660
|
/** Filter `openPositions` to the given connector and wrap. */
|
|
1439
1661
|
static from(args) {
|
|
1440
1662
|
const { openPositions, connector } = args;
|
|
@@ -1458,10 +1680,10 @@ var PearHoldings = class _PearHoldings {
|
|
|
1458
1680
|
return totals;
|
|
1459
1681
|
}
|
|
1460
1682
|
/**
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1683
|
+
* Which open DB position holds each active asset — the fallback target for a
|
|
1684
|
+
* synthetic when no matched position already carries that asset. First open
|
|
1685
|
+
* position with a non-zero leg wins per symbol.
|
|
1686
|
+
*/
|
|
1465
1687
|
positionBySymbol() {
|
|
1466
1688
|
const existingBySymbol = /* @__PURE__ */ new Map();
|
|
1467
1689
|
for (const position of this.filtered) {
|
|
@@ -1481,18 +1703,37 @@ var PearHoldings = class _PearHoldings {
|
|
|
1481
1703
|
function buildApplicables(args) {
|
|
1482
1704
|
const { tradeAccountId, connector, orphanFills, openPositions, entryBySymbol, exchangePositions } = args;
|
|
1483
1705
|
const snapshot = ExchangeSnapshot.from(exchangePositions);
|
|
1484
|
-
const pear = PearHoldings.from({
|
|
1706
|
+
const pear = PearHoldings.from({
|
|
1707
|
+
openPositions,
|
|
1708
|
+
connector
|
|
1709
|
+
});
|
|
1485
1710
|
const now = Date.now();
|
|
1486
|
-
const fills = addFillsForMissedHistory({
|
|
1487
|
-
|
|
1711
|
+
const fills = addFillsForMissedHistory({
|
|
1712
|
+
orphanFills,
|
|
1713
|
+
pear,
|
|
1714
|
+
connector,
|
|
1715
|
+
entryBySymbol,
|
|
1716
|
+
snapshot
|
|
1717
|
+
});
|
|
1718
|
+
const matched = matchPositions({
|
|
1719
|
+
tradeAccountId,
|
|
1720
|
+
orphanFills: fills,
|
|
1721
|
+
pear
|
|
1722
|
+
});
|
|
1488
1723
|
const { positions, synthByPosition } = alignPositionsWithExchange({
|
|
1489
1724
|
positions: matched,
|
|
1490
1725
|
pear,
|
|
1491
1726
|
snapshot,
|
|
1492
1727
|
tradeAccountId
|
|
1493
1728
|
});
|
|
1494
|
-
return assembleApplicables({
|
|
1729
|
+
return assembleApplicables({
|
|
1730
|
+
positions,
|
|
1731
|
+
connector,
|
|
1732
|
+
synthByPosition,
|
|
1733
|
+
now
|
|
1734
|
+
});
|
|
1495
1735
|
}
|
|
1736
|
+
__name(buildApplicables, "buildApplicables");
|
|
1496
1737
|
|
|
1497
1738
|
// src/sync/cutoff.ts
|
|
1498
1739
|
function selectFirstSyncCutoff(args) {
|
|
@@ -1515,7 +1756,9 @@ function selectFirstSyncCutoff(args) {
|
|
|
1515
1756
|
for (let boundary = fills.length; boundary >= 0; boundary--) {
|
|
1516
1757
|
const withinAnchor = pearIndex === null || boundary <= pearIndex;
|
|
1517
1758
|
if (withinAnchor && book.isAllZero() && isStrictTimestampBoundary(fills, boundary)) {
|
|
1518
|
-
return boundary === 0 ? null : {
|
|
1759
|
+
return boundary === 0 ? null : {
|
|
1760
|
+
keepFromIndex: boundary
|
|
1761
|
+
};
|
|
1519
1762
|
}
|
|
1520
1763
|
const preceding = fills[boundary - 1];
|
|
1521
1764
|
if (preceding) {
|
|
@@ -1524,6 +1767,7 @@ function selectFirstSyncCutoff(args) {
|
|
|
1524
1767
|
}
|
|
1525
1768
|
return null;
|
|
1526
1769
|
}
|
|
1770
|
+
__name(selectFirstSyncCutoff, "selectFirstSyncCutoff");
|
|
1527
1771
|
function findFirstPearFillIndex(connector, fills) {
|
|
1528
1772
|
const index = fills.findIndex((fill) => isPearCloid(connector, fill.cloid));
|
|
1529
1773
|
if (index === -1) {
|
|
@@ -1531,6 +1775,7 @@ function findFirstPearFillIndex(connector, fills) {
|
|
|
1531
1775
|
}
|
|
1532
1776
|
return index;
|
|
1533
1777
|
}
|
|
1778
|
+
__name(findFirstPearFillIndex, "findFirstPearFillIndex");
|
|
1534
1779
|
function isStrictTimestampBoundary(fills, boundary) {
|
|
1535
1780
|
const before = fills[boundary - 1];
|
|
1536
1781
|
const after = fills[boundary];
|
|
@@ -1539,26 +1784,13 @@ function isStrictTimestampBoundary(fills, boundary) {
|
|
|
1539
1784
|
}
|
|
1540
1785
|
return before.timestamp < after.timestamp;
|
|
1541
1786
|
}
|
|
1787
|
+
__name(isStrictTimestampBoundary, "isStrictTimestampBoundary");
|
|
1542
1788
|
|
|
1543
1789
|
// src/sync/compute.ts
|
|
1544
1790
|
async function computeSyncPayload(args) {
|
|
1545
|
-
const {
|
|
1546
|
-
tradeAccountId,
|
|
1547
|
-
connector,
|
|
1548
|
-
openPositions,
|
|
1549
|
-
entryBySymbol,
|
|
1550
|
-
provisionalFills,
|
|
1551
|
-
confirmedExchangeFillIds,
|
|
1552
|
-
externalFills,
|
|
1553
|
-
exchangePositions,
|
|
1554
|
-
neverSynced
|
|
1555
|
-
} = args;
|
|
1791
|
+
const { tradeAccountId, connector, openPositions, entryBySymbol, provisionalFills, confirmedExchangeFillIds, externalFills, exchangePositions, neverSynced } = args;
|
|
1556
1792
|
const fillsToProcess = _prepareExternalFills(externalFills, confirmedExchangeFillIds);
|
|
1557
|
-
const {
|
|
1558
|
-
reconcilables,
|
|
1559
|
-
confirmables: confirmableFills,
|
|
1560
|
-
orphans
|
|
1561
|
-
} = _segregateExternalFills(provisionalFills, fillsToProcess);
|
|
1793
|
+
const { reconcilables, confirmables: confirmableFills, orphans } = _segregateExternalFills(provisionalFills, fillsToProcess);
|
|
1562
1794
|
const confirmablesByPosition = groupConfirmablesByPosition(confirmableFills);
|
|
1563
1795
|
const { keptOrphans, firstSyncTrim } = applyFirstSyncCutoff({
|
|
1564
1796
|
neverSynced,
|
|
@@ -1591,11 +1823,10 @@ async function computeSyncPayload(args) {
|
|
|
1591
1823
|
firstSyncTrim
|
|
1592
1824
|
};
|
|
1593
1825
|
}
|
|
1826
|
+
__name(computeSyncPayload, "computeSyncPayload");
|
|
1594
1827
|
function _prepareExternalFills(externalFills, confirmedExchangeFillIdsInput) {
|
|
1595
1828
|
const dedupedByExchangeFillId = /* @__PURE__ */ new Map();
|
|
1596
|
-
const confirmedExchangeFillIds = new Set(
|
|
1597
|
-
Array.from(confirmedExchangeFillIdsInput).map((exchangeFillId) => _normalizeExchangeFillId(exchangeFillId)).filter((exchangeFillId) => exchangeFillId !== void 0)
|
|
1598
|
-
);
|
|
1829
|
+
const confirmedExchangeFillIds = new Set(Array.from(confirmedExchangeFillIdsInput).map((exchangeFillId) => _normalizeExchangeFillId(exchangeFillId)).filter((exchangeFillId) => exchangeFillId !== void 0));
|
|
1599
1830
|
const candidates = externalFills.reduce((acc, fill) => {
|
|
1600
1831
|
const normalizedExchangeFillId = _normalizeExchangeFillId(fill.exchangeFillId);
|
|
1601
1832
|
if (!normalizedExchangeFillId) {
|
|
@@ -1610,7 +1841,10 @@ function _prepareExternalFills(externalFills, confirmedExchangeFillIdsInput) {
|
|
|
1610
1841
|
if (confirmedExchangeFillIds.has(normalizedExchangeFillId)) {
|
|
1611
1842
|
return acc;
|
|
1612
1843
|
}
|
|
1613
|
-
acc.push({
|
|
1844
|
+
acc.push({
|
|
1845
|
+
...fill,
|
|
1846
|
+
exchangeFillId: normalizedExchangeFillId
|
|
1847
|
+
});
|
|
1614
1848
|
return acc;
|
|
1615
1849
|
}, []);
|
|
1616
1850
|
for (const fill of candidates) {
|
|
@@ -1621,6 +1855,7 @@ function _prepareExternalFills(externalFills, confirmedExchangeFillIdsInput) {
|
|
|
1621
1855
|
}
|
|
1622
1856
|
return Array.from(dedupedByExchangeFillId.values()).sort((a, b) => a.timestamp - b.timestamp);
|
|
1623
1857
|
}
|
|
1858
|
+
__name(_prepareExternalFills, "_prepareExternalFills");
|
|
1624
1859
|
function _segregateExternalFills(provisionalFills, externalFills) {
|
|
1625
1860
|
const provisionalFillsMap = /* @__PURE__ */ new Map();
|
|
1626
1861
|
for (const fill of provisionalFills) {
|
|
@@ -1637,7 +1872,10 @@ function _segregateExternalFills(provisionalFills, externalFills) {
|
|
|
1637
1872
|
if (!cloid) {
|
|
1638
1873
|
orphans.push(fill);
|
|
1639
1874
|
} else {
|
|
1640
|
-
fillsByCloid.set(cloid, [
|
|
1875
|
+
fillsByCloid.set(cloid, [
|
|
1876
|
+
...fillsByCloid.get(cloid) || [],
|
|
1877
|
+
fill
|
|
1878
|
+
]);
|
|
1641
1879
|
}
|
|
1642
1880
|
}
|
|
1643
1881
|
const reconcilables = {};
|
|
@@ -1651,9 +1889,18 @@ function _segregateExternalFills(provisionalFills, externalFills) {
|
|
|
1651
1889
|
throw new Error("Corrupted state: sibling group must contain at least 2 fills for reconcile");
|
|
1652
1890
|
}
|
|
1653
1891
|
reconcilables[cloid] = [
|
|
1654
|
-
{
|
|
1655
|
-
|
|
1656
|
-
|
|
1892
|
+
{
|
|
1893
|
+
...first,
|
|
1894
|
+
cloid
|
|
1895
|
+
},
|
|
1896
|
+
{
|
|
1897
|
+
...second,
|
|
1898
|
+
cloid
|
|
1899
|
+
},
|
|
1900
|
+
...rest.map((fill) => ({
|
|
1901
|
+
...fill,
|
|
1902
|
+
cloid
|
|
1903
|
+
}))
|
|
1657
1904
|
];
|
|
1658
1905
|
} else {
|
|
1659
1906
|
orphans.push(...fills);
|
|
@@ -1673,8 +1920,13 @@ function _segregateExternalFills(provisionalFills, externalFills) {
|
|
|
1673
1920
|
}
|
|
1674
1921
|
}
|
|
1675
1922
|
}
|
|
1676
|
-
return {
|
|
1923
|
+
return {
|
|
1924
|
+
reconcilables,
|
|
1925
|
+
confirmables: confirmableFills,
|
|
1926
|
+
orphans
|
|
1927
|
+
};
|
|
1677
1928
|
}
|
|
1929
|
+
__name(_segregateExternalFills, "_segregateExternalFills");
|
|
1678
1930
|
function _normalizeCloid(cloid) {
|
|
1679
1931
|
const value = cloid?.trim();
|
|
1680
1932
|
if (!value) {
|
|
@@ -1682,6 +1934,7 @@ function _normalizeCloid(cloid) {
|
|
|
1682
1934
|
}
|
|
1683
1935
|
return value;
|
|
1684
1936
|
}
|
|
1937
|
+
__name(_normalizeCloid, "_normalizeCloid");
|
|
1685
1938
|
function _normalizeExchangeFillId(exchangeFillId) {
|
|
1686
1939
|
const value = exchangeFillId?.trim().toLowerCase();
|
|
1687
1940
|
if (!value) {
|
|
@@ -1689,6 +1942,7 @@ function _normalizeExchangeFillId(exchangeFillId) {
|
|
|
1689
1942
|
}
|
|
1690
1943
|
return value;
|
|
1691
1944
|
}
|
|
1945
|
+
__name(_normalizeExchangeFillId, "_normalizeExchangeFillId");
|
|
1692
1946
|
function _isValidExternalQuantity(quantity) {
|
|
1693
1947
|
if (/e/i.test(quantity)) {
|
|
1694
1948
|
return false;
|
|
@@ -1698,18 +1952,24 @@ function _isValidExternalQuantity(quantity) {
|
|
|
1698
1952
|
}
|
|
1699
1953
|
return isGreaterThan(quantity, ZERO);
|
|
1700
1954
|
}
|
|
1955
|
+
__name(_isValidExternalQuantity, "_isValidExternalQuantity");
|
|
1701
1956
|
function _isValidExternalTimestamp(timestamp) {
|
|
1702
1957
|
if (!Number.isFinite(timestamp) || timestamp < 0) {
|
|
1703
1958
|
return false;
|
|
1704
1959
|
}
|
|
1705
1960
|
return timestamp <= Date.now() + WATERMARK_FUTURE_SKEW_MS;
|
|
1706
1961
|
}
|
|
1962
|
+
__name(_isValidExternalTimestamp, "_isValidExternalTimestamp");
|
|
1707
1963
|
function groupConfirmablesByPosition(confirmableFills) {
|
|
1708
1964
|
return confirmableFills.reduce((acc, fill) => {
|
|
1709
|
-
acc[fill.positionId] = [
|
|
1965
|
+
acc[fill.positionId] = [
|
|
1966
|
+
...acc[fill.positionId] || [],
|
|
1967
|
+
fill
|
|
1968
|
+
];
|
|
1710
1969
|
return acc;
|
|
1711
1970
|
}, {});
|
|
1712
1971
|
}
|
|
1972
|
+
__name(groupConfirmablesByPosition, "groupConfirmablesByPosition");
|
|
1713
1973
|
function latestFillTimestamp(fills) {
|
|
1714
1974
|
const last = fills[fills.length - 1];
|
|
1715
1975
|
if (!last) {
|
|
@@ -1717,10 +1977,14 @@ function latestFillTimestamp(fills) {
|
|
|
1717
1977
|
}
|
|
1718
1978
|
return last.timestamp;
|
|
1719
1979
|
}
|
|
1980
|
+
__name(latestFillTimestamp, "latestFillTimestamp");
|
|
1720
1981
|
function applyFirstSyncCutoff(args) {
|
|
1721
1982
|
const { neverSynced, fillsToProcess, orphans, exchangePositions, connector } = args;
|
|
1722
1983
|
if (!neverSynced || fillsToProcess.length === 0) {
|
|
1723
|
-
return {
|
|
1984
|
+
return {
|
|
1985
|
+
keptOrphans: orphans,
|
|
1986
|
+
firstSyncTrim: null
|
|
1987
|
+
};
|
|
1724
1988
|
}
|
|
1725
1989
|
const cutoff = selectFirstSyncCutoff({
|
|
1726
1990
|
fills: fillsToProcess,
|
|
@@ -1728,7 +1992,10 @@ function applyFirstSyncCutoff(args) {
|
|
|
1728
1992
|
connector
|
|
1729
1993
|
});
|
|
1730
1994
|
if (!cutoff) {
|
|
1731
|
-
return {
|
|
1995
|
+
return {
|
|
1996
|
+
keptOrphans: orphans,
|
|
1997
|
+
firstSyncTrim: null
|
|
1998
|
+
};
|
|
1732
1999
|
}
|
|
1733
2000
|
const keptIds = new Set(fillsToProcess.slice(cutoff.keepFromIndex).map((fill) => fill.exchangeFillId));
|
|
1734
2001
|
const keptOrphans = [];
|
|
@@ -1737,7 +2004,10 @@ function applyFirstSyncCutoff(args) {
|
|
|
1737
2004
|
(keptIds.has(orphan.exchangeFillId) ? keptOrphans : droppedOrphans).push(orphan);
|
|
1738
2005
|
}
|
|
1739
2006
|
if (droppedOrphans.length === 0) {
|
|
1740
|
-
return {
|
|
2007
|
+
return {
|
|
2008
|
+
keptOrphans,
|
|
2009
|
+
firstSyncTrim: null
|
|
2010
|
+
};
|
|
1741
2011
|
}
|
|
1742
2012
|
const firstKeptFill = fillsToProcess[cutoff.keepFromIndex];
|
|
1743
2013
|
return {
|
|
@@ -1749,9 +2019,11 @@ function applyFirstSyncCutoff(args) {
|
|
|
1749
2019
|
}
|
|
1750
2020
|
};
|
|
1751
2021
|
}
|
|
2022
|
+
__name(applyFirstSyncCutoff, "applyFirstSyncCutoff");
|
|
1752
2023
|
function isEmptyCycle(reconcilables, confirmablesByPosition, applicables) {
|
|
1753
2024
|
return Object.keys(reconcilables).length === 0 && Object.keys(confirmablesByPosition).length === 0 && applicables.length === 0;
|
|
1754
2025
|
}
|
|
2026
|
+
__name(isEmptyCycle, "isEmptyCycle");
|
|
1755
2027
|
|
|
1756
2028
|
// src/validate-leverage.ts
|
|
1757
2029
|
function validateLeverage(leverage, instrument) {
|
|
@@ -1763,32 +2035,104 @@ function validateLeverage(leverage, instrument) {
|
|
|
1763
2035
|
}
|
|
1764
2036
|
return true;
|
|
1765
2037
|
}
|
|
2038
|
+
__name(validateLeverage, "validateLeverage");
|
|
1766
2039
|
function validateQuantity(quantity, price, precision) {
|
|
1767
|
-
const parsedQuantity = new
|
|
2040
|
+
const parsedQuantity = new BigNumber7(quantity);
|
|
1768
2041
|
if (!parsedQuantity.isFinite() || parsedQuantity.lte(0)) {
|
|
1769
2042
|
return false;
|
|
1770
2043
|
}
|
|
1771
|
-
const parsedPrice = new
|
|
2044
|
+
const parsedPrice = new BigNumber7(price);
|
|
1772
2045
|
if (!parsedPrice.isFinite() || parsedPrice.lte(0)) {
|
|
1773
2046
|
return false;
|
|
1774
2047
|
}
|
|
1775
|
-
const step = new
|
|
2048
|
+
const step = new BigNumber7(precision.quantity.step);
|
|
1776
2049
|
const scaled = parsedQuantity.div(step);
|
|
1777
2050
|
if (!scaled.isInteger()) {
|
|
1778
2051
|
return false;
|
|
1779
2052
|
}
|
|
1780
2053
|
const notional = contractsToBase(parsedQuantity, precision.quantity.contractSize).times(parsedPrice);
|
|
1781
2054
|
if (precision.notional?.min !== void 0 && precision.notional.min !== null && precision.notional.min > 0) {
|
|
1782
|
-
if (notional.lt(new
|
|
2055
|
+
if (notional.lt(new BigNumber7(precision.notional.min))) {
|
|
1783
2056
|
return false;
|
|
1784
2057
|
}
|
|
1785
2058
|
}
|
|
1786
2059
|
if (precision.notional?.max !== void 0 && precision.notional.max !== null && precision.notional.max > 0) {
|
|
1787
|
-
if (notional.gt(new
|
|
2060
|
+
if (notional.gt(new BigNumber7(precision.notional.max))) {
|
|
1788
2061
|
return false;
|
|
1789
2062
|
}
|
|
1790
2063
|
}
|
|
1791
2064
|
return true;
|
|
1792
2065
|
}
|
|
2066
|
+
__name(validateQuantity, "validateQuantity");
|
|
2067
|
+
var STORAGE_PREFIX = "pear.x-handoff.";
|
|
2068
|
+
function startXHandoff(storage) {
|
|
2069
|
+
const handoffId = base64urlnopad.encode(randomBytes(16));
|
|
2070
|
+
const verifier = base64urlnopad.encode(randomBytes(32));
|
|
2071
|
+
storage.setItem(`${STORAGE_PREFIX}${handoffId}`, verifier);
|
|
2072
|
+
return {
|
|
2073
|
+
handoffId,
|
|
2074
|
+
challenge: base64urlnopad.encode(sha256(utf8ToBytes(verifier)))
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
__name(startXHandoff, "startXHandoff");
|
|
2078
|
+
function finishXHandoff(href, storage) {
|
|
2079
|
+
const url = new URL(href);
|
|
2080
|
+
const linkStatus = url.searchParams.get("x_link");
|
|
2081
|
+
const loginStatus = url.searchParams.get("x_login");
|
|
2082
|
+
const reason = url.searchParams.get("x_reason") ?? void 0;
|
|
2083
|
+
if (linkStatus === "connected" || linkStatus === "reconnected") {
|
|
2084
|
+
return {
|
|
2085
|
+
kind: "link-succeeded",
|
|
2086
|
+
status: linkStatus
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
if (linkStatus) {
|
|
2090
|
+
return {
|
|
2091
|
+
kind: "link-failed",
|
|
2092
|
+
reason
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
if (!loginStatus) {
|
|
2096
|
+
return void 0;
|
|
2097
|
+
}
|
|
2098
|
+
const verifier = takeVerifier(url.searchParams.get("x_handoff"), storage);
|
|
2099
|
+
const ticket = new URLSearchParams(url.hash.replace(/^#/, "")).get("x_ticket");
|
|
2100
|
+
if (loginStatus !== "signed_in" || !ticket || !verifier) {
|
|
2101
|
+
return {
|
|
2102
|
+
kind: "login-failed",
|
|
2103
|
+
reason: loginStatus === "signed_in" ? "wrong_browser" : reason
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
return {
|
|
2107
|
+
kind: "login-settled",
|
|
2108
|
+
ticket,
|
|
2109
|
+
verifier
|
|
2110
|
+
};
|
|
2111
|
+
}
|
|
2112
|
+
__name(finishXHandoff, "finishXHandoff");
|
|
2113
|
+
function stripXReturn(href) {
|
|
2114
|
+
const url = new URL(href);
|
|
2115
|
+
for (const key of [
|
|
2116
|
+
"x_link",
|
|
2117
|
+
"x_login",
|
|
2118
|
+
"x_reason",
|
|
2119
|
+
"x_handoff"
|
|
2120
|
+
]) {
|
|
2121
|
+
url.searchParams.delete(key);
|
|
2122
|
+
}
|
|
2123
|
+
url.hash = "";
|
|
2124
|
+
return `${url.pathname}${url.search}`;
|
|
2125
|
+
}
|
|
2126
|
+
__name(stripXReturn, "stripXReturn");
|
|
2127
|
+
function takeVerifier(handoffId, storage) {
|
|
2128
|
+
if (!handoffId) {
|
|
2129
|
+
return void 0;
|
|
2130
|
+
}
|
|
2131
|
+
const key = `${STORAGE_PREFIX}${handoffId}`;
|
|
2132
|
+
const verifier = storage.getItem(key) ?? void 0;
|
|
2133
|
+
storage.removeItem(key);
|
|
2134
|
+
return verifier;
|
|
2135
|
+
}
|
|
2136
|
+
__name(takeVerifier, "takeVerifier");
|
|
1793
2137
|
|
|
1794
|
-
export { BIPS, ZERO, abs, add, aggregateRealizedPositionState, baseToContracts, buildApplicables, compareValue, computeAssetEntryNotional, computeAssetGrossRealizedPnL, computeAssetRealizedPnLs, computeAssetUndeterminedSizes, computeAssetUnrealizedPnL, computeAssetUnrealizedPnLs, computeBasketWeightedRatioV1, computeEntryPriceForAsset, computePositionEntryPrices, computeRealizedPnlBySymbol, computeRealizedPnlFromFills, computeRealizedPositionState, computeSyncPayload, computeUnrealizedPositionState, computeUnrealizedPositionStateFromEntryPrices, configureProxy, contractsToBase, countDecimals, deriveBasketKey, divide, exponentiate, generateDeterministicPositionKey, isEqualTo, isFiniteDecimal, isGreaterThan, isGreaterThanOrEqual, isLessThan, isLessThanOrEqual, isNegative, isPositive, isZero, min, multiply, negate, parse, precisePrice, preciseQuantity, proxyFetch, sideSign, sign, signed, signedBySide, subtract, toDecimalString, validateLeverage, validateQuantity };
|
|
2138
|
+
export { BIPS, ZERO, abs, add, aggregateRealizedPositionState, baseToContracts, buildApplicables, compareValue, computeAssetEntryNotional, computeAssetGrossRealizedPnL, computeAssetRealizedPnLs, computeAssetUndeterminedSizes, computeAssetUnrealizedPnL, computeAssetUnrealizedPnLs, computeBasketWeightedRatioV1, computeEntryPriceForAsset, computePositionEntryPrices, computeRealizedPnlBySymbol, computeRealizedPnlFromFills, computeRealizedPositionState, computeSyncPayload, computeUnrealizedPositionState, computeUnrealizedPositionStateFromEntryPrices, configureProxy, contractsToBase, countDecimals, deriveBasketKey, divide, exponentiate, finishXHandoff, generateDeterministicPositionKey, isEqualTo, isFiniteDecimal, isGreaterThan, isGreaterThanOrEqual, isLessThan, isLessThanOrEqual, isNegative, isPositive, isZero, min, multiply, negate, parse, precisePrice, preciseQuantity, proxyFetch, sideSign, sign, signed, signedBySide, startXHandoff, stripXReturn, subtract, toDecimalString, validateLeverage, validateQuantity };
|