@ex-master/core
Advanced tools
Comparing version 0.1.11 to 0.1.12
@@ -33,2 +33,3 @@ import { Decimal } from 'decimal.js'; | ||
pendingUpdatesPriorityScale?: number; | ||
parallel?: boolean; | ||
} | ||
@@ -47,3 +48,4 @@ export declare class Exchange { | ||
private pendingUpdatesPriorityScale; | ||
constructor(client: Client, dashboard: Dashboard, {abortUpdateOnOperationalBalanceChange, pendingUpdatesPriorityScale}?: ExchangeOptions); | ||
private parallel; | ||
constructor(client: Client, dashboard: Dashboard, {abortUpdateOnOperationalBalanceChange, pendingUpdatesPriorityScale, parallel}?: ExchangeOptions); | ||
readonly activeOrders: ActiveOrder[]; | ||
@@ -63,2 +65,6 @@ readonly markets: Market[]; | ||
private _updateOrders(descriptors, updateMark); | ||
private freeAndOrder(descriptor, nextUpdates); | ||
private isBalanceSufficient({market, type, price, size}); | ||
private freeMoney(key, target, updates); | ||
private freeStock(key, target, updates); | ||
private order({label, market, type, price, size, priority}); | ||
@@ -65,0 +71,0 @@ private cancelOrder({id, label, market}); |
@@ -12,3 +12,3 @@ "use strict"; | ||
class Exchange { | ||
constructor(client, dashboard, { abortUpdateOnOperationalBalanceChange = true, pendingUpdatesPriorityScale = 2, } = {}) { | ||
constructor(client, dashboard, { abortUpdateOnOperationalBalanceChange = true, pendingUpdatesPriorityScale = 2, parallel = false, } = {}) { | ||
this.client = client; | ||
@@ -31,2 +31,3 @@ this.dashboard = dashboard; | ||
this.pendingUpdatesPriorityScale = pendingUpdatesPriorityScale; | ||
this.parallel = parallel; | ||
} | ||
@@ -219,67 +220,88 @@ get activeOrders() { | ||
].sort(compareByPriorityAndType); | ||
let freeMoney = async (key, target, updates) => { | ||
let available; | ||
while ((available = this.account.getBalance(key).available).lessThan(target)) { | ||
let index = updates.findIndex(({ type, active }) => type === 'bid' && !!active && active.market.money === key); | ||
if (index < 0) { | ||
return available; | ||
for (let i = 0; i < updates.length; i++) { | ||
let { active, descriptor } = updates[i]; | ||
if (active && | ||
descriptor && | ||
this.parallel && | ||
this.isBalanceSufficient(descriptor)) { | ||
let promises = []; | ||
if (active) { | ||
promises.push(this.cancelOrder(active)); | ||
} | ||
let update = updates[index]; | ||
let active = update.active; | ||
await this.cancelOrder(active); | ||
update.active = undefined; | ||
promises.push(this.freeAndOrder(descriptor, updates.slice(i + 1))); | ||
await Promise.all(promises); | ||
} | ||
return target; | ||
}; | ||
let freeStock = async (key, target, updates) => { | ||
let available; | ||
while ((available = this.account.getBalance(key).available).lessThan(target)) { | ||
let index = updates.findIndex(({ type, active }) => type === 'ask' && !!active && active.market.stock === key); | ||
if (index < 0) { | ||
return available; | ||
else { | ||
if (active) { | ||
await this.cancelOrder(active); | ||
} | ||
let update = updates[index]; | ||
let active = update.active; | ||
await this.cancelOrder(active); | ||
update.active = undefined; | ||
if (descriptor) { | ||
await this.freeAndOrder(descriptor, updates.slice(i + 1)); | ||
} | ||
} | ||
return target; | ||
}; | ||
for (let i = 0; i < updates.length; i++) { | ||
let { type, active, descriptor } = updates[i]; | ||
if (active) { | ||
await this.cancelOrder(active); | ||
if (updateMark !== this.activeUpdateMark) { | ||
return; | ||
} | ||
if (descriptor) { | ||
let { market, label, price, size } = descriptor; | ||
let originalSize = size; | ||
if (type === 'bid') { | ||
let value = price.mul(size); | ||
let available = await freeMoney(market.money, value, updates.slice(i + 1)); | ||
if (available.lessThan(value)) { | ||
size = this.ensureSizePrecision(market, available.dividedBy(price)); | ||
} | ||
} | ||
else { | ||
let available = await freeStock(market.stock, size, updates.slice(i + 1)); | ||
if (available.lessThan(size)) { | ||
size = this.ensureSizePrecision(market, available); | ||
} | ||
} | ||
if (size.isZero() || size.lessThan(this.getMinOrderSize(market))) { | ||
this.dashboard.info(type === 'bid' | ||
? `insufficient money, skipping order "${label}" ${price} * ${originalSize}.` | ||
: `insufficient stock, skipping order "${label}" ${price} * ${originalSize}.`); | ||
continue; | ||
} | ||
await this.order({ | ||
...descriptor, | ||
size, | ||
}); | ||
if (updateMark !== this.activeUpdateMark) { | ||
return; | ||
} | ||
} | ||
} | ||
async freeAndOrder(descriptor, nextUpdates) { | ||
let { type, market, label, price, size } = descriptor; | ||
let originalSize = size; | ||
if (type === 'bid') { | ||
let value = price.mul(size); | ||
let available = await this.freeMoney(market.money, value, nextUpdates); | ||
if (available.lessThan(value)) { | ||
size = this.ensureSizePrecision(market, available.dividedBy(price)); | ||
} | ||
} | ||
else { | ||
let available = await this.freeStock(market.stock, size, nextUpdates); | ||
if (available.lessThan(size)) { | ||
size = this.ensureSizePrecision(market, available); | ||
} | ||
} | ||
if (size.isZero() || size.lessThan(this.getMinOrderSize(market))) { | ||
this.dashboard.info(type === 'bid' | ||
? `insufficient money, skipping order "${label}" ${price} * ${originalSize}.` | ||
: `insufficient stock, skipping order "${label}" ${price} * ${originalSize}.`); | ||
return; | ||
} | ||
await this.order({ | ||
...descriptor, | ||
size, | ||
}); | ||
} | ||
isBalanceSufficient({ market, type, price, size, }) { | ||
let target = type === 'ask' ? size : price.mul(size); | ||
let key = type === 'ask' ? market.stock : market.money; | ||
return this.account.getBalance(key).available.greaterThanOrEqualTo(target); | ||
} | ||
async freeMoney(key, target, updates) { | ||
let available; | ||
while ((available = this.account.getBalance(key).available).lessThan(target)) { | ||
let index = updates.findIndex(({ type, active }) => type === 'bid' && !!active && active.market.money === key); | ||
if (index < 0) { | ||
return available; | ||
} | ||
let update = updates[index]; | ||
let active = update.active; | ||
await this.cancelOrder(active); | ||
update.active = undefined; | ||
} | ||
return target; | ||
} | ||
async freeStock(key, target, updates) { | ||
let available; | ||
while ((available = this.account.getBalance(key).available).lessThan(target)) { | ||
let index = updates.findIndex(({ type, active }) => type === 'ask' && !!active && active.market.stock === key); | ||
if (index < 0) { | ||
return available; | ||
} | ||
let update = updates[index]; | ||
let active = update.active; | ||
await this.cancelOrder(active); | ||
update.active = undefined; | ||
} | ||
return target; | ||
} | ||
async order({ label, market, type, price, size, priority, }) { | ||
@@ -286,0 +308,0 @@ this.dashboard.info(`[${stringifyMarket(market)}] create order "${label}" ${price} * ${size}`); |
{ | ||
"name": "@ex-master/core", | ||
"version": "0.1.11", | ||
"version": "0.1.12", | ||
"main": "bld/index.js", | ||
@@ -5,0 +5,0 @@ "types": "bld/index.d.ts", |
34074
801