@reyaxyz/account-figures 0.0.0-stage → 0.2.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/LICENSE.md +1 -0
- package/README.md +153 -2
- package/dist/decimal.d.ts +32 -0
- package/dist/decimal.js +82 -0
- package/dist/engine.d.ts +115 -0
- package/dist/engine.js +318 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +15 -0
- package/dist/figures.d.ts +81 -0
- package/dist/figures.js +107 -0
- package/dist/format.d.ts +8 -0
- package/dist/format.js +30 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +33 -0
- package/dist/liquidation.d.ts +29 -0
- package/dist/liquidation.js +117 -0
- package/dist/snapshot.d.ts +123 -0
- package/dist/snapshot.js +259 -0
- package/package.json +43 -4
package/dist/engine.js
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.applyFill = exports.admit = exports.project = exports.projectD = exports.roundingAllowance = exports.marginText = exports.riskText = exports.parseMargin = exports.parseRisk = exports.SETTLEMENT_RESERVE = exports.INITIAL_MARGIN = exports.LIQUIDATION_THRESHOLD = exports.SETTLEMENT_RESERVE_RATE = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* The matching engine's margin projection and admission verdict, ported from
|
|
9
|
+
* the Agent Kit's `agent_kit/scripts/_margin.py` (kit 0.12.20), which ports the
|
|
10
|
+
* engine's risk engine (`matching-engine/src/base/risk_engine`, revision
|
|
11
|
+
* 4d8ed012): `compute.rs` (`project_account_with_lots`, `finish_projection`)
|
|
12
|
+
* and `checks/taker_margin.rs` (`admit_taker`).
|
|
13
|
+
*
|
|
14
|
+
* All amounts are rUSD. Like the engine, cash is floored and requirements are
|
|
15
|
+
* rounded up to rUSD's 6 decimals per component, and E18 steps are reproduced
|
|
16
|
+
* where it rounds. Every amount in and out is a decimal string.
|
|
17
|
+
*
|
|
18
|
+
* The account figures use `project` only for the liquidation and initial
|
|
19
|
+
* margin, which depend on positions alone. For the engine's admission verdict
|
|
20
|
+
* (`admit`), the balances must be credited by the engine's own collateral
|
|
21
|
+
* rules, which give some collateral no credit: `buildMarginSnapshot` values
|
|
22
|
+
* collateral as Core does, not as the engine admits it.
|
|
23
|
+
*/
|
|
24
|
+
const bignumber_js_1 = __importDefault(require("bignumber.js"));
|
|
25
|
+
const decimal_1 = require("./decimal");
|
|
26
|
+
const errors_1 = require("./errors");
|
|
27
|
+
/**
|
|
28
|
+
* The engine's V1 policy constant (`[risk] reserve_rate`), applied once to the
|
|
29
|
+
* notional of the order and of the account's matched-but-unsettled fills. V2
|
|
30
|
+
* does not publish it.
|
|
31
|
+
*/
|
|
32
|
+
exports.SETTLEMENT_RESERVE_RATE = '0.01';
|
|
33
|
+
exports.LIQUIDATION_THRESHOLD = 'below_liquidation_threshold';
|
|
34
|
+
exports.INITIAL_MARGIN = 'below_initial_margin';
|
|
35
|
+
exports.SETTLEMENT_RESERVE = 'below_settlement_reserve';
|
|
36
|
+
function parseRisk(market, risk) {
|
|
37
|
+
return {
|
|
38
|
+
base: (0, decimal_1.parseDecimal)(risk.base, `${market} base`),
|
|
39
|
+
mark: (0, decimal_1.parseDecimal)(risk.mark, `${market} mark price`),
|
|
40
|
+
marginRate: (0, decimal_1.parseDecimal)(risk.marginRate, `${market} margin rate`),
|
|
41
|
+
entry: (0, decimal_1.parseDecimal)(risk.entry, `${market} entry price`),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
exports.parseRisk = parseRisk;
|
|
45
|
+
function parseMargin(margin) {
|
|
46
|
+
const markets = new Map();
|
|
47
|
+
for (const [market, risk] of Object.entries(margin.markets)) {
|
|
48
|
+
markets.set(market, parseRisk(market, risk));
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
realBalance: (0, decimal_1.parseDecimal)(margin.realBalance, 'real balance'),
|
|
52
|
+
marginBalance: (0, decimal_1.parseDecimal)(margin.marginBalance, 'margin balance'),
|
|
53
|
+
markets,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
exports.parseMargin = parseMargin;
|
|
57
|
+
function riskText(risk) {
|
|
58
|
+
return {
|
|
59
|
+
base: (0, decimal_1.toText)(risk.base),
|
|
60
|
+
mark: (0, decimal_1.toText)(risk.mark),
|
|
61
|
+
marginRate: (0, decimal_1.toText)(risk.marginRate),
|
|
62
|
+
entry: (0, decimal_1.toText)(risk.entry),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
exports.riskText = riskText;
|
|
66
|
+
function marginText(margin) {
|
|
67
|
+
const markets = {};
|
|
68
|
+
for (const [market, risk] of margin.markets) {
|
|
69
|
+
markets[market] = riskText(risk);
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
realBalance: (0, decimal_1.toText)(margin.realBalance),
|
|
73
|
+
marginBalance: (0, decimal_1.toText)(margin.marginBalance),
|
|
74
|
+
markets,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
exports.marginText = marginText;
|
|
78
|
+
function parseLot(lot) {
|
|
79
|
+
return {
|
|
80
|
+
market: lot.market,
|
|
81
|
+
qty: (0, decimal_1.parseDecimal)(lot.qty, `${lot.market} lot qty`),
|
|
82
|
+
price: (0, decimal_1.parseDecimal)(lot.price, `${lot.market} lot price`),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function parseProjection(p) {
|
|
86
|
+
return {
|
|
87
|
+
mbFloor: (0, decimal_1.parseDecimal)(p.mbFloor, 'mbFloor'),
|
|
88
|
+
rbFloor: (0, decimal_1.parseDecimal)(p.rbFloor, 'rbFloor'),
|
|
89
|
+
lm: (0, decimal_1.parseDecimal)(p.lm, 'lm'),
|
|
90
|
+
im: (0, decimal_1.parseDecimal)(p.im, 'im'),
|
|
91
|
+
reserve: (0, decimal_1.parseDecimal)(p.reserve, 'reserve'),
|
|
92
|
+
h: (0, decimal_1.parseDecimal)(p.h, 'h'),
|
|
93
|
+
c: (0, decimal_1.parseDecimal)(p.c, 'c'),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function projectionText(p) {
|
|
97
|
+
return {
|
|
98
|
+
mbFloor: (0, decimal_1.toText)(p.mbFloor),
|
|
99
|
+
rbFloor: (0, decimal_1.toText)(p.rbFloor),
|
|
100
|
+
lm: (0, decimal_1.toText)(p.lm),
|
|
101
|
+
im: (0, decimal_1.toText)(p.im),
|
|
102
|
+
reserve: (0, decimal_1.toText)(p.reserve),
|
|
103
|
+
h: (0, decimal_1.toText)(p.h),
|
|
104
|
+
c: (0, decimal_1.toText)(p.c),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// --------------------------------------------------------------------------
|
|
108
|
+
// Projection
|
|
109
|
+
// --------------------------------------------------------------------------
|
|
110
|
+
class Fold {
|
|
111
|
+
constructor(buys = decimal_1.ZERO,
|
|
112
|
+
/** Summed signed, so never positive. */
|
|
113
|
+
sells = decimal_1.ZERO, instantLoss = decimal_1.ZERO, reserveNotional = decimal_1.ZERO, transitions = 0) {
|
|
114
|
+
this.buys = buys;
|
|
115
|
+
this.sells = sells;
|
|
116
|
+
this.instantLoss = instantLoss;
|
|
117
|
+
this.reserveNotional = reserveNotional;
|
|
118
|
+
this.transitions = transitions;
|
|
119
|
+
}
|
|
120
|
+
add(lot, mark, unit) {
|
|
121
|
+
if (lot.qty.gt(0)) {
|
|
122
|
+
this.buys = this.buys.plus(lot.qty);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
this.sells = this.sells.plus(lot.qty);
|
|
126
|
+
}
|
|
127
|
+
// A fill priced worse than the mark loses at once; a better one is never credited.
|
|
128
|
+
this.instantLoss = this.instantLoss.plus((0, decimal_1.floorTo)((0, decimal_1.minOf)((0, decimal_1.floorTo)(lot.qty.times(mark.minus(lot.price))), decimal_1.ZERO), unit));
|
|
129
|
+
this.reserveNotional = this.reserveNotional.plus((0, decimal_1.floorTo)(lot.qty.abs().times(lot.price)));
|
|
130
|
+
if (!lot.qty.isZero()) {
|
|
131
|
+
this.transitions += 1;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
merged(other) {
|
|
135
|
+
return new Fold(this.buys.plus(other.buys), this.sells.plus(other.sells), this.instantLoss.plus(other.instantLoss), this.reserveNotional.plus(other.reserveNotional), this.transitions + other.transitions);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function roundingAllowanceD(worstBase, transitions, decimals) {
|
|
139
|
+
if (transitions === 0) {
|
|
140
|
+
return decimal_1.ZERO;
|
|
141
|
+
}
|
|
142
|
+
const drift = worstBase.integerValue(bignumber_js_1.default.ROUND_CEIL).plus(2).times(decimal_1.E18);
|
|
143
|
+
return (0, decimal_1.ceilTo)(drift, (0, decimal_1.nativeUnit)(decimals)).times(transitions);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Core's average-entry and PnL truncation bound: one native unit per
|
|
147
|
+
* transition for any realistic size.
|
|
148
|
+
*/
|
|
149
|
+
function roundingAllowance(worstBase, transitions, decimals = decimal_1.RUSD_DECIMALS) {
|
|
150
|
+
return (0, decimal_1.toText)(roundingAllowanceD((0, decimal_1.parseDecimal)(worstBase, 'worst base'), transitions, decimals));
|
|
151
|
+
}
|
|
152
|
+
exports.roundingAllowance = roundingAllowance;
|
|
153
|
+
function projectD(margin, imMultiplier, candidates = [], inFlight = [], reserveRate = new decimal_1.Dec(exports.SETTLEMENT_RESERVE_RATE), decimals = decimal_1.RUSD_DECIMALS) {
|
|
154
|
+
const unit = (0, decimal_1.nativeUnit)(decimals);
|
|
155
|
+
let mbFloor = margin.marginBalance;
|
|
156
|
+
let rbFloor = margin.realBalance;
|
|
157
|
+
let lm = decimal_1.ZERO;
|
|
158
|
+
let reserveNotional = decimal_1.ZERO;
|
|
159
|
+
const names = [
|
|
160
|
+
...new Set([
|
|
161
|
+
...margin.markets.keys(),
|
|
162
|
+
...inFlight.map((lot) => lot.market),
|
|
163
|
+
...candidates.map((lot) => lot.market),
|
|
164
|
+
]),
|
|
165
|
+
].sort();
|
|
166
|
+
for (const market of names) {
|
|
167
|
+
const risk = margin.markets.get(market);
|
|
168
|
+
if (risk === undefined) {
|
|
169
|
+
throw new errors_1.AccountFiguresInputError(`No mark or margin rate for ${market}`);
|
|
170
|
+
}
|
|
171
|
+
const optional = new Fold();
|
|
172
|
+
const candidate = new Fold();
|
|
173
|
+
for (const lot of inFlight) {
|
|
174
|
+
if (lot.market === market) {
|
|
175
|
+
optional.add(lot, risk.mark, unit);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
let candidateQty = decimal_1.ZERO;
|
|
179
|
+
for (const lot of candidates) {
|
|
180
|
+
if (lot.market === market) {
|
|
181
|
+
candidate.add(lot, risk.mark, unit);
|
|
182
|
+
candidateQty = candidateQty.plus(lot.qty);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const possible = optional.merged(candidate);
|
|
186
|
+
const settled = risk.base;
|
|
187
|
+
const worst = (0, decimal_1.maxOf)(settled.abs(), settled.plus(possible.buys).abs(), settled.plus(possible.sells).abs());
|
|
188
|
+
const delta = possible.instantLoss.minus(roundingAllowanceD(worst, (settled.isZero() ? 0 : 1) + possible.transitions, decimals));
|
|
189
|
+
mbFloor = mbFloor.plus(delta);
|
|
190
|
+
rbFloor = rbFloor.plus(delta);
|
|
191
|
+
// Exposure is the worst settle/bust corner of the unsettled lots, shifted by the candidate.
|
|
192
|
+
const shifted = settled.plus(candidateQty);
|
|
193
|
+
const exposureBase = (0, decimal_1.maxOf)(shifted.abs(), shifted.plus(optional.buys).abs(), shifted.plus(optional.sells).abs());
|
|
194
|
+
lm = lm.plus((0, decimal_1.ceilTo)((0, decimal_1.ceilTo)(risk.marginRate.times((0, decimal_1.ceilTo)(exposureBase.times(risk.mark)))), unit));
|
|
195
|
+
if (!settled.isZero()) {
|
|
196
|
+
// Closing part of the position realizes its loss at mark into the real balance.
|
|
197
|
+
const opposite = settled.gt(0) ? possible.sells.negated() : possible.buys;
|
|
198
|
+
const closable = (0, decimal_1.minOf)(settled.abs(), opposite);
|
|
199
|
+
const signed = settled.gt(0) ? closable : closable.negated();
|
|
200
|
+
rbFloor = rbFloor.plus((0, decimal_1.floorTo)((0, decimal_1.minOf)((0, decimal_1.floorTo)(signed.times(risk.mark.minus(risk.entry))), decimal_1.ZERO), unit));
|
|
201
|
+
}
|
|
202
|
+
reserveNotional = reserveNotional.plus(possible.reserveNotional);
|
|
203
|
+
}
|
|
204
|
+
const im = (0, decimal_1.ceilTo)((0, decimal_1.ceilTo)(imMultiplier.times(lm)), unit);
|
|
205
|
+
const reserve = (0, decimal_1.ceilTo)(reserveRate.times(reserveNotional), unit);
|
|
206
|
+
const h = (0, decimal_1.minOf)(mbFloor.minus(lm), rbFloor);
|
|
207
|
+
return { mbFloor, rbFloor, lm, im, reserve, h, c: h.minus(reserve) };
|
|
208
|
+
}
|
|
209
|
+
exports.projectD = projectD;
|
|
210
|
+
/**
|
|
211
|
+
* The engine's `project_account_with_lots`: settled state plus unsettled lots
|
|
212
|
+
* plus candidates. With no lots it is the settled account, whose `lm` and `im`
|
|
213
|
+
* are the account's LMR and IMR.
|
|
214
|
+
*/
|
|
215
|
+
function project(margin, candidates, options) {
|
|
216
|
+
return projectionText(projectD(parseMargin(margin), (0, decimal_1.parseDecimal)(options.imMultiplier, 'IM multiplier'), candidates.map(parseLot), (options.inFlight ?? []).map(parseLot), (0, decimal_1.parseDecimal)(options.reserveRate ?? exports.SETTLEMENT_RESERVE_RATE, 'reserve rate'), options.decimals ?? decimal_1.RUSD_DECIMALS));
|
|
217
|
+
}
|
|
218
|
+
exports.project = project;
|
|
219
|
+
// --------------------------------------------------------------------------
|
|
220
|
+
// Verdict
|
|
221
|
+
// --------------------------------------------------------------------------
|
|
222
|
+
function shortfallText(delta) {
|
|
223
|
+
return (0, decimal_1.toText)(delta.abs());
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* The engine's `admit_taker`: the liquidation-threshold precondition for every
|
|
227
|
+
* request, then initial margin before and after and the settlement reserve
|
|
228
|
+
* after, for an ordinary one only. `strictReduceOnly` is the reduce-only flag
|
|
229
|
+
* or a TP/SL; an order that merely opposes the position is ordinary. The
|
|
230
|
+
* messages are the engine's own.
|
|
231
|
+
*/
|
|
232
|
+
function admit(before, after, strictReduceOnly) {
|
|
233
|
+
const p0 = parseProjection(before);
|
|
234
|
+
const pq = parseProjection(after);
|
|
235
|
+
if (p0.h.lt(0)) {
|
|
236
|
+
return {
|
|
237
|
+
code: exports.LIQUIDATION_THRESHOLD,
|
|
238
|
+
errorCode: 'ACCOUNT_BELOW_LIQUIDATION_MARGIN_ERROR',
|
|
239
|
+
shortfall: (0, decimal_1.toText)(p0.h.negated()),
|
|
240
|
+
engineMessage: `This account is ${shortfallText(p0.h)} rUSD below its liquidation safety threshold. ` +
|
|
241
|
+
'Add collateral before placing another order.',
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (strictReduceOnly) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
for (const projection of [p0, pq]) {
|
|
248
|
+
if (projection.mbFloor.lt(projection.im)) {
|
|
249
|
+
const delta = projection.mbFloor.minus(projection.im);
|
|
250
|
+
return {
|
|
251
|
+
code: exports.INITIAL_MARGIN,
|
|
252
|
+
errorCode: 'ACCOUNT_BELOW_INITIAL_MARGIN_ERROR',
|
|
253
|
+
shortfall: (0, decimal_1.toText)(delta.negated()),
|
|
254
|
+
engineMessage: `Insufficient margin: the account is ${shortfallText(delta)} rUSD below the initial margin ` +
|
|
255
|
+
'requirement for this order. Add collateral, reduce the order size, or submit a reduce-only order.',
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (pq.h.lt(pq.reserve)) {
|
|
260
|
+
const delta = pq.h.minus(pq.reserve);
|
|
261
|
+
return {
|
|
262
|
+
code: exports.SETTLEMENT_RESERVE,
|
|
263
|
+
errorCode: 'ACCOUNT_BELOW_INITIAL_MARGIN_ERROR',
|
|
264
|
+
shortfall: (0, decimal_1.toText)(delta.negated()),
|
|
265
|
+
engineMessage: `Insufficient margin: this order would leave the account ${shortfallText(delta)} rUSD below its ` +
|
|
266
|
+
'settlement reserve. Reduce the order size or add collateral.',
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
exports.admit = admit;
|
|
272
|
+
// --------------------------------------------------------------------------
|
|
273
|
+
// A fill applied the way Core updates a position
|
|
274
|
+
// --------------------------------------------------------------------------
|
|
275
|
+
/** The account after `lot` fills in full at its price, settled the way Core updates a position. */
|
|
276
|
+
function applyFill(margin, lot, fee = '0') {
|
|
277
|
+
const parsed = parseMargin(margin);
|
|
278
|
+
const fill = parseLot(lot);
|
|
279
|
+
const feeD = (0, decimal_1.parseDecimal)(fee, 'fee');
|
|
280
|
+
const risk = parsed.markets.get(fill.market);
|
|
281
|
+
if (risk === undefined) {
|
|
282
|
+
throw new errors_1.AccountFiguresInputError(`No mark or margin rate for ${fill.market}`);
|
|
283
|
+
}
|
|
284
|
+
const base = risk.base;
|
|
285
|
+
let entry = risk.entry;
|
|
286
|
+
let realized = decimal_1.ZERO;
|
|
287
|
+
const total = base.plus(fill.qty);
|
|
288
|
+
if (base.isZero() || base.times(fill.qty).gt(0)) {
|
|
289
|
+
if (total.isZero()) {
|
|
290
|
+
throw new errors_1.AccountFiguresInputError(`A fill on a flat ${fill.market} position needs a non-zero qty`);
|
|
291
|
+
}
|
|
292
|
+
entry = base.times(entry).plus(fill.qty.times(fill.price)).div(total);
|
|
293
|
+
}
|
|
294
|
+
else if (base.times(total).gte(0)) {
|
|
295
|
+
realized = fill.qty.negated().times(fill.price.minus(entry));
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
realized = base.times(fill.price.minus(entry));
|
|
299
|
+
entry = fill.price;
|
|
300
|
+
}
|
|
301
|
+
if (total.isZero()) {
|
|
302
|
+
entry = risk.mark;
|
|
303
|
+
}
|
|
304
|
+
const oldUpnl = base.times(risk.mark.minus(risk.entry));
|
|
305
|
+
const newUpnl = total.times(risk.mark.minus(entry));
|
|
306
|
+
const markets = new Map(parsed.markets);
|
|
307
|
+
markets.set(fill.market, { ...risk, base: total, entry });
|
|
308
|
+
return marginText({
|
|
309
|
+
realBalance: parsed.realBalance.plus(realized).minus(feeD),
|
|
310
|
+
marginBalance: parsed.marginBalance
|
|
311
|
+
.plus(realized)
|
|
312
|
+
.minus(feeD)
|
|
313
|
+
.minus(oldUpnl)
|
|
314
|
+
.plus(newUpnl),
|
|
315
|
+
markets,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
exports.applyFill = applyFill;
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input the figures cannot be computed from: a missing or non-numeric price,
|
|
3
|
+
* market, asset definition or margin parameter. Missing data is never counted
|
|
4
|
+
* as zero, since that would produce wrong risk numbers.
|
|
5
|
+
*/
|
|
6
|
+
export declare class AccountFiguresInputError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccountFiguresInputError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Input the figures cannot be computed from: a missing or non-numeric price,
|
|
6
|
+
* market, asset definition or margin parameter. Missing data is never counted
|
|
7
|
+
* as zero, since that would produce wrong risk numbers.
|
|
8
|
+
*/
|
|
9
|
+
class AccountFiguresInputError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'AccountFiguresInputError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.AccountFiguresInputError = AccountFiguresInputError;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { CollateralRow, MarginInputs, MarginSnapshot, PositionDetail } from './snapshot';
|
|
2
|
+
/** What the figures leave out, for a reader of them. */
|
|
3
|
+
export declare const MARGIN_ASSUMPTIONS: readonly string[];
|
|
4
|
+
export interface PositionRisk {
|
|
5
|
+
/** This position's own liquidation margin requirement, rounded up to 6 dp. */
|
|
6
|
+
lmr: string;
|
|
7
|
+
/**
|
|
8
|
+
* The liquidation price on the tick, rounded toward the mark. `null` when no
|
|
9
|
+
* positive price is the liquidation line.
|
|
10
|
+
*/
|
|
11
|
+
liquidationPrice: string | null;
|
|
12
|
+
/** The same price before rounding onto the tick. */
|
|
13
|
+
liquidationPriceExact: string | null;
|
|
14
|
+
/**
|
|
15
|
+
* How far the mark must move against the position to reach the liquidation
|
|
16
|
+
* price, as a fraction of the mark (from the exact price). Negative when the
|
|
17
|
+
* account is already past the line.
|
|
18
|
+
*/
|
|
19
|
+
distanceToLiquidation: string | null;
|
|
20
|
+
/**
|
|
21
|
+
* The account holds non-stable collateral whose price follows this
|
|
22
|
+
* position's market (WETH or wstETH with an ETH perp, BTC with a BTC perp).
|
|
23
|
+
* The liquidation price holds that collateral's price fixed, which it is
|
|
24
|
+
* not: for a long, the collateral falls with the mark and liquidation comes
|
|
25
|
+
* sooner than shown.
|
|
26
|
+
*/
|
|
27
|
+
assumesFixedCollateral: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface PositionFigures extends PositionDetail, PositionRisk {
|
|
30
|
+
/** |qty| × mark. */
|
|
31
|
+
notional: string;
|
|
32
|
+
/** s, the market's liquidation-margin parameter. */
|
|
33
|
+
liquidationMarginRate: string;
|
|
34
|
+
}
|
|
35
|
+
export interface AccountFigures {
|
|
36
|
+
accountId: number | string;
|
|
37
|
+
/**
|
|
38
|
+
* Margin balance: collateral as Core values it (price × (1 − haircut), a
|
|
39
|
+
* debt at price / (1 − haircut)) + unrealised PnL at mark. Realised PnL and
|
|
40
|
+
* funding are already in V2's rUSD balance.
|
|
41
|
+
*/
|
|
42
|
+
equity: string;
|
|
43
|
+
/** Collateral as Core values it, without unrealised PnL. */
|
|
44
|
+
realBalance: string;
|
|
45
|
+
unrealizedPnl: string;
|
|
46
|
+
/** Liquidation margin requirement (LMR): the account is liquidated when equity falls below it. */
|
|
47
|
+
lmr: string;
|
|
48
|
+
/** Initial margin requirement (IMR): new exposure needs equity at or above it. */
|
|
49
|
+
imr: string;
|
|
50
|
+
imMultiplier: string;
|
|
51
|
+
/**
|
|
52
|
+
* LMR / margin balance (`equity`): what the app calls "Cross margin ratio".
|
|
53
|
+
* The figure liquidation depends on; 1 is liquidation. `null` when equity
|
|
54
|
+
* is not positive.
|
|
55
|
+
*/
|
|
56
|
+
liquidationMarginUsage: string | null;
|
|
57
|
+
/**
|
|
58
|
+
* IMR / margin balance (`equity`): what the app calls "Cross margin usage".
|
|
59
|
+
* At 1 no new position fits; liquidation comes later, at
|
|
60
|
+
* `liquidationMarginUsage` 1. `null` when equity is not positive.
|
|
61
|
+
*/
|
|
62
|
+
initialMarginUsage: string | null;
|
|
63
|
+
/** equity < LMR. */
|
|
64
|
+
liquidatable: boolean;
|
|
65
|
+
/** equity − LMR. */
|
|
66
|
+
marginBuffer: string;
|
|
67
|
+
/** equity − IMR: the room for new exposure (negative when below the IMR). */
|
|
68
|
+
availableMargin: string;
|
|
69
|
+
collateral: CollateralRow[];
|
|
70
|
+
positions: PositionFigures[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Liquidation margin, liquidation price and distance for one held market, on
|
|
74
|
+
* the settled account. `lmr` is the account's LMR when the caller already has
|
|
75
|
+
* it.
|
|
76
|
+
*/
|
|
77
|
+
export declare function positionRisk(snapshot: MarginSnapshot, symbol: string, lmr?: string): PositionRisk;
|
|
78
|
+
/** Account-level equity, requirements, usage and buffers, and each position's figures. */
|
|
79
|
+
export declare function accountFigures(snapshot: MarginSnapshot): AccountFigures;
|
|
80
|
+
/** One account's figures straight from V2 reads. */
|
|
81
|
+
export declare function computeAccountFigures(inputs: MarginInputs): AccountFigures;
|
package/dist/figures.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeAccountFigures = exports.accountFigures = exports.positionRisk = exports.MARGIN_ASSUMPTIONS = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Account-level and per-position figures on the settled account. The shape
|
|
6
|
+
* follows the Agent Kit's `account_figures` and `position_risk` (kit 0.12.20);
|
|
7
|
+
* the inputs are valued as Core values them (see `snapshot.ts`). Every amount
|
|
8
|
+
* is exact; `format.ts` rounds for display.
|
|
9
|
+
*/
|
|
10
|
+
const decimal_1 = require("./decimal");
|
|
11
|
+
const engine_1 = require("./engine");
|
|
12
|
+
const liquidation_1 = require("./liquidation");
|
|
13
|
+
const snapshot_1 = require("./snapshot");
|
|
14
|
+
const errors_1 = require("./errors");
|
|
15
|
+
/** What the figures leave out, for a reader of them. */
|
|
16
|
+
exports.MARGIN_ASSUMPTIONS = [
|
|
17
|
+
'An estimate from public V2 data of the margin Core computes; Core can differ slightly.',
|
|
18
|
+
'Equity (margin balance) = each collateral at its oracle price × (1 − haircut), a debt at price / (1 − haircut), ' +
|
|
19
|
+
"+ unrealized PnL at mark. The USDC balance already includes realized PnL, funding and ADL cashflows up to V2's latest market trackers.",
|
|
20
|
+
'Liquidation happens below the liquidation margin; liquidation margin usage = liquidation margin / equity (1 = liquidation).',
|
|
21
|
+
'Liquidation price per position: cross margin, every other mark, collateral price and funding unchanged. ' +
|
|
22
|
+
"It errs when a collateral's price follows the position's market (assumesFixedCollateral).",
|
|
23
|
+
'Not visible: matched fills that have not settled yet (seconds), and funding since the latest market trackers.',
|
|
24
|
+
];
|
|
25
|
+
function settled(snapshot) {
|
|
26
|
+
const margin = (0, engine_1.parseMargin)(snapshot.margin);
|
|
27
|
+
const projection = (0, engine_1.projectD)(margin, (0, decimal_1.parseDecimal)(snapshot.imMultiplier, 'IM multiplier'));
|
|
28
|
+
return { margin, projection };
|
|
29
|
+
}
|
|
30
|
+
/** Whether a held collateral's price follows `symbol`'s market (rUSD never does). */
|
|
31
|
+
function collateralFollows(snapshot, symbol) {
|
|
32
|
+
const market = (0, snapshot_1.marketUnderlying)(symbol);
|
|
33
|
+
return snapshot.collateral.some((c) => c.asset !== snapshot_1.RUSD &&
|
|
34
|
+
!(0, decimal_1.parseDecimal)(c.balance, `${c.asset} balance`).isZero() &&
|
|
35
|
+
(0, snapshot_1.collateralUnderlying)(c.asset) === market);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Liquidation margin, liquidation price and distance for one held market, on
|
|
39
|
+
* the settled account. `lmr` is the account's LMR when the caller already has
|
|
40
|
+
* it.
|
|
41
|
+
*/
|
|
42
|
+
function positionRisk(snapshot, symbol, lmr) {
|
|
43
|
+
const margin = (0, engine_1.parseMargin)(snapshot.margin);
|
|
44
|
+
const lm = lmr === undefined
|
|
45
|
+
? settled(snapshot).projection.lm
|
|
46
|
+
: (0, decimal_1.parseDecimal)(lmr, 'liquidation margin');
|
|
47
|
+
const risk = margin.markets.get(symbol);
|
|
48
|
+
const definition = snapshot.definitions[symbol];
|
|
49
|
+
if (risk === undefined || definition === undefined) {
|
|
50
|
+
throw new errors_1.AccountFiguresInputError(`No position or market data for ${symbol}`);
|
|
51
|
+
}
|
|
52
|
+
const price = (0, liquidation_1.liquidationPriceD)(margin, symbol, lm);
|
|
53
|
+
const tick = (0, decimal_1.parseDecimal)(definition.tickSize, `${symbol} tick size`);
|
|
54
|
+
const ownLm = (0, decimal_1.ceilTo)((0, decimal_1.ceilTo)(risk.marginRate.times((0, decimal_1.ceilTo)(risk.base.abs().times(risk.mark)))), (0, decimal_1.nativeUnit)(decimal_1.RUSD_DECIMALS));
|
|
55
|
+
const rounded = (0, liquidation_1.roundTowardMarkD)(price, risk, tick);
|
|
56
|
+
const distance = (0, liquidation_1.distanceToLiquidationD)(risk, price);
|
|
57
|
+
return {
|
|
58
|
+
lmr: (0, decimal_1.toText)(ownLm),
|
|
59
|
+
liquidationPrice: rounded === null ? null : (0, decimal_1.toText)(rounded),
|
|
60
|
+
liquidationPriceExact: price === null ? null : (0, decimal_1.toText)(price),
|
|
61
|
+
distanceToLiquidation: distance === null ? null : (0, decimal_1.toText)(distance),
|
|
62
|
+
assumesFixedCollateral: collateralFollows(snapshot, symbol),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
exports.positionRisk = positionRisk;
|
|
66
|
+
/** Account-level equity, requirements, usage and buffers, and each position's figures. */
|
|
67
|
+
function accountFigures(snapshot) {
|
|
68
|
+
const { margin, projection } = settled(snapshot);
|
|
69
|
+
const equity = margin.marginBalance;
|
|
70
|
+
const liquidationUsage = (0, liquidation_1.marginUsageD)(equity, projection.lm);
|
|
71
|
+
const initialUsage = (0, liquidation_1.marginUsageD)(equity, projection.im);
|
|
72
|
+
const lmr = (0, decimal_1.toText)(projection.lm);
|
|
73
|
+
const positions = snapshot.positions.map((detail) => {
|
|
74
|
+
const risk = margin.markets.get(detail.symbol);
|
|
75
|
+
if (risk === undefined) {
|
|
76
|
+
throw new errors_1.AccountFiguresInputError(`No position or market data for ${detail.symbol}`);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
...detail,
|
|
80
|
+
notional: (0, decimal_1.toText)(risk.base.abs().times(risk.mark)),
|
|
81
|
+
liquidationMarginRate: (0, decimal_1.toText)(risk.marginRate),
|
|
82
|
+
...positionRisk(snapshot, detail.symbol, lmr),
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
accountId: snapshot.accountId,
|
|
87
|
+
equity: (0, decimal_1.toText)(equity),
|
|
88
|
+
realBalance: (0, decimal_1.toText)(margin.realBalance),
|
|
89
|
+
unrealizedPnl: snapshot.unrealizedPnl,
|
|
90
|
+
lmr,
|
|
91
|
+
imr: (0, decimal_1.toText)(projection.im),
|
|
92
|
+
imMultiplier: snapshot.imMultiplier,
|
|
93
|
+
liquidationMarginUsage: liquidationUsage === null ? null : (0, decimal_1.toText)(liquidationUsage),
|
|
94
|
+
initialMarginUsage: initialUsage === null ? null : (0, decimal_1.toText)(initialUsage),
|
|
95
|
+
liquidatable: equity.lt(projection.lm),
|
|
96
|
+
marginBuffer: (0, decimal_1.toText)(equity.minus(projection.lm)),
|
|
97
|
+
availableMargin: (0, decimal_1.toText)(equity.minus(projection.im)),
|
|
98
|
+
collateral: snapshot.collateral,
|
|
99
|
+
positions,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
exports.accountFigures = accountFigures;
|
|
103
|
+
/** One account's figures straight from V2 reads. */
|
|
104
|
+
function computeAccountFigures(inputs) {
|
|
105
|
+
return accountFigures((0, snapshot_1.buildMarginSnapshot)(inputs));
|
|
106
|
+
}
|
|
107
|
+
exports.computeAccountFigures = computeAccountFigures;
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type Rounding = 'down' | 'up';
|
|
2
|
+
/** A USD amount to 2 dp, e.g. `formatUsd(equity)` or `formatUsd(lmr, 'up')`. */
|
|
3
|
+
export declare function formatUsd(value: string, rounding?: Rounding): string;
|
|
4
|
+
/**
|
|
5
|
+
* A ratio as a percentage to 2 dp, e.g. `formatPct(liquidationMarginUsage, 'up')` or
|
|
6
|
+
* `formatPct(distanceToLiquidation, 'down')`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatPct(ratio: string, rounding: Rounding): string;
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.formatPct = exports.formatUsd = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Display rounding, with the kit's conventions: cash, equity, PnL and buffers
|
|
9
|
+
* round down; requirements and margin usage round up; the distance to
|
|
10
|
+
* liquidation rounds down. Each rounding errs toward the riskier reading.
|
|
11
|
+
*/
|
|
12
|
+
const bignumber_js_1 = __importDefault(require("bignumber.js"));
|
|
13
|
+
const decimal_1 = require("./decimal");
|
|
14
|
+
function fixed2(value, rounding) {
|
|
15
|
+
const text = value.toFixed(2, rounding === 'down' ? bignumber_js_1.default.ROUND_FLOOR : bignumber_js_1.default.ROUND_CEIL);
|
|
16
|
+
return text === '-0.00' ? '0.00' : text;
|
|
17
|
+
}
|
|
18
|
+
/** A USD amount to 2 dp, e.g. `formatUsd(equity)` or `formatUsd(lmr, 'up')`. */
|
|
19
|
+
function formatUsd(value, rounding = 'down') {
|
|
20
|
+
return fixed2((0, decimal_1.parseDecimal)(value, 'USD amount'), rounding);
|
|
21
|
+
}
|
|
22
|
+
exports.formatUsd = formatUsd;
|
|
23
|
+
/**
|
|
24
|
+
* A ratio as a percentage to 2 dp, e.g. `formatPct(liquidationMarginUsage, 'up')` or
|
|
25
|
+
* `formatPct(distanceToLiquidation, 'down')`.
|
|
26
|
+
*/
|
|
27
|
+
function formatPct(ratio, rounding) {
|
|
28
|
+
return fixed2((0, decimal_1.parseDecimal)(ratio, 'ratio').times(100), rounding);
|
|
29
|
+
}
|
|
30
|
+
exports.formatPct = formatPct;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { AccountFiguresInputError } from './errors';
|
|
2
|
+
export { RUSD_DECIMALS } from './decimal';
|
|
3
|
+
export type { Decimalish } from './decimal';
|
|
4
|
+
export { admit, applyFill, INITIAL_MARGIN, LIQUIDATION_THRESHOLD, project, roundingAllowance, SETTLEMENT_RESERVE, SETTLEMENT_RESERVE_RATE, } from './engine';
|
|
5
|
+
export type { AccountMargin, Lot, MarketRisk, Projection, ProjectOptions, Rejection, RejectionCode, } from './engine';
|
|
6
|
+
export { distanceToLiquidation, liquidationPrice, marginUsage, roundTowardMark, } from './liquidation';
|
|
7
|
+
export { buildMarginSnapshot, imMultiplierFrom, RUSD } from './snapshot';
|
|
8
|
+
export type { CollateralRow, MarginInputs, MarginSnapshot, PositionDetail, V2AccountBalance, V2AssetDefinition, V2AssetOraclePrice, V2MarketDefinition, V2MarketSummary, V2Position, } from './snapshot';
|
|
9
|
+
export { accountFigures, computeAccountFigures, MARGIN_ASSUMPTIONS, positionRisk, } from './figures';
|
|
10
|
+
export type { AccountFigures, PositionFigures, PositionRisk } from './figures';
|
|
11
|
+
export { formatPct, formatUsd } from './format';
|
|
12
|
+
export type { Rounding } from './format';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatUsd = exports.formatPct = exports.positionRisk = exports.MARGIN_ASSUMPTIONS = exports.computeAccountFigures = exports.accountFigures = exports.RUSD = exports.imMultiplierFrom = exports.buildMarginSnapshot = exports.roundTowardMark = exports.marginUsage = exports.liquidationPrice = exports.distanceToLiquidation = exports.SETTLEMENT_RESERVE_RATE = exports.SETTLEMENT_RESERVE = exports.roundingAllowance = exports.project = exports.LIQUIDATION_THRESHOLD = exports.INITIAL_MARGIN = exports.applyFill = exports.admit = exports.RUSD_DECIMALS = exports.AccountFiguresInputError = void 0;
|
|
4
|
+
var errors_1 = require("./errors");
|
|
5
|
+
Object.defineProperty(exports, "AccountFiguresInputError", { enumerable: true, get: function () { return errors_1.AccountFiguresInputError; } });
|
|
6
|
+
var decimal_1 = require("./decimal");
|
|
7
|
+
Object.defineProperty(exports, "RUSD_DECIMALS", { enumerable: true, get: function () { return decimal_1.RUSD_DECIMALS; } });
|
|
8
|
+
var engine_1 = require("./engine");
|
|
9
|
+
Object.defineProperty(exports, "admit", { enumerable: true, get: function () { return engine_1.admit; } });
|
|
10
|
+
Object.defineProperty(exports, "applyFill", { enumerable: true, get: function () { return engine_1.applyFill; } });
|
|
11
|
+
Object.defineProperty(exports, "INITIAL_MARGIN", { enumerable: true, get: function () { return engine_1.INITIAL_MARGIN; } });
|
|
12
|
+
Object.defineProperty(exports, "LIQUIDATION_THRESHOLD", { enumerable: true, get: function () { return engine_1.LIQUIDATION_THRESHOLD; } });
|
|
13
|
+
Object.defineProperty(exports, "project", { enumerable: true, get: function () { return engine_1.project; } });
|
|
14
|
+
Object.defineProperty(exports, "roundingAllowance", { enumerable: true, get: function () { return engine_1.roundingAllowance; } });
|
|
15
|
+
Object.defineProperty(exports, "SETTLEMENT_RESERVE", { enumerable: true, get: function () { return engine_1.SETTLEMENT_RESERVE; } });
|
|
16
|
+
Object.defineProperty(exports, "SETTLEMENT_RESERVE_RATE", { enumerable: true, get: function () { return engine_1.SETTLEMENT_RESERVE_RATE; } });
|
|
17
|
+
var liquidation_1 = require("./liquidation");
|
|
18
|
+
Object.defineProperty(exports, "distanceToLiquidation", { enumerable: true, get: function () { return liquidation_1.distanceToLiquidation; } });
|
|
19
|
+
Object.defineProperty(exports, "liquidationPrice", { enumerable: true, get: function () { return liquidation_1.liquidationPrice; } });
|
|
20
|
+
Object.defineProperty(exports, "marginUsage", { enumerable: true, get: function () { return liquidation_1.marginUsage; } });
|
|
21
|
+
Object.defineProperty(exports, "roundTowardMark", { enumerable: true, get: function () { return liquidation_1.roundTowardMark; } });
|
|
22
|
+
var snapshot_1 = require("./snapshot");
|
|
23
|
+
Object.defineProperty(exports, "buildMarginSnapshot", { enumerable: true, get: function () { return snapshot_1.buildMarginSnapshot; } });
|
|
24
|
+
Object.defineProperty(exports, "imMultiplierFrom", { enumerable: true, get: function () { return snapshot_1.imMultiplierFrom; } });
|
|
25
|
+
Object.defineProperty(exports, "RUSD", { enumerable: true, get: function () { return snapshot_1.RUSD; } });
|
|
26
|
+
var figures_1 = require("./figures");
|
|
27
|
+
Object.defineProperty(exports, "accountFigures", { enumerable: true, get: function () { return figures_1.accountFigures; } });
|
|
28
|
+
Object.defineProperty(exports, "computeAccountFigures", { enumerable: true, get: function () { return figures_1.computeAccountFigures; } });
|
|
29
|
+
Object.defineProperty(exports, "MARGIN_ASSUMPTIONS", { enumerable: true, get: function () { return figures_1.MARGIN_ASSUMPTIONS; } });
|
|
30
|
+
Object.defineProperty(exports, "positionRisk", { enumerable: true, get: function () { return figures_1.positionRisk; } });
|
|
31
|
+
var format_1 = require("./format");
|
|
32
|
+
Object.defineProperty(exports, "formatPct", { enumerable: true, get: function () { return format_1.formatPct; } });
|
|
33
|
+
Object.defineProperty(exports, "formatUsd", { enumerable: true, get: function () { return format_1.formatUsd; } });
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Dec } from './decimal';
|
|
2
|
+
import { AccountMargin, MarginD, MarketRisk, RiskD } from './engine';
|
|
3
|
+
export declare function marginUsageD(marginBalance: Dec, lm: Dec): Dec | null;
|
|
4
|
+
/**
|
|
5
|
+
* Liquidation margin / equity: 1 is the liquidation line. `null` when equity
|
|
6
|
+
* is not positive.
|
|
7
|
+
*/
|
|
8
|
+
export declare function marginUsage(marginBalance: string, lm: string): string | null;
|
|
9
|
+
export declare function liquidationPriceD(margin: MarginD, market: string, lm: Dec): Dec | null;
|
|
10
|
+
/**
|
|
11
|
+
* Cross margin: the price in `market` at which equity equals the liquidation
|
|
12
|
+
* margin `lm` (the settled projection's), every other mark, collateral price
|
|
13
|
+
* and funding unchanged. `null` for a flat market and when no positive price
|
|
14
|
+
* is the liquidation line. Exact; see `roundTowardMark` for display.
|
|
15
|
+
*/
|
|
16
|
+
export declare function liquidationPrice(margin: AccountMargin, market: string, lm: string): string | null;
|
|
17
|
+
export declare function distanceToLiquidationD(risk: RiskD, price: Dec | null): Dec | null;
|
|
18
|
+
/**
|
|
19
|
+
* How far the mark must move against the position to reach `price`, as a
|
|
20
|
+
* fraction of the mark. Negative when the account is already past the
|
|
21
|
+
* liquidation line.
|
|
22
|
+
*/
|
|
23
|
+
export declare function distanceToLiquidation(risk: MarketRisk, price: string | null): string | null;
|
|
24
|
+
export declare function roundTowardMarkD(price: Dec | null, risk: RiskD, tick: Dec): Dec | null;
|
|
25
|
+
/**
|
|
26
|
+
* A liquidation price rounded onto the tick toward the mark, so it never reads
|
|
27
|
+
* farther away than it is: up for a long, down for a short.
|
|
28
|
+
*/
|
|
29
|
+
export declare function roundTowardMark(price: string | null, risk: MarketRisk, tick: string): string | null;
|