Add routes (#2)

* Start adding routes

* Finish adding routes
This commit is contained in:
Nathaniel Parke 2020-11-12 11:44:18 +08:00 committed by GitHub
parent 02e960d445
commit 564e6461cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 290 additions and 9 deletions

View File

@ -1,2 +1,3 @@
lib/
node_modules/
secrets.json

View File

@ -12,7 +12,8 @@ import {
import {
Coin,
Dir,
Exchange, Fill,
Exchange,
Fill,
L2OrderBook,
MarketInfo,
Order,
@ -743,6 +744,27 @@ export class SerumApi {
);
}
async cancelByClientId(
orderId: string,
coin: Coin,
priceCurrency: Coin
): Promise<void> {
const { transaction, signers } = await this.makeCancelByClientIdTransaction(
orderId,
coin,
priceCurrency
);
const txid = await this.sendTransaction(
transaction,
signers,
DEFAULT_TIMEOUT,
() => {}
);
logger.debug(
`finished sending cancel transaction:\n${orderId}\ntxid ${txid}`
);
}
async makeCancelByClientIdTransaction(
orderId: string,
coin: Coin,
@ -768,7 +790,8 @@ export class SerumApi {
// Assume we sent with lowest sort open orders account
account = accountsForMarket.sort(this.compareOpenOrdersAccounts)[0];
logger.debug(
`Did not find order (${orderId}) in open order accounts. Using ${account.publicKey.toBase58()} as account.`
`Did not find order (${orderId}) in open order accounts.
Using ${account.publicKey.toBase58()} as account.`
);
}
logger.info(
@ -792,6 +815,59 @@ export class SerumApi {
};
}
async cancelByStandardOrderId(
orderId: string,
coin: Coin,
priceCurrency: Coin
): Promise<void> {
const {
transaction,
signers,
} = await this.makeCancelByStandardIdTransaction(
orderId,
coin,
priceCurrency
);
const txid = await this._connection.sendTransaction(transaction, signers, {
skipPreflight: true,
});
await this.awaitTransactionSignatureConfirmation(txid);
}
async makeCancelByStandardIdTransaction(
orderId: string,
coin: Coin,
priceCurrency: Coin
): Promise<{ transaction: Transaction; signers: Account[] }> {
const market = new Pair(coin, priceCurrency);
let order = this.getOrderFromOwnOrdersCache(orderId, market);
if (!order) {
this.getOwnOrders(coin, priceCurrency);
order = this.getOrderFromOwnOrdersCache(orderId, market);
if (!order) {
throw Error("Could not find order for cancellation.");
}
}
logger.info(
`Cancelling ${orderId} ${coin} ${priceCurrency} using orderId ${order.info.orderId}`
);
const serumMarket = await this.getMarketFromAddress(
this.getMarketAddress(coin, priceCurrency)
);
const transaction = await serumMarket.makeCancelOrderTransaction(
this._connection,
this._publicKey,
order.info.toSerumOrder()
);
transaction.add(serumMarket.makeMatchOrdersTransaction(5));
const signers = [new Account(this._privateKey)];
return {
transaction,
signers,
};
}
getOrderFromOwnOrdersCache(
orderId: string,
market: Pair
@ -910,11 +986,7 @@ export class SerumApi {
).then((fills) => fills.reduce((acc, curr) => [...acc, ...curr]));
}
parseRawFills(
rawFills: RawTrade[],
coin: Coin,
priceCurrency: Coin
): Fill[] {
parseRawFills(rawFills: RawTrade[], coin: Coin, priceCurrency: Coin): Fill[] {
const time = getUnixTs();
const parseFill = (rawFill): Fill => {
return {

View File

@ -1,7 +1,8 @@
import express from "express";
import { SerumApi } from "./exchange/api";
import expressAsyncHandler from "express-async-handler";
import { logger } from "./utils";
import { DirUtil, logger } from "./utils";
import { SerumApi } from "./exchange";
import { Coin, OrderType } from "./exchange/types";
const router = express.Router();
let api: SerumApi;
@ -32,4 +33,211 @@ router.get(
})
);
router.get(
"/orderbook/:coin-:quote",
expressAsyncHandler(async (req, res, next) => {
logger.info("Received request to api getOrderbook");
api
.getWsOrderBook(req.params.coin, req.params.quote)
.then((orderBook) => res.send({ status: "ok", data: orderBook }))
.catch((err) => next(err));
})
);
router.get(
"/trades/:coin-:quote",
expressAsyncHandler(async (req, res, next) => {
logger.info("Received request to api getTrades");
api
.getTrades(req.params.coin, req.params.quote)
.then((trades) => res.send({ status: "ok", data: trades }))
.catch((err) => {
logger.info(err);
next(err);
});
})
);
router.post(
"/place_order",
expressAsyncHandler(async (req, res, next) => {
logger.info(`Order parameters ${JSON.stringify(req.body)}`);
api
.placeOrder(
DirUtil.parse(req.body.side),
req.body.coin,
req.body.priceCurrency,
req.body.quantity,
req.body.price,
OrderType[req.body.orderType],
{
clientId: req.body.clientId,
orderEdge: req.body.orderEdge,
}
)
.then((id) => res.send({ status: "ok", data: { id: id } }))
.catch((err) => {
logger.log("error", `${req.params.exchange} make_order error: ${err}`);
try {
const body = {
status: "error",
data: {
errorType: err.name,
errorMessage: err.message || JSON.stringify(err),
stack: (err.stack && err.stack.toString()) || JSON.stringify(err),
},
};
res.send(body);
} catch (e) {
try {
const body = {
status: "error",
data: { errorMessage: err, name: undefined, stack: undefined },
};
res.send(body);
} catch (f) {
next(err);
}
}
});
})
);
router.post(
"/cancel",
expressAsyncHandler(async (req, res, next) => {
const coin = req.body.coin;
const priceCurrency = req.body.priceCurrency;
const orderId = req.body.orderId;
const clientOrderId = req.body.clientOrderId;
if (!coin) {
const body = {
status: "error",
data: {
errorMessage: "Coin parameter missing from cancel request",
},
};
res.send(body);
} else if (!priceCurrency) {
const body = {
status: "error",
data: {
errorMessage: "Price currency parameter missing from cancel request",
},
};
res.send(body);
} else if (!orderId && !clientOrderId) {
const body = {
status: "error",
data: {
errorMessage:
"Order id and client order id missing from cancel request",
},
};
res.send(body);
}
let cancelFn: (
orderId: string,
coin: Coin,
priceCurrency: Coin
) => Promise<void>;
if (clientOrderId) {
cancelFn = api.cancelByClientId;
} else {
cancelFn = api.cancelByStandardOrderId;
}
cancelFn(req.params.orderId, req.params.coin, req.params.priceCurrency)
.then((result) => res.send({ status: "ok", data: {} }))
.catch((err) => {
logger.error(
`${req.params.coin}/${req.params.priceCurrency} cancel
${req.params.orderId} received error ${JSON.stringify(err)}`
);
try {
const body = {
status: "error",
data: {
errorType: err.name,
errorMessage: err.message || JSON.stringify(err),
stack: (err.stack && err.stack.toString()) || JSON.stringify(err),
},
};
res.send(body);
} catch (e) {
try {
const body = {
status: "error",
data: { errorMessage: err, name: undefined, stack: undefined },
};
res.send(body);
} catch (f) {
next(err);
}
}
});
})
);
router.get(
"/own_orders/:coin-:quote",
expressAsyncHandler(async (req, res, next) => {
api
.getOwnOrders(req.params.coin, req.params.priceCurrency)
.then((orders) => res.send({ status: "ok", data: { orders } }))
.catch((err) => {
logger.log(
"error",
`Call to own_orders encountered error ${err.name}: \n ${err.stack}`
);
res.send({
status: "error",
data: {
errorType: err.name,
errorMessage: err.message,
stack: err.stack.toString(),
},
});
});
})
);
router.get(
"/fills/:coin-:quote",
expressAsyncHandler(async (req, res, next) => {
if ("coin" in req.query && "priceCurrency" in req.query) {
api
.getFills(req.params.coin, req.params.priceCurrency)
.then((fills) => res.send({ status: "ok", data: fills }))
.catch((err) => next(err));
} else {
api
.getFills()
.then((fills) => res.send({ status: "ok", data: fills }))
.catch((err) => next(err));
}
})
);
router.get(
"/balances",
expressAsyncHandler(async (req, res, next) => {
api
.getBalances()
.then((balances) => res.send({ status: "ok", data: balances }))
.catch((err) => next(err));
})
);
router.post(
"/settle",
expressAsyncHandler(async (req, res, next) => {
api
.settleFunds(req.body.coin, req.body.priceCurrency)
.then((_) => res.send({ status: "ok", data: {} }))
.catch((err) => next(err));
})
);
export { router as default };