@pear-protocol/utils 0.5.3 → 0.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/basket/basket.types.d.ts +104 -1
- package/dist/basket/index.d.ts +2 -0
- package/dist/basket/max-size.d.ts +40 -0
- package/dist/index.js +182 -1
- package/package.json +1 -1
|
@@ -1,6 +1,109 @@
|
|
|
1
|
-
import type { InstrumentId, Side } from '@pear-protocol/types';
|
|
1
|
+
import type { HttpTypes, InstrumentId, Side } from '@pear-protocol/types';
|
|
2
2
|
export type BasketLeg = {
|
|
3
3
|
symbol: InstrumentId;
|
|
4
4
|
side: Side;
|
|
5
5
|
weight: number;
|
|
6
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
* Mirrors the shipped basket model (`BasketItem` in the frontend): unsigned `weight`
|
|
9
|
+
* with `side` alongside, weights summing to 100. Sizing ignores `side` — margin
|
|
10
|
+
* requirement does not depend on direction — and normalises by Σweight.
|
|
11
|
+
*/
|
|
12
|
+
export type MaxBasketSizeLeg = {
|
|
13
|
+
asset: InstrumentId;
|
|
14
|
+
side: Side;
|
|
15
|
+
/** Unsigned percentage. The set sums to 100. */
|
|
16
|
+
weight: number;
|
|
17
|
+
};
|
|
18
|
+
export type MaxBasketSizeInput = {
|
|
19
|
+
legs: MaxBasketSizeLeg[];
|
|
20
|
+
/** What the caller is requesting for the whole basket, before each leg's own venue max caps it. */
|
|
21
|
+
requestedLeverage: number;
|
|
22
|
+
};
|
|
23
|
+
/** Same shape as `MaxBasketSizeLeg`, but leverage is requested per leg instead of once for the basket. */
|
|
24
|
+
export type MaxBasketSizePerAssetLeverageLeg = MaxBasketSizeLeg & {
|
|
25
|
+
/** What the caller is requesting for THIS leg, before the venue max caps it. */
|
|
26
|
+
requestedLeverage: number;
|
|
27
|
+
};
|
|
28
|
+
export type MaxBasketSizePerAssetLeverageInput = {
|
|
29
|
+
legs: MaxBasketSizePerAssetLeverageLeg[];
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Either shape a caller can size a basket from, tagged by how leverage is requested —
|
|
33
|
+
* once for the whole basket, or once per leg. `sizeBasketByLeverageMode` switches on it.
|
|
34
|
+
*/
|
|
35
|
+
export type MaxBasketSizeRequest = ({
|
|
36
|
+
leverageMode: 'shared';
|
|
37
|
+
} & MaxBasketSizeInput) | ({
|
|
38
|
+
leverageMode: 'perAsset';
|
|
39
|
+
} & MaxBasketSizePerAssetLeverageInput);
|
|
40
|
+
export type MaxBasketSizeResult = {
|
|
41
|
+
/**
|
|
42
|
+
* The whole basket's max GROSS notional in USD — Σ leg notional, not per-side and not per-asset.
|
|
43
|
+
* The basket is atomic, so this is one ceiling for the entire structure, not a per-leg maximum.
|
|
44
|
+
* Null when a leg names an asset the caller's instrument catalog does not list, or names a quote
|
|
45
|
+
* currency the caller's `balanceByQuote` map does not fund — either gap leaves the basket unsized.
|
|
46
|
+
*/
|
|
47
|
+
maxNotional: string | null;
|
|
48
|
+
/**
|
|
49
|
+
* The margin `maxNotional` spends, summed across every pool it draws from. For a single budget
|
|
50
|
+
* (`computeMaxBasketSize`) this is always the whole budget supplied, since that is exactly the
|
|
51
|
+
* point at which the basket stops growing. Across several quote-currency pools
|
|
52
|
+
* (`computeMultiQuoteBasketSize`) it is the total actually drawn — the full balance of whichever
|
|
53
|
+
* pool is the tightest constraint, but not necessarily the others. `null` on the same terms as
|
|
54
|
+
* `maxNotional`.
|
|
55
|
+
*/
|
|
56
|
+
marginRequired: string | null;
|
|
57
|
+
/**
|
|
58
|
+
* `marginRequired` broken out per leg, in the caller's own leg order — `null` when a leg carries
|
|
59
|
+
* no identity to report it under (`computeMaxBasketSize`/`computeMultiQuoteBasketSize` called
|
|
60
|
+
* directly on anonymous `CappedBasketLeg`s), never partial: every leg reports or none does.
|
|
61
|
+
*/
|
|
62
|
+
legs: LegMarginRequired[] | null;
|
|
63
|
+
/**
|
|
64
|
+
* `marginRequired` grouped by quote currency instead of by leg, e.g. `{ USDC: '20', USDT: '20' }`
|
|
65
|
+
* — how much of each currency the basket actually needs, not how much each asset draws. Two legs
|
|
66
|
+
* settling in the same currency fold into one entry here, unlike `legs`. Always `null` from
|
|
67
|
+
* `computeMaxBasketSize` — a single budget has no quote to group by.
|
|
68
|
+
*/
|
|
69
|
+
marginRequiredByQuote: Record<string, string> | null;
|
|
70
|
+
};
|
|
71
|
+
/** One leg's own share of `marginRequired`, identified so the caller can show it per asset. */
|
|
72
|
+
export type LegMarginRequired = {
|
|
73
|
+
asset: InstrumentId;
|
|
74
|
+
/** The leverage this leg was actually granted — after the venue-max cap, not the raw request. */
|
|
75
|
+
effectiveLeverage: string;
|
|
76
|
+
marginRequired: string;
|
|
77
|
+
};
|
|
78
|
+
/** One basket leg with its leverage already capped by the caller. */
|
|
79
|
+
export type CappedBasketLeg = {
|
|
80
|
+
/** Unsigned, in the caller's own scale — `computeMaxBasketSize` normalises by Σweight. */
|
|
81
|
+
weight: number;
|
|
82
|
+
effectiveLeverage: string;
|
|
83
|
+
/** Identifies this leg in `MaxBasketSizeResult.legs`. Omit to keep the leg anonymous. */
|
|
84
|
+
asset?: InstrumentId;
|
|
85
|
+
};
|
|
86
|
+
/** The one margin budget every leg funds out of. */
|
|
87
|
+
export type MaxBasketSizeCalculation = {
|
|
88
|
+
/** USD-equivalent margin available. Every connector this sizes for funds a basket from one balance. */
|
|
89
|
+
budget: string;
|
|
90
|
+
legs: CappedBasketLeg[];
|
|
91
|
+
};
|
|
92
|
+
/** One capped leg, tagged with the quote-currency pool its margin draws from. */
|
|
93
|
+
export type CappedBasketLegWithQuote = CappedBasketLeg & {
|
|
94
|
+
/** The instrument's quote asset, e.g. `"USDC"` — the key into `MultiQuoteBasketSizeCalculation.balanceByQuote`. */
|
|
95
|
+
quote: string;
|
|
96
|
+
};
|
|
97
|
+
/** The margin budgets a basket whose legs settle in more than one quote currency funds out of. */
|
|
98
|
+
export type MultiQuoteBasketSizeCalculation = {
|
|
99
|
+
/** Available margin per quote currency, e.g. `{ USDC: '500', USDT: '300' }`. */
|
|
100
|
+
balanceByQuote: Record<string, string>;
|
|
101
|
+
legs: CappedBasketLegWithQuote[];
|
|
102
|
+
};
|
|
103
|
+
/** What the sizing functions need beyond the legs: balance per quote pool, and the leverage ceilings. */
|
|
104
|
+
export type BasketSizingContext = {
|
|
105
|
+
/** Available margin per quote currency. A leg whose instrument quotes in a currency absent here can't be sized. */
|
|
106
|
+
balanceByQuote: Record<string, string>;
|
|
107
|
+
/** Off the core-sdk instrument catalog — only `id`, `leverage`, and `quote` are read. */
|
|
108
|
+
instruments: Pick<HttpTypes.Instrument, 'id' | 'leverage' | 'quote'>[];
|
|
109
|
+
};
|
package/dist/basket/index.d.ts
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { BasketSizingContext, MaxBasketSizeCalculation, MaxBasketSizeInput, MaxBasketSizePerAssetLeverageInput, MaxBasketSizeRequest, MaxBasketSizeResult, MultiQuoteBasketSizeCalculation } from './basket.types';
|
|
2
|
+
/**
|
|
3
|
+
* The largest gross notional this basket can reach while every leg stays funded at its weight.
|
|
4
|
+
*
|
|
5
|
+
* Takes the caller's figures as given — a malformed budget or leverage is the caller's own
|
|
6
|
+
* responsibility to keep clean (enforce it with a schema at the boundary if that matters to you),
|
|
7
|
+
* not something this function guards against.
|
|
8
|
+
*/
|
|
9
|
+
export declare function computeMaxBasketSize(input: MaxBasketSizeCalculation): MaxBasketSizeResult;
|
|
10
|
+
/**
|
|
11
|
+
* The largest gross notional this basket can reach when its legs settle in more than one quote
|
|
12
|
+
* currency. Each leg's margin draws from its own quote pool, and the pool that runs out first caps
|
|
13
|
+
* the whole basket — the same "tightest constraint wins" logic `computeMaxBasketSize` applies to
|
|
14
|
+
* leverage, one level up. A basket whose legs all share one quote reduces to exactly one pool, so
|
|
15
|
+
* this is a strict superset of `computeMaxBasketSize`'s single-budget math.
|
|
16
|
+
*
|
|
17
|
+
* Takes the caller's figures as given — a malformed balance or leverage is the caller's own
|
|
18
|
+
* responsibility to keep clean, not something this function guards against.
|
|
19
|
+
*/
|
|
20
|
+
export declare function computeMultiQuoteBasketSize(input: MultiQuoteBasketSizeCalculation): MaxBasketSizeResult;
|
|
21
|
+
/**
|
|
22
|
+
* Sizes a basket that caps leverage at nothing but the caller-supplied max —
|
|
23
|
+
* `effectiveLeverage = min(requested, catalogMax)` — and funds each leg out of its own instrument's
|
|
24
|
+
* quote-currency pool.
|
|
25
|
+
*
|
|
26
|
+
* A leg naming an asset the caller's catalog does not list leaves its ceiling and pool unknown, and
|
|
27
|
+
* a leg naming a quote the caller's balance map does not list leaves its pool's budget unknown —
|
|
28
|
+
* either gap reports no size rather than a wrong one.
|
|
29
|
+
*/
|
|
30
|
+
export declare function sizeBasket(input: MaxBasketSizeInput, context: BasketSizingContext): MaxBasketSizeResult;
|
|
31
|
+
/**
|
|
32
|
+
* Sizes a basket the same way `sizeBasket` does, but each leg requests its own leverage
|
|
33
|
+
* instead of the whole basket sharing one figure — still capped at that leg's own venue max.
|
|
34
|
+
*/
|
|
35
|
+
export declare function sizeBasketPerAssetLeverage(input: MaxBasketSizePerAssetLeverageInput, context: BasketSizingContext): MaxBasketSizeResult;
|
|
36
|
+
/**
|
|
37
|
+
* Single entry point for sizing a basket regardless of how the caller requests
|
|
38
|
+
* leverage — dispatches on `leverageMode` to the shared-basket or per-asset implementation.
|
|
39
|
+
*/
|
|
40
|
+
export declare function sizeBasketByLeverageMode(request: MaxBasketSizeRequest, context: BasketSizingContext): MaxBasketSizeResult;
|
package/dist/index.js
CHANGED
|
@@ -328,6 +328,187 @@ function deriveBasketKey(longInstrumentIds, shortInstrumentIds) {
|
|
|
328
328
|
return bytesToHex(sha256(new TextEncoder().encode(payload)));
|
|
329
329
|
}
|
|
330
330
|
__name(deriveBasketKey, "deriveBasketKey");
|
|
331
|
+
|
|
332
|
+
// src/basket/max-size.ts
|
|
333
|
+
function computeMaxBasketSize(input) {
|
|
334
|
+
const weightSum = sumWeights(input.legs);
|
|
335
|
+
if (isZero(weightSum)) {
|
|
336
|
+
return unsizedResult();
|
|
337
|
+
}
|
|
338
|
+
const demands = input.legs.map((leg) => toLegDemand(leg, weightSum));
|
|
339
|
+
const size = divide(input.budget, sumDemand(demands));
|
|
340
|
+
return {
|
|
341
|
+
maxNotional: size.toFixed(),
|
|
342
|
+
marginRequired: input.budget,
|
|
343
|
+
legs: legMarginBreakdown(demands, size),
|
|
344
|
+
marginRequiredByQuote: null
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
__name(computeMaxBasketSize, "computeMaxBasketSize");
|
|
348
|
+
function computeMultiQuoteBasketSize(input) {
|
|
349
|
+
const weightSum = sumWeights(input.legs);
|
|
350
|
+
if (isZero(weightSum)) {
|
|
351
|
+
return unsizedResult();
|
|
352
|
+
}
|
|
353
|
+
const demands = input.legs.map((leg) => toLegDemand(leg, weightSum));
|
|
354
|
+
const demandByQuote = demandPerQuote(demands);
|
|
355
|
+
let binding;
|
|
356
|
+
for (const [quote, demand] of demandByQuote) {
|
|
357
|
+
const balance = input.balanceByQuote[quote];
|
|
358
|
+
if (balance === void 0) {
|
|
359
|
+
return unsizedResult();
|
|
360
|
+
}
|
|
361
|
+
if (isZero(demand)) {
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
const notional = divide(balance, demand);
|
|
365
|
+
if (binding === void 0 || isLessThan(notional, binding.maxNotional)) {
|
|
366
|
+
binding = {
|
|
367
|
+
quote,
|
|
368
|
+
balance,
|
|
369
|
+
maxNotional: notional
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (binding === void 0) {
|
|
374
|
+
return unsizedResult();
|
|
375
|
+
}
|
|
376
|
+
const marginByQuote = marginRequiredByQuote(demandByQuote, binding);
|
|
377
|
+
const marginRequired = Object.values(marginByQuote).reduce((total, drawn) => add(total, drawn), ZERO);
|
|
378
|
+
return {
|
|
379
|
+
maxNotional: binding.maxNotional.toFixed(),
|
|
380
|
+
marginRequired: marginRequired.toFixed(),
|
|
381
|
+
legs: legMarginBreakdown(demands, binding.maxNotional),
|
|
382
|
+
marginRequiredByQuote: marginByQuote
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
__name(computeMultiQuoteBasketSize, "computeMultiQuoteBasketSize");
|
|
386
|
+
function sizeBasket(input, context) {
|
|
387
|
+
const legs = capLegsAgainstCatalog(input.legs.map((leg) => ({
|
|
388
|
+
...leg,
|
|
389
|
+
requestedLeverage: input.requestedLeverage
|
|
390
|
+
})), context.instruments);
|
|
391
|
+
if (legs === null) {
|
|
392
|
+
return unsizedResult();
|
|
393
|
+
}
|
|
394
|
+
return computeMultiQuoteBasketSize({
|
|
395
|
+
balanceByQuote: context.balanceByQuote,
|
|
396
|
+
legs
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
__name(sizeBasket, "sizeBasket");
|
|
400
|
+
function sizeBasketPerAssetLeverage(input, context) {
|
|
401
|
+
const legs = capLegsAgainstCatalog(input.legs, context.instruments);
|
|
402
|
+
if (legs === null) {
|
|
403
|
+
return unsizedResult();
|
|
404
|
+
}
|
|
405
|
+
return computeMultiQuoteBasketSize({
|
|
406
|
+
balanceByQuote: context.balanceByQuote,
|
|
407
|
+
legs
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
__name(sizeBasketPerAssetLeverage, "sizeBasketPerAssetLeverage");
|
|
411
|
+
function sizeBasketByLeverageMode(request, context) {
|
|
412
|
+
switch (request.leverageMode) {
|
|
413
|
+
case "shared":
|
|
414
|
+
return sizeBasket(request, context);
|
|
415
|
+
case "perAsset":
|
|
416
|
+
return sizeBasketPerAssetLeverage(request, context);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
__name(sizeBasketByLeverageMode, "sizeBasketByLeverageMode");
|
|
420
|
+
function capLegsAgainstCatalog(legs, instruments) {
|
|
421
|
+
const instrumentById = new Map(instruments.map((instrument) => [
|
|
422
|
+
instrument.id,
|
|
423
|
+
instrument
|
|
424
|
+
]));
|
|
425
|
+
const capped = [];
|
|
426
|
+
for (const leg of legs) {
|
|
427
|
+
const instrument = instrumentById.get(leg.asset);
|
|
428
|
+
if (instrument === void 0) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
capped.push({
|
|
432
|
+
asset: leg.asset,
|
|
433
|
+
weight: leg.weight,
|
|
434
|
+
effectiveLeverage: cappedLeverage(leg.requestedLeverage, instrument.leverage),
|
|
435
|
+
quote: instrument.quote
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
return capped;
|
|
439
|
+
}
|
|
440
|
+
__name(capLegsAgainstCatalog, "capLegsAgainstCatalog");
|
|
441
|
+
function cappedLeverage(requested, maxLeverage) {
|
|
442
|
+
const granted = min(String(requested), String(maxLeverage));
|
|
443
|
+
return isFiniteDecimal(granted) && granted.isGreaterThan(0) ? granted.toFixed() : "1";
|
|
444
|
+
}
|
|
445
|
+
__name(cappedLeverage, "cappedLeverage");
|
|
446
|
+
function sumWeights(legs) {
|
|
447
|
+
let total = ZERO;
|
|
448
|
+
for (const leg of legs) {
|
|
449
|
+
total = add(total, String(leg.weight));
|
|
450
|
+
}
|
|
451
|
+
return total;
|
|
452
|
+
}
|
|
453
|
+
__name(sumWeights, "sumWeights");
|
|
454
|
+
function unsizedResult() {
|
|
455
|
+
return {
|
|
456
|
+
maxNotional: null,
|
|
457
|
+
marginRequired: null,
|
|
458
|
+
legs: null,
|
|
459
|
+
marginRequiredByQuote: null
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
__name(unsizedResult, "unsizedResult");
|
|
463
|
+
function toLegDemand(leg, weightSum) {
|
|
464
|
+
const share = divide(String(leg.weight), weightSum);
|
|
465
|
+
const marginFraction = divide("1", leg.effectiveLeverage);
|
|
466
|
+
return {
|
|
467
|
+
leg,
|
|
468
|
+
demand: multiply(share, marginFraction)
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
__name(toLegDemand, "toLegDemand");
|
|
472
|
+
function legMarginBreakdown(demands, maxNotional) {
|
|
473
|
+
const breakdown = [];
|
|
474
|
+
for (const { leg, demand } of demands) {
|
|
475
|
+
if (leg.asset === void 0) {
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
breakdown.push({
|
|
479
|
+
asset: leg.asset,
|
|
480
|
+
effectiveLeverage: leg.effectiveLeverage,
|
|
481
|
+
marginRequired: multiply(maxNotional, demand).toFixed()
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
return breakdown;
|
|
485
|
+
}
|
|
486
|
+
__name(legMarginBreakdown, "legMarginBreakdown");
|
|
487
|
+
function sumDemand(demands) {
|
|
488
|
+
let total = ZERO;
|
|
489
|
+
for (const { demand } of demands) {
|
|
490
|
+
total = add(total, demand);
|
|
491
|
+
}
|
|
492
|
+
return total;
|
|
493
|
+
}
|
|
494
|
+
__name(sumDemand, "sumDemand");
|
|
495
|
+
function demandPerQuote(demands) {
|
|
496
|
+
const demandByQuote = /* @__PURE__ */ new Map();
|
|
497
|
+
for (const { leg, demand } of demands) {
|
|
498
|
+
const current = demandByQuote.get(leg.quote) ?? ZERO;
|
|
499
|
+
demandByQuote.set(leg.quote, add(current, demand));
|
|
500
|
+
}
|
|
501
|
+
return demandByQuote;
|
|
502
|
+
}
|
|
503
|
+
__name(demandPerQuote, "demandPerQuote");
|
|
504
|
+
function marginRequiredByQuote(demandByQuote, binding) {
|
|
505
|
+
const marginByQuote = {};
|
|
506
|
+
for (const [quote, demand] of demandByQuote) {
|
|
507
|
+
marginByQuote[quote] = quote === binding.quote ? parse(binding.balance).toFixed() : multiply(binding.maxNotional, demand).toFixed();
|
|
508
|
+
}
|
|
509
|
+
return marginByQuote;
|
|
510
|
+
}
|
|
511
|
+
__name(marginRequiredByQuote, "marginRequiredByQuote");
|
|
331
512
|
function contractsToBase(contracts, contractSize) {
|
|
332
513
|
return new BigNumber7(contracts).times(contractSize);
|
|
333
514
|
}
|
|
@@ -2135,4 +2316,4 @@ function takeVerifier(handoffId, storage) {
|
|
|
2135
2316
|
}
|
|
2136
2317
|
__name(takeVerifier, "takeVerifier");
|
|
2137
2318
|
|
|
2138
|
-
export { BIPS, ZERO, abs, add, aggregateRealizedPositionState, baseToContracts, buildApplicables, compareValue, computeAssetEntryNotional, computeAssetGrossRealizedPnL, computeAssetRealizedPnLs, computeAssetUndeterminedSizes, computeAssetUnrealizedPnL, computeAssetUnrealizedPnLs, computeBasketWeightedRatioV1, computeEntryPriceForAsset, computePositionEntryPrices, computeRealizedPnlBySymbol, computeRealizedPnlFromFills, computeRealizedPositionState, computeSyncPayload, computeUnrealizedPositionState, computeUnrealizedPositionStateFromEntryPrices, configureProxy, contractsToBase, countDecimals, deriveBasketKey, divide, exponentiate, finishXHandoff, generateDeterministicPositionKey, isEqualTo, isFiniteDecimal, isGreaterThan, isGreaterThanOrEqual, isLessThan, isLessThanOrEqual, isNegative, isPositive, isZero, min, multiply, negate, parse, precisePrice, preciseQuantity, proxyFetch, sideSign, sign, signed, signedBySide, startXHandoff, stripXReturn, subtract, toDecimalString, validateLeverage, validateQuantity };
|
|
2319
|
+
export { BIPS, ZERO, abs, add, aggregateRealizedPositionState, baseToContracts, buildApplicables, compareValue, computeAssetEntryNotional, computeAssetGrossRealizedPnL, computeAssetRealizedPnLs, computeAssetUndeterminedSizes, computeAssetUnrealizedPnL, computeAssetUnrealizedPnLs, computeBasketWeightedRatioV1, 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 };
|