@strkfarm/sdk 2.0.0-dev.29 → 2.0.0-dev.30
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.browser.global.js +543 -258
- package/dist/index.browser.mjs +466 -181
- package/dist/index.d.ts +44 -25
- package/dist/index.js +466 -181
- package/dist/index.mjs +466 -181
- package/package.json +1 -1
- package/src/modules/ExtendedWrapperSDk/types.ts +1 -0
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +153 -11
- package/src/modules/ekubo-quoter.ts +2 -0
- package/src/strategies/universal-adapters/extended-adapter.ts +15 -14
- package/src/strategies/universal-adapters/vesu-adapter.ts +4 -1
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +26 -17
- package/src/strategies/vesu-extended-strategy/services/executionService.ts +15 -14
- package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +265 -134
- package/src/strategies/vesu-extended-strategy/services/ltv-imbalance-rebalance-math.ts +73 -20
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +59 -9
- package/src/utils/cacheClass.ts +11 -2
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Provides a clean interface to interact with the Extended Exchange trading API
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import BigNumber from "bignumber.js";
|
|
6
7
|
import {
|
|
7
8
|
CreateOrderRequest,
|
|
8
9
|
WithdrawRequest,
|
|
@@ -26,12 +27,88 @@ import {
|
|
|
26
27
|
UpdateLeverageRequest,
|
|
27
28
|
} from "./types";
|
|
28
29
|
|
|
30
|
+
type ExtendedTradingRules = {
|
|
31
|
+
minOrderSize: BigNumber;
|
|
32
|
+
qtyStep: BigNumber;
|
|
33
|
+
priceStep: BigNumber;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function asRecord(v: unknown): Record<string, unknown> | null {
|
|
37
|
+
return v !== null && typeof v === "object" && !Array.isArray(v)
|
|
38
|
+
? (v as Record<string, unknown>)
|
|
39
|
+
: null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function pickTradingConfig(row: Record<string, unknown>): Record<string, unknown> | null {
|
|
43
|
+
const tc = row.trading_config ?? row.tradingConfig;
|
|
44
|
+
return asRecord(tc);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function readTcString(
|
|
48
|
+
tc: Record<string, unknown>,
|
|
49
|
+
snake: string,
|
|
50
|
+
camel: string,
|
|
51
|
+
): string | undefined {
|
|
52
|
+
const v = tc[snake] ?? tc[camel];
|
|
53
|
+
if (v === undefined || v === null) return undefined;
|
|
54
|
+
return String(v).trim();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function tradingRulesFromMarketRow(
|
|
58
|
+
marketName: string,
|
|
59
|
+
row: unknown,
|
|
60
|
+
): ExtendedTradingRules {
|
|
61
|
+
const r = asRecord(row);
|
|
62
|
+
if (!r) {
|
|
63
|
+
throw new Error(`ExtendedWrapper: invalid market payload for ${marketName}`);
|
|
64
|
+
}
|
|
65
|
+
const tc = pickTradingConfig(r);
|
|
66
|
+
if (!tc) {
|
|
67
|
+
throw new Error(`ExtendedWrapper: missing tradingConfig for market ${marketName}`);
|
|
68
|
+
}
|
|
69
|
+
const minS = readTcString(tc, "min_order_size", "minOrderSize");
|
|
70
|
+
const qtyStep = readTcString(tc, "min_order_size_change", "minOrderSizeChange");
|
|
71
|
+
const pxStep = readTcString(tc, "min_price_change", "minPriceChange");
|
|
72
|
+
if (!minS || !qtyStep || !pxStep) {
|
|
73
|
+
throw new Error(`ExtendedWrapper: incomplete tradingConfig for market ${marketName}`);
|
|
74
|
+
}
|
|
75
|
+
const minOrderSize = new BigNumber(minS);
|
|
76
|
+
const qty = new BigNumber(qtyStep);
|
|
77
|
+
const px = new BigNumber(pxStep);
|
|
78
|
+
if (!minOrderSize.isFinite() || minOrderSize.lte(0)) {
|
|
79
|
+
throw new Error(`ExtendedWrapper: invalid minOrderSize for ${marketName}`);
|
|
80
|
+
}
|
|
81
|
+
if (!qty.isFinite() || qty.lte(0)) {
|
|
82
|
+
throw new Error(`ExtendedWrapper: invalid minOrderSizeChange for ${marketName}`);
|
|
83
|
+
}
|
|
84
|
+
if (!px.isFinite() || px.lte(0)) {
|
|
85
|
+
throw new Error(`ExtendedWrapper: invalid minPriceChange for ${marketName}`);
|
|
86
|
+
}
|
|
87
|
+
return { minOrderSize, qtyStep: qty, priceStep: px };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function roundToStepBn(value: BigNumber, step: BigNumber): BigNumber {
|
|
91
|
+
if (step.lte(0)) return value;
|
|
92
|
+
return value.div(step).round(0, BigNumber.ROUND_HALF_UP).times(step);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function formatBnForApi(bn: BigNumber, step: BigNumber): string {
|
|
96
|
+
const dp = Math.max(step.decimalPlaces() ?? 0, bn.decimalPlaces() ?? 0, 0);
|
|
97
|
+
return Number(bn.toFixed(Math.min(80, dp))).toString();
|
|
98
|
+
}
|
|
99
|
+
|
|
29
100
|
export class ExtendedWrapper {
|
|
30
101
|
private readUrl: string;
|
|
31
102
|
private writeUrl: string;
|
|
32
103
|
private apiKey?: string;
|
|
33
104
|
private timeout: number;
|
|
34
105
|
private retries: number;
|
|
106
|
+
/** Per-market rules from GET /markets (tradingConfig); retained for process lifetime (no TTL). */
|
|
107
|
+
private marketTradingRulesCache = new Map<string, ExtendedTradingRules>();
|
|
108
|
+
private marketTradingRulesInflight = new Map<
|
|
109
|
+
string,
|
|
110
|
+
Promise<ExtendedTradingRules>
|
|
111
|
+
>();
|
|
35
112
|
|
|
36
113
|
constructor(config: ExtendedWrapperConfig) {
|
|
37
114
|
this.apiKey = config.apiKey;
|
|
@@ -126,15 +203,80 @@ export class ExtendedWrapper {
|
|
|
126
203
|
throw lastError || new Error("Request failed after all retries");
|
|
127
204
|
}
|
|
128
205
|
|
|
206
|
+
private async resolveTradingRules(
|
|
207
|
+
marketName: string,
|
|
208
|
+
): Promise<ExtendedTradingRules> {
|
|
209
|
+
const cached = this.marketTradingRulesCache.get(marketName);
|
|
210
|
+
if (cached) return cached;
|
|
211
|
+
const existing = this.marketTradingRulesInflight.get(marketName);
|
|
212
|
+
if (existing) return existing;
|
|
213
|
+
const inflight = (async () => {
|
|
214
|
+
const res = await this.getMarkets(marketName);
|
|
215
|
+
if (res.status !== "OK") {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`ExtendedWrapper: getMarkets failed for ${marketName}: ${res.message}`,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
const rows = res.data;
|
|
221
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`ExtendedWrapper: empty markets response for ${marketName}`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
const row = rows.find((m) => asRecord(m)?.name === marketName);
|
|
227
|
+
if (!row) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`ExtendedWrapper: market ${marketName} not found in markets list`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
const rules = tradingRulesFromMarketRow(marketName, row);
|
|
233
|
+
this.marketTradingRulesCache.set(marketName, rules);
|
|
234
|
+
return rules;
|
|
235
|
+
})();
|
|
236
|
+
this.marketTradingRulesInflight.set(marketName, inflight);
|
|
237
|
+
void inflight.finally(() => {
|
|
238
|
+
if (this.marketTradingRulesInflight.get(marketName) === inflight) {
|
|
239
|
+
this.marketTradingRulesInflight.delete(marketName);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
return inflight;
|
|
243
|
+
}
|
|
244
|
+
|
|
129
245
|
/**
|
|
130
246
|
* Create a new order on Extended Exchange
|
|
131
247
|
*/
|
|
132
248
|
async createOrder(
|
|
133
249
|
request: CreateOrderRequest,
|
|
134
250
|
): Promise<ExtendedApiResponse<PlacedOrder>> {
|
|
251
|
+
const rules = await this.resolveTradingRules(request.market_name);
|
|
252
|
+
const amountBn = new BigNumber(String(request.amount).trim());
|
|
253
|
+
const priceBn = new BigNumber(String(request.price).trim());
|
|
254
|
+
if (!amountBn.isFinite() || amountBn.lte(0)) {
|
|
255
|
+
throw new Error(`ExtendedWrapper: invalid order amount=${request.amount}`);
|
|
256
|
+
}
|
|
257
|
+
if (!priceBn.isFinite() || priceBn.lte(0)) {
|
|
258
|
+
throw new Error(`ExtendedWrapper: invalid order price=${request.price}`);
|
|
259
|
+
}
|
|
260
|
+
if (amountBn.lt(rules.minOrderSize)) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`ExtendedWrapper: order amount ${request.amount} is below minOrderSize ${rules.minOrderSize.toFixed()} for ${request.market_name}`,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
const adjAmount = roundToStepBn(amountBn, rules.qtyStep);
|
|
266
|
+
const adjPrice = roundToStepBn(priceBn, rules.priceStep);
|
|
267
|
+
if (adjAmount.lt(rules.minOrderSize)) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`ExtendedWrapper: amount after tick rounding ${formatBnForApi(adjAmount, rules.qtyStep)} is below minOrderSize ${rules.minOrderSize.toFixed()} (${request.market_name})`,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
const payload: CreateOrderRequest = {
|
|
273
|
+
...request,
|
|
274
|
+
amount: formatBnForApi(adjAmount, rules.qtyStep),
|
|
275
|
+
price: formatBnForApi(adjPrice, rules.priceStep),
|
|
276
|
+
};
|
|
135
277
|
return this.makeRequest<PlacedOrder>("/api/v1/orders", false, {
|
|
136
278
|
method: "POST",
|
|
137
|
-
body: JSON.stringify(
|
|
279
|
+
body: JSON.stringify(payload),
|
|
138
280
|
});
|
|
139
281
|
}
|
|
140
282
|
|
|
@@ -323,11 +465,11 @@ export class ExtendedWrapper {
|
|
|
323
465
|
amount: string,
|
|
324
466
|
price: string,
|
|
325
467
|
options: {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
468
|
+
post_only?: boolean;
|
|
469
|
+
reduce_only?: boolean;
|
|
470
|
+
previous_order_id?: number;
|
|
471
|
+
external_id?: string;
|
|
472
|
+
time_in_force?: TimeInForce;
|
|
331
473
|
} = {},
|
|
332
474
|
): Promise<ExtendedApiResponse<PlacedOrder>> {
|
|
333
475
|
return this.createOrder({
|
|
@@ -356,11 +498,11 @@ export class ExtendedWrapper {
|
|
|
356
498
|
amount: string,
|
|
357
499
|
price: string,
|
|
358
500
|
options: {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
501
|
+
post_only?: boolean;
|
|
502
|
+
reduce_only?: boolean;
|
|
503
|
+
previous_order_id?: number;
|
|
504
|
+
external_id?: string;
|
|
505
|
+
time_in_force?: TimeInForce;
|
|
364
506
|
} = {},
|
|
365
507
|
): Promise<ExtendedApiResponse<PlacedOrder>> {
|
|
366
508
|
return this.createOrder({
|
|
@@ -46,6 +46,8 @@ export class EkuboQuoter {
|
|
|
46
46
|
const url = this.ENDPOINT.replace("{{AMOUNT}}", amount.toWei()).replace("{{TOKEN_FROM_ADDRESS}}", fromToken).replace("{{TOKEN_TO_ADDRESS}}", toToken);
|
|
47
47
|
logger.verbose(`EkuboQuoter::_callQuoterApi url: ${url}`);
|
|
48
48
|
const quote = await axios.get(url);
|
|
49
|
+
// console.log('quote', quote.data);
|
|
50
|
+
// console.log('')
|
|
49
51
|
return quote.data as EkuboQuote;
|
|
50
52
|
} catch (error: any) {
|
|
51
53
|
logger.error(`EkuboQuoter::_callQuoterApi error: ${error.message}`);
|
|
@@ -519,14 +519,15 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
519
519
|
logger.error("error initializing client");
|
|
520
520
|
return null;
|
|
521
521
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
522
|
+
// todo, instead get levgerage and log errors if not ideal lever already
|
|
523
|
+
// const setLeverage = await this.setLeverage(
|
|
524
|
+
// leverage,
|
|
525
|
+
// this.config.extendedMarketName,
|
|
526
|
+
// );
|
|
527
|
+
// if (!setLeverage) {
|
|
528
|
+
// logger.error("error depositing or setting leverage");
|
|
529
|
+
// return null;
|
|
530
|
+
// }
|
|
530
531
|
const { ask, bid } = await this.fetchOrderBookFromExtended();
|
|
531
532
|
if (
|
|
532
533
|
!ask ||
|
|
@@ -705,14 +706,14 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
705
706
|
const result =
|
|
706
707
|
side === OrderSide.SELL
|
|
707
708
|
? await client.createSellOrder(marketName, amount, price, {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
709
|
+
post_only: false,
|
|
710
|
+
reduce_only: false,
|
|
711
|
+
time_in_force: TimeInForce.IOC,
|
|
711
712
|
})
|
|
712
713
|
: await client.createBuyOrder(marketName, amount, price, {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
714
|
+
post_only: false,
|
|
715
|
+
reduce_only: true,
|
|
716
|
+
time_in_force: TimeInForce.IOC,
|
|
716
717
|
});
|
|
717
718
|
if (result.data.id) {
|
|
718
719
|
const position_id = result.data.id.toString();
|
|
@@ -648,7 +648,7 @@ export class VesuAdapter extends CacheClass {
|
|
|
648
648
|
throw new Error('LTV is 0');
|
|
649
649
|
}
|
|
650
650
|
this.setCache(CACHE_KEY, ltv, 300000); // ttl: 5min
|
|
651
|
-
return
|
|
651
|
+
return ltv;
|
|
652
652
|
}
|
|
653
653
|
|
|
654
654
|
async getPositions(config: IConfig, blockNumber: BlockIdentifier = 'latest'): Promise<VaultPosition[]> {
|
|
@@ -752,6 +752,9 @@ export class VesuAdapter extends CacheClass {
|
|
|
752
752
|
debtPrice = (await pricer.getPrice(this.config.debt.priceProxySymbol || this.config.debt.symbol)).price;
|
|
753
753
|
}
|
|
754
754
|
logger.verbose(`VesuAdapter::getAssetPrices collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
755
|
+
if (isNaN(collateralPrice) || isNaN(debtPrice) || collateralPrice == 0 || debtPrice == 0) {
|
|
756
|
+
throw new Error(`VesuAdapter::getAssetPrices collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
757
|
+
}
|
|
755
758
|
return {
|
|
756
759
|
collateralTokenAmount,
|
|
757
760
|
collateralUSDAmount,
|
|
@@ -196,25 +196,34 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
private _buildZeroAmountSwapsWithWeights(
|
|
199
|
-
quote:
|
|
199
|
+
quote: EkuboQuote,
|
|
200
200
|
token: TokenInfo,
|
|
201
201
|
reverseRoutes = false,
|
|
202
202
|
): { swaps: Swap[]; weights: Web3Number[] } {
|
|
203
203
|
const swaps: Swap[] = quote.splits.map((split) => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
204
|
+
let sellToken = token.address;
|
|
205
|
+
const routeNodes = split.route.map((_route: any) => {
|
|
206
|
+
// switch to other for next route.
|
|
207
|
+
let isSellToken0 = sellToken.eqString(_route.pool_key.token0);
|
|
208
|
+
isSellToken0 = reverseRoutes ? !isSellToken0 : isSellToken0;
|
|
209
|
+
sellToken = isSellToken0 ? ContractAddr.from(_route.pool_key.token1) : ContractAddr.from(_route.pool_key.token0);
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
pool_key: {
|
|
213
|
+
token0: ContractAddr.from(_route.pool_key.token0),
|
|
214
|
+
token1: ContractAddr.from(_route.pool_key.token1),
|
|
215
|
+
fee: _route.pool_key.fee,
|
|
216
|
+
tick_spacing: _route.pool_key.tick_spacing.toString(),
|
|
217
|
+
extension: _route.pool_key.extension,
|
|
218
|
+
},
|
|
219
|
+
sqrt_ratio_limit: isSellToken0 ? Web3Number.fromWei(MIN_SQRT_RATIO_LIMIT.toString(), 18) : Web3Number.fromWei(MAX_SQRT_RATIO_LIMIT.toString(), 18),
|
|
220
|
+
skip_ahead: Web3Number.fromWei(_route.skip_ahead, 0),
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
// incorrect logic, hence commented. Ekubo already returns routes in correct order.
|
|
224
|
+
// if (reverseRoutes) {
|
|
225
|
+
// routeNodes.reverse();
|
|
226
|
+
// }
|
|
218
227
|
return {
|
|
219
228
|
route: routeNodes,
|
|
220
229
|
token_amount: {
|
|
@@ -684,7 +693,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
684
693
|
dexPrice
|
|
685
694
|
);
|
|
686
695
|
|
|
687
|
-
logger.
|
|
696
|
+
logger.info(
|
|
688
697
|
`${VesuMultiplyAdapter.name}::_getIncreaseCalldata debtAmount: ${debtAmount}, addedCollateral: ${addedCollateral}`
|
|
689
698
|
);
|
|
690
699
|
let leverSwap: Swap[] = [];
|
|
@@ -738,7 +747,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
738
747
|
await new Promise((resolve) => setTimeout(resolve, 10000));
|
|
739
748
|
} else {
|
|
740
749
|
throw new Error(
|
|
741
|
-
`VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap`
|
|
750
|
+
`VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap, debtAmount=${debtAmount.toNumber()}, collateralPrice=${collateralPrice}, debtPrice=${debtPrice}`
|
|
742
751
|
);
|
|
743
752
|
}
|
|
744
753
|
} catch (error) {
|
|
@@ -1082,14 +1082,14 @@ export class ExecutionService {
|
|
|
1082
1082
|
}
|
|
1083
1083
|
|
|
1084
1084
|
// set extended leverage
|
|
1085
|
-
const setLevResult = await this._config.extendedAdapter.setLeverage(calculateExtendedLevergae().toString(), this._config.extendedAdapter.config.extendedMarketName);
|
|
1086
|
-
if (!setLevResult) {
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
}
|
|
1085
|
+
// const setLevResult = await this._config.extendedAdapter.setLeverage(calculateExtendedLevergae().toString(), this._config.extendedAdapter.config.extendedMarketName);
|
|
1086
|
+
// if (!setLevResult) {
|
|
1087
|
+
// logger.error(
|
|
1088
|
+
// `${this._tag}::_executeCoordinatedCase failed to set leverage`,
|
|
1089
|
+
// );
|
|
1090
|
+
// results.push(this._failureResult(extendedRoute));
|
|
1091
|
+
// return results;
|
|
1092
|
+
// }
|
|
1093
1093
|
|
|
1094
1094
|
const isIncrease = ExecutionService.INCREASE_EXPOSURE_ROUTES.has(
|
|
1095
1095
|
extendedRoute.type,
|
|
@@ -1116,8 +1116,9 @@ export class ExecutionService {
|
|
|
1116
1116
|
onChainCalls = callArrays.flat();
|
|
1117
1117
|
} catch (err) {
|
|
1118
1118
|
logger.error(
|
|
1119
|
-
`${this._tag}::_executeCoordinatedCase on-chain call construction failed
|
|
1119
|
+
`${this._tag}::_executeCoordinatedCase on-chain call construction failed:`,
|
|
1120
1120
|
);
|
|
1121
|
+
console.error(err);
|
|
1121
1122
|
await this._emitEvent(ExecutionEventType.FAILURE, {
|
|
1122
1123
|
routeSummary: `coordinated on-chain build for case "${caseEntry.case.id}"`,
|
|
1123
1124
|
error: `${err}`,
|
|
@@ -1207,7 +1208,7 @@ export class ExecutionService {
|
|
|
1207
1208
|
const timeoutMs =
|
|
1208
1209
|
this._config.extendedFillTimeoutMs ?? DEFAULT_EXTENDED_FILL_TIMEOUT_MS;
|
|
1209
1210
|
const extResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
1210
|
-
btcAmount,
|
|
1211
|
+
Math.abs(btcAmount),
|
|
1211
1212
|
executionPrice,
|
|
1212
1213
|
side,
|
|
1213
1214
|
{
|
|
@@ -1873,7 +1874,7 @@ export class ExecutionService {
|
|
|
1873
1874
|
return this._failureResult(route);
|
|
1874
1875
|
}
|
|
1875
1876
|
const closeResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
1876
|
-
positionToClose.toNumber(),
|
|
1877
|
+
Math.abs(positionToClose.toNumber()),
|
|
1877
1878
|
midPrice,
|
|
1878
1879
|
OrderSide.BUY,
|
|
1879
1880
|
);
|
|
@@ -1894,7 +1895,7 @@ export class ExecutionService {
|
|
|
1894
1895
|
);
|
|
1895
1896
|
// todo need to ensure this one passes for sure
|
|
1896
1897
|
const reopenResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
1897
|
-
positionToClose.toNumber(),
|
|
1898
|
+
Math.abs(positionToClose.toNumber()),
|
|
1898
1899
|
midPrice,
|
|
1899
1900
|
OrderSide.SELL,
|
|
1900
1901
|
);
|
|
@@ -1957,7 +1958,7 @@ export class ExecutionService {
|
|
|
1957
1958
|
return this._failureResult(route);
|
|
1958
1959
|
}
|
|
1959
1960
|
const result = await this._executeExtendedLimitOrderWithRecovery(
|
|
1960
|
-
route.amount.toNumber(),
|
|
1961
|
+
Math.abs(route.amount.toNumber()),
|
|
1961
1962
|
midPrice,
|
|
1962
1963
|
OrderSide.SELL,
|
|
1963
1964
|
);
|
|
@@ -2013,7 +2014,7 @@ export class ExecutionService {
|
|
|
2013
2014
|
return this._failureResult(route);
|
|
2014
2015
|
}
|
|
2015
2016
|
const result = await this._executeExtendedLimitOrderWithRecovery(
|
|
2016
|
-
route.amount.toNumber(),
|
|
2017
|
+
Math.abs(route.amount.toNumber()),
|
|
2017
2018
|
midPrice,
|
|
2018
2019
|
OrderSide.BUY,
|
|
2019
2020
|
);
|