commit
eefd1f95bc
|
@ -12,6 +12,7 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blockworks-foundation/mango-client": "^3.0.13",
|
"@blockworks-foundation/mango-client": "^3.0.13",
|
||||||
|
"ansi-regex": "5.0.1",
|
||||||
"big.js": "^6.1.1",
|
"big.js": "^6.1.1",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
|
|
|
@ -8,6 +8,9 @@ import {
|
||||||
getMultipleAccounts,
|
getMultipleAccounts,
|
||||||
getTokenBySymbol,
|
getTokenBySymbol,
|
||||||
GroupConfig,
|
GroupConfig,
|
||||||
|
makeCancelPerpOrderInstruction,
|
||||||
|
makeCancelSpotOrderInstruction,
|
||||||
|
makeSettleFundsInstruction,
|
||||||
MangoAccount,
|
MangoAccount,
|
||||||
MangoClient,
|
MangoClient,
|
||||||
MangoGroup,
|
MangoGroup,
|
||||||
|
@ -15,6 +18,7 @@ import {
|
||||||
PerpMarket,
|
PerpMarket,
|
||||||
PerpMarketLayout,
|
PerpMarketLayout,
|
||||||
PerpOrder,
|
PerpOrder,
|
||||||
|
QUOTE_INDEX,
|
||||||
} from "@blockworks-foundation/mango-client";
|
} from "@blockworks-foundation/mango-client";
|
||||||
import { Market, Orderbook } from "@project-serum/serum";
|
import { Market, Orderbook } from "@project-serum/serum";
|
||||||
import { Order } from "@project-serum/serum/lib/market";
|
import { Order } from "@project-serum/serum/lib/market";
|
||||||
|
@ -32,6 +36,7 @@ import os from "os";
|
||||||
import { OrderInfo } from "types";
|
import { OrderInfo } from "types";
|
||||||
import { logger, zipDict } from "./utils";
|
import { logger, zipDict } from "./utils";
|
||||||
import BN from "bn.js";
|
import BN from "bn.js";
|
||||||
|
import { Transaction } from "@solana/web3.js";
|
||||||
|
|
||||||
class MangoSimpleClient {
|
class MangoSimpleClient {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -422,15 +427,37 @@ class MangoSimpleClient {
|
||||||
public async cancelAllOrders(): Promise<void> {
|
public async cancelAllOrders(): Promise<void> {
|
||||||
const allMarkets = await this.fetchAllMarkets();
|
const allMarkets = await this.fetchAllMarkets();
|
||||||
const orders = (await this.fetchAllBidsAndAsks(true)).flat();
|
const orders = (await this.fetchAllBidsAndAsks(true)).flat();
|
||||||
// todo combine multiple cancels into one transaction
|
|
||||||
await Promise.all(
|
const transactions = await Promise.all(
|
||||||
orders.map((orderInfo) =>
|
orders.map((orderInfo) =>
|
||||||
this.cancelOrder(
|
this.buildCancelOrderTransaction(
|
||||||
orderInfo,
|
orderInfo,
|
||||||
allMarkets[orderInfo.market.account.publicKey.toBase58()]
|
allMarkets[orderInfo.market.account.publicKey.toBase58()]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let i, j;
|
||||||
|
// assuming we can fit 10 cancel order transactions in a solana transaction
|
||||||
|
// we could switch to computing actual transactionSize every time we add an
|
||||||
|
// instruction and use a dynamic chunk size
|
||||||
|
const chunk = 10;
|
||||||
|
const transactionsToSend: Transaction[] = [];
|
||||||
|
|
||||||
|
for (i = 0, j = transactions.length; i < j; i += chunk) {
|
||||||
|
const transactionsChunk = transactions.slice(i, i + chunk);
|
||||||
|
const transactionToSend = new Transaction();
|
||||||
|
for (const transaction of transactionsChunk) {
|
||||||
|
for (const instruction of transaction.instructions) {
|
||||||
|
transactionToSend.add(instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transactionsToSend.push(transactionToSend);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const transaction of transactionsToSend) {
|
||||||
|
await this.client.sendTransaction(transaction, this.owner, []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async cancelOrder(orderInfo: OrderInfo, market?: Market | PerpMarket) {
|
public async cancelOrder(orderInfo: OrderInfo, market?: Market | PerpMarket) {
|
||||||
|
@ -479,6 +506,55 @@ class MangoSimpleClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async buildCancelOrderTransaction(
|
||||||
|
orderInfo: OrderInfo,
|
||||||
|
market?: Market | PerpMarket
|
||||||
|
): Promise<Transaction> {
|
||||||
|
if (orderInfo.market.config.kind === "perp") {
|
||||||
|
const perpMarketConfig = getMarketByBaseSymbolAndKind(
|
||||||
|
this.mangoGroupConfig,
|
||||||
|
orderInfo.market.config.baseSymbol,
|
||||||
|
"perp"
|
||||||
|
);
|
||||||
|
if (market === undefined) {
|
||||||
|
market = await this.mangoGroup.loadPerpMarket(
|
||||||
|
this.connection,
|
||||||
|
perpMarketConfig.marketIndex,
|
||||||
|
perpMarketConfig.baseDecimals,
|
||||||
|
perpMarketConfig.quoteDecimals
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this.buildCancelPerpOrderInstruction(
|
||||||
|
this.mangoGroup,
|
||||||
|
this.mangoAccount,
|
||||||
|
this.owner,
|
||||||
|
market as PerpMarket,
|
||||||
|
orderInfo.order as PerpOrder
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const spotMarketConfig = getMarketByBaseSymbolAndKind(
|
||||||
|
this.mangoGroupConfig,
|
||||||
|
orderInfo.market.config.baseSymbol,
|
||||||
|
"spot"
|
||||||
|
);
|
||||||
|
if (market === undefined) {
|
||||||
|
market = await Market.load(
|
||||||
|
this.connection,
|
||||||
|
spotMarketConfig.publicKey,
|
||||||
|
undefined,
|
||||||
|
this.mangoGroupConfig.serumProgramId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return await this.buildCancelSpotOrderTransaction(
|
||||||
|
this.mangoGroup,
|
||||||
|
this.mangoAccount,
|
||||||
|
this.owner,
|
||||||
|
market as Market,
|
||||||
|
orderInfo.order as Order
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async getOrderByOrderId(orderId: string): Promise<OrderInfo[]> {
|
public async getOrderByOrderId(orderId: string): Promise<OrderInfo[]> {
|
||||||
const orders = (await this.fetchAllBidsAndAsks(true)).flat();
|
const orders = (await this.fetchAllBidsAndAsks(true)).flat();
|
||||||
const orderInfos = orders.filter(
|
const orderInfos = orders.filter(
|
||||||
|
@ -580,6 +656,102 @@ class MangoSimpleClient {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private buildCancelPerpOrderInstruction(
|
||||||
|
mangoGroup: MangoGroup,
|
||||||
|
mangoAccount: MangoAccount,
|
||||||
|
owner: Account,
|
||||||
|
perpMarket: PerpMarket,
|
||||||
|
order: PerpOrder,
|
||||||
|
invalidIdOk = false // Don't throw error if order is invalid
|
||||||
|
): Transaction {
|
||||||
|
const instruction = makeCancelPerpOrderInstruction(
|
||||||
|
this.mangoGroupConfig.mangoProgramId,
|
||||||
|
mangoGroup.publicKey,
|
||||||
|
mangoAccount.publicKey,
|
||||||
|
owner.publicKey,
|
||||||
|
perpMarket.publicKey,
|
||||||
|
perpMarket.bids,
|
||||||
|
perpMarket.asks,
|
||||||
|
order,
|
||||||
|
invalidIdOk
|
||||||
|
);
|
||||||
|
|
||||||
|
const transaction = new Transaction();
|
||||||
|
transaction.add(instruction);
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async buildCancelSpotOrderTransaction(
|
||||||
|
mangoGroup: MangoGroup,
|
||||||
|
mangoAccount: MangoAccount,
|
||||||
|
owner: Account,
|
||||||
|
spotMarket: Market,
|
||||||
|
order: Order
|
||||||
|
): Promise<Transaction> {
|
||||||
|
const transaction = new Transaction();
|
||||||
|
const instruction = makeCancelSpotOrderInstruction(
|
||||||
|
this.mangoGroupConfig.mangoProgramId,
|
||||||
|
mangoGroup.publicKey,
|
||||||
|
owner.publicKey,
|
||||||
|
mangoAccount.publicKey,
|
||||||
|
spotMarket.programId,
|
||||||
|
spotMarket.publicKey,
|
||||||
|
spotMarket["_decoded"].bids,
|
||||||
|
spotMarket["_decoded"].asks,
|
||||||
|
order.openOrdersAddress,
|
||||||
|
mangoGroup.signerKey,
|
||||||
|
spotMarket["_decoded"].eventQueue,
|
||||||
|
order
|
||||||
|
);
|
||||||
|
transaction.add(instruction);
|
||||||
|
|
||||||
|
const dexSigner = await PublicKey.createProgramAddress(
|
||||||
|
[
|
||||||
|
spotMarket.publicKey.toBuffer(),
|
||||||
|
spotMarket["_decoded"].vaultSignerNonce.toArrayLike(Buffer, "le", 8),
|
||||||
|
],
|
||||||
|
spotMarket.programId
|
||||||
|
);
|
||||||
|
|
||||||
|
const marketIndex = mangoGroup.getSpotMarketIndex(spotMarket.publicKey);
|
||||||
|
if (!mangoGroup.rootBankAccounts.length) {
|
||||||
|
await mangoGroup.loadRootBanks(this.connection);
|
||||||
|
}
|
||||||
|
const baseRootBank = mangoGroup.rootBankAccounts[marketIndex];
|
||||||
|
const quoteRootBank = mangoGroup.rootBankAccounts[QUOTE_INDEX];
|
||||||
|
const baseNodeBank = baseRootBank?.nodeBankAccounts[0];
|
||||||
|
const quoteNodeBank = quoteRootBank?.nodeBankAccounts[0];
|
||||||
|
|
||||||
|
if (!baseNodeBank || !quoteNodeBank) {
|
||||||
|
throw new Error("Invalid or missing node banks");
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo what is a makeSettleFundsInstruction?
|
||||||
|
const settleFundsInstruction = makeSettleFundsInstruction(
|
||||||
|
this.mangoGroupConfig.mangoProgramId,
|
||||||
|
mangoGroup.publicKey,
|
||||||
|
mangoGroup.mangoCache,
|
||||||
|
owner.publicKey,
|
||||||
|
mangoAccount.publicKey,
|
||||||
|
spotMarket.programId,
|
||||||
|
spotMarket.publicKey,
|
||||||
|
mangoAccount.spotOpenOrders[marketIndex],
|
||||||
|
mangoGroup.signerKey,
|
||||||
|
spotMarket["_decoded"].baseVault,
|
||||||
|
spotMarket["_decoded"].quoteVault,
|
||||||
|
mangoGroup.tokens[marketIndex].rootBank,
|
||||||
|
baseNodeBank.publicKey,
|
||||||
|
mangoGroup.tokens[QUOTE_INDEX].rootBank,
|
||||||
|
quoteNodeBank.publicKey,
|
||||||
|
baseNodeBank.vault,
|
||||||
|
quoteNodeBank.vault,
|
||||||
|
dexSigner
|
||||||
|
);
|
||||||
|
transaction.add(settleFundsInstruction);
|
||||||
|
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
private roundRobinClusterUrl() {
|
private roundRobinClusterUrl() {
|
||||||
if (process.env.CLUSTER_URL) {
|
if (process.env.CLUSTER_URL) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,8 +1,23 @@
|
||||||
import { Config, GroupConfig } from "@blockworks-foundation/mango-client";
|
import { Config, GroupConfig } from "@blockworks-foundation/mango-client";
|
||||||
import { I80F48 } from "@blockworks-foundation/mango-client/lib/src/fixednum";
|
import { I80F48 } from "@blockworks-foundation/mango-client/lib/src/fixednum";
|
||||||
import { CustomValidator } from "express-validator";
|
import { CustomValidator } from "express-validator";
|
||||||
/// logging related
|
|
||||||
import pino from "pino";
|
import pino from "pino";
|
||||||
|
import { Transaction, Connection, Account } from "@solana/web3.js";
|
||||||
|
|
||||||
|
/// solana related
|
||||||
|
|
||||||
|
export async function transactionSize(
|
||||||
|
connection: Connection,
|
||||||
|
singleTransaction: Transaction,
|
||||||
|
owner: Account
|
||||||
|
) {
|
||||||
|
singleTransaction.recentBlockhash = (
|
||||||
|
await connection.getRecentBlockhash()
|
||||||
|
).blockhash;
|
||||||
|
singleTransaction.setSigners(owner.publicKey);
|
||||||
|
singleTransaction.sign(this.owner);
|
||||||
|
return singleTransaction.serialize().length;
|
||||||
|
}
|
||||||
|
|
||||||
/// mango related
|
/// mango related
|
||||||
|
|
||||||
|
|
|
@ -335,16 +335,16 @@ ansi-align@^3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
string-width "^3.0.0"
|
string-width "^3.0.0"
|
||||||
|
|
||||||
|
ansi-regex@5.0.1, ansi-regex@^5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
|
||||||
|
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
|
||||||
|
|
||||||
ansi-regex@^4.1.0:
|
ansi-regex@^4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
|
||||||
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
|
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
|
||||||
|
|
||||||
ansi-regex@^5.0.1:
|
|
||||||
version "5.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
|
|
||||||
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
|
|
||||||
|
|
||||||
ansi-styles@^3.2.1:
|
ansi-styles@^3.2.1:
|
||||||
version "3.2.1"
|
version "3.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
from decimal import Decimal
|
||||||
|
from threading import Timer
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
@ -39,41 +41,41 @@ class MangoServiceV3Client:
|
||||||
else:
|
else:
|
||||||
self.BASE_URL = "http://localhost:3000/api"
|
self.BASE_URL = "http://localhost:3000/api"
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def get_open_positions(self) -> List[Position]:
|
def get_open_positions(self) -> List[Position]:
|
||||||
response = httpx.get(f"{self.BASE_URL}/positions", timeout=10.0)
|
response = httpx.get(f"{self.BASE_URL}/positions", timeout=10.0)
|
||||||
return parse_obj_as(List[Position], json.loads(response.text)["result"])
|
return parse_obj_as(List[Position], json.loads(response.text)["result"])
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def get_balances(self) -> List[Balance]:
|
def get_balances(self) -> List[Balance]:
|
||||||
response = httpx.get(f"{self.BASE_URL}/wallet/balances", timeout=10.0)
|
response = httpx.get(f"{self.BASE_URL}/wallet/balances", timeout=10.0)
|
||||||
return parse_obj_as(List[Balance], json.loads(response.text)["result"])
|
return parse_obj_as(List[Balance], json.loads(response.text)["result"])
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def get_markets(self) -> List[Market]:
|
def get_markets(self) -> List[Market]:
|
||||||
response = httpx.get(f"{self.BASE_URL}/markets", timeout=10.0)
|
response = httpx.get(f"{self.BASE_URL}/markets", timeout=10.0)
|
||||||
return parse_obj_as(List[Market], json.loads(response.text)["result"])
|
return parse_obj_as(List[Market], json.loads(response.text)["result"])
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def get_market_by_market_name(self, market_name: str) -> List[Market]:
|
def get_market_by_market_name(self, market_name: str) -> List[Market]:
|
||||||
response = httpx.get(f"{self.BASE_URL}/markets/{market_name}", timeout=10.0)
|
response = httpx.get(f"{self.BASE_URL}/markets/{market_name}", timeout=10.0)
|
||||||
return parse_obj_as(List[Market], json.loads(response.text)["result"])
|
return parse_obj_as(List[Market], json.loads(response.text)["result"])
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def get_orderboook(self, market_name: str, depth: int = 30) -> Orderbook:
|
def get_orderboook(self, market_name: str, depth: int = 30) -> Orderbook:
|
||||||
response = httpx.get(
|
response = httpx.get(
|
||||||
f"{self.BASE_URL}/markets/{market_name}/orderbook?depth={depth}"
|
f"{self.BASE_URL}/markets/{market_name}/orderbook?depth={depth}"
|
||||||
)
|
)
|
||||||
return parse_obj_as(Orderbook, json.loads(response.text)["result"])
|
return parse_obj_as(Orderbook, json.loads(response.text)["result"])
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def get_trades(self, market_name: str) -> List[Trade]:
|
def get_trades(self, market_name: str) -> List[Trade]:
|
||||||
response = httpx.get(
|
response = httpx.get(
|
||||||
f"{self.BASE_URL}/markets/{market_name}/trades", timeout=10.0
|
f"{self.BASE_URL}/markets/{market_name}/trades", timeout=10.0
|
||||||
)
|
)
|
||||||
return parse_obj_as(List[Trade], json.loads(response.text)["result"])
|
return parse_obj_as(List[Trade], json.loads(response.text)["result"])
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def get_candles(
|
def get_candles(
|
||||||
self, market_name: str, resolution: int, start_time: int, end_time: int
|
self, market_name: str, resolution: int, start_time: int, end_time: int
|
||||||
) -> List[Candle]:
|
) -> List[Candle]:
|
||||||
|
@ -82,19 +84,19 @@ class MangoServiceV3Client:
|
||||||
)
|
)
|
||||||
return parse_obj_as(List[Candle], json.loads(response.text)["result"])
|
return parse_obj_as(List[Candle], json.loads(response.text)["result"])
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def get_orders(self,) -> List[Order]:
|
def get_orders(self,) -> List[Order]:
|
||||||
response = httpx.get(f"{self.BASE_URL}/orders", timeout=10.0)
|
response = httpx.get(f"{self.BASE_URL}/orders", timeout=10.0)
|
||||||
return parse_obj_as(List[Order], json.loads(response.text)["result"])
|
return parse_obj_as(List[Order], json.loads(response.text)["result"])
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def get_orders_by_market_name(self, market_name: str) -> List[Order]:
|
def get_orders_by_market_name(self, market_name: str) -> List[Order]:
|
||||||
response = httpx.get(
|
response = httpx.get(
|
||||||
f"{self.BASE_URL}/orders?market={market_name}", timeout=10.0
|
f"{self.BASE_URL}/orders?market={market_name}", timeout=10.0
|
||||||
)
|
)
|
||||||
return parse_obj_as(List[Order], json.loads(response.text)["result"])
|
return parse_obj_as(List[Order], json.loads(response.text)["result"])
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def place_order(self, order: PlaceOrder) -> None:
|
def place_order(self, order: PlaceOrder) -> None:
|
||||||
response = httpx.post(
|
response = httpx.post(
|
||||||
f"{self.BASE_URL}/orders", json=order.dict(by_alias=True), timeout=10.0
|
f"{self.BASE_URL}/orders", json=order.dict(by_alias=True), timeout=10.0
|
||||||
|
@ -104,17 +106,17 @@ class MangoServiceV3Client:
|
||||||
# List[BadRequestError], json.loads(response.text)["errors"]
|
# List[BadRequestError], json.loads(response.text)["errors"]
|
||||||
# )
|
# )
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def cancel_order_by_client_id(self, client_id):
|
def cancel_order_by_client_id(self, client_id):
|
||||||
response = httpx.delete(
|
response = httpx.delete(
|
||||||
f"{self.BASE_URL}/orders/by_client_id/{client_id}", timeout=10.0
|
f"{self.BASE_URL}/orders/by_client_id/{client_id}", timeout=10.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def cancel_order_by_order_id(self, order_id):
|
def cancel_order_by_order_id(self, order_id):
|
||||||
response = httpx.delete(f"{self.BASE_URL}/orders/{order_id}", timeout=10.0)
|
response = httpx.delete(f"{self.BASE_URL}/orders/{order_id}", timeout=10.0)
|
||||||
|
|
||||||
@delayed(os.environ["DELAY"])
|
@delayed(2)
|
||||||
def cancel_all_orders(self):
|
def cancel_all_orders(self):
|
||||||
response = httpx.delete(f"{self.BASE_URL}/orders", timeout=10.0)
|
response = httpx.delete(f"{self.BASE_URL}/orders", timeout=10.0)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue