@pear-protocol/utils 0.1.0 → 0.3.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 +1 -0
- package/dist/index.js +76 -3
- package/dist/position/compute.d.ts +16 -0
- package/dist/proxy.d.ts +34 -0
- package/dist/proxy.test.d.ts +1 -0
- package/dist/sync/index.d.ts +1 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from './math/index';
|
|
|
6
6
|
export * from './position/index';
|
|
7
7
|
export * from './precise-price';
|
|
8
8
|
export * from './precise-quantity';
|
|
9
|
+
export * from './proxy';
|
|
9
10
|
export * from './sync/index';
|
|
10
11
|
export * from './validate-leverage';
|
|
11
12
|
export * from './validate-quantity';
|
package/dist/index.js
CHANGED
|
@@ -411,6 +411,37 @@ function computePositionGrossRealizedPnL(assetPnLs) {
|
|
|
411
411
|
function computePositionNetRealizedPnL(assetPnLs) {
|
|
412
412
|
return assetPnLs.reduce((acc, asset) => acc.plus(asset.netPnl), ZERO);
|
|
413
413
|
}
|
|
414
|
+
function computePositionEntryPrices(exposureMap, fills) {
|
|
415
|
+
const entryPrices = {};
|
|
416
|
+
for (const instrumentId of Object.keys(exposureMap)) {
|
|
417
|
+
const assetFills = fills.filter((fill) => fill.symbol === instrumentId);
|
|
418
|
+
const entryPrice = computeEntryPriceForAsset(assetFills);
|
|
419
|
+
if (isZero(entryPrice)) continue;
|
|
420
|
+
entryPrices[instrumentId] = entryPrice.toFixed();
|
|
421
|
+
}
|
|
422
|
+
return entryPrices;
|
|
423
|
+
}
|
|
424
|
+
function computeUnrealizedPositionStateFromEntryPrices(exposureMap, entryPrices, priceMap) {
|
|
425
|
+
const assetUPnLs = [];
|
|
426
|
+
for (const [id, signedQuantityString] of Object.entries(exposureMap)) {
|
|
427
|
+
const currentPriceString = priceMap[id];
|
|
428
|
+
const entryPriceString = entryPrices[id];
|
|
429
|
+
if (!currentPriceString || !entryPriceString) continue;
|
|
430
|
+
const signedQuantity = parse(signedQuantityString);
|
|
431
|
+
if (isZero(signedQuantity)) continue;
|
|
432
|
+
const currentPrice = parse(currentPriceString);
|
|
433
|
+
const entryPrice2 = parse(entryPriceString);
|
|
434
|
+
const upnl2 = multiply(signedQuantity, subtract(currentPrice, entryPrice2));
|
|
435
|
+
assetUPnLs.push({ id, signedQuantity, entryPrice: entryPrice2, currentPrice, upnl: upnl2 });
|
|
436
|
+
}
|
|
437
|
+
const entryPrice = computePositionEntryPrice(assetUPnLs);
|
|
438
|
+
const entryNotional = computePositionEntryNotional(assetUPnLs);
|
|
439
|
+
const markPrice = computePositionCurrentPrice(assetUPnLs);
|
|
440
|
+
const markNotional = computePositionMarkNotional(assetUPnLs);
|
|
441
|
+
const upnl = computePositionUnrealizedPnL(assetUPnLs);
|
|
442
|
+
const upnlBips = computePositionUnrealizedPnLBips(upnl, entryNotional);
|
|
443
|
+
return { assetUPnLs, entryPrice, entryNotional, markPrice, markNotional, upnl, upnlBips };
|
|
444
|
+
}
|
|
414
445
|
function generateDeterministicPositionKey(input) {
|
|
415
446
|
const symbols = [...input.symbols].sort((a, b) => a.localeCompare(b));
|
|
416
447
|
const payload = JSON.stringify({
|
|
@@ -512,6 +543,45 @@ function preciseStepGridQuantity(raw, precision, side) {
|
|
|
512
543
|
return final.toFixed();
|
|
513
544
|
}
|
|
514
545
|
|
|
546
|
+
// src/proxy.ts
|
|
547
|
+
var activeOrigin = null;
|
|
548
|
+
function configureProxy(config) {
|
|
549
|
+
if (!config?.enabled || !config.url) {
|
|
550
|
+
activeOrigin = null;
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
activeOrigin = new URL(config.url).origin;
|
|
555
|
+
} catch {
|
|
556
|
+
activeOrigin = null;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function toProxyUrl(target, origin) {
|
|
560
|
+
const proxied = new URL(origin);
|
|
561
|
+
proxied.pathname = target.pathname;
|
|
562
|
+
proxied.search = target.search;
|
|
563
|
+
return proxied.toString();
|
|
564
|
+
}
|
|
565
|
+
async function withProxyFallback(proxied, direct) {
|
|
566
|
+
try {
|
|
567
|
+
const response = await proxied();
|
|
568
|
+
if (response.status < 500) return response;
|
|
569
|
+
} catch {
|
|
570
|
+
}
|
|
571
|
+
return direct();
|
|
572
|
+
}
|
|
573
|
+
function proxyFetch(input, init) {
|
|
574
|
+
const origin = activeOrigin;
|
|
575
|
+
if (!origin) return fetch(input, init);
|
|
576
|
+
const target = new URL(input);
|
|
577
|
+
const headers = new Headers(init?.headers);
|
|
578
|
+
headers.set("X-Proxy-Target", target.host);
|
|
579
|
+
return withProxyFallback(
|
|
580
|
+
() => fetch(toProxyUrl(target, origin), { ...init, headers }),
|
|
581
|
+
() => fetch(input, init)
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
|
|
515
585
|
// src/sync/applicables/constants.ts
|
|
516
586
|
var DUST = parse("0.00000001");
|
|
517
587
|
var MISSED_HISTORY_FILL_PREFIX = "opening-synthetic-";
|
|
@@ -750,7 +820,10 @@ var WorkingBook = class _WorkingBook {
|
|
|
750
820
|
const book = new _WorkingBook(tradeAccountId);
|
|
751
821
|
for (const p of openPositions) {
|
|
752
822
|
const position = {
|
|
753
|
-
|
|
823
|
+
// Trust the position's own stored key — never recompute it from exposure.
|
|
824
|
+
// A partial fill on the original order leaves exposure short of the
|
|
825
|
+
// symbols the key was hashed from, so a recomputed key would never match.
|
|
826
|
+
positionKey: p.positionKey,
|
|
754
827
|
exposure: Exposure.fromRecord(p.exposure),
|
|
755
828
|
parts: [],
|
|
756
829
|
closed: false,
|
|
@@ -1036,7 +1109,7 @@ function assignSyntheticsToPositions(args) {
|
|
|
1036
1109
|
let target = positions.find((pos) => !pos.closed && pos.parts.some((part) => part.source.symbol === symbol));
|
|
1037
1110
|
if (!target) {
|
|
1038
1111
|
const existing = existingBySymbol.get(symbol);
|
|
1039
|
-
const positionKey = existing ?
|
|
1112
|
+
const positionKey = existing ? existing.positionKey : keyOf([symbol]);
|
|
1040
1113
|
target = positions.find((pos) => pos.positionKey === positionKey) ?? created.find((pos) => pos.positionKey === positionKey);
|
|
1041
1114
|
if (!target) {
|
|
1042
1115
|
target = existing ? (
|
|
@@ -1677,4 +1750,4 @@ function validateQuantity(quantity, price, precision) {
|
|
|
1677
1750
|
return true;
|
|
1678
1751
|
}
|
|
1679
1752
|
|
|
1680
|
-
export { BIPS, ZERO, abs, add, baseToContracts, compareValue, computeAssetEntryNotional, computeAssetGrossRealizedPnL, computeAssetRealizedPnLs, computeAssetUndeterminedSizes, computeAssetUnrealizedPnL, computeAssetUnrealizedPnLs, computeBasketWeightedRatioV1, computeEntryPriceForAsset, computeRealizedPnlBySymbol, computeRealizedPnlFromFills, computeRealizedPositionState, computeSyncPayload, computeUnrealizedPositionState, contractsToBase, countDecimals, divide, exponentiate, generateDeterministicPositionKey, isEqualTo, isFiniteDecimal, isGreaterThan, isGreaterThanOrEqual, isLessThan, isLessThanOrEqual, isNegative, isPositive, isZero, min, multiply, negate, parse, precisePrice, preciseQuantity, sideSign, sign, signed, signedBySide, subtract, toDecimalString, validateLeverage, validateQuantity };
|
|
1753
|
+
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 };
|
|
@@ -19,4 +19,20 @@ export declare function computeUnrealizedPositionState(exposureMap: ExposureMap,
|
|
|
19
19
|
* @returns The realized position state.
|
|
20
20
|
*/
|
|
21
21
|
export declare function computeRealizedPositionState(fills: Fill[]): RealizedPositionState;
|
|
22
|
+
/**
|
|
23
|
+
* Weighted-average entry (cost-basis) price per instrument in a position, derived purely from
|
|
24
|
+
* fills — no mark price needed. This is the authoritative source for a position's entry price;
|
|
25
|
+
* callers that already have it (e.g. from a position API response) should NOT re-derive it a
|
|
26
|
+
* second time by replaying fills themselves.
|
|
27
|
+
*
|
|
28
|
+
* Formula (per instrument): cost-basis average via computeEntryPriceForAsset.
|
|
29
|
+
*/
|
|
30
|
+
export declare function computePositionEntryPrices(exposureMap: ExposureMap, fills: Fill[]): Record<InstrumentId, string>;
|
|
31
|
+
/**
|
|
32
|
+
* Unrealized position state derived from ALREADY-KNOWN entry prices (e.g. the entryPrices field
|
|
33
|
+
* on a position API response) instead of replaying the fills ledger. Prefer this over
|
|
34
|
+
* computeUnrealizedPositionState whenever the caller already has entry prices — it needs no
|
|
35
|
+
* fills fetch at all, only a live mark-price map.
|
|
36
|
+
*/
|
|
37
|
+
export declare function computeUnrealizedPositionStateFromEntryPrices(exposureMap: ExposureMap, entryPrices: Record<InstrumentId, string>, priceMap: PriceMap): UnrealizedPositionState;
|
|
22
38
|
export {};
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional forward-proxy for outbound SDK HTTP calls.
|
|
3
|
+
*
|
|
4
|
+
* When enabled, every request is re-pointed at the proxy's origin with the original destination
|
|
5
|
+
* carried in the `X-Proxy-Target` header. The proxy restores method, path, query, body, and headers
|
|
6
|
+
* before forwarding upstream (see pear-proxy). Off by default — a transparent passthrough to the
|
|
7
|
+
* global `fetch`.
|
|
8
|
+
*
|
|
9
|
+
* Config is INJECTED by the caller via the SDK constructors (`configureProxy`), never read from the
|
|
10
|
+
* environment: the SDKs also run in the browser, where `process.env` does not exist.
|
|
11
|
+
*
|
|
12
|
+
* Fallback: the proxy is best-effort. If the proxied call fails for any reason other than a genuine
|
|
13
|
+
* 4xx response — a network/transport error, or a proxy-side 5xx such as the load-shed 503 or the
|
|
14
|
+
* request-timeout 504 — the request is retried once directly against the original target, un-proxied.
|
|
15
|
+
* A 4xx passes straight through, since that is the upstream's own answer and a direct retry would
|
|
16
|
+
* only repeat it.
|
|
17
|
+
*/
|
|
18
|
+
export type ProxyConfig = {
|
|
19
|
+
/** Master switch. When false (or the URL is missing), `proxyFetch` is a transparent passthrough. */
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
/** Proxy base URL; only its origin (scheme + host + port) is used. */
|
|
22
|
+
url?: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Set (or clear) the forward-proxy used by every `proxyFetch` call. Called from the SDK
|
|
26
|
+
* constructors with the caller-supplied config. Passing a disabled/absent config turns it off.
|
|
27
|
+
*/
|
|
28
|
+
export declare function configureProxy(config: ProxyConfig | null | undefined): void;
|
|
29
|
+
/**
|
|
30
|
+
* `fetch` for a string/URL request that routes through the configured proxy when enabled (with a
|
|
31
|
+
* direct-call fallback); otherwise calls the global `fetch` unchanged. All SDK call sites pass a
|
|
32
|
+
* URL + init, so a `Request`-object input is intentionally not supported.
|
|
33
|
+
*/
|
|
34
|
+
export declare function proxyFetch(input: string | URL, init?: RequestInit): Promise<Response>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/sync/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pear-protocol/utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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.14.1",
|
|
31
31
|
"bignumber.js": "9.3.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|