@saturnnetwork/market-maker-strategy
Version:
Market Making Strategy for Saturn Network DEX
156 lines (155 loc) • 7.42 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = __importDefault(require("lodash"));
const chalk_1 = __importDefault(require("chalk"));
const bignumber_js_1 = require("bignumber.js");
class OrderBookPrinter {
constructor(orderbook) {
this.orderbook = orderbook;
}
print() {
console.log(chalk_1.default.yellow(`Order Book snapshot at ${new Date()}`));
let chartist = new OrderBookChart(this.orderbook.buys, this.orderbook.sells);
console.log(chartist.plot());
}
}
exports.OrderBookPrinter = OrderBookPrinter;
class OrderBookChart {
constructor(buys, sells) {
this.truncated = false;
this.plot = () => {
let min = new bignumber_js_1.BigNumber(0);
let max = this.buyDepth.gt(this.sellDepth) ? this.buyDepth : this.sellDepth;
let range = max;
let offset = 3;
let padding = ' ';
let height = 8;
let ratio = new bignumber_js_1.BigNumber(height).dividedBy(range);
let min2 = Math.round(min.times(ratio).toNumber());
let max2 = Math.round(max.times(ratio).toNumber());
let rows = Math.abs(max2 - min2);
let width = this.buys.length + this.sells.length + offset;
let format = (x) => (padding + x.toFixed(2)).slice(-padding.length);
let result = new Array(rows + 1);
for (let i = 0; i <= rows; i++) {
result[i] = new Array(width);
for (let j = 0; j < width; j++) {
result[i][j] = ' ';
}
}
for (let y = min2; y <= max2; y++) {
let label = format(max.toNumber() - (y - min2) * range.toNumber() / rows);
result[y - min2][Math.max(offset - label.length, 0)] = label;
result[y - min2][offset - 1] = '┤';
}
let y0 = Math.round(this.buyDepthAtIndex(0).times(ratio).toNumber()) - min2;
result[rows - y0][offset - 1] = chalk_1.default.green('┼');
for (let x = 0; x < this.buys.length - 1; x++) {
let y0 = Math.round(this.buyDepthAtIndex(x + 0).times(ratio).toNumber()) - min2;
let y1 = Math.round(this.buyDepthAtIndex(x + 1).times(ratio).toNumber()) - min2;
if (y0 == y1) {
result[rows - y0][x + offset] = chalk_1.default.green('─');
}
else {
result[rows - y1][x + offset] = chalk_1.default.green((y0 > y1) ? '╰' : '╭');
result[rows - y0][x + offset] = chalk_1.default.green((y0 > y1) ? '╮' : '╯');
let from = Math.min(y0, y1);
let to = Math.max(y0, y1);
for (let y = from + 1; y < to; y++) {
result[rows - y][x + offset] = chalk_1.default.green('│');
}
}
}
let from = Math.round(this.sellDepthAtIndex(0).times(ratio).toNumber()) - min2;
let to = Math.round(this.sellDepthAtIndex(1).times(ratio).toNumber()) - min2;
if (to != from) {
let to = Math.round(this.sellDepthAtIndex(1).times(ratio).toNumber()) - min2;
result[rows][this.buys.length + offset - 1] = chalk_1.default.red('╯');
result[rows - to][this.buys.length + offset - 1] = chalk_1.default.red('╭');
for (let y = 1; y < to; y++) {
result[rows - y][this.buys.length + offset - 1] = chalk_1.default.red('│');
}
}
else {
result[rows][this.buys.length + offset - 1] = chalk_1.default.red('─');
}
for (let x = 1; x < this.sells.length - 1; x++) {
let y0 = Math.round(this.sellDepthAtIndex(x + 0).times(ratio).toNumber()) - min2;
let y1 = Math.round(this.sellDepthAtIndex(x + 1).times(ratio).toNumber()) - min2;
if (y0 == y1) {
result[rows - y0][x + offset + this.buys.length - 1] = chalk_1.default.red('─');
}
else {
result[rows - y1][x + offset + this.buys.length - 1] = chalk_1.default.red((y0 > y1) ? '╰' : '╭');
result[rows - y0][x + offset + this.buys.length - 1] = chalk_1.default.red((y0 > y1) ? '╮' : '╯');
let from = Math.min(y0, y1);
let to = Math.max(y0, y1);
for (let y = from + 1; y < to; y++) {
result[rows - y][x + offset + this.buys.length - 1] = chalk_1.default.red('│');
}
}
}
if (this.truncated) {
result[0][width] = chalk_1.default.red('↑');
}
return result.map(function (x) { return x.join(''); }).join('\n');
};
this.buys = sells.sort((a, b) => {
let aprice = new bignumber_js_1.BigNumber(a.price);
let bprice = new bignumber_js_1.BigNumber(b.price);
return aprice.minus(bprice).toNumber();
});
this.buyDepth = lodash_1.default.reduce(lodash_1.default.map(sells, (order) => {
return this.etherBalance(order);
}), (x, y) => x.plus(y), new bignumber_js_1.BigNumber(0));
buys = this.filterSellOrders(buys);
this.sells = buys.sort((a, b) => {
let aprice = new bignumber_js_1.BigNumber(a.price);
let bprice = new bignumber_js_1.BigNumber(b.price);
return aprice.minus(bprice).toNumber();
});
this.sellDepth = lodash_1.default.reduce(lodash_1.default.map(buys, (order) => {
return this.etherBalance(order);
}), (x, y) => x.plus(y), new bignumber_js_1.BigNumber(0));
}
buyDepthAtIndex(idx) {
let result = this.buyDepth;
for (let i = 0; i <= idx; ++i) {
result = result.minus(this.etherBalance(this.buys[i]));
}
return result;
}
sellDepthAtIndex(idx) {
let result = new bignumber_js_1.BigNumber(0);
for (let i = 0; i <= idx; ++i) {
result = result.plus(this.etherBalance(this.sells[i]));
}
return result;
}
filterSellOrders(orders) {
let upperLimit = this.buyDepth.times(new bignumber_js_1.BigNumber(1.5));
let currentLimit = new bignumber_js_1.BigNumber(0);
let result = [];
result.push(orders[0]);
result.push(orders[1]);
for (let i = 2; i < orders.length; ++i) {
currentLimit = currentLimit.plus(this.etherBalance(orders[i]));
if (currentLimit.lte(upperLimit)) {
result.push(orders[i]);
}
else {
orders[i].balance = upperLimit.dividedBy(new bignumber_js_1.BigNumber(orders[i].price)).toFixed();
result.push(orders[i]);
this.truncated = true;
break;
}
}
return result;
}
etherBalance(order) {
return new bignumber_js_1.BigNumber(order.balance).times(new bignumber_js_1.BigNumber(order.price));
}
}