@pear-protocol/utils 0.8.0 → 1.0.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 +13 -18
- package/dist/asset/compute.d.ts +51 -2
- package/dist/asset/index.d.ts +3 -1
- package/dist/asset/transition.types.d.ts +68 -0
- package/dist/asset/transitions.d.ts +13 -0
- package/dist/fills/group.d.ts +25 -0
- package/dist/fills/index.d.ts +1 -0
- package/dist/index.js +403 -66
- package/dist/position/compute.d.ts +14 -1
- package/dist/position/key.d.ts +3 -0
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { InstrumentId } from '@pear-protocol/types';
|
|
2
2
|
import BigNumber from 'bignumber.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { AssetPositionBasis, AssetReduction } from './transition.types';
|
|
4
4
|
export type AssetUnrealizedPnL = {
|
|
5
5
|
id: InstrumentId;
|
|
6
6
|
signedQuantity: BigNumber;
|
|
@@ -11,27 +11,14 @@ export type AssetUnrealizedPnL = {
|
|
|
11
11
|
/**
|
|
12
12
|
* One reduction of the position, attributed to the fill that made it.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* priced close from an unpriceable one by looking at the fill alone. `reduceOnly` is
|
|
17
|
-
* an order intent and says nothing about what actually closed, and a 0-price synthetic
|
|
18
|
-
* can equally well OPEN a position — so the walk is the only thing that knows, and it
|
|
19
|
-
* says so here instead of leaving the answer to be guessed downstream.
|
|
14
|
+
* The reduction itself is exactly what the transition walk reports — the aggregate adds
|
|
15
|
+
* only the fill it came from, so the two can never describe a close differently.
|
|
20
16
|
*
|
|
21
17
|
* The walk is time-agnostic (a Fill carries no timestamp), so callers that need to
|
|
22
18
|
* filter by time resolve fillId against their own fill records.
|
|
23
19
|
*/
|
|
24
|
-
export type AssetClosure = {
|
|
25
|
-
kind: 'PRICED';
|
|
20
|
+
export type AssetClosure = AssetReduction & {
|
|
26
21
|
fillId: string;
|
|
27
|
-
gross: BigNumber;
|
|
28
|
-
entryPrice: BigNumber;
|
|
29
|
-
exitPrice: BigNumber;
|
|
30
|
-
closedQuantity: BigNumber;
|
|
31
|
-
} | {
|
|
32
|
-
kind: 'UNPRICED';
|
|
33
|
-
fillId: string;
|
|
34
|
-
closedQuantity: BigNumber;
|
|
35
22
|
};
|
|
36
23
|
export type AssetRealizedPnL = {
|
|
37
24
|
id: InstrumentId;
|
|
@@ -41,9 +28,17 @@ export type AssetRealizedPnL = {
|
|
|
41
28
|
exitQuantity: BigNumber;
|
|
42
29
|
exitNotional: BigNumber;
|
|
43
30
|
exitVwap: BigNumber | null;
|
|
31
|
+
residualQuantity: BigNumber;
|
|
32
|
+
residualBasis: AssetPositionBasis;
|
|
44
33
|
totalFees: BigNumber;
|
|
45
34
|
tradeFees: BigNumber;
|
|
46
35
|
pearFees: BigNumber;
|
|
47
36
|
closures: AssetClosure[];
|
|
48
37
|
};
|
|
49
|
-
|
|
38
|
+
/** Whether the fill ledger provides a complete deployed-capital denominator. */
|
|
39
|
+
export type DeployedCapitalState = {
|
|
40
|
+
kind: 'MEASURED';
|
|
41
|
+
capital: BigNumber;
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'UNPRICED';
|
|
44
|
+
};
|
package/dist/asset/compute.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { InstrumentId } from '@pear-protocol/types';
|
|
2
2
|
import BigNumber from 'bignumber.js';
|
|
3
3
|
import type { Fill } from '../fills/fill.types';
|
|
4
|
-
import type { AssetRealizedPnL, AssetUnrealizedPnL,
|
|
4
|
+
import type { AssetRealizedPnL, AssetUnrealizedPnL, DeployedCapitalState } from './asset.types';
|
|
5
|
+
import type { AssetFillTransition, AssetTransitionFill } from './transition.types';
|
|
5
6
|
type ExposureMap = Record<InstrumentId, string>;
|
|
6
7
|
type PriceMap = Record<InstrumentId, string>;
|
|
7
8
|
/**
|
|
@@ -37,6 +38,41 @@ export declare function computeAssetUndeterminedSizes(fills: Fill[]): Record<Ins
|
|
|
37
38
|
* Formula: Σ(signedQty × (currentPrice − entryPrice))
|
|
38
39
|
*/
|
|
39
40
|
export declare function computeAssetUnrealizedPnLs(exposureMap: ExposureMap, priceMap: PriceMap, fills: Fill[]): AssetUnrealizedPnL[];
|
|
41
|
+
/** One replay of a position's ledger, answering both questions the walk can answer. */
|
|
42
|
+
export type PositionLedgerWalk = {
|
|
43
|
+
assets: AssetRealizedPnL[];
|
|
44
|
+
capital: DeployedCapitalState;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Replay one position's complete ledger once, deriving realized PnL and deployed capital.
|
|
48
|
+
*
|
|
49
|
+
* The two answers were previously two replays of the same fills — computeAssetRealizedPnLs
|
|
50
|
+
* per asset, then computeDeployedCapitalState across the basket — because they accumulate
|
|
51
|
+
* differently, not because they read different transitions. They read exactly the same ones:
|
|
52
|
+
* the transition walk keys its running state by symbol, so replaying an asset's fills alone
|
|
53
|
+
* and replaying them inside the basket produce an identical transition for every fill.
|
|
54
|
+
*
|
|
55
|
+
* Folding them also removes the possibility that the position's realized PnL and the capital
|
|
56
|
+
* it is measured against ever describe different replays of the same ledger.
|
|
57
|
+
*
|
|
58
|
+
* Chronological order across legs is required, as it is for the capital walk alone: the peak
|
|
59
|
+
* is the most every leg ever committed at one time, which only a cross-leg sequence can say.
|
|
60
|
+
*
|
|
61
|
+
* @param fills - One position's complete ledger, chronological across every leg.
|
|
62
|
+
* @returns The per-asset realized rows and the basket's capital state.
|
|
63
|
+
*/
|
|
64
|
+
export declare function walkPositionLedger(fills: Fill[]): PositionLedgerWalk;
|
|
65
|
+
/**
|
|
66
|
+
* The same walk, over transitions a caller has already computed.
|
|
67
|
+
*
|
|
68
|
+
* Exposed because the transition stream is the expensive part and more than one consumer
|
|
69
|
+
* reads it: a caller that needs both this and its own per-transition output can compute the
|
|
70
|
+
* stream once and hand it to both, rather than replaying the ledger for each.
|
|
71
|
+
*
|
|
72
|
+
* @param transitions - One position's transitions, in the ledger's own order.
|
|
73
|
+
* @returns The per-asset realized rows and the basket's capital state.
|
|
74
|
+
*/
|
|
75
|
+
export declare function walkPositionTransitions(transitions: readonly AssetFillTransition[]): PositionLedgerWalk;
|
|
40
76
|
/**
|
|
41
77
|
* Compute realized PnL for an entire position.
|
|
42
78
|
*
|
|
@@ -46,6 +82,13 @@ export declare function computeAssetRealizedPnLs(fills: Fill[]): AssetRealizedPn
|
|
|
46
82
|
/**
|
|
47
83
|
* The most cash the position's selected legs had committed at one time.
|
|
48
84
|
*
|
|
85
|
+
* Returned as a state rather than a number: MEASURED carries that peak, and UNPRICED
|
|
86
|
+
* refuses the measurement outright once a zero-price fill has opened selected exposure —
|
|
87
|
+
* an open, an increase, or the opening half of a flip. Cash committed at an unknown price
|
|
88
|
+
* can only raise the true peak, so a peak built without it would err the return large,
|
|
89
|
+
* the unsafe direction, and no number is offered instead. A zero-price reduction stays
|
|
90
|
+
* measurable, because what a close releases depends on the basis held, not the exit price.
|
|
91
|
+
*
|
|
49
92
|
* Cash, not market value: each opening fill commits what it actually paid, at its own
|
|
50
93
|
* execution price, and each closing fill releases the share of that commitment the quantity
|
|
51
94
|
* it closed was holding. No price beyond the fills' own is read, which matters because there
|
|
@@ -82,5 +125,11 @@ export declare function computeAssetRealizedPnLs(fills: Fill[]): AssetRealizedPn
|
|
|
82
125
|
* That only ever adds to the peak, so the denominator errs large and the return it produces
|
|
83
126
|
* errs small — the safe direction for a public board.
|
|
84
127
|
*/
|
|
85
|
-
export declare function
|
|
128
|
+
export declare function computeDeployedCapitalState(fills: AssetTransitionFill[], assets: Set<string> | undefined): DeployedCapitalState;
|
|
129
|
+
/**
|
|
130
|
+
* The measurable peak deployed capital, or zero when the ledger contains unpriced basis.
|
|
131
|
+
*
|
|
132
|
+
* @deprecated Use computeDeployedCapitalState when zero and unpriced need different handling.
|
|
133
|
+
*/
|
|
134
|
+
export declare function computeDeployedCapital(fills: AssetTransitionFill[], assets: Set<string> | undefined): BigNumber;
|
|
86
135
|
export {};
|
package/dist/asset/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
export type { AssetClosure, AssetRealizedPnL, AssetUnrealizedPnL,
|
|
1
|
+
export type { AssetClosure, AssetRealizedPnL, AssetUnrealizedPnL, DeployedCapitalState, } from './asset.types';
|
|
2
2
|
export * from './compute';
|
|
3
|
+
export type { AssetFillTransition, AssetPositionBasis, AssetPositionState, AssetReduction, AssetTransitionFill, } from './transition.types';
|
|
4
|
+
export * from './transitions';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** Public contracts for per-fill exposure and cost-basis transitions. */
|
|
2
|
+
import type BigNumber from 'bignumber.js';
|
|
3
|
+
import type { Fill } from '../fills/fill.types';
|
|
4
|
+
/** The fill fields required to derive exposure and cost-basis transitions. */
|
|
5
|
+
export type AssetTransitionFill = Pick<Fill, 'symbol' | 'side' | 'price' | 'quantity'>;
|
|
6
|
+
/** Whether an asset is flat, has a known average basis, or carries unpriceable basis. */
|
|
7
|
+
export type AssetPositionBasis = {
|
|
8
|
+
kind: 'FLAT';
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'PRICED';
|
|
11
|
+
entryPrice: BigNumber;
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'UNPRICED';
|
|
14
|
+
};
|
|
15
|
+
/** The signed exposure and cost-basis state immediately beside one fill. */
|
|
16
|
+
export type AssetPositionState = {
|
|
17
|
+
signedQuantity: BigNumber;
|
|
18
|
+
basis: AssetPositionBasis;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* The priced or unpriceable portion of a fill that reduced existing exposure.
|
|
22
|
+
*
|
|
23
|
+
* A discriminated union rather than an optional grossPnl, because whether a close could
|
|
24
|
+
* be valued is a property of the replay, not of the fill: a caller cannot tell a priced
|
|
25
|
+
* close from an unpriceable one by looking at the fill alone. `reduceOnly` is an order
|
|
26
|
+
* intent and says nothing about what actually closed, and a 0-price synthetic can equally
|
|
27
|
+
* well OPEN a position — so the walk is the only thing that knows, and it says so here
|
|
28
|
+
* instead of leaving the answer to be guessed downstream.
|
|
29
|
+
*/
|
|
30
|
+
export type AssetReduction = {
|
|
31
|
+
kind: 'PRICED';
|
|
32
|
+
quantity: BigNumber;
|
|
33
|
+
entryPrice: BigNumber;
|
|
34
|
+
exitPrice: BigNumber;
|
|
35
|
+
grossPnl: BigNumber;
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'UNPRICED';
|
|
38
|
+
quantity: BigNumber;
|
|
39
|
+
};
|
|
40
|
+
type AssetFillTransitionBase<TFill extends AssetTransitionFill> = {
|
|
41
|
+
fill: TFill;
|
|
42
|
+
positionBefore: AssetPositionState;
|
|
43
|
+
positionAfter: AssetPositionState;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* One fill's complete effect on its asset's exposure and basis.
|
|
47
|
+
*
|
|
48
|
+
* The original generic fill is retained so callers keep metadata such as timestamp,
|
|
49
|
+
* provenance, and settlement status without resolving a fill id after the walk.
|
|
50
|
+
*/
|
|
51
|
+
export type AssetFillTransition<TFill extends AssetTransitionFill = Fill> = (AssetFillTransitionBase<TFill> & {
|
|
52
|
+
kind: 'OPEN';
|
|
53
|
+
openedQuantity: BigNumber;
|
|
54
|
+
}) | (AssetFillTransitionBase<TFill> & {
|
|
55
|
+
kind: 'INCREASE';
|
|
56
|
+
increasedQuantity: BigNumber;
|
|
57
|
+
}) | (AssetFillTransitionBase<TFill> & {
|
|
58
|
+
kind: 'REDUCE';
|
|
59
|
+
reduction: AssetReduction;
|
|
60
|
+
}) | (AssetFillTransitionBase<TFill> & {
|
|
61
|
+
kind: 'CLOSE';
|
|
62
|
+
reduction: AssetReduction;
|
|
63
|
+
}) | (AssetFillTransitionBase<TFill> & {
|
|
64
|
+
kind: 'FLIP';
|
|
65
|
+
reduction: AssetReduction;
|
|
66
|
+
openedQuantity: BigNumber;
|
|
67
|
+
});
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AssetFillTransition, AssetTransitionFill } from './transition.types';
|
|
2
|
+
/**
|
|
3
|
+
* Replay a complete chronological fill ledger into one transition per input fill.
|
|
4
|
+
*
|
|
5
|
+
* Asset state is independent while output order is identical to input order. Equal-time
|
|
6
|
+
* ordering is therefore the caller's responsibility and is preserved without re-sorting.
|
|
7
|
+
*
|
|
8
|
+
* Preconditions, not enforced: every fill carries a positive finite quantity and a
|
|
9
|
+
* non-negative finite price (price "0" marks an unpriceable synthetic). A zero-quantity
|
|
10
|
+
* fill is not a valid ledger entry and yields a transition whose kind does not describe
|
|
11
|
+
* its (absent) exposure effect.
|
|
12
|
+
*/
|
|
13
|
+
export declare function computeFillTransitions<TFill extends AssetTransitionFill>(fills: readonly TFill[]): AssetFillTransition<TFill>[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bucket fills under the position that owns them, preserving the caller's order.
|
|
3
|
+
*
|
|
4
|
+
* Two walks depend on that order, so this must never re-sort. The realized-PnL walk reads
|
|
5
|
+
* each asset's sequence on its own; the deployed-capital walk spans every leg at once, and
|
|
6
|
+
* so needs the sequence chronological ACROSS legs — the stricter of the two requirements,
|
|
7
|
+
* and the one a caller is likeliest to break.
|
|
8
|
+
*
|
|
9
|
+
* Every history that replays a ledger — portfolio, leaderboard, closed and open positions,
|
|
10
|
+
* trade history — partitions it here first, so the contract is stated once for all of them.
|
|
11
|
+
*/
|
|
12
|
+
export declare function groupFillsByPosition<Fill extends {
|
|
13
|
+
positionId: string;
|
|
14
|
+
}>(fills: readonly Fill[]): Map<string, Fill[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Bucket one position's fills under the asset each traded, preserving the caller's order.
|
|
17
|
+
*
|
|
18
|
+
* The per-leg readers walk a bucket in sequence — entry VWAP prices the opens in the order
|
|
19
|
+
* they happened, and a leg's side falls back to its first fill — so this must never re-sort
|
|
20
|
+
* either. An asset the walk reported but the ledger never filled has no bucket at all,
|
|
21
|
+
* which is a real state, not a lookup miss.
|
|
22
|
+
*/
|
|
23
|
+
export declare function groupFillsByAsset<Fill extends {
|
|
24
|
+
symbol: string;
|
|
25
|
+
}>(fills: readonly Fill[]): Map<string, Fill[]>;
|
package/dist/fills/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -142,6 +142,192 @@ function computeEntryPriceForAsset(fills) {
|
|
|
142
142
|
}
|
|
143
143
|
__name(computeEntryPriceForAsset, "computeEntryPriceForAsset");
|
|
144
144
|
|
|
145
|
+
// src/asset/transitions.ts
|
|
146
|
+
function computeFillTransitions(fills) {
|
|
147
|
+
const stateByAsset = /* @__PURE__ */ new Map();
|
|
148
|
+
const transitions = [];
|
|
149
|
+
for (const fill of fills) {
|
|
150
|
+
const currentState = stateByAsset.get(fill.symbol) ?? {
|
|
151
|
+
signedQuantity: ZERO,
|
|
152
|
+
entryPrice: ZERO,
|
|
153
|
+
basisUnpriced: false
|
|
154
|
+
};
|
|
155
|
+
const result = transitionFill(currentState, fill);
|
|
156
|
+
transitions.push(result.transition);
|
|
157
|
+
stateByAsset.set(fill.symbol, result.nextState);
|
|
158
|
+
}
|
|
159
|
+
return transitions;
|
|
160
|
+
}
|
|
161
|
+
__name(computeFillTransitions, "computeFillTransitions");
|
|
162
|
+
function transitionFill(currentState, fill) {
|
|
163
|
+
const price = parse(fill.price);
|
|
164
|
+
const quantity = parse(fill.quantity);
|
|
165
|
+
const signedQuantity = signedBySide(quantity, fill.side);
|
|
166
|
+
const nextSignedQuantity = add(currentState.signedQuantity, signedQuantity);
|
|
167
|
+
const kind = classifyTransition(currentState.signedQuantity, signedQuantity, nextSignedQuantity);
|
|
168
|
+
const nextState = advanceState(currentState, kind, price, quantity, nextSignedQuantity);
|
|
169
|
+
const positionBefore = toPositionState(currentState);
|
|
170
|
+
const positionAfter = toPositionState(nextState);
|
|
171
|
+
switch (kind) {
|
|
172
|
+
case "OPEN":
|
|
173
|
+
return {
|
|
174
|
+
nextState,
|
|
175
|
+
transition: {
|
|
176
|
+
kind,
|
|
177
|
+
fill,
|
|
178
|
+
positionBefore,
|
|
179
|
+
positionAfter,
|
|
180
|
+
openedQuantity: quantity
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
case "INCREASE":
|
|
184
|
+
return {
|
|
185
|
+
nextState,
|
|
186
|
+
transition: {
|
|
187
|
+
kind,
|
|
188
|
+
fill,
|
|
189
|
+
positionBefore,
|
|
190
|
+
positionAfter,
|
|
191
|
+
increasedQuantity: quantity
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
case "REDUCE": {
|
|
195
|
+
const reduction = buildReduction(currentState, price, quantity);
|
|
196
|
+
return {
|
|
197
|
+
nextState,
|
|
198
|
+
transition: {
|
|
199
|
+
kind,
|
|
200
|
+
fill,
|
|
201
|
+
positionBefore,
|
|
202
|
+
positionAfter,
|
|
203
|
+
reduction
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
case "CLOSE": {
|
|
208
|
+
const reduction = buildReduction(currentState, price, quantity);
|
|
209
|
+
return {
|
|
210
|
+
nextState,
|
|
211
|
+
transition: {
|
|
212
|
+
kind,
|
|
213
|
+
fill,
|
|
214
|
+
positionBefore,
|
|
215
|
+
positionAfter,
|
|
216
|
+
reduction
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
case "FLIP": {
|
|
221
|
+
const reduction = buildReduction(currentState, price, quantity);
|
|
222
|
+
return {
|
|
223
|
+
nextState,
|
|
224
|
+
transition: {
|
|
225
|
+
kind,
|
|
226
|
+
fill,
|
|
227
|
+
positionBefore,
|
|
228
|
+
positionAfter,
|
|
229
|
+
reduction,
|
|
230
|
+
openedQuantity: nextSignedQuantity.abs()
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
__name(transitionFill, "transitionFill");
|
|
237
|
+
function classifyTransition(currentQuantity, fillQuantity, nextQuantity) {
|
|
238
|
+
if (isZero(currentQuantity)) {
|
|
239
|
+
return "OPEN";
|
|
240
|
+
}
|
|
241
|
+
if (isPositive(currentQuantity) === isPositive(fillQuantity)) {
|
|
242
|
+
return "INCREASE";
|
|
243
|
+
}
|
|
244
|
+
if (isZero(nextQuantity)) {
|
|
245
|
+
return "CLOSE";
|
|
246
|
+
}
|
|
247
|
+
if (isPositive(nextQuantity) !== isPositive(currentQuantity)) {
|
|
248
|
+
return "FLIP";
|
|
249
|
+
}
|
|
250
|
+
return "REDUCE";
|
|
251
|
+
}
|
|
252
|
+
__name(classifyTransition, "classifyTransition");
|
|
253
|
+
function buildReduction(currentState, exitPrice, fillQuantity) {
|
|
254
|
+
const quantity = min(currentState.signedQuantity.abs(), fillQuantity);
|
|
255
|
+
if (currentState.basisUnpriced || isZero(exitPrice)) {
|
|
256
|
+
return {
|
|
257
|
+
kind: "UNPRICED",
|
|
258
|
+
quantity
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const grossPnl = isPositive(currentState.signedQuantity) ? multiply(quantity, subtract(exitPrice, currentState.entryPrice)) : multiply(quantity, subtract(currentState.entryPrice, exitPrice));
|
|
262
|
+
return {
|
|
263
|
+
kind: "PRICED",
|
|
264
|
+
quantity,
|
|
265
|
+
entryPrice: currentState.entryPrice,
|
|
266
|
+
exitPrice,
|
|
267
|
+
grossPnl
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
__name(buildReduction, "buildReduction");
|
|
271
|
+
function advanceState(currentState, kind, price, fillQuantity, nextSignedQuantity) {
|
|
272
|
+
switch (kind) {
|
|
273
|
+
case "OPEN":
|
|
274
|
+
case "FLIP":
|
|
275
|
+
return {
|
|
276
|
+
signedQuantity: nextSignedQuantity,
|
|
277
|
+
entryPrice: price,
|
|
278
|
+
basisUnpriced: isZero(price)
|
|
279
|
+
};
|
|
280
|
+
case "INCREASE": {
|
|
281
|
+
const entryNotional = multiply(currentState.entryPrice, currentState.signedQuantity.abs());
|
|
282
|
+
const addedNotional = multiply(price, fillQuantity);
|
|
283
|
+
const entryPrice = divide(add(entryNotional, addedNotional), nextSignedQuantity.abs());
|
|
284
|
+
return {
|
|
285
|
+
signedQuantity: nextSignedQuantity,
|
|
286
|
+
entryPrice,
|
|
287
|
+
basisUnpriced: currentState.basisUnpriced || isZero(price)
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
case "REDUCE":
|
|
291
|
+
return {
|
|
292
|
+
...currentState,
|
|
293
|
+
signedQuantity: nextSignedQuantity
|
|
294
|
+
};
|
|
295
|
+
case "CLOSE":
|
|
296
|
+
return {
|
|
297
|
+
signedQuantity: ZERO,
|
|
298
|
+
entryPrice: ZERO,
|
|
299
|
+
basisUnpriced: false
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
__name(advanceState, "advanceState");
|
|
304
|
+
function toPositionState(state) {
|
|
305
|
+
if (isZero(state.signedQuantity)) {
|
|
306
|
+
return {
|
|
307
|
+
signedQuantity: state.signedQuantity,
|
|
308
|
+
basis: {
|
|
309
|
+
kind: "FLAT"
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
if (state.basisUnpriced) {
|
|
314
|
+
return {
|
|
315
|
+
signedQuantity: state.signedQuantity,
|
|
316
|
+
basis: {
|
|
317
|
+
kind: "UNPRICED"
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
signedQuantity: state.signedQuantity,
|
|
323
|
+
basis: {
|
|
324
|
+
kind: "PRICED",
|
|
325
|
+
entryPrice: state.entryPrice
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
__name(toPositionState, "toPositionState");
|
|
330
|
+
|
|
145
331
|
// src/asset/compute.ts
|
|
146
332
|
function computeAssetEntryNotional(signedQuantity, fills) {
|
|
147
333
|
if (signedQuantity.isZero()) {
|
|
@@ -207,6 +393,49 @@ function computeAssetUnrealizedPnLs(exposureMap, priceMap, fills) {
|
|
|
207
393
|
return result;
|
|
208
394
|
}
|
|
209
395
|
__name(computeAssetUnrealizedPnLs, "computeAssetUnrealizedPnLs");
|
|
396
|
+
function walkPositionLedger(fills) {
|
|
397
|
+
return walkPositionTransitions(computeFillTransitions(fills));
|
|
398
|
+
}
|
|
399
|
+
__name(walkPositionLedger, "walkPositionLedger");
|
|
400
|
+
function walkPositionTransitions(transitions) {
|
|
401
|
+
const realizedByAsset = /* @__PURE__ */ new Map();
|
|
402
|
+
const capitalByAsset = /* @__PURE__ */ new Map();
|
|
403
|
+
let committed = ZERO;
|
|
404
|
+
let peak = ZERO;
|
|
405
|
+
let capitalUnpriced = false;
|
|
406
|
+
for (const transition of transitions) {
|
|
407
|
+
const { fill, positionAfter } = transition;
|
|
408
|
+
const realized = realizedByAsset.get(fill.symbol) ?? createAssetRealizedAccumulator();
|
|
409
|
+
accumulateAssetRealization(realized, transition);
|
|
410
|
+
realizedByAsset.set(fill.symbol, realized);
|
|
411
|
+
if (positionAfter.basis.kind === "UNPRICED") {
|
|
412
|
+
capitalUnpriced = true;
|
|
413
|
+
}
|
|
414
|
+
if (capitalUnpriced) {
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
const previousCapital = capitalByAsset.get(fill.symbol) ?? ZERO;
|
|
418
|
+
const nextCapital = capitalAfterTransition(transition, previousCapital);
|
|
419
|
+
committed = add(subtract(committed, previousCapital), nextCapital);
|
|
420
|
+
capitalByAsset.set(fill.symbol, nextCapital);
|
|
421
|
+
if (isGreaterThan(committed, peak)) {
|
|
422
|
+
peak = committed;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
const assets = [
|
|
426
|
+
...realizedByAsset
|
|
427
|
+
].map(([id, accumulator]) => finalizeAssetRealization(id, accumulator));
|
|
428
|
+
return {
|
|
429
|
+
assets,
|
|
430
|
+
capital: capitalUnpriced ? {
|
|
431
|
+
kind: "UNPRICED"
|
|
432
|
+
} : {
|
|
433
|
+
kind: "MEASURED",
|
|
434
|
+
capital: peak
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
__name(walkPositionTransitions, "walkPositionTransitions");
|
|
210
439
|
function computeAssetRealizedPnLs(fills) {
|
|
211
440
|
const bySymbol = /* @__PURE__ */ new Map();
|
|
212
441
|
for (const fill of fills) {
|
|
@@ -219,7 +448,7 @@ function computeAssetRealizedPnLs(fills) {
|
|
|
219
448
|
const tradeFees = assetFills.reduce((acc, fill) => acc.plus(new BigNumber7(fill.tradeFee ?? "0")), ZERO);
|
|
220
449
|
const pearFees = assetFills.reduce((acc, fill) => acc.plus(new BigNumber7(fill.pearFee ?? "0")), ZERO);
|
|
221
450
|
const totalFees = tradeFees.plus(pearFees);
|
|
222
|
-
const { gross: grossPnl, undeterminedSize, closures, exitQuantity, exitNotional } = walkAssetFills(assetFills);
|
|
451
|
+
const { gross: grossPnl, undeterminedSize, closures, exitQuantity, exitNotional, residualQuantity, residualBasis } = walkAssetFills(assetFills);
|
|
223
452
|
const netPnl = grossPnl.minus(totalFees);
|
|
224
453
|
const exitVwap = isZero(exitQuantity) ? null : divide(exitNotional, exitQuantity);
|
|
225
454
|
result.push({
|
|
@@ -233,106 +462,188 @@ function computeAssetRealizedPnLs(fills) {
|
|
|
233
462
|
totalFees,
|
|
234
463
|
tradeFees,
|
|
235
464
|
pearFees,
|
|
465
|
+
residualQuantity,
|
|
466
|
+
residualBasis,
|
|
236
467
|
closures
|
|
237
468
|
});
|
|
238
469
|
}
|
|
239
470
|
return result;
|
|
240
471
|
}
|
|
241
472
|
__name(computeAssetRealizedPnLs, "computeAssetRealizedPnLs");
|
|
242
|
-
function
|
|
243
|
-
const
|
|
473
|
+
function computeDeployedCapitalState(fills, assets) {
|
|
474
|
+
const capitalByAsset = /* @__PURE__ */ new Map();
|
|
244
475
|
let committed = ZERO;
|
|
245
476
|
let peak = ZERO;
|
|
246
|
-
for (const
|
|
477
|
+
for (const transition of computeFillTransitions(fills)) {
|
|
478
|
+
const { fill, positionAfter } = transition;
|
|
247
479
|
if (assets && !assets.has(fill.symbol.toUpperCase())) {
|
|
248
480
|
continue;
|
|
249
481
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
482
|
+
if (positionAfter.basis.kind === "UNPRICED") {
|
|
483
|
+
return {
|
|
484
|
+
kind: "UNPRICED"
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
const previousCapital = capitalByAsset.get(fill.symbol) ?? ZERO;
|
|
488
|
+
let nextCapital;
|
|
489
|
+
switch (transition.kind) {
|
|
490
|
+
case "OPEN":
|
|
491
|
+
nextCapital = multiply(transition.openedQuantity, fill.price);
|
|
492
|
+
break;
|
|
493
|
+
case "INCREASE":
|
|
494
|
+
nextCapital = add(previousCapital, multiply(transition.increasedQuantity, fill.price));
|
|
495
|
+
break;
|
|
496
|
+
case "REDUCE": {
|
|
497
|
+
const released = divide(multiply(previousCapital, transition.reduction.quantity), transition.positionBefore.signedQuantity.abs());
|
|
498
|
+
nextCapital = subtract(previousCapital, released);
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
case "CLOSE":
|
|
502
|
+
nextCapital = ZERO;
|
|
503
|
+
break;
|
|
504
|
+
case "FLIP":
|
|
505
|
+
nextCapital = multiply(transition.openedQuantity, fill.price);
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
committed = add(subtract(committed, previousCapital), nextCapital);
|
|
509
|
+
capitalByAsset.set(fill.symbol, nextCapital);
|
|
264
510
|
if (isGreaterThan(committed, peak)) {
|
|
265
511
|
peak = committed;
|
|
266
512
|
}
|
|
267
513
|
}
|
|
268
|
-
return
|
|
514
|
+
return {
|
|
515
|
+
kind: "MEASURED",
|
|
516
|
+
capital: peak
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
__name(computeDeployedCapitalState, "computeDeployedCapitalState");
|
|
520
|
+
function computeDeployedCapital(fills, assets) {
|
|
521
|
+
const state = computeDeployedCapitalState(fills, assets);
|
|
522
|
+
switch (state.kind) {
|
|
523
|
+
case "MEASURED":
|
|
524
|
+
return state.capital;
|
|
525
|
+
case "UNPRICED":
|
|
526
|
+
return ZERO;
|
|
527
|
+
}
|
|
269
528
|
}
|
|
270
529
|
__name(computeDeployedCapital, "computeDeployedCapital");
|
|
271
530
|
function walkAssetFills(fills) {
|
|
272
|
-
|
|
273
|
-
let positionSize = ZERO;
|
|
274
|
-
let basisUnpriced = false;
|
|
531
|
+
const transitions = computeFillTransitions(fills);
|
|
275
532
|
let gross = ZERO;
|
|
276
533
|
let undeterminedSize = ZERO;
|
|
277
534
|
const closures = [];
|
|
278
535
|
let exitQuantity = ZERO;
|
|
279
536
|
let exitNotional = ZERO;
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
closures.push({
|
|
290
|
-
kind: "UNPRICED",
|
|
291
|
-
fillId: fill.id,
|
|
292
|
-
closedQuantity: closedQty
|
|
293
|
-
});
|
|
294
|
-
} else {
|
|
295
|
-
const realized = isPositive(positionSize) ? multiply(closedQty, subtract(price, entryPrice)) : multiply(closedQty, subtract(entryPrice, price));
|
|
296
|
-
gross = add(gross, realized);
|
|
297
|
-
closures.push({
|
|
298
|
-
kind: "PRICED",
|
|
299
|
-
fillId: fill.id,
|
|
300
|
-
gross: realized,
|
|
301
|
-
entryPrice,
|
|
302
|
-
exitPrice: price,
|
|
303
|
-
closedQuantity: closedQty
|
|
304
|
-
});
|
|
305
|
-
exitQuantity = add(exitQuantity, closedQty);
|
|
306
|
-
exitNotional = add(exitNotional, multiply(closedQty, price));
|
|
307
|
-
}
|
|
537
|
+
let residualQuantity = ZERO;
|
|
538
|
+
let residualBasis = {
|
|
539
|
+
kind: "FLAT"
|
|
540
|
+
};
|
|
541
|
+
for (const transition of transitions) {
|
|
542
|
+
residualQuantity = transition.positionAfter.signedQuantity;
|
|
543
|
+
residualBasis = transition.positionAfter.basis;
|
|
544
|
+
if (transition.kind === "OPEN" || transition.kind === "INCREASE") {
|
|
545
|
+
continue;
|
|
308
546
|
}
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
547
|
+
const { reduction } = transition;
|
|
548
|
+
closures.push({
|
|
549
|
+
...reduction,
|
|
550
|
+
fillId: transition.fill.id
|
|
551
|
+
});
|
|
552
|
+
if (reduction.kind === "UNPRICED") {
|
|
553
|
+
undeterminedSize = add(undeterminedSize, reduction.quantity);
|
|
554
|
+
continue;
|
|
317
555
|
}
|
|
318
|
-
|
|
556
|
+
gross = add(gross, reduction.grossPnl);
|
|
557
|
+
exitQuantity = add(exitQuantity, reduction.quantity);
|
|
558
|
+
exitNotional = add(exitNotional, multiply(reduction.quantity, reduction.exitPrice));
|
|
319
559
|
}
|
|
320
560
|
return {
|
|
321
561
|
gross,
|
|
322
562
|
undeterminedSize,
|
|
323
563
|
closures,
|
|
324
564
|
exitQuantity,
|
|
325
|
-
exitNotional
|
|
565
|
+
exitNotional,
|
|
566
|
+
residualQuantity,
|
|
567
|
+
residualBasis
|
|
326
568
|
};
|
|
327
569
|
}
|
|
328
570
|
__name(walkAssetFills, "walkAssetFills");
|
|
329
|
-
function
|
|
330
|
-
|
|
331
|
-
|
|
571
|
+
function createAssetRealizedAccumulator() {
|
|
572
|
+
return {
|
|
573
|
+
gross: ZERO,
|
|
574
|
+
undeterminedSize: ZERO,
|
|
575
|
+
closures: [],
|
|
576
|
+
exitQuantity: ZERO,
|
|
577
|
+
exitNotional: ZERO,
|
|
578
|
+
residualQuantity: ZERO,
|
|
579
|
+
residualBasis: {
|
|
580
|
+
kind: "FLAT"
|
|
581
|
+
},
|
|
582
|
+
tradeFees: ZERO,
|
|
583
|
+
pearFees: ZERO
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
__name(createAssetRealizedAccumulator, "createAssetRealizedAccumulator");
|
|
587
|
+
function accumulateAssetRealization(accumulator, transition) {
|
|
588
|
+
const { fill } = transition;
|
|
589
|
+
accumulator.tradeFees = add(accumulator.tradeFees, fill.tradeFee ?? "0");
|
|
590
|
+
accumulator.pearFees = add(accumulator.pearFees, fill.pearFee ?? "0");
|
|
591
|
+
accumulator.residualQuantity = transition.positionAfter.signedQuantity;
|
|
592
|
+
accumulator.residualBasis = transition.positionAfter.basis;
|
|
593
|
+
if (transition.kind === "OPEN" || transition.kind === "INCREASE") {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
const { reduction } = transition;
|
|
597
|
+
accumulator.closures.push({
|
|
598
|
+
...reduction,
|
|
599
|
+
fillId: fill.id
|
|
600
|
+
});
|
|
601
|
+
if (reduction.kind === "UNPRICED") {
|
|
602
|
+
accumulator.undeterminedSize = add(accumulator.undeterminedSize, reduction.quantity);
|
|
603
|
+
return;
|
|
332
604
|
}
|
|
333
|
-
|
|
605
|
+
accumulator.gross = add(accumulator.gross, reduction.grossPnl);
|
|
606
|
+
accumulator.exitQuantity = add(accumulator.exitQuantity, reduction.quantity);
|
|
607
|
+
accumulator.exitNotional = add(accumulator.exitNotional, multiply(reduction.quantity, reduction.exitPrice));
|
|
334
608
|
}
|
|
335
|
-
__name(
|
|
609
|
+
__name(accumulateAssetRealization, "accumulateAssetRealization");
|
|
610
|
+
function finalizeAssetRealization(id, accumulator) {
|
|
611
|
+
const totalFees = add(accumulator.tradeFees, accumulator.pearFees);
|
|
612
|
+
const exitVwap = isZero(accumulator.exitQuantity) ? null : divide(accumulator.exitNotional, accumulator.exitQuantity);
|
|
613
|
+
return {
|
|
614
|
+
id,
|
|
615
|
+
grossPnl: accumulator.gross,
|
|
616
|
+
netPnl: subtract(accumulator.gross, totalFees),
|
|
617
|
+
undeterminedSize: accumulator.undeterminedSize,
|
|
618
|
+
exitQuantity: accumulator.exitQuantity,
|
|
619
|
+
exitNotional: accumulator.exitNotional,
|
|
620
|
+
exitVwap,
|
|
621
|
+
totalFees,
|
|
622
|
+
tradeFees: accumulator.tradeFees,
|
|
623
|
+
pearFees: accumulator.pearFees,
|
|
624
|
+
residualQuantity: accumulator.residualQuantity,
|
|
625
|
+
residualBasis: accumulator.residualBasis,
|
|
626
|
+
closures: accumulator.closures
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
__name(finalizeAssetRealization, "finalizeAssetRealization");
|
|
630
|
+
function capitalAfterTransition(transition, previousCapital) {
|
|
631
|
+
switch (transition.kind) {
|
|
632
|
+
case "OPEN":
|
|
633
|
+
return multiply(transition.openedQuantity, transition.fill.price);
|
|
634
|
+
case "INCREASE":
|
|
635
|
+
return add(previousCapital, multiply(transition.increasedQuantity, transition.fill.price));
|
|
636
|
+
case "REDUCE": {
|
|
637
|
+
const released = divide(multiply(previousCapital, transition.reduction.quantity), transition.positionBefore.signedQuantity.abs());
|
|
638
|
+
return subtract(previousCapital, released);
|
|
639
|
+
}
|
|
640
|
+
case "CLOSE":
|
|
641
|
+
return ZERO;
|
|
642
|
+
case "FLIP":
|
|
643
|
+
return multiply(transition.openedQuantity, transition.fill.price);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
__name(capitalAfterTransition, "capitalAfterTransition");
|
|
336
647
|
|
|
337
648
|
// src/basket/compute.ts
|
|
338
649
|
function computeBasketWeightedRatioV1(basket, priceMap) {
|
|
@@ -553,6 +864,28 @@ function baseToContracts(base, contractSize) {
|
|
|
553
864
|
return new BigNumber7(base).div(contractSize);
|
|
554
865
|
}
|
|
555
866
|
__name(baseToContracts, "baseToContracts");
|
|
867
|
+
|
|
868
|
+
// src/fills/group.ts
|
|
869
|
+
function groupFillsByPosition(fills) {
|
|
870
|
+
const byPosition = /* @__PURE__ */ new Map();
|
|
871
|
+
for (const fill of fills) {
|
|
872
|
+
const positionFills = byPosition.get(fill.positionId) ?? [];
|
|
873
|
+
positionFills.push(fill);
|
|
874
|
+
byPosition.set(fill.positionId, positionFills);
|
|
875
|
+
}
|
|
876
|
+
return byPosition;
|
|
877
|
+
}
|
|
878
|
+
__name(groupFillsByPosition, "groupFillsByPosition");
|
|
879
|
+
function groupFillsByAsset(fills) {
|
|
880
|
+
const byAsset = /* @__PURE__ */ new Map();
|
|
881
|
+
for (const fill of fills) {
|
|
882
|
+
const assetFills = byAsset.get(fill.symbol) ?? [];
|
|
883
|
+
assetFills.push(fill);
|
|
884
|
+
byAsset.set(fill.symbol, assetFills);
|
|
885
|
+
}
|
|
886
|
+
return byAsset;
|
|
887
|
+
}
|
|
888
|
+
__name(groupFillsByAsset, "groupFillsByAsset");
|
|
556
889
|
function computeRealizedPnlBySymbol(fills) {
|
|
557
890
|
const bySymbol = groupFillsBySymbol(fills);
|
|
558
891
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -653,6 +986,10 @@ function aggregateRealizedPositionState(assetPnLs) {
|
|
|
653
986
|
};
|
|
654
987
|
}
|
|
655
988
|
__name(aggregateRealizedPositionState, "aggregateRealizedPositionState");
|
|
989
|
+
function sumAttributedFunding(fundingByAsset) {
|
|
990
|
+
return Object.values(fundingByAsset).reduce((total, assetFunding) => add(total, assetFunding), ZERO);
|
|
991
|
+
}
|
|
992
|
+
__name(sumAttributedFunding, "sumAttributedFunding");
|
|
656
993
|
function computePositionEntryPrice(assetUPnLs) {
|
|
657
994
|
const { totalWeightedPrice, totalWeight } = assetUPnLs.reduce((acc, { signedQuantity, entryPrice }) => {
|
|
658
995
|
const absQuantity = signedQuantity.abs();
|
|
@@ -2352,4 +2689,4 @@ function takeVerifier(handoffId, storage) {
|
|
|
2352
2689
|
}
|
|
2353
2690
|
__name(takeVerifier, "takeVerifier");
|
|
2354
2691
|
|
|
2355
|
-
export { BIPS, ZERO, abs, add, aggregateRealizedPositionState, baseToContracts, buildApplicables, compareValue, computeAssetEntryNotional, computeAssetGrossRealizedPnL, computeAssetRealizedPnLs, computeAssetUndeterminedSizes, computeAssetUnrealizedPnL, computeAssetUnrealizedPnLs, computeBasketWeightedRatioV1, computeDeployedCapital, computeEntryPriceForAsset, computeMaxBasketSize, computeMultiQuoteBasketSize, 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, sizeBasket, sizeBasketByLeverageMode, sizeBasketPerAssetLeverage, startXHandoff, stripXReturn, subtract, toDecimalString, validateLeverage, validateQuantity };
|
|
2692
|
+
export { BIPS, ZERO, abs, add, aggregateRealizedPositionState, baseToContracts, buildApplicables, compareValue, computeAssetEntryNotional, computeAssetGrossRealizedPnL, computeAssetRealizedPnLs, computeAssetUndeterminedSizes, computeAssetUnrealizedPnL, computeAssetUnrealizedPnLs, computeBasketWeightedRatioV1, computeDeployedCapital, computeDeployedCapitalState, computeEntryPriceForAsset, computeFillTransitions, computeMaxBasketSize, computeMultiQuoteBasketSize, computePositionEntryPrices, computeRealizedPnlBySymbol, computeRealizedPnlFromFills, computeRealizedPositionState, computeSyncPayload, computeUnrealizedPositionState, computeUnrealizedPositionStateFromEntryPrices, configureProxy, contractsToBase, countDecimals, deriveBasketKey, divide, exponentiate, finishXHandoff, generateDeterministicPositionKey, groupFillsByAsset, groupFillsByPosition, isEqualTo, isFiniteDecimal, isGreaterThan, isGreaterThanOrEqual, isLessThan, isLessThanOrEqual, isNegative, isPositive, isZero, min, multiply, negate, parse, precisePrice, preciseQuantity, proxyFetch, sideSign, sign, signed, signedBySide, sizeBasket, sizeBasketByLeverageMode, sizeBasketPerAssetLeverage, startXHandoff, stripXReturn, subtract, sumAttributedFunding, toDecimalString, validateLeverage, validateQuantity, walkPositionLedger, walkPositionTransitions };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { InstrumentId } from '@pear-protocol/types';
|
|
1
|
+
import type { InstrumentId, PositionTotalFundingPayment } from '@pear-protocol/types';
|
|
2
|
+
import BigNumber from 'bignumber.js';
|
|
2
3
|
import type { AssetRealizedPnL } from '../asset/asset.types';
|
|
3
4
|
import type { Fill } from '../fills/fill.types';
|
|
4
5
|
import type { RealizedPositionState, UnrealizedPositionState } from './position.types';
|
|
@@ -32,6 +33,18 @@ export declare function computeRealizedPositionState(fills: Fill[]): RealizedPos
|
|
|
32
33
|
* @returns The realized position state those rows add up to.
|
|
33
34
|
*/
|
|
34
35
|
export declare function aggregateRealizedPositionState(assetPnLs: AssetRealizedPnL[]): RealizedPositionState;
|
|
36
|
+
/**
|
|
37
|
+
* Total the funding attributed to one position, across every asset that earned any.
|
|
38
|
+
*
|
|
39
|
+
* Deliberately totals the attribution map rather than the position's legs. Funding
|
|
40
|
+
* follows exposure, not fills, so an asset that carried exposure without a fill has
|
|
41
|
+
* funding and no leg. Summing the legs would silently drop it, which is why this total
|
|
42
|
+
* can exceed the breakdown a caller can see.
|
|
43
|
+
*
|
|
44
|
+
* @param fundingByAsset - Funding attributed per asset, as the wire records it.
|
|
45
|
+
* @returns The position's net funding carry.
|
|
46
|
+
*/
|
|
47
|
+
export declare function sumAttributedFunding(fundingByAsset: PositionTotalFundingPayment): BigNumber;
|
|
35
48
|
/**
|
|
36
49
|
* Weighted-average entry (cost-basis) price per instrument in a position, derived purely from
|
|
37
50
|
* fills — no mark price needed. This is the authoritative source for a position's entry price;
|
package/dist/position/key.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ type PositionKeyInput = {
|
|
|
10
10
|
* no side — same-asset positions therefore produce the SAME key, which is
|
|
11
11
|
* exactly what the active-position unique index relies on and what makes the
|
|
12
12
|
* apply UPSERT find-or-create the single correct target.
|
|
13
|
+
*
|
|
14
|
+
* PHASE-1 LEG ID WARNING: `symbols` must be instrument ids
|
|
15
|
+
* (`leg.id ?? leg.symbol`, or exposure keys). A phase-3 ticker here re-hashes and orphans every position.
|
|
13
16
|
*/
|
|
14
17
|
export declare function generateDeterministicPositionKey(input: PositionKeyInput): string;
|
|
15
18
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pear-protocol/utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.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": "^
|
|
30
|
+
"@pear-protocol/types": "^2.0.0",
|
|
31
31
|
"@scure/base": "^2.3.0",
|
|
32
32
|
"bignumber.js": "9.3.1"
|
|
33
33
|
},
|