@pear-protocol/utils 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/asset/asset.types.d.ts +26 -2
- package/dist/asset/index.d.ts +1 -1
- package/dist/basket/derive-basket-key.d.ts +11 -0
- package/dist/basket/derive-basket-key.test.d.ts +1 -0
- package/dist/basket/index.d.ts +1 -0
- package/dist/index.js +42 -7
- package/dist/position/compute.d.ts +13 -0
- package/package.json +2 -2
|
@@ -7,17 +7,41 @@ export type AssetUnrealizedPnL = {
|
|
|
7
7
|
currentPrice: BigNumber;
|
|
8
8
|
upnl: BigNumber;
|
|
9
9
|
};
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* One reduction of the position, attributed to the fill that made it.
|
|
12
|
+
*
|
|
13
|
+
* A discriminated union rather than an optional gross, because whether a close could
|
|
14
|
+
* be valued is a property of the replay, not of the fill: a caller cannot tell a
|
|
15
|
+
* priced close from an unpriceable one by looking at the fill alone. `reduceOnly` is
|
|
16
|
+
* an order intent and says nothing about what actually closed, and a 0-price synthetic
|
|
17
|
+
* can equally well OPEN a position — so the walk is the only thing that knows, and it
|
|
18
|
+
* says so here instead of leaving the answer to be guessed downstream.
|
|
19
|
+
*
|
|
20
|
+
* The walk is time-agnostic (a Fill carries no timestamp), so callers that need to
|
|
21
|
+
* filter by time resolve fillId against their own fill records.
|
|
22
|
+
*/
|
|
23
|
+
export type AssetClosure = {
|
|
24
|
+
kind: 'PRICED';
|
|
11
25
|
fillId: string;
|
|
12
26
|
gross: BigNumber;
|
|
27
|
+
entryPrice: BigNumber;
|
|
28
|
+
exitPrice: BigNumber;
|
|
29
|
+
closedQuantity: BigNumber;
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'UNPRICED';
|
|
32
|
+
fillId: string;
|
|
33
|
+
closedQuantity: BigNumber;
|
|
13
34
|
};
|
|
14
35
|
export type AssetRealizedPnL = {
|
|
15
36
|
id: InstrumentId;
|
|
16
37
|
grossPnl: BigNumber;
|
|
17
38
|
netPnl: BigNumber;
|
|
18
39
|
undeterminedSize: BigNumber;
|
|
40
|
+
exitQuantity: BigNumber;
|
|
41
|
+
exitNotional: BigNumber;
|
|
42
|
+
exitVwap: BigNumber | null;
|
|
19
43
|
totalFees: BigNumber;
|
|
20
44
|
tradeFees: BigNumber;
|
|
21
45
|
pearFees: BigNumber;
|
|
22
|
-
|
|
46
|
+
closures: AssetClosure[];
|
|
23
47
|
};
|
package/dist/asset/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { AssetClosure, AssetRealizedPnL, AssetUnrealizedPnL } from './asset.types';
|
|
2
2
|
export * from './compute';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { InstrumentId } from '@pear-protocol/types';
|
|
2
|
+
/**
|
|
3
|
+
* The canonical basket key: sha256 hex over the instrument ids a basket holds long
|
|
4
|
+
* and short, each side sorted, spelled as {"long":[...],"short":[...]}.
|
|
5
|
+
*
|
|
6
|
+
* This is the `basketKey` the backend reports on /markets/baskets rows and accepts
|
|
7
|
+
* in /markets/baskets/positions, derived byte-for-byte the way the backend derives
|
|
8
|
+
* it (deriveBasketKeyFromParts). The format is FROZEN: persisted rows are stored
|
|
9
|
+
* under it, so neither the sort, the payload spelling, nor the hash may change.
|
|
10
|
+
*/
|
|
11
|
+
export declare function deriveBasketKey(longInstrumentIds: InstrumentId[], shortInstrumentIds: InstrumentId[]): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/basket/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -186,17 +186,21 @@ function computeAssetRealizedPnLs(fills) {
|
|
|
186
186
|
const tradeFees = assetFills.reduce((acc, fill) => acc.plus(new BigNumber9(fill.tradeFee ?? "0")), ZERO);
|
|
187
187
|
const pearFees = assetFills.reduce((acc, fill) => acc.plus(new BigNumber9(fill.pearFee ?? "0")), ZERO);
|
|
188
188
|
const totalFees = tradeFees.plus(pearFees);
|
|
189
|
-
const { gross: grossPnl, undeterminedSize,
|
|
189
|
+
const { gross: grossPnl, undeterminedSize, closures, exitQuantity, exitNotional } = walkAssetFills(assetFills);
|
|
190
190
|
const netPnl = grossPnl.minus(totalFees);
|
|
191
|
+
const exitVwap = isZero(exitQuantity) ? null : divide(exitNotional, exitQuantity);
|
|
191
192
|
result.push({
|
|
192
193
|
id,
|
|
193
194
|
grossPnl,
|
|
194
195
|
netPnl,
|
|
195
196
|
undeterminedSize,
|
|
197
|
+
exitQuantity,
|
|
198
|
+
exitNotional,
|
|
199
|
+
exitVwap,
|
|
196
200
|
totalFees,
|
|
197
201
|
tradeFees,
|
|
198
202
|
pearFees,
|
|
199
|
-
|
|
203
|
+
closures
|
|
200
204
|
});
|
|
201
205
|
}
|
|
202
206
|
return result;
|
|
@@ -207,7 +211,9 @@ function walkAssetFills(fills) {
|
|
|
207
211
|
let basisUnpriced = false;
|
|
208
212
|
let gross = ZERO;
|
|
209
213
|
let undeterminedSize = ZERO;
|
|
210
|
-
const
|
|
214
|
+
const closures = [];
|
|
215
|
+
let exitQuantity = ZERO;
|
|
216
|
+
let exitNotional = ZERO;
|
|
211
217
|
for (const fill of fills) {
|
|
212
218
|
const price = parse(fill.price);
|
|
213
219
|
const quantity = parse(fill.quantity);
|
|
@@ -217,10 +223,24 @@ function walkAssetFills(fills) {
|
|
|
217
223
|
const closedQty = min(positionSize.abs(), signedQuantity.abs());
|
|
218
224
|
if (basisUnpriced || isZero(price)) {
|
|
219
225
|
undeterminedSize = add(undeterminedSize, closedQty);
|
|
226
|
+
closures.push({
|
|
227
|
+
kind: "UNPRICED",
|
|
228
|
+
fillId: fill.id,
|
|
229
|
+
closedQuantity: closedQty
|
|
230
|
+
});
|
|
220
231
|
} else {
|
|
221
232
|
const realized = isPositive(positionSize) ? multiply(closedQty, subtract(price, entryPrice)) : multiply(closedQty, subtract(entryPrice, price));
|
|
222
233
|
gross = add(gross, realized);
|
|
223
|
-
|
|
234
|
+
closures.push({
|
|
235
|
+
kind: "PRICED",
|
|
236
|
+
fillId: fill.id,
|
|
237
|
+
gross: realized,
|
|
238
|
+
entryPrice,
|
|
239
|
+
exitPrice: price,
|
|
240
|
+
closedQuantity: closedQty
|
|
241
|
+
});
|
|
242
|
+
exitQuantity = add(exitQuantity, closedQty);
|
|
243
|
+
exitNotional = add(exitNotional, multiply(closedQty, price));
|
|
224
244
|
}
|
|
225
245
|
}
|
|
226
246
|
const isSameDirection = !isZero(positionSize) && isPositive(positionSize) === isPositive(signedQuantity);
|
|
@@ -234,7 +254,13 @@ function walkAssetFills(fills) {
|
|
|
234
254
|
}
|
|
235
255
|
positionSize = newPositionSize;
|
|
236
256
|
}
|
|
237
|
-
return {
|
|
257
|
+
return {
|
|
258
|
+
gross,
|
|
259
|
+
undeterminedSize,
|
|
260
|
+
closures,
|
|
261
|
+
exitQuantity,
|
|
262
|
+
exitNotional
|
|
263
|
+
};
|
|
238
264
|
}
|
|
239
265
|
function computeBasketWeightedRatioV1(basket, priceMap) {
|
|
240
266
|
let logSum = 0;
|
|
@@ -252,6 +278,13 @@ function computeBasketWeightedRatioV1(basket, priceMap) {
|
|
|
252
278
|
}
|
|
253
279
|
return exponentiate(logSum);
|
|
254
280
|
}
|
|
281
|
+
function deriveBasketKey(longInstrumentIds, shortInstrumentIds) {
|
|
282
|
+
const payload = JSON.stringify({
|
|
283
|
+
long: [...longInstrumentIds].sort((a, b) => a.localeCompare(b)),
|
|
284
|
+
short: [...shortInstrumentIds].sort((a, b) => a.localeCompare(b))
|
|
285
|
+
});
|
|
286
|
+
return bytesToHex(sha256(new TextEncoder().encode(payload)));
|
|
287
|
+
}
|
|
255
288
|
function contractsToBase(contracts, contractSize) {
|
|
256
289
|
return new BigNumber9(contracts).times(contractSize);
|
|
257
290
|
}
|
|
@@ -331,7 +364,9 @@ function computeUnrealizedPositionState(exposureMap, priceMap, fills) {
|
|
|
331
364
|
};
|
|
332
365
|
}
|
|
333
366
|
function computeRealizedPositionState(fills) {
|
|
334
|
-
|
|
367
|
+
return aggregateRealizedPositionState(computeAssetRealizedPnLs(fills));
|
|
368
|
+
}
|
|
369
|
+
function aggregateRealizedPositionState(assetPnLs) {
|
|
335
370
|
const grossPnl = computePositionGrossRealizedPnL(assetPnLs);
|
|
336
371
|
const netPnl = computePositionNetRealizedPnL(assetPnLs);
|
|
337
372
|
const tradeFees = assetPnLs.reduce((acc, asset) => acc.plus(asset.tradeFees), ZERO);
|
|
@@ -1756,4 +1791,4 @@ function validateQuantity(quantity, price, precision) {
|
|
|
1756
1791
|
return true;
|
|
1757
1792
|
}
|
|
1758
1793
|
|
|
1759
|
-
export { BIPS, ZERO, abs, add, baseToContracts, buildApplicables, compareValue, computeAssetEntryNotional, computeAssetGrossRealizedPnL, computeAssetRealizedPnLs, computeAssetUndeterminedSizes, computeAssetUnrealizedPnL, computeAssetUnrealizedPnLs, computeBasketWeightedRatioV1, computeEntryPriceForAsset, computePositionEntryPrices, computeRealizedPnlBySymbol, computeRealizedPnlFromFills, computeRealizedPositionState, computeSyncPayload, computeUnrealizedPositionState, computeUnrealizedPositionStateFromEntryPrices, configureProxy, contractsToBase, countDecimals, 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 };
|
|
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 };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { InstrumentId } from '@pear-protocol/types';
|
|
2
|
+
import type { AssetRealizedPnL } from '../asset/asset.types';
|
|
2
3
|
import type { Fill } from '../fills/fill.types';
|
|
3
4
|
import type { RealizedPositionState, UnrealizedPositionState } from './position.types';
|
|
4
5
|
type ExposureMap = Record<InstrumentId, string>;
|
|
@@ -19,6 +20,18 @@ export declare function computeUnrealizedPositionState(exposureMap: ExposureMap,
|
|
|
19
20
|
* @returns The realized position state.
|
|
20
21
|
*/
|
|
21
22
|
export declare function computeRealizedPositionState(fills: Fill[]): RealizedPositionState;
|
|
23
|
+
/**
|
|
24
|
+
* Roll already-walked per-asset rows up into the position total.
|
|
25
|
+
*
|
|
26
|
+
* Separated from computeRealizedPositionState so a caller that already holds the
|
|
27
|
+
* per-asset rows can total them without walking the ledger a second time. The
|
|
28
|
+
* position total and the per-asset detail then come from one execution of the walk,
|
|
29
|
+
* which is the guarantee that makes them impossible to disagree.
|
|
30
|
+
*
|
|
31
|
+
* @param assetPnLs - Per-asset realized rows from computeAssetRealizedPnLs.
|
|
32
|
+
* @returns The realized position state those rows add up to.
|
|
33
|
+
*/
|
|
34
|
+
export declare function aggregateRealizedPositionState(assetPnLs: AssetRealizedPnL[]): RealizedPositionState;
|
|
22
35
|
/**
|
|
23
36
|
* Weighted-average entry (cost-basis) price per instrument in a position, derived purely from
|
|
24
37
|
* fills — no mark price needed. This is the authoritative source for a position's entry price;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pear-protocol/utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Pear Protocol Utility functions",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@noble/hashes": "^1.3.2",
|
|
30
|
-
"@pear-protocol/types": "^1.
|
|
30
|
+
"@pear-protocol/types": "^1.17.0",
|
|
31
31
|
"bignumber.js": "9.3.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|