@pear-protocol/utils 1.6.1 → 1.7.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/index.d.ts +2 -1
- package/dist/index.js +214 -1364
- package/dist/market-reference-price.d.ts +52 -0
- package/dist/precise-quantity-with-mode.d.ts +21 -0
- package/dist/vaults/action-typed-data.d.ts +18 -1
- package/package.json +2 -2
- package/dist/sync/applicables/basket.d.ts +0 -39
- package/dist/sync/applicables/constants.d.ts +0 -26
- package/dist/sync/applicables/exposure.d.ts +0 -39
- package/dist/sync/applicables/fills.d.ts +0 -3
- package/dist/sync/applicables/index.d.ts +0 -37
- package/dist/sync/applicables/matching.d.ts +0 -20
- package/dist/sync/applicables/position.d.ts +0 -19
- package/dist/sync/applicables/snapshot.d.ts +0 -41
- package/dist/sync/applicables/synthetics.d.ts +0 -53
- package/dist/sync/applicables/types.d.ts +0 -82
- package/dist/sync/compute.d.ts +0 -28
- package/dist/sync/cutoff.d.ts +0 -35
- package/dist/sync/index.d.ts +0 -3
- package/dist/sync/types.d.ts +0 -25
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** The reference price a market order is priced from, so it reaches the opposite side of the book (#785). */
|
|
2
|
+
import type { Connector, InstrumentPrecision, Side } from '@pear-protocol/types';
|
|
3
|
+
import { type GridPrice, type PriceRounding } from './precise-price';
|
|
4
|
+
/**
|
|
5
|
+
* Where a market leg's price starts (#1180):
|
|
6
|
+
* - `touch`: the opposite side of the book, so a manual order fills even on a wide spread;
|
|
7
|
+
* - `mid`: the mid, so an automated order never prices past the user's slippage from it.
|
|
8
|
+
*/
|
|
9
|
+
export type MarketPricing = 'touch' | 'mid';
|
|
10
|
+
/** Every origin a market order can be placed from. */
|
|
11
|
+
type MarketOrigin = 'auto-rebalance' | 'copy' | 'direct' | 'manual-rebalance' | 'schedule' | 'trigger' | 'vault-liquidity';
|
|
12
|
+
/** A cached price-feed value with its top of book, either side absent when the feed has not published it. */
|
|
13
|
+
export type MarketQuote = {
|
|
14
|
+
p: string;
|
|
15
|
+
bid: string | null;
|
|
16
|
+
ask: string | null;
|
|
17
|
+
};
|
|
18
|
+
/** The price a market leg's slippage is applied to, and which way to round it onto the grid. */
|
|
19
|
+
export type MarketReference = {
|
|
20
|
+
price: string;
|
|
21
|
+
rounding: PriceRounding;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The limit a market leg is placed at: its reference (the touch or the mid, per `pricing`) padded by the
|
|
25
|
+
* account's slippage tolerance and rounded onto the venue's price grid. Every market prepare prices a
|
|
26
|
+
* leg without a caller bound this way, and a close TWAP values its smallest slice at the same limit.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveMarketLimitPrice(connector: Connector, quote: MarketQuote, precision: InstrumentPrecision, side: Side, slippageToleranceBps: number, pricing: MarketPricing): GridPrice;
|
|
29
|
+
/**
|
|
30
|
+
* The price a market leg's slippage is applied to: the best ask for a BUY, the best bid for a SELL.
|
|
31
|
+
*
|
|
32
|
+
* The spread is the price of trading now, not slippage, so the limit starts at the touch and the
|
|
33
|
+
* user's slippage tolerance covers the market moving before the fill. Priced from the mid instead,
|
|
34
|
+
* a slippage smaller than half the spread cannot reach the book, and the venue rejects the IOC order.
|
|
35
|
+
* A quote without a usable touch (a mark series, a top of book not yet received, a crossed book)
|
|
36
|
+
* falls back to the mid, exactly as before.
|
|
37
|
+
*
|
|
38
|
+
* A touch already sits on the grid on the far side of the book, so the padded touch rounds inwards
|
|
39
|
+
* and still reaches it; rounding outwards would add a grid step beyond the slippage. The mid sits
|
|
40
|
+
* inside the spread, so a mid fallback keeps rounding outwards to cross.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveMarketReference(quote: MarketQuote, side: Side, pricing: MarketPricing): MarketReference;
|
|
43
|
+
/**
|
|
44
|
+
* The pricing an order's origin gets when it opens or adjusts: a person placing a market order wants it
|
|
45
|
+
* filled, so it reaches the touch; an automated order (a trigger, a schedule slice, an auto-rebalance) is
|
|
46
|
+
* price-sensitive, so it is bounded at the user's slippage from the mid and simply does not fill across a
|
|
47
|
+
* wider spread (#1180). A close never asks: it always reaches the touch, because a close that does not fill
|
|
48
|
+
* leaves the position's risk open, which costs more than the spread. The one exception is a keeper's
|
|
49
|
+
* vault-liquidity close, bounded at the mid (`closePricingFor`): an unfilled cut leaves the position as it was.
|
|
50
|
+
*/
|
|
51
|
+
export declare function marketPricingFor(origin: MarketOrigin): MarketPricing;
|
|
52
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** How a leg's amount becomes a venue quantity. Pure, so every caller sizes a leg by one rule. */
|
|
2
|
+
import type { Connector, InstrumentPrecision, Side } from '@pear-protocol/types';
|
|
3
|
+
/**
|
|
4
|
+
* Apply venue precision, capping constrained USD additions without changing reductions or explicit quantities.
|
|
5
|
+
* A USD amount is sized against `referencePrice`, which the caller chooses per path:
|
|
6
|
+
* - unconstrained: the unpadded mid, never the slippage-padded limit (dividing by the limit would inflate a
|
|
7
|
+
* SELL and shrink a BUY), so the planned notional equals the requested amount at the mid;
|
|
8
|
+
* - constrained (a deadline or any capped leg): the leg's own order limit, so a USD addition that is not
|
|
9
|
+
* reduce-only never exceeds its amount at that limit, the budget the caller enforces. A
|
|
10
|
+
* reduce-only SELL keeps its upward rounding and may exceed it; a cap below the mid grows a SELL's value
|
|
11
|
+
* at the mid, which is the caller's chosen price.
|
|
12
|
+
* The venue minimum is still the caller's check, at the limit: Hyperliquid does not document
|
|
13
|
+
* which price its minimum uses, and at the limit a passing order cannot be refused for it.
|
|
14
|
+
*/
|
|
15
|
+
export declare function preciseQuantityWithMode(connector: Connector, leg: {
|
|
16
|
+
symbol: string;
|
|
17
|
+
side: Side;
|
|
18
|
+
mode: 'QUANTITY' | 'USD';
|
|
19
|
+
amount: string;
|
|
20
|
+
reduceOnly: boolean;
|
|
21
|
+
}, precision: InstrumentPrecision, referencePrice: string, constrainUsdQuantity?: boolean): string;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Every action a leader-signed route may carry. A new route must add its member
|
|
3
3
|
* here, which forces the typed-data switch below to name it at build time.
|
|
4
4
|
*/
|
|
5
|
-
export type LeaderActionName = 'withdraw-to-evm' | 'set-trader' | 'agent-approve' | 'set-unified-account' | 'update-metadata';
|
|
5
|
+
export type LeaderActionName = 'withdraw-to-evm' | 'set-trader' | 'agent-approve' | 'set-unified-account' | 'set-account-mode' | 'update-metadata';
|
|
6
6
|
/**
|
|
7
7
|
* EIP-712 domain for off-chain vault-service auth. `chainId` is pinned to 999
|
|
8
8
|
* (HyperEVM) by the shared contract — signing is offline, so there is no chain
|
|
@@ -78,6 +78,21 @@ export declare const VAULT_ACTION_TYPES: {
|
|
|
78
78
|
readonly type: "uint256";
|
|
79
79
|
}];
|
|
80
80
|
};
|
|
81
|
+
readonly 'set-account-mode': {
|
|
82
|
+
readonly SetAccountMode: readonly [{
|
|
83
|
+
readonly name: "vault";
|
|
84
|
+
readonly type: "address";
|
|
85
|
+
}, {
|
|
86
|
+
readonly name: "mode";
|
|
87
|
+
readonly type: "string";
|
|
88
|
+
}, {
|
|
89
|
+
readonly name: "nonce";
|
|
90
|
+
readonly type: "string";
|
|
91
|
+
}, {
|
|
92
|
+
readonly name: "deadline";
|
|
93
|
+
readonly type: "uint256";
|
|
94
|
+
}];
|
|
95
|
+
};
|
|
81
96
|
readonly 'update-metadata': {
|
|
82
97
|
readonly UpdateVaultMetadata: readonly [{
|
|
83
98
|
readonly name: "vault";
|
|
@@ -149,6 +164,7 @@ export type LeaderActionTypedData = {
|
|
|
149
164
|
name?: string;
|
|
150
165
|
about?: string;
|
|
151
166
|
imageUri?: string;
|
|
167
|
+
mode?: string;
|
|
152
168
|
nonce: string;
|
|
153
169
|
deadline: bigint;
|
|
154
170
|
};
|
|
@@ -161,6 +177,7 @@ export type LeaderActionTypedData = {
|
|
|
161
177
|
* - token = body.tokenId ?? '0', verbatim string (withdraw only)
|
|
162
178
|
* - trader = body.traderAddress, checksummed (set-trader only)
|
|
163
179
|
* - name / about / imageUri = the full display set, verbatim strings; '' means cleared (update-metadata only)
|
|
180
|
+
* - mode = body.mode, verbatim string (set-account-mode only)
|
|
164
181
|
* - nonce = body.nonce (client-generated; salts the signature for idempotency)
|
|
165
182
|
* - deadline = body.deadline (unix seconds) as uint256
|
|
166
183
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pear-protocol/utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.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": "^2.
|
|
30
|
+
"@pear-protocol/types": "^2.27.0",
|
|
31
31
|
"@scure/base": "^2.3.0",
|
|
32
32
|
"bignumber.js": "9.3.1"
|
|
33
33
|
},
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detecting baskets — grouping raw external fills into the orders they were
|
|
3
|
-
* placed as, each combined into per-symbol legs with FIFO queues for splitting
|
|
4
|
-
* across positions.
|
|
5
|
-
*/
|
|
6
|
-
import type { HttpTypes, Side } from '@pear-protocol/types';
|
|
7
|
-
import type { FillPart } from './types';
|
|
8
|
-
/**
|
|
9
|
-
* A detected order: its legs combined by symbol+side. Legs carry per-source FIFO
|
|
10
|
-
* queues, so {@link CombinedLeg.take} can split a single exchange fill across
|
|
11
|
-
* several positions.
|
|
12
|
-
*/
|
|
13
|
-
export declare class Basket {
|
|
14
|
-
readonly legs: CombinedLeg[];
|
|
15
|
-
readonly fills: HttpTypes.UnknownExternalFill[];
|
|
16
|
-
private constructor();
|
|
17
|
-
/** Detect baskets from a fill stream, returned in ascending time order. */
|
|
18
|
-
static detect(fills: HttpTypes.UnknownExternalFill[]): Basket[];
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* One combined symbol+side leg of a basket. Owns the per-source FIFO queue, so
|
|
22
|
-
* taking quantity from the leg (splitting fills across positions) is a method on
|
|
23
|
-
* its own state instead of an outside helper mutating a plain object.
|
|
24
|
-
*/
|
|
25
|
-
export declare class CombinedLeg {
|
|
26
|
-
readonly symbol: string;
|
|
27
|
-
readonly side: Side;
|
|
28
|
-
private _totalSize;
|
|
29
|
-
private readonly queue;
|
|
30
|
-
constructor(symbol: string, side: Side);
|
|
31
|
-
get totalSize(): BigNumber;
|
|
32
|
-
/** Append a fill to this leg's queue and grow the total size. */
|
|
33
|
-
addFill(fill: HttpTypes.UnknownExternalFill): void;
|
|
34
|
-
/**
|
|
35
|
-
* Take `qty` of fill slices from this leg (FIFO across source fills, consumes the queue).
|
|
36
|
-
* Returns the parts taken, which are consumed from the queue.
|
|
37
|
-
*/
|
|
38
|
-
take(qty: BigNumber): FillPart[];
|
|
39
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { HttpTypes } from '@pear-protocol/types';
|
|
2
|
-
import type { BasketRule } from './types';
|
|
3
|
-
/**
|
|
4
|
-
* Float-precision dust floor. Exchange quantities are exact decimal strings and the
|
|
5
|
-
* whole matcher is BigNumber, so a real full close nets to exact zero. This floor only
|
|
6
|
-
* absorbs sub-1e-8 dust that a stray float (anywhere upstream) could leave behind —
|
|
7
|
-
* without it, that dust keeps a position "open", so a later reopen of the same pair
|
|
8
|
-
* merges in and every lifecycle collapses onto one position. NOT a per-instrument
|
|
9
|
-
* tolerance: a genuine partial close (>= 1e-8) stays open, as it should.
|
|
10
|
-
*/
|
|
11
|
-
export declare const DUST: BigNumber;
|
|
12
|
-
/**
|
|
13
|
-
* How long before an open position's creation a fill may still belong to it. Fills can
|
|
14
|
-
* reach sync out of order by seconds across cycles (observed up to ~30s), so a strict
|
|
15
|
-
* cutoff would split one position in two; anything older predates the position and must
|
|
16
|
-
* not be replayed into it (see pear-protocol/backend#1067).
|
|
17
|
-
*/
|
|
18
|
-
export declare const LATE_FILL_GRACE_MS: number;
|
|
19
|
-
export declare const MISSED_HISTORY_FILL_PREFIX = "opening-synthetic-";
|
|
20
|
-
export declare const BASKET_RULES: BasketRule[];
|
|
21
|
-
/**
|
|
22
|
-
* True when `cloid` is a Pear-originated cloid for the given connector.
|
|
23
|
-
* A missing or empty cloid is always false; connector-specific prefix rules
|
|
24
|
-
* are applied via the `PEAR_CLOID_CHECK` table.
|
|
25
|
-
*/
|
|
26
|
-
export declare function isPearCloid(connector: HttpTypes.Connector, cloid: string | null | undefined): boolean;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { Side, SignedDecimalString } from '@pear-protocol/types';
|
|
2
|
-
import type { FillPart } from './types';
|
|
3
|
-
/**
|
|
4
|
-
* Signed per-asset exposure of a position — the running `symbol -> quantity` book
|
|
5
|
-
* (BUY positive, SELL negative). Wraps the raw map so the signed-quantity math, the
|
|
6
|
-
* exact-zero closure rule, and leg-set queries live in one place instead of scattered helpers.
|
|
7
|
-
*
|
|
8
|
-
* Mutable by design: the matcher mutates a working position's exposure in place while
|
|
9
|
-
* replaying fills. Use {@link clone} when a caller needs an independent copy.
|
|
10
|
-
*/
|
|
11
|
-
export declare class Exposure {
|
|
12
|
-
private readonly legs;
|
|
13
|
-
private constructor();
|
|
14
|
-
/** Create an exposure with no legs — the starting state for a new position. */
|
|
15
|
-
static empty(): Exposure;
|
|
16
|
-
/** Build from a wire `Record<symbol, quantity>` (existing DB position exposure). */
|
|
17
|
-
static fromRecord(record: Record<string, string>): Exposure;
|
|
18
|
-
/** Net signed exposure a set of fill parts contributes for `symbol` (BUY +, SELL −). */
|
|
19
|
-
static netFromParts(parts: FillPart[], symbol: string): BigNumber;
|
|
20
|
-
/** Return an independent copy of this exposure so mutations do not alias. */
|
|
21
|
-
clone(): Exposure;
|
|
22
|
-
/** Signed exposure for `symbol` (0 when absent). */
|
|
23
|
-
get(symbol: string): BigNumber;
|
|
24
|
-
/** Apply a signed `delta` to `symbol`, creating the leg if it does not exist. */
|
|
25
|
-
addDelta(symbol: string, delta: BigNumber): void;
|
|
26
|
-
/** Convert a side+quantity pair to a signed delta and forward to `addDelta`. */
|
|
27
|
-
addSigned(symbol: string, quantity: string, side: Side): void;
|
|
28
|
-
/** Active (non-zero) long and short leg sets. */
|
|
29
|
-
activeLegs(): {
|
|
30
|
-
longs: Set<string>;
|
|
31
|
-
shorts: Set<string>;
|
|
32
|
-
};
|
|
33
|
-
/** True when every leg is exactly zero (a fully CLOSED position). */
|
|
34
|
-
isAllZero(): boolean;
|
|
35
|
-
/** All symbols with a leg, including zeroed ones (a closed leg still carries cost basis). */
|
|
36
|
-
symbols(): string[];
|
|
37
|
-
/** Canonical signed-string view for the wire (`+0` for zero). */
|
|
38
|
-
toSigned(): Record<string, SignedDecimalString>;
|
|
39
|
-
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { HttpTypes } from '@pear-protocol/types';
|
|
2
|
-
/** Format one canonical source receipt without changing its quantity or fees. */
|
|
3
|
-
export declare function sourceToApplicableFill(fill: HttpTypes.UnknownExternalFill, connector: HttpTypes.Connector): HttpTypes.ApplicableFill;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Turn a batch of external fills into `applicables` — fill baskets the server
|
|
3
|
-
* find-or-creates into positions.
|
|
4
|
-
*
|
|
5
|
-
* Each fill is processed against the currently open positions, oldest first:
|
|
6
|
-
* 1. reduce opposing positions in creation order,
|
|
7
|
-
* 2. consume each opposing leg before opening its remainder,
|
|
8
|
-
* 3. else merge the leftover into an open position already holding those legs, or open a new one.
|
|
9
|
-
* A fill can be split across several positions when it closes more than one.
|
|
10
|
-
*
|
|
11
|
-
* Once fills are assigned, one synthetic per asset covers any remaining gap to the
|
|
12
|
-
* exchange snapshot (the source of truth). Position exposure is NOT computed here —
|
|
13
|
-
* the server sums the assigned fills; the opening cost basis (initialExposureUSD) is
|
|
14
|
-
* derived per position from its own real fills and carried in `metadata`.
|
|
15
|
-
*
|
|
16
|
-
* Pipeline: snapshot → add missed-history fills → match → align with exchange → assemble.
|
|
17
|
-
* Diff outcomes: discover (new fill), gap (residual vs snapshot), gone (netted to zero)
|
|
18
|
-
*/
|
|
19
|
-
import type { HttpTypes } from '@pear-protocol/types';
|
|
20
|
-
/**
|
|
21
|
-
* Build the `applicables` bucket: baskets of fills (real + synthetic) keyed by a
|
|
22
|
-
* deterministic positionKey, plus creation metadata. Positions are derived by
|
|
23
|
-
* replaying fills through dynamic matching (oldest-first cross-
|
|
24
|
-
* position close, subset-merge of leftover opens), so same-asset exposure
|
|
25
|
-
* consolidates instead of fragmenting per basket symbol-set.
|
|
26
|
-
*/
|
|
27
|
-
export declare function buildApplicables(args: {
|
|
28
|
-
tradeAccountId: string;
|
|
29
|
-
connector: HttpTypes.Connector;
|
|
30
|
-
orphanFills: HttpTypes.UnknownExternalFill[];
|
|
31
|
-
openPositions: HttpTypes.OpenPositionLite[];
|
|
32
|
-
entryBySymbol: Record<string, string>;
|
|
33
|
-
exchangePositions: HttpTypes.ExternalPosition[];
|
|
34
|
-
}): {
|
|
35
|
-
fills: HttpTypes.ApplicableFill[];
|
|
36
|
-
applicables: HttpTypes.ApplicablePosition[];
|
|
37
|
-
};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { HttpTypes } from '@pear-protocol/types';
|
|
2
|
-
import type { PearHoldings } from './snapshot';
|
|
3
|
-
import type { MatchedPosition } from './types';
|
|
4
|
-
/**
|
|
5
|
-
* Replay external fills to decide which position each fill slice belongs to. Flow :
|
|
6
|
-
* - applyCloses reduces holders (the oldest eligible position first),
|
|
7
|
-
* - resolveLeftoverTarget merges into a position whose FULL leg set (active and
|
|
8
|
-
* dormant) is exactly the leftover's, else opens new (same-shape ADDs consolidate here).
|
|
9
|
-
* The asset SET of a position never grows, so positionKey == key(opening symbols).
|
|
10
|
-
*
|
|
11
|
-
* Every phase only sees positions that already existed when the basket happened: a
|
|
12
|
-
* basket older than an open position's creation (beyond LATE_FILL_GRACE_MS) cannot
|
|
13
|
-
* close, reduce or merge into it, so a late-fetched earlier lifecycle is not bound to a
|
|
14
|
-
* position opened after it (pear-protocol/backend#1067).
|
|
15
|
-
*/
|
|
16
|
-
export declare function matchPositions(args: {
|
|
17
|
-
tradeAccountId: string;
|
|
18
|
-
orphanFills: HttpTypes.UnknownExternalFill[];
|
|
19
|
-
pear: PearHoldings;
|
|
20
|
-
}): MatchedPosition[];
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builds the final `ApplicablePosition` output — folds assigned synthetics into
|
|
3
|
-
* exposure, derives status/origin/metadata.
|
|
4
|
-
*/
|
|
5
|
-
import type { HttpTypes } from '@pear-protocol/types';
|
|
6
|
-
import type { MatchedPosition, NeededSynthetic } from './types';
|
|
7
|
-
/**
|
|
8
|
-
* Assemble canonical receipts once and position assignments separately, dropping
|
|
9
|
-
* unchanged positions that produced no fills.
|
|
10
|
-
*/
|
|
11
|
-
export declare function assembleApplicables(args: {
|
|
12
|
-
positions: MatchedPosition[];
|
|
13
|
-
connector: HttpTypes.Connector;
|
|
14
|
-
synthByPosition: Map<MatchedPosition, NeededSynthetic[]>;
|
|
15
|
-
now: number;
|
|
16
|
-
}): {
|
|
17
|
-
fills: HttpTypes.ApplicableFill[];
|
|
18
|
-
applicables: HttpTypes.ApplicablePosition[];
|
|
19
|
-
};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type { HttpTypes } from '@pear-protocol/types';
|
|
2
|
-
/**
|
|
3
|
-
* The exchange position snapshot — the enforced source of truth for exposure. Wraps the
|
|
4
|
-
* per-symbol lookup so callers ask `snapshot.signedSize(symbol)` instead of threading a
|
|
5
|
-
* raw map plus a sign helper.
|
|
6
|
-
*/
|
|
7
|
-
export declare class ExchangeSnapshot {
|
|
8
|
-
private readonly bySymbol;
|
|
9
|
-
private constructor();
|
|
10
|
-
/** Index `positions` by symbol for O(1) lookup. */
|
|
11
|
-
static from(positions: HttpTypes.ExternalPosition[]): ExchangeSnapshot;
|
|
12
|
-
/** Raw exchange position for `symbol`, or `undefined` when the exchange holds nothing. */
|
|
13
|
-
get(symbol: string): HttpTypes.ExternalPosition | undefined;
|
|
14
|
-
/** Signed exchange exposure for `symbol` (0 when the exchange has no position). */
|
|
15
|
-
signedSize(symbol: string): BigNumber;
|
|
16
|
-
/** All symbols the exchange currently reports a position for. */
|
|
17
|
-
symbols(): string[];
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* What Pear's DB currently holds for one connector — the open positions, filtered
|
|
21
|
-
* once, plus the per-symbol views the synthetic steps need.
|
|
22
|
-
*/
|
|
23
|
-
export declare class PearHoldings {
|
|
24
|
-
private readonly filtered;
|
|
25
|
-
private constructor();
|
|
26
|
-
/** Filter `openPositions` to the given connector and wrap. */
|
|
27
|
-
static from(args: {
|
|
28
|
-
openPositions: HttpTypes.OpenPositionLite[];
|
|
29
|
-
connector: HttpTypes.Connector;
|
|
30
|
-
}): PearHoldings;
|
|
31
|
-
/** The open Pear positions for this connector. */
|
|
32
|
-
positions(): HttpTypes.OpenPositionLite[];
|
|
33
|
-
/** Sum existing open Pear exposure per symbol. */
|
|
34
|
-
totalBySymbol(): Map<string, BigNumber>;
|
|
35
|
-
/**
|
|
36
|
-
* Which open DB position holds each active asset — the fallback target for a
|
|
37
|
-
* synthetic when no matched position already carries that asset. First open
|
|
38
|
-
* position with a non-zero leg wins per symbol.
|
|
39
|
-
*/
|
|
40
|
-
positionBySymbol(): Map<string, HttpTypes.OpenPositionLite>;
|
|
41
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Everything synthetic lives here — the missed-history fills added before matching,
|
|
3
|
-
* the per-asset check against the exchange afterwards, and the assignment of each
|
|
4
|
-
* needed synthetic to one position.
|
|
5
|
-
*/
|
|
6
|
-
import type { HttpTypes, Side } from '@pear-protocol/types';
|
|
7
|
-
import type { ExchangeSnapshot, PearHoldings } from './snapshot';
|
|
8
|
-
import type { MatchedPosition, NeededSynthetic } from './types';
|
|
9
|
-
/** True when an external fill was minted by the missed-history pass (its id carries the prefix). */
|
|
10
|
-
export declare function isMissedHistoryFill(fill: HttpTypes.UnknownExternalFill): boolean;
|
|
11
|
-
/**
|
|
12
|
-
* The exchange only returns recent fills — anything older is invisible to us.
|
|
13
|
-
* This adds stand-in (synthetic) fills covering that missed history, so the
|
|
14
|
-
* matcher replays the correct running exposure from the start. Returns the
|
|
15
|
-
* complete fill list, oldest first.
|
|
16
|
-
*/
|
|
17
|
-
export declare function addFillsForMissedHistory(args: {
|
|
18
|
-
orphanFills: HttpTypes.UnknownExternalFill[];
|
|
19
|
-
pear: PearHoldings;
|
|
20
|
-
connector: HttpTypes.Connector;
|
|
21
|
-
entryBySymbol: Record<string, string>;
|
|
22
|
-
snapshot: ExchangeSnapshot;
|
|
23
|
-
}): HttpTypes.UnknownExternalFill[];
|
|
24
|
-
/**
|
|
25
|
-
* Compare what we now hold per asset with what the exchange says, and create one
|
|
26
|
-
* correcting synthetic per asset that is off — assigned to exactly one position
|
|
27
|
-
* (creating the position if none holds that asset). This is the safety net for
|
|
28
|
-
* residual drift, the only discovery path when no fills were fetched at all
|
|
29
|
-
* (e.g. first sync of an account whose fill history is beyond the fetch window),
|
|
30
|
-
* and the only close path for positions the exchange dropped without any fill
|
|
31
|
-
* activity this cycle. Returns the position list (extended with any created
|
|
32
|
-
* positions) plus the synthetics per position — the input array is never mutated.
|
|
33
|
-
*/
|
|
34
|
-
export declare function alignPositionsWithExchange(args: {
|
|
35
|
-
positions: MatchedPosition[];
|
|
36
|
-
pear: PearHoldings;
|
|
37
|
-
snapshot: ExchangeSnapshot;
|
|
38
|
-
tradeAccountId: string;
|
|
39
|
-
}): {
|
|
40
|
-
positions: MatchedPosition[];
|
|
41
|
-
synthByPosition: Map<MatchedPosition, NeededSynthetic[]>;
|
|
42
|
-
};
|
|
43
|
-
/** Build the content-addressed synthetic fill for a position lifecycle/asset/side. */
|
|
44
|
-
export declare function buildSyntheticFill(args: {
|
|
45
|
-
positionKey: string;
|
|
46
|
-
lifecycleStartedAt: number | null;
|
|
47
|
-
connector: HttpTypes.Connector;
|
|
48
|
-
symbol: string;
|
|
49
|
-
side: Side;
|
|
50
|
-
quantity: string;
|
|
51
|
-
timestamp: number;
|
|
52
|
-
closedReason?: HttpTypes.PositionClosedReason;
|
|
53
|
-
}): HttpTypes.ApplicableFill;
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import type { HttpTypes, Side } from '@pear-protocol/types';
|
|
2
|
-
import type { CombinedLeg } from './basket';
|
|
3
|
-
import type { Exposure } from './exposure';
|
|
4
|
-
/**
|
|
5
|
-
* A basket-detection rule. Fills for which `matches` is true AND that produce the same
|
|
6
|
-
* `key` are grouped into one basket. Rules are tried in order; the first match wins. A
|
|
7
|
-
* fill matching no rule falls back to the default (its own exchangeFillId), so it never
|
|
8
|
-
* groups with anything else.
|
|
9
|
-
*/
|
|
10
|
-
export type BasketRule = {
|
|
11
|
-
matches: (fill: HttpTypes.UnknownExternalFill) => boolean;
|
|
12
|
-
key: (fill: HttpTypes.UnknownExternalFill) => string;
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* A slice of a source external fill assigned to one virtual position. A single
|
|
16
|
-
* external fill can be split across positions (e.g. a SELL that closes two open
|
|
17
|
-
* longs), so each slice tracks its source for fill-id and fee derivation.
|
|
18
|
-
*/
|
|
19
|
-
export type FillPart = {
|
|
20
|
-
source: HttpTypes.UnknownExternalFill;
|
|
21
|
-
quantity: string;
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* A working position the matcher builds while replaying fills.
|
|
25
|
-
* Mutable mid-replay — its `exposure` is updated in place as fills land.
|
|
26
|
-
*/
|
|
27
|
-
export type WorkingPosition = {
|
|
28
|
-
positionKey: string;
|
|
29
|
-
createdAt: number;
|
|
30
|
-
orderId: string;
|
|
31
|
-
exposure: Exposure;
|
|
32
|
-
parts: FillPart[];
|
|
33
|
-
closed: boolean;
|
|
34
|
-
/** Origin of the seeded existing position; undefined for positions opened mid-replay. */
|
|
35
|
-
existingOrigin?: HttpTypes.PositionOrigin;
|
|
36
|
-
/**
|
|
37
|
-
* Earliest basket time (ms) this position may take fills from: its creation minus the
|
|
38
|
-
* late-fill grace for a seeded position, -Infinity for one opened mid-replay.
|
|
39
|
-
*/
|
|
40
|
-
liveFrom: number;
|
|
41
|
-
};
|
|
42
|
-
/** A matched position that received fill slices, ready to become an applicable. */
|
|
43
|
-
export type MatchedPosition = {
|
|
44
|
-
positionKey: string;
|
|
45
|
-
parts: FillPart[];
|
|
46
|
-
/** Lifecycle ended within this batch (exposure returned to zero). */
|
|
47
|
-
closed: boolean;
|
|
48
|
-
/**
|
|
49
|
-
* Exposure after replaying real fills (existing position + applied part deltas).
|
|
50
|
-
* Does NOT yet include the correcting synthetics from the align step — assembly
|
|
51
|
-
* folds those in to get the final exposure.
|
|
52
|
-
*/
|
|
53
|
-
exposure: Exposure;
|
|
54
|
-
/** Origin of the seeded existing position; undefined when this applicable creates a position. */
|
|
55
|
-
existingOrigin?: HttpTypes.PositionOrigin;
|
|
56
|
-
/**
|
|
57
|
-
* When this lifecycle of the position key began (ms): the stored creation time of a seeded
|
|
58
|
-
* position, else the earliest fill this replay assigned to it. Null for a position the align
|
|
59
|
-
* step mints from the snapshot alone — no fill or stored row dates that lifecycle.
|
|
60
|
-
*/
|
|
61
|
-
lifecycleStartedAt: number | null;
|
|
62
|
-
};
|
|
63
|
-
/** One combined leg that could not be closed against existing exposure — an open. */
|
|
64
|
-
export type Leftover = {
|
|
65
|
-
side: Side;
|
|
66
|
-
qty: BigNumber;
|
|
67
|
-
leg: CombinedLeg;
|
|
68
|
-
};
|
|
69
|
-
/** A per-asset difference vs the exchange: 'gone' = exchange no longer has the asset, 'gap' = quantities differ. */
|
|
70
|
-
export type ExposureGap = {
|
|
71
|
-
kind: 'gone' | 'gap';
|
|
72
|
-
symbol: string;
|
|
73
|
-
side: Side;
|
|
74
|
-
quantity: string;
|
|
75
|
-
};
|
|
76
|
-
/** A synthetic fill the server needs to mint for an asset, plus where to attach it. */
|
|
77
|
-
export type NeededSynthetic = {
|
|
78
|
-
symbol: string;
|
|
79
|
-
side: Side;
|
|
80
|
-
quantity: string;
|
|
81
|
-
closedReason?: HttpTypes.PositionClosedReason;
|
|
82
|
-
};
|
package/dist/sync/compute.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { type HttpTypes } from '@pear-protocol/types';
|
|
2
|
-
import type { ComputeSyncPayloadArgs, ComputeSyncPayloadResult } from './types';
|
|
3
|
-
/**
|
|
4
|
-
* Derive the full sync payload from raw exchange data and local state.
|
|
5
|
-
*
|
|
6
|
-
* Async without internal awaits — the Promise signature is part of the public
|
|
7
|
-
* API consumed by exchanges-sdk, so it stays.
|
|
8
|
-
*/
|
|
9
|
-
export declare function computeSyncPayload(args: ComputeSyncPayloadArgs): Promise<ComputeSyncPayloadResult | null>;
|
|
10
|
-
/**
|
|
11
|
-
* Validate, normalise, deduplicate, and filter raw external fills.
|
|
12
|
-
* Fills with invalid ids/quantities/timestamps or already-confirmed ids are dropped.
|
|
13
|
-
* Duplicates (same exchangeFillId) keep the earliest timestamp; result is ascending-sorted.
|
|
14
|
-
*/
|
|
15
|
-
export declare function _prepareExternalFills(externalFills: HttpTypes.UnknownExternalFill[], confirmedExchangeFillIdsInput: string[]): HttpTypes.UnknownExternalFill[];
|
|
16
|
-
/**
|
|
17
|
-
* Drop external fills whose cloid is claimed by a provisional fill we own.
|
|
18
|
-
* The server-side syncer owns confirming those fills; the client applies orphans only.
|
|
19
|
-
*/
|
|
20
|
-
export declare function _excludeClaimedFills(provisionalFills: HttpTypes.ProvisionalFill[], externalFills: HttpTypes.UnknownExternalFill[]): HttpTypes.UnknownExternalFill[];
|
|
21
|
-
/** Trim a cloid; empty or whitespace-only collapses to undefined. */
|
|
22
|
-
export declare function _normalizeCloid(cloid?: string | null): string | undefined;
|
|
23
|
-
/** Normalise an exchange fill id: trim + lowercase; absent or blank → undefined. */
|
|
24
|
-
export declare function _normalizeExchangeFillId(exchangeFillId?: string | null): string | undefined;
|
|
25
|
-
/** Rejects scientific-notation quantities (e.g. 1e-8) that cannot be safely parsed as fills. */
|
|
26
|
-
export declare function _isValidExternalQuantity(quantity: string): boolean;
|
|
27
|
-
/** Returns true only when the timestamp is a non-negative finite number no further than clock skew ahead of now. */
|
|
28
|
-
export declare function _isValidExternalTimestamp(timestamp: number): boolean;
|
package/dist/sync/cutoff.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* First-sync history cutoff selection.
|
|
3
|
-
*
|
|
4
|
-
* An account's first sync fetches a fill history that can reach back through many
|
|
5
|
-
* finished lifecycles; replaying all of it would resurrect long-closed positions.
|
|
6
|
-
* This module replays the history BACKWARD from the exchange snapshot (the source
|
|
7
|
-
* of truth for current exposure) and picks the most recent boundary where the
|
|
8
|
-
* account was EXACTLY flat, so everything before it can be dropped. Seeding from
|
|
9
|
-
* the snapshot means a truncated history — a position opened before the fetched
|
|
10
|
-
* window — leaves a residual at every boundary and never produces a fake cutoff.
|
|
11
|
-
*/
|
|
12
|
-
import type { HttpTypes } from '@pear-protocol/types';
|
|
13
|
-
/** Trim decision: keep `fills.slice(keepFromIndex)`; `fills.length` trims everything. */
|
|
14
|
-
export type FirstSyncCutoff = {
|
|
15
|
-
keepFromIndex: number;
|
|
16
|
-
};
|
|
17
|
-
/**
|
|
18
|
-
* Selects the first-sync cutoff boundary from an ascending-sorted, prepared fill
|
|
19
|
-
* history and the current exchange snapshot.
|
|
20
|
-
*
|
|
21
|
-
* Boundary i is the account state BEFORE fills[i] (boundary fills.length is the
|
|
22
|
-
* snapshot itself). A boundary qualifies when every leg of the backward-replayed
|
|
23
|
-
* book is exactly zero (BigNumber-exact — the DUST tolerance does NOT apply) and
|
|
24
|
-
* the boundary is not inside a same-millisecond fill group (Hyperliquid merges
|
|
25
|
-
* feeds whose same-ms order is not execution order). On Hyperliquid the cutoff is
|
|
26
|
-
* additionally capped at the first Pear-placed fill so it is always kept.
|
|
27
|
-
*
|
|
28
|
-
* Returns null to keep everything: empty history, no qualifying boundary, or the
|
|
29
|
-
* qualifying boundary is 0 (a cutoff of 0 trims nothing, so it canonicalizes to null).
|
|
30
|
-
*/
|
|
31
|
-
export declare function selectFirstSyncCutoff(args: {
|
|
32
|
-
fills: HttpTypes.UnknownExternalFill[];
|
|
33
|
-
exchangePositions: HttpTypes.ExternalPosition[];
|
|
34
|
-
connector: HttpTypes.Connector;
|
|
35
|
-
}): FirstSyncCutoff | null;
|
package/dist/sync/index.d.ts
DELETED
package/dist/sync/types.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { HttpTypes } from '@pear-protocol/types';
|
|
2
|
-
export type ComputeSyncPayloadArgs = {
|
|
3
|
-
tradeAccountId: string;
|
|
4
|
-
connector: HttpTypes.Connector;
|
|
5
|
-
openPositions: HttpTypes.OpenPositionLite[];
|
|
6
|
-
entryBySymbol: Record<string, string>;
|
|
7
|
-
provisionalFills: HttpTypes.ProvisionalFill[];
|
|
8
|
-
confirmedExchangeFillIds: string[];
|
|
9
|
-
externalFills: HttpTypes.UnknownExternalFill[];
|
|
10
|
-
exchangePositions: HttpTypes.ExternalPosition[];
|
|
11
|
-
neverSynced: boolean;
|
|
12
|
-
};
|
|
13
|
-
export type ComputeSyncPayloadResult = {
|
|
14
|
-
fills: HttpTypes.ApplicableFill[];
|
|
15
|
-
applicables: HttpTypes.ApplicablePosition[];
|
|
16
|
-
exchangePositions: HttpTypes.ExternalPosition[];
|
|
17
|
-
meta: {
|
|
18
|
-
externalMaxTs: number | null;
|
|
19
|
-
};
|
|
20
|
-
firstSyncTrim: {
|
|
21
|
-
cutoffTs: number | null;
|
|
22
|
-
trimmedCount: number;
|
|
23
|
-
trimmedMaxTs: number;
|
|
24
|
-
} | null;
|
|
25
|
-
};
|