@pear-protocol/utils 0.5.0 → 0.5.2
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 +533 -185
- 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,79 @@ 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 longOk = [
|
|
967
|
-
|
|
1111
|
+
const longOk = [
|
|
1112
|
+
...leftoverLongs
|
|
1113
|
+
].every((a) => longs.has(a));
|
|
1114
|
+
const shortOk = [
|
|
1115
|
+
...leftoverShorts
|
|
1116
|
+
].every((a) => shorts.has(a));
|
|
968
1117
|
return longOk && shortOk;
|
|
969
1118
|
});
|
|
970
1119
|
if (target) {
|
|
971
1120
|
return target;
|
|
972
1121
|
}
|
|
973
|
-
const symbols = [
|
|
1122
|
+
const symbols = [
|
|
1123
|
+
.../* @__PURE__ */ new Set([
|
|
1124
|
+
...leftoverLongs,
|
|
1125
|
+
...leftoverShorts
|
|
1126
|
+
])
|
|
1127
|
+
];
|
|
974
1128
|
const positionKey = book.keyFor(symbols);
|
|
975
1129
|
return book.findOpenByKey(positionKey) ?? book.create(positionKey);
|
|
976
1130
|
}
|
|
1131
|
+
__name(resolveLeftoverTarget, "resolveLeftoverTarget");
|
|
977
1132
|
function applyLeftover(target, leftover) {
|
|
978
1133
|
for (const entry of leftover) {
|
|
979
1134
|
target.parts.push(...entry.leg.take(entry.qty));
|
|
980
1135
|
target.exposure.addSigned(entry.leg.symbol, entry.qty.toFixed(), entry.side);
|
|
981
1136
|
}
|
|
982
1137
|
}
|
|
1138
|
+
__name(applyLeftover, "applyLeftover");
|
|
983
1139
|
|
|
984
1140
|
// src/sync/applicables/synthetics.ts
|
|
985
1141
|
function isMissedHistoryFill(fill) {
|
|
986
1142
|
return fill.exchangeFillId.startsWith(MISSED_HISTORY_FILL_PREFIX);
|
|
987
1143
|
}
|
|
1144
|
+
__name(isMissedHistoryFill, "isMissedHistoryFill");
|
|
988
1145
|
function addFillsForMissedHistory(args) {
|
|
989
1146
|
const missedHistoryFills = buildMissedHistoryFills(args);
|
|
990
|
-
return [
|
|
1147
|
+
return [
|
|
1148
|
+
...missedHistoryFills,
|
|
1149
|
+
...args.orphanFills
|
|
1150
|
+
].sort((a, b) => a.timestamp - b.timestamp);
|
|
991
1151
|
}
|
|
1152
|
+
__name(addFillsForMissedHistory, "addFillsForMissedHistory");
|
|
992
1153
|
function alignPositionsWithExchange(args) {
|
|
993
1154
|
const { positions, pear, snapshot, tradeAccountId } = args;
|
|
994
|
-
const neededSynthetics = findNeededSynthetics({
|
|
995
|
-
|
|
1155
|
+
const neededSynthetics = findNeededSynthetics({
|
|
1156
|
+
positions,
|
|
1157
|
+
pear,
|
|
1158
|
+
snapshot
|
|
1159
|
+
});
|
|
1160
|
+
return assignSyntheticsToPositions({
|
|
1161
|
+
neededSynthetics,
|
|
1162
|
+
positions,
|
|
1163
|
+
pear,
|
|
1164
|
+
tradeAccountId
|
|
1165
|
+
});
|
|
996
1166
|
}
|
|
1167
|
+
__name(alignPositionsWithExchange, "alignPositionsWithExchange");
|
|
997
1168
|
function buildSyntheticFill(args) {
|
|
998
1169
|
return {
|
|
999
1170
|
// Content-addressed id — no wall-clock, no positionId, so re-syncs collide
|
|
@@ -1020,6 +1191,7 @@ function buildSyntheticFill(args) {
|
|
|
1020
1191
|
liquidation: null
|
|
1021
1192
|
};
|
|
1022
1193
|
}
|
|
1194
|
+
__name(buildSyntheticFill, "buildSyntheticFill");
|
|
1023
1195
|
function buildMissedHistoryFills(args) {
|
|
1024
1196
|
const { orphanFills, pear, connector, entryBySymbol, snapshot } = args;
|
|
1025
1197
|
const fillTotals = /* @__PURE__ */ new Map();
|
|
@@ -1030,13 +1202,16 @@ function buildMissedHistoryFills(args) {
|
|
|
1030
1202
|
orphanNotional.set(fill.symbol, add(orphanNotional.get(fill.symbol) ?? ZERO, notional));
|
|
1031
1203
|
}
|
|
1032
1204
|
const pearTotals = pear.totalBySymbol();
|
|
1033
|
-
const assets = /* @__PURE__ */ new Set([
|
|
1205
|
+
const assets = /* @__PURE__ */ new Set([
|
|
1206
|
+
...orphanFills.map((fill) => fill.symbol),
|
|
1207
|
+
...snapshot.symbols()
|
|
1208
|
+
]);
|
|
1034
1209
|
const earliestTs = orphanFills.reduce((min2, fill) => Math.min(min2, fill.timestamp), Infinity);
|
|
1035
1210
|
if (!Number.isFinite(earliestTs)) {
|
|
1036
1211
|
return [];
|
|
1037
1212
|
}
|
|
1038
1213
|
const watermark = Math.max(1, earliestTs - 1);
|
|
1039
|
-
const haveBySymbol = (symbol) => add(pearTotals.get(symbol) ?? ZERO, fillTotals.get(symbol) ?? ZERO);
|
|
1214
|
+
const haveBySymbol = /* @__PURE__ */ __name((symbol) => add(pearTotals.get(symbol) ?? ZERO, fillTotals.get(symbol) ?? ZERO), "haveBySymbol");
|
|
1040
1215
|
const result = [];
|
|
1041
1216
|
for (const gap of gapsToTarget({
|
|
1042
1217
|
assets,
|
|
@@ -1068,6 +1243,7 @@ function buildMissedHistoryFills(args) {
|
|
|
1068
1243
|
}
|
|
1069
1244
|
return result;
|
|
1070
1245
|
}
|
|
1246
|
+
__name(buildMissedHistoryFills, "buildMissedHistoryFills");
|
|
1071
1247
|
function priceForMissedHistoryFill(args) {
|
|
1072
1248
|
const { exchangePosition, syntheticQty, pearQty, pearEntry, orphanNotional } = args;
|
|
1073
1249
|
const fallback = exchangePosition?.entryPrice ?? "0";
|
|
@@ -1089,6 +1265,7 @@ function priceForMissedHistoryFill(args) {
|
|
|
1089
1265
|
const derived = divide(subtract(targetNotional, realNotional), synthQty);
|
|
1090
1266
|
return derived.isFinite() && derived.isPositive() ? derived.toFixed() : fallback;
|
|
1091
1267
|
}
|
|
1268
|
+
__name(priceForMissedHistoryFill, "priceForMissedHistoryFill");
|
|
1092
1269
|
function findNeededSynthetics(args) {
|
|
1093
1270
|
const { positions, pear, snapshot } = args;
|
|
1094
1271
|
const pearTotals = pear.totalBySymbol();
|
|
@@ -1104,13 +1281,13 @@ function findNeededSynthetics(args) {
|
|
|
1104
1281
|
for (const symbol of pearTotals.keys()) {
|
|
1105
1282
|
assets.add(symbol);
|
|
1106
1283
|
}
|
|
1107
|
-
const haveBySymbol = (symbol) => {
|
|
1284
|
+
const haveBySymbol = /* @__PURE__ */ __name((symbol) => {
|
|
1108
1285
|
let totalFills = ZERO;
|
|
1109
1286
|
for (const pos of positions) {
|
|
1110
1287
|
totalFills = add(totalFills, Exposure.netFromParts(pos.parts, symbol));
|
|
1111
1288
|
}
|
|
1112
1289
|
return add(pearTotals.get(symbol) ?? ZERO, totalFills);
|
|
1113
|
-
};
|
|
1290
|
+
}, "haveBySymbol");
|
|
1114
1291
|
const result = /* @__PURE__ */ new Map();
|
|
1115
1292
|
for (const gap of gapsToTarget({
|
|
1116
1293
|
assets,
|
|
@@ -1137,9 +1314,13 @@ function findNeededSynthetics(args) {
|
|
|
1137
1314
|
}
|
|
1138
1315
|
return result;
|
|
1139
1316
|
}
|
|
1317
|
+
__name(findNeededSynthetics, "findNeededSynthetics");
|
|
1140
1318
|
function assignSyntheticsToPositions(args) {
|
|
1141
1319
|
const { neededSynthetics, positions, pear, tradeAccountId } = args;
|
|
1142
|
-
const keyOf = (symbols) => generateDeterministicPositionKey({
|
|
1320
|
+
const keyOf = /* @__PURE__ */ __name((symbols) => generateDeterministicPositionKey({
|
|
1321
|
+
tradeAccountId,
|
|
1322
|
+
symbols
|
|
1323
|
+
}), "keyOf");
|
|
1143
1324
|
const existingBySymbol = pear.positionBySymbol();
|
|
1144
1325
|
const created = [];
|
|
1145
1326
|
const byPosition = /* @__PURE__ */ new Map();
|
|
@@ -1147,11 +1328,12 @@ function assignSyntheticsToPositions(args) {
|
|
|
1147
1328
|
let target = positions.find((pos) => !pos.closed && pos.parts.some((part) => part.source.symbol === symbol));
|
|
1148
1329
|
if (!target) {
|
|
1149
1330
|
const existing = existingBySymbol.get(symbol);
|
|
1150
|
-
const positionKey = existing ? existing.positionKey : keyOf([
|
|
1331
|
+
const positionKey = existing ? existing.positionKey : keyOf([
|
|
1332
|
+
symbol
|
|
1333
|
+
]);
|
|
1151
1334
|
target = positions.find((pos) => pos.positionKey === positionKey) ?? created.find((pos) => pos.positionKey === positionKey);
|
|
1152
1335
|
if (!target) {
|
|
1153
1336
|
target = existing ? (
|
|
1154
|
-
// Stand-in for an existing DB position untouched by this batch's fills:
|
|
1155
1337
|
// seed its exposure and origin so folding the synthetic yields the true
|
|
1156
1338
|
// final state (a gone-close nets to zero -> CLOSED/NETTED, a gap writes
|
|
1157
1339
|
// the full total, not just the delta).
|
|
@@ -1175,8 +1357,15 @@ function assignSyntheticsToPositions(args) {
|
|
|
1175
1357
|
list.push(spec);
|
|
1176
1358
|
byPosition.set(target, list);
|
|
1177
1359
|
}
|
|
1178
|
-
return {
|
|
1360
|
+
return {
|
|
1361
|
+
positions: [
|
|
1362
|
+
...positions,
|
|
1363
|
+
...created
|
|
1364
|
+
],
|
|
1365
|
+
synthByPosition: byPosition
|
|
1366
|
+
};
|
|
1179
1367
|
}
|
|
1368
|
+
__name(assignSyntheticsToPositions, "assignSyntheticsToPositions");
|
|
1180
1369
|
function gapsToTarget(args) {
|
|
1181
1370
|
const { assets, haveBySymbol, snapshot } = args;
|
|
1182
1371
|
const gaps = [];
|
|
@@ -1192,6 +1381,7 @@ function gapsToTarget(args) {
|
|
|
1192
1381
|
}
|
|
1193
1382
|
return gaps;
|
|
1194
1383
|
}
|
|
1384
|
+
__name(gapsToTarget, "gapsToTarget");
|
|
1195
1385
|
function findGapToTarget(args) {
|
|
1196
1386
|
const { symbol, have, target } = args;
|
|
1197
1387
|
if (isZero(target)) {
|
|
@@ -1216,6 +1406,7 @@ function findGapToTarget(args) {
|
|
|
1216
1406
|
}
|
|
1217
1407
|
return null;
|
|
1218
1408
|
}
|
|
1409
|
+
__name(findGapToTarget, "findGapToTarget");
|
|
1219
1410
|
|
|
1220
1411
|
// src/sync/applicables/fills.ts
|
|
1221
1412
|
function assignPartIds(positions) {
|
|
@@ -1231,13 +1422,18 @@ function assignPartIds(positions) {
|
|
|
1231
1422
|
for (const [sourceId, parts] of bySource) {
|
|
1232
1423
|
const single = parts.length === 1 ? parts[0] : void 0;
|
|
1233
1424
|
if (single && isEqualTo(single.quantity, single.source.quantity)) {
|
|
1234
|
-
resolution.set(single, {
|
|
1425
|
+
resolution.set(single, {
|
|
1426
|
+
exchangeFillId: sourceId
|
|
1427
|
+
});
|
|
1235
1428
|
continue;
|
|
1236
1429
|
}
|
|
1237
1430
|
parts.forEach((part, i) => {
|
|
1238
1431
|
resolution.set(part, {
|
|
1239
1432
|
exchangeFillId: `${sourceId}-p${i}`,
|
|
1240
|
-
original: {
|
|
1433
|
+
original: {
|
|
1434
|
+
exchangeFillId: sourceId,
|
|
1435
|
+
quantity: part.source.quantity
|
|
1436
|
+
}
|
|
1241
1437
|
});
|
|
1242
1438
|
});
|
|
1243
1439
|
}
|
|
@@ -1249,6 +1445,7 @@ function assignPartIds(positions) {
|
|
|
1249
1445
|
return id;
|
|
1250
1446
|
};
|
|
1251
1447
|
}
|
|
1448
|
+
__name(assignPartIds, "assignPartIds");
|
|
1252
1449
|
function partToApplicableFill(part, id, connector) {
|
|
1253
1450
|
const fill = part.source;
|
|
1254
1451
|
const base = {
|
|
@@ -1283,9 +1480,12 @@ function partToApplicableFill(part, id, connector) {
|
|
|
1283
1480
|
tradeFee: feeShareForPart(fill.tradeFee, part.quantity, fill.quantity),
|
|
1284
1481
|
builderFee: feeShareForPart(fill.builderFee, part.quantity, fill.quantity),
|
|
1285
1482
|
liquidation: fill.liquidation,
|
|
1286
|
-
...id.original ? {
|
|
1483
|
+
...id.original ? {
|
|
1484
|
+
original: id.original
|
|
1485
|
+
} : {}
|
|
1287
1486
|
};
|
|
1288
1487
|
}
|
|
1488
|
+
__name(partToApplicableFill, "partToApplicableFill");
|
|
1289
1489
|
function feeShareForPart(totalFee, partQty, sourceQty) {
|
|
1290
1490
|
if (isEqualTo(partQty, sourceQty)) {
|
|
1291
1491
|
return totalFee;
|
|
@@ -1296,6 +1496,7 @@ function feeShareForPart(totalFee, partQty, sourceQty) {
|
|
|
1296
1496
|
const feeShare = divide(multiply(totalFee, partQty), sourceQty);
|
|
1297
1497
|
return feeShare.toFixed();
|
|
1298
1498
|
}
|
|
1499
|
+
__name(feeShareForPart, "feeShareForPart");
|
|
1299
1500
|
|
|
1300
1501
|
// src/sync/applicables/position.ts
|
|
1301
1502
|
function assembleApplicables(args) {
|
|
@@ -1310,17 +1511,19 @@ function assembleApplicables(args) {
|
|
|
1310
1511
|
}
|
|
1311
1512
|
return applicables;
|
|
1312
1513
|
}
|
|
1514
|
+
__name(assembleApplicables, "assembleApplicables");
|
|
1313
1515
|
function assembleOneApplicable(pos, connector, partIdFor, assignedSynthetics, now) {
|
|
1314
1516
|
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
|
-
|
|
1517
|
+
const syntheticFills = assignedSynthetics.map((synthetic) => buildSyntheticFill({
|
|
1518
|
+
positionKey: pos.positionKey,
|
|
1519
|
+
connector,
|
|
1520
|
+
timestamp: anchorSyntheticTimestamp(realFills, synthetic.symbol, now),
|
|
1521
|
+
...synthetic
|
|
1522
|
+
}));
|
|
1523
|
+
const fills = [
|
|
1524
|
+
...realFills,
|
|
1525
|
+
...syntheticFills
|
|
1526
|
+
].sort((a, b) => a.timestamp - b.timestamp);
|
|
1324
1527
|
if (fills.length === 0) {
|
|
1325
1528
|
return null;
|
|
1326
1529
|
}
|
|
@@ -1333,11 +1536,14 @@ function assembleOneApplicable(pos, connector, partIdFor, assignedSynthetics, no
|
|
|
1333
1536
|
exposure: exposure.toSigned(),
|
|
1334
1537
|
origin,
|
|
1335
1538
|
status,
|
|
1336
|
-
...closed ? {
|
|
1539
|
+
...closed ? {
|
|
1540
|
+
closedReason: "NETTED"
|
|
1541
|
+
} : {},
|
|
1337
1542
|
fills,
|
|
1338
1543
|
metadata: buildMetadata(new Set(exposure.symbols()), realFills)
|
|
1339
1544
|
};
|
|
1340
1545
|
}
|
|
1546
|
+
__name(assembleOneApplicable, "assembleOneApplicable");
|
|
1341
1547
|
function finalExposure(pos, synthetics) {
|
|
1342
1548
|
const exposure = pos.exposure.clone();
|
|
1343
1549
|
for (const synthetic of synthetics) {
|
|
@@ -1345,6 +1551,7 @@ function finalExposure(pos, synthetics) {
|
|
|
1345
1551
|
}
|
|
1346
1552
|
return exposure;
|
|
1347
1553
|
}
|
|
1554
|
+
__name(finalExposure, "finalExposure");
|
|
1348
1555
|
function anchorSyntheticTimestamp(realFills, symbol, now) {
|
|
1349
1556
|
const oldestForSymbol = realFills.find((fill) => fill.symbol === symbol)?.timestamp;
|
|
1350
1557
|
if (oldestForSymbol !== void 0) {
|
|
@@ -1353,12 +1560,16 @@ function anchorSyntheticTimestamp(realFills, symbol, now) {
|
|
|
1353
1560
|
const oldestRealTs = realFills[0]?.timestamp;
|
|
1354
1561
|
return oldestRealTs !== void 0 ? Math.max(1, oldestRealTs - 1) : now;
|
|
1355
1562
|
}
|
|
1563
|
+
__name(anchorSyntheticTimestamp, "anchorSyntheticTimestamp");
|
|
1356
1564
|
function buildMetadata(symbols, realFills) {
|
|
1357
1565
|
const initialExposureUSD = {};
|
|
1358
1566
|
for (const symbol of symbols) {
|
|
1359
|
-
const legFills = realFills.flatMap(
|
|
1360
|
-
|
|
1361
|
-
|
|
1567
|
+
const legFills = realFills.flatMap((fill) => fill.symbol === symbol && fill.usd != null ? [
|
|
1568
|
+
{
|
|
1569
|
+
side: fill.side,
|
|
1570
|
+
usd: fill.usd
|
|
1571
|
+
}
|
|
1572
|
+
] : []);
|
|
1362
1573
|
const opener = legFills[0];
|
|
1363
1574
|
if (!opener) {
|
|
1364
1575
|
continue;
|
|
@@ -1367,8 +1578,11 @@ function buildMetadata(symbols, realFills) {
|
|
|
1367
1578
|
const signedNotional = signed(opener.side === "BUY" ? notional : notional.negated());
|
|
1368
1579
|
initialExposureUSD[symbol] = signedNotional;
|
|
1369
1580
|
}
|
|
1370
|
-
return {
|
|
1581
|
+
return {
|
|
1582
|
+
initialExposureUSD
|
|
1583
|
+
};
|
|
1371
1584
|
}
|
|
1585
|
+
__name(buildMetadata, "buildMetadata");
|
|
1372
1586
|
function mergeOrigin(existing, fills) {
|
|
1373
1587
|
let anyExternal = false;
|
|
1374
1588
|
let anyPear = false;
|
|
@@ -1396,16 +1610,23 @@ function mergeOrigin(existing, fills) {
|
|
|
1396
1610
|
}
|
|
1397
1611
|
return anyPear ? "PEAR" : "EXTERNAL";
|
|
1398
1612
|
}
|
|
1613
|
+
__name(mergeOrigin, "mergeOrigin");
|
|
1399
1614
|
|
|
1400
1615
|
// src/sync/applicables/snapshot.ts
|
|
1401
1616
|
var ExchangeSnapshot = class _ExchangeSnapshot {
|
|
1617
|
+
static {
|
|
1618
|
+
__name(this, "ExchangeSnapshot");
|
|
1619
|
+
}
|
|
1620
|
+
bySymbol;
|
|
1402
1621
|
constructor(bySymbol) {
|
|
1403
1622
|
this.bySymbol = bySymbol;
|
|
1404
1623
|
}
|
|
1405
|
-
bySymbol;
|
|
1406
1624
|
/** Index `positions` by symbol for O(1) lookup. */
|
|
1407
1625
|
static from(positions) {
|
|
1408
|
-
return new _ExchangeSnapshot(new Map(positions.map((position) => [
|
|
1626
|
+
return new _ExchangeSnapshot(new Map(positions.map((position) => [
|
|
1627
|
+
position.symbol,
|
|
1628
|
+
position
|
|
1629
|
+
])));
|
|
1409
1630
|
}
|
|
1410
1631
|
/** Raw exchange position for `symbol`, or `undefined` when the exchange holds nothing. */
|
|
1411
1632
|
get(symbol) {
|
|
@@ -1427,14 +1648,19 @@ var ExchangeSnapshot = class _ExchangeSnapshot {
|
|
|
1427
1648
|
}
|
|
1428
1649
|
/** All symbols the exchange currently reports a position for. */
|
|
1429
1650
|
symbols() {
|
|
1430
|
-
return [
|
|
1651
|
+
return [
|
|
1652
|
+
...this.bySymbol.keys()
|
|
1653
|
+
];
|
|
1431
1654
|
}
|
|
1432
1655
|
};
|
|
1433
1656
|
var PearHoldings = class _PearHoldings {
|
|
1657
|
+
static {
|
|
1658
|
+
__name(this, "PearHoldings");
|
|
1659
|
+
}
|
|
1660
|
+
filtered;
|
|
1434
1661
|
constructor(filtered) {
|
|
1435
1662
|
this.filtered = filtered;
|
|
1436
1663
|
}
|
|
1437
|
-
filtered;
|
|
1438
1664
|
/** Filter `openPositions` to the given connector and wrap. */
|
|
1439
1665
|
static from(args) {
|
|
1440
1666
|
const { openPositions, connector } = args;
|
|
@@ -1458,10 +1684,10 @@ var PearHoldings = class _PearHoldings {
|
|
|
1458
1684
|
return totals;
|
|
1459
1685
|
}
|
|
1460
1686
|
/**
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1687
|
+
* Which open DB position holds each active asset — the fallback target for a
|
|
1688
|
+
* synthetic when no matched position already carries that asset. First open
|
|
1689
|
+
* position with a non-zero leg wins per symbol.
|
|
1690
|
+
*/
|
|
1465
1691
|
positionBySymbol() {
|
|
1466
1692
|
const existingBySymbol = /* @__PURE__ */ new Map();
|
|
1467
1693
|
for (const position of this.filtered) {
|
|
@@ -1481,18 +1707,37 @@ var PearHoldings = class _PearHoldings {
|
|
|
1481
1707
|
function buildApplicables(args) {
|
|
1482
1708
|
const { tradeAccountId, connector, orphanFills, openPositions, entryBySymbol, exchangePositions } = args;
|
|
1483
1709
|
const snapshot = ExchangeSnapshot.from(exchangePositions);
|
|
1484
|
-
const pear = PearHoldings.from({
|
|
1710
|
+
const pear = PearHoldings.from({
|
|
1711
|
+
openPositions,
|
|
1712
|
+
connector
|
|
1713
|
+
});
|
|
1485
1714
|
const now = Date.now();
|
|
1486
|
-
const fills = addFillsForMissedHistory({
|
|
1487
|
-
|
|
1715
|
+
const fills = addFillsForMissedHistory({
|
|
1716
|
+
orphanFills,
|
|
1717
|
+
pear,
|
|
1718
|
+
connector,
|
|
1719
|
+
entryBySymbol,
|
|
1720
|
+
snapshot
|
|
1721
|
+
});
|
|
1722
|
+
const matched = matchPositions({
|
|
1723
|
+
tradeAccountId,
|
|
1724
|
+
orphanFills: fills,
|
|
1725
|
+
pear
|
|
1726
|
+
});
|
|
1488
1727
|
const { positions, synthByPosition } = alignPositionsWithExchange({
|
|
1489
1728
|
positions: matched,
|
|
1490
1729
|
pear,
|
|
1491
1730
|
snapshot,
|
|
1492
1731
|
tradeAccountId
|
|
1493
1732
|
});
|
|
1494
|
-
return assembleApplicables({
|
|
1733
|
+
return assembleApplicables({
|
|
1734
|
+
positions,
|
|
1735
|
+
connector,
|
|
1736
|
+
synthByPosition,
|
|
1737
|
+
now
|
|
1738
|
+
});
|
|
1495
1739
|
}
|
|
1740
|
+
__name(buildApplicables, "buildApplicables");
|
|
1496
1741
|
|
|
1497
1742
|
// src/sync/cutoff.ts
|
|
1498
1743
|
function selectFirstSyncCutoff(args) {
|
|
@@ -1515,7 +1760,9 @@ function selectFirstSyncCutoff(args) {
|
|
|
1515
1760
|
for (let boundary = fills.length; boundary >= 0; boundary--) {
|
|
1516
1761
|
const withinAnchor = pearIndex === null || boundary <= pearIndex;
|
|
1517
1762
|
if (withinAnchor && book.isAllZero() && isStrictTimestampBoundary(fills, boundary)) {
|
|
1518
|
-
return boundary === 0 ? null : {
|
|
1763
|
+
return boundary === 0 ? null : {
|
|
1764
|
+
keepFromIndex: boundary
|
|
1765
|
+
};
|
|
1519
1766
|
}
|
|
1520
1767
|
const preceding = fills[boundary - 1];
|
|
1521
1768
|
if (preceding) {
|
|
@@ -1524,6 +1771,7 @@ function selectFirstSyncCutoff(args) {
|
|
|
1524
1771
|
}
|
|
1525
1772
|
return null;
|
|
1526
1773
|
}
|
|
1774
|
+
__name(selectFirstSyncCutoff, "selectFirstSyncCutoff");
|
|
1527
1775
|
function findFirstPearFillIndex(connector, fills) {
|
|
1528
1776
|
const index = fills.findIndex((fill) => isPearCloid(connector, fill.cloid));
|
|
1529
1777
|
if (index === -1) {
|
|
@@ -1531,6 +1779,7 @@ function findFirstPearFillIndex(connector, fills) {
|
|
|
1531
1779
|
}
|
|
1532
1780
|
return index;
|
|
1533
1781
|
}
|
|
1782
|
+
__name(findFirstPearFillIndex, "findFirstPearFillIndex");
|
|
1534
1783
|
function isStrictTimestampBoundary(fills, boundary) {
|
|
1535
1784
|
const before = fills[boundary - 1];
|
|
1536
1785
|
const after = fills[boundary];
|
|
@@ -1539,26 +1788,13 @@ function isStrictTimestampBoundary(fills, boundary) {
|
|
|
1539
1788
|
}
|
|
1540
1789
|
return before.timestamp < after.timestamp;
|
|
1541
1790
|
}
|
|
1791
|
+
__name(isStrictTimestampBoundary, "isStrictTimestampBoundary");
|
|
1542
1792
|
|
|
1543
1793
|
// src/sync/compute.ts
|
|
1544
1794
|
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;
|
|
1795
|
+
const { tradeAccountId, connector, openPositions, entryBySymbol, provisionalFills, confirmedExchangeFillIds, externalFills, exchangePositions, neverSynced } = args;
|
|
1556
1796
|
const fillsToProcess = _prepareExternalFills(externalFills, confirmedExchangeFillIds);
|
|
1557
|
-
const {
|
|
1558
|
-
reconcilables,
|
|
1559
|
-
confirmables: confirmableFills,
|
|
1560
|
-
orphans
|
|
1561
|
-
} = _segregateExternalFills(provisionalFills, fillsToProcess);
|
|
1797
|
+
const { reconcilables, confirmables: confirmableFills, orphans } = _segregateExternalFills(provisionalFills, fillsToProcess);
|
|
1562
1798
|
const confirmablesByPosition = groupConfirmablesByPosition(confirmableFills);
|
|
1563
1799
|
const { keptOrphans, firstSyncTrim } = applyFirstSyncCutoff({
|
|
1564
1800
|
neverSynced,
|
|
@@ -1591,11 +1827,10 @@ async function computeSyncPayload(args) {
|
|
|
1591
1827
|
firstSyncTrim
|
|
1592
1828
|
};
|
|
1593
1829
|
}
|
|
1830
|
+
__name(computeSyncPayload, "computeSyncPayload");
|
|
1594
1831
|
function _prepareExternalFills(externalFills, confirmedExchangeFillIdsInput) {
|
|
1595
1832
|
const dedupedByExchangeFillId = /* @__PURE__ */ new Map();
|
|
1596
|
-
const confirmedExchangeFillIds = new Set(
|
|
1597
|
-
Array.from(confirmedExchangeFillIdsInput).map((exchangeFillId) => _normalizeExchangeFillId(exchangeFillId)).filter((exchangeFillId) => exchangeFillId !== void 0)
|
|
1598
|
-
);
|
|
1833
|
+
const confirmedExchangeFillIds = new Set(Array.from(confirmedExchangeFillIdsInput).map((exchangeFillId) => _normalizeExchangeFillId(exchangeFillId)).filter((exchangeFillId) => exchangeFillId !== void 0));
|
|
1599
1834
|
const candidates = externalFills.reduce((acc, fill) => {
|
|
1600
1835
|
const normalizedExchangeFillId = _normalizeExchangeFillId(fill.exchangeFillId);
|
|
1601
1836
|
if (!normalizedExchangeFillId) {
|
|
@@ -1610,7 +1845,10 @@ function _prepareExternalFills(externalFills, confirmedExchangeFillIdsInput) {
|
|
|
1610
1845
|
if (confirmedExchangeFillIds.has(normalizedExchangeFillId)) {
|
|
1611
1846
|
return acc;
|
|
1612
1847
|
}
|
|
1613
|
-
acc.push({
|
|
1848
|
+
acc.push({
|
|
1849
|
+
...fill,
|
|
1850
|
+
exchangeFillId: normalizedExchangeFillId
|
|
1851
|
+
});
|
|
1614
1852
|
return acc;
|
|
1615
1853
|
}, []);
|
|
1616
1854
|
for (const fill of candidates) {
|
|
@@ -1621,6 +1859,7 @@ function _prepareExternalFills(externalFills, confirmedExchangeFillIdsInput) {
|
|
|
1621
1859
|
}
|
|
1622
1860
|
return Array.from(dedupedByExchangeFillId.values()).sort((a, b) => a.timestamp - b.timestamp);
|
|
1623
1861
|
}
|
|
1862
|
+
__name(_prepareExternalFills, "_prepareExternalFills");
|
|
1624
1863
|
function _segregateExternalFills(provisionalFills, externalFills) {
|
|
1625
1864
|
const provisionalFillsMap = /* @__PURE__ */ new Map();
|
|
1626
1865
|
for (const fill of provisionalFills) {
|
|
@@ -1637,7 +1876,10 @@ function _segregateExternalFills(provisionalFills, externalFills) {
|
|
|
1637
1876
|
if (!cloid) {
|
|
1638
1877
|
orphans.push(fill);
|
|
1639
1878
|
} else {
|
|
1640
|
-
fillsByCloid.set(cloid, [
|
|
1879
|
+
fillsByCloid.set(cloid, [
|
|
1880
|
+
...fillsByCloid.get(cloid) || [],
|
|
1881
|
+
fill
|
|
1882
|
+
]);
|
|
1641
1883
|
}
|
|
1642
1884
|
}
|
|
1643
1885
|
const reconcilables = {};
|
|
@@ -1651,9 +1893,18 @@ function _segregateExternalFills(provisionalFills, externalFills) {
|
|
|
1651
1893
|
throw new Error("Corrupted state: sibling group must contain at least 2 fills for reconcile");
|
|
1652
1894
|
}
|
|
1653
1895
|
reconcilables[cloid] = [
|
|
1654
|
-
{
|
|
1655
|
-
|
|
1656
|
-
|
|
1896
|
+
{
|
|
1897
|
+
...first,
|
|
1898
|
+
cloid
|
|
1899
|
+
},
|
|
1900
|
+
{
|
|
1901
|
+
...second,
|
|
1902
|
+
cloid
|
|
1903
|
+
},
|
|
1904
|
+
...rest.map((fill) => ({
|
|
1905
|
+
...fill,
|
|
1906
|
+
cloid
|
|
1907
|
+
}))
|
|
1657
1908
|
];
|
|
1658
1909
|
} else {
|
|
1659
1910
|
orphans.push(...fills);
|
|
@@ -1673,8 +1924,13 @@ function _segregateExternalFills(provisionalFills, externalFills) {
|
|
|
1673
1924
|
}
|
|
1674
1925
|
}
|
|
1675
1926
|
}
|
|
1676
|
-
return {
|
|
1927
|
+
return {
|
|
1928
|
+
reconcilables,
|
|
1929
|
+
confirmables: confirmableFills,
|
|
1930
|
+
orphans
|
|
1931
|
+
};
|
|
1677
1932
|
}
|
|
1933
|
+
__name(_segregateExternalFills, "_segregateExternalFills");
|
|
1678
1934
|
function _normalizeCloid(cloid) {
|
|
1679
1935
|
const value = cloid?.trim();
|
|
1680
1936
|
if (!value) {
|
|
@@ -1682,6 +1938,7 @@ function _normalizeCloid(cloid) {
|
|
|
1682
1938
|
}
|
|
1683
1939
|
return value;
|
|
1684
1940
|
}
|
|
1941
|
+
__name(_normalizeCloid, "_normalizeCloid");
|
|
1685
1942
|
function _normalizeExchangeFillId(exchangeFillId) {
|
|
1686
1943
|
const value = exchangeFillId?.trim().toLowerCase();
|
|
1687
1944
|
if (!value) {
|
|
@@ -1689,6 +1946,7 @@ function _normalizeExchangeFillId(exchangeFillId) {
|
|
|
1689
1946
|
}
|
|
1690
1947
|
return value;
|
|
1691
1948
|
}
|
|
1949
|
+
__name(_normalizeExchangeFillId, "_normalizeExchangeFillId");
|
|
1692
1950
|
function _isValidExternalQuantity(quantity) {
|
|
1693
1951
|
if (/e/i.test(quantity)) {
|
|
1694
1952
|
return false;
|
|
@@ -1698,18 +1956,24 @@ function _isValidExternalQuantity(quantity) {
|
|
|
1698
1956
|
}
|
|
1699
1957
|
return isGreaterThan(quantity, ZERO);
|
|
1700
1958
|
}
|
|
1959
|
+
__name(_isValidExternalQuantity, "_isValidExternalQuantity");
|
|
1701
1960
|
function _isValidExternalTimestamp(timestamp) {
|
|
1702
1961
|
if (!Number.isFinite(timestamp) || timestamp < 0) {
|
|
1703
1962
|
return false;
|
|
1704
1963
|
}
|
|
1705
1964
|
return timestamp <= Date.now() + WATERMARK_FUTURE_SKEW_MS;
|
|
1706
1965
|
}
|
|
1966
|
+
__name(_isValidExternalTimestamp, "_isValidExternalTimestamp");
|
|
1707
1967
|
function groupConfirmablesByPosition(confirmableFills) {
|
|
1708
1968
|
return confirmableFills.reduce((acc, fill) => {
|
|
1709
|
-
acc[fill.positionId] = [
|
|
1969
|
+
acc[fill.positionId] = [
|
|
1970
|
+
...acc[fill.positionId] || [],
|
|
1971
|
+
fill
|
|
1972
|
+
];
|
|
1710
1973
|
return acc;
|
|
1711
1974
|
}, {});
|
|
1712
1975
|
}
|
|
1976
|
+
__name(groupConfirmablesByPosition, "groupConfirmablesByPosition");
|
|
1713
1977
|
function latestFillTimestamp(fills) {
|
|
1714
1978
|
const last = fills[fills.length - 1];
|
|
1715
1979
|
if (!last) {
|
|
@@ -1717,10 +1981,14 @@ function latestFillTimestamp(fills) {
|
|
|
1717
1981
|
}
|
|
1718
1982
|
return last.timestamp;
|
|
1719
1983
|
}
|
|
1984
|
+
__name(latestFillTimestamp, "latestFillTimestamp");
|
|
1720
1985
|
function applyFirstSyncCutoff(args) {
|
|
1721
1986
|
const { neverSynced, fillsToProcess, orphans, exchangePositions, connector } = args;
|
|
1722
1987
|
if (!neverSynced || fillsToProcess.length === 0) {
|
|
1723
|
-
return {
|
|
1988
|
+
return {
|
|
1989
|
+
keptOrphans: orphans,
|
|
1990
|
+
firstSyncTrim: null
|
|
1991
|
+
};
|
|
1724
1992
|
}
|
|
1725
1993
|
const cutoff = selectFirstSyncCutoff({
|
|
1726
1994
|
fills: fillsToProcess,
|
|
@@ -1728,7 +1996,10 @@ function applyFirstSyncCutoff(args) {
|
|
|
1728
1996
|
connector
|
|
1729
1997
|
});
|
|
1730
1998
|
if (!cutoff) {
|
|
1731
|
-
return {
|
|
1999
|
+
return {
|
|
2000
|
+
keptOrphans: orphans,
|
|
2001
|
+
firstSyncTrim: null
|
|
2002
|
+
};
|
|
1732
2003
|
}
|
|
1733
2004
|
const keptIds = new Set(fillsToProcess.slice(cutoff.keepFromIndex).map((fill) => fill.exchangeFillId));
|
|
1734
2005
|
const keptOrphans = [];
|
|
@@ -1737,7 +2008,10 @@ function applyFirstSyncCutoff(args) {
|
|
|
1737
2008
|
(keptIds.has(orphan.exchangeFillId) ? keptOrphans : droppedOrphans).push(orphan);
|
|
1738
2009
|
}
|
|
1739
2010
|
if (droppedOrphans.length === 0) {
|
|
1740
|
-
return {
|
|
2011
|
+
return {
|
|
2012
|
+
keptOrphans,
|
|
2013
|
+
firstSyncTrim: null
|
|
2014
|
+
};
|
|
1741
2015
|
}
|
|
1742
2016
|
const firstKeptFill = fillsToProcess[cutoff.keepFromIndex];
|
|
1743
2017
|
return {
|
|
@@ -1749,9 +2023,11 @@ function applyFirstSyncCutoff(args) {
|
|
|
1749
2023
|
}
|
|
1750
2024
|
};
|
|
1751
2025
|
}
|
|
2026
|
+
__name(applyFirstSyncCutoff, "applyFirstSyncCutoff");
|
|
1752
2027
|
function isEmptyCycle(reconcilables, confirmablesByPosition, applicables) {
|
|
1753
2028
|
return Object.keys(reconcilables).length === 0 && Object.keys(confirmablesByPosition).length === 0 && applicables.length === 0;
|
|
1754
2029
|
}
|
|
2030
|
+
__name(isEmptyCycle, "isEmptyCycle");
|
|
1755
2031
|
|
|
1756
2032
|
// src/validate-leverage.ts
|
|
1757
2033
|
function validateLeverage(leverage, instrument) {
|
|
@@ -1763,32 +2039,104 @@ function validateLeverage(leverage, instrument) {
|
|
|
1763
2039
|
}
|
|
1764
2040
|
return true;
|
|
1765
2041
|
}
|
|
2042
|
+
__name(validateLeverage, "validateLeverage");
|
|
1766
2043
|
function validateQuantity(quantity, price, precision) {
|
|
1767
|
-
const parsedQuantity = new
|
|
2044
|
+
const parsedQuantity = new BigNumber7(quantity);
|
|
1768
2045
|
if (!parsedQuantity.isFinite() || parsedQuantity.lte(0)) {
|
|
1769
2046
|
return false;
|
|
1770
2047
|
}
|
|
1771
|
-
const parsedPrice = new
|
|
2048
|
+
const parsedPrice = new BigNumber7(price);
|
|
1772
2049
|
if (!parsedPrice.isFinite() || parsedPrice.lte(0)) {
|
|
1773
2050
|
return false;
|
|
1774
2051
|
}
|
|
1775
|
-
const step = new
|
|
2052
|
+
const step = new BigNumber7(precision.quantity.step);
|
|
1776
2053
|
const scaled = parsedQuantity.div(step);
|
|
1777
2054
|
if (!scaled.isInteger()) {
|
|
1778
2055
|
return false;
|
|
1779
2056
|
}
|
|
1780
2057
|
const notional = contractsToBase(parsedQuantity, precision.quantity.contractSize).times(parsedPrice);
|
|
1781
2058
|
if (precision.notional?.min !== void 0 && precision.notional.min !== null && precision.notional.min > 0) {
|
|
1782
|
-
if (notional.lt(new
|
|
2059
|
+
if (notional.lt(new BigNumber7(precision.notional.min))) {
|
|
1783
2060
|
return false;
|
|
1784
2061
|
}
|
|
1785
2062
|
}
|
|
1786
2063
|
if (precision.notional?.max !== void 0 && precision.notional.max !== null && precision.notional.max > 0) {
|
|
1787
|
-
if (notional.gt(new
|
|
2064
|
+
if (notional.gt(new BigNumber7(precision.notional.max))) {
|
|
1788
2065
|
return false;
|
|
1789
2066
|
}
|
|
1790
2067
|
}
|
|
1791
2068
|
return true;
|
|
1792
2069
|
}
|
|
2070
|
+
__name(validateQuantity, "validateQuantity");
|
|
2071
|
+
var STORAGE_PREFIX = "pear.x-handoff.";
|
|
2072
|
+
function startXHandoff(storage) {
|
|
2073
|
+
const handoffId = base64urlnopad.encode(randomBytes(16));
|
|
2074
|
+
const verifier = base64urlnopad.encode(randomBytes(32));
|
|
2075
|
+
storage.setItem(`${STORAGE_PREFIX}${handoffId}`, verifier);
|
|
2076
|
+
return {
|
|
2077
|
+
handoffId,
|
|
2078
|
+
challenge: base64urlnopad.encode(sha256(utf8ToBytes(verifier)))
|
|
2079
|
+
};
|
|
2080
|
+
}
|
|
2081
|
+
__name(startXHandoff, "startXHandoff");
|
|
2082
|
+
function finishXHandoff(href, storage) {
|
|
2083
|
+
const url = new URL(href);
|
|
2084
|
+
const linkStatus = url.searchParams.get("x_link");
|
|
2085
|
+
const loginStatus = url.searchParams.get("x_login");
|
|
2086
|
+
const reason = url.searchParams.get("x_reason") ?? void 0;
|
|
2087
|
+
if (linkStatus === "connected" || linkStatus === "reconnected") {
|
|
2088
|
+
return {
|
|
2089
|
+
kind: "link-succeeded",
|
|
2090
|
+
status: linkStatus
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
if (linkStatus) {
|
|
2094
|
+
return {
|
|
2095
|
+
kind: "link-failed",
|
|
2096
|
+
reason
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
if (!loginStatus) {
|
|
2100
|
+
return void 0;
|
|
2101
|
+
}
|
|
2102
|
+
const verifier = takeVerifier(url.searchParams.get("x_handoff"), storage);
|
|
2103
|
+
const ticket = new URLSearchParams(url.hash.replace(/^#/, "")).get("x_ticket");
|
|
2104
|
+
if (loginStatus !== "signed_in" || !ticket || !verifier) {
|
|
2105
|
+
return {
|
|
2106
|
+
kind: "login-failed",
|
|
2107
|
+
reason: loginStatus === "signed_in" ? "wrong_browser" : reason
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
return {
|
|
2111
|
+
kind: "login-settled",
|
|
2112
|
+
ticket,
|
|
2113
|
+
verifier
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
__name(finishXHandoff, "finishXHandoff");
|
|
2117
|
+
function stripXReturn(href) {
|
|
2118
|
+
const url = new URL(href);
|
|
2119
|
+
for (const key of [
|
|
2120
|
+
"x_link",
|
|
2121
|
+
"x_login",
|
|
2122
|
+
"x_reason",
|
|
2123
|
+
"x_handoff"
|
|
2124
|
+
]) {
|
|
2125
|
+
url.searchParams.delete(key);
|
|
2126
|
+
}
|
|
2127
|
+
url.hash = "";
|
|
2128
|
+
return `${url.pathname}${url.search}`;
|
|
2129
|
+
}
|
|
2130
|
+
__name(stripXReturn, "stripXReturn");
|
|
2131
|
+
function takeVerifier(handoffId, storage) {
|
|
2132
|
+
if (!handoffId) {
|
|
2133
|
+
return void 0;
|
|
2134
|
+
}
|
|
2135
|
+
const key = `${STORAGE_PREFIX}${handoffId}`;
|
|
2136
|
+
const verifier = storage.getItem(key) ?? void 0;
|
|
2137
|
+
storage.removeItem(key);
|
|
2138
|
+
return verifier;
|
|
2139
|
+
}
|
|
2140
|
+
__name(takeVerifier, "takeVerifier");
|
|
1793
2141
|
|
|
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 };
|
|
2142
|
+
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 };
|