This repository has been archived on 2025-10-29. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
gekko/exchange/orders/limit.js
T
2021-06-06 20:25:14 +02:00

244 lines
4.9 KiB
JavaScript

// NOTE: this is currently broken, see
// @link https://github.com/askmike/gekko/issues/2398
throw ':(';
/*
The limit order is a simple order:
- It is created at the specified price
- If it were to cross it will throw instead (only if postOnly is specified)
- It can be moved
*/
const _ = require('lodash');
const async = require('async');
const events = require('events');
const moment = require('moment');
const errors = require('../exchangeErrors');
const BaseOrder = require('./order');
const states = require('./states');
class LimitOrder extends BaseOrder {
constructor(api) {
super(api);
}
create(side, amount, params) {
this.side = side;
this.postOnly = params.postOnly;
this.status = states.SUBMITTED;
this.emitStatus();
this.createOrder(price, amount);
}
createOrder(price, amount) {
this.amount = this.api.roundAmount(amount);
this.price = this.api.roundPrice(price);
// note: this assumes ticker data to be up to date
if(this.postOnly) {
if(side === 'buy' && this.price > this.data.ticker.ask)
throw new Error('Order crosses the book');
else if(side === 'sell' && this.price < this.data.ticker.bid)
throw new Error('Order crosses the book');
}
this.submit({
side: this.side,
amount: this.api.roundAmount(this.amount - alreadyFilled),
price: this.price,
alreadyFilled: this.filled
});
}
handleCreate(err, id) {
if(err)
throw err;
this.status = states.OPEN;
this.emitStatus();
this.id = id;
if(this.cancelling)
return this.cancel();
if(this.movingAmount)
return this.moveAmount();
if(this.movingPrice)
return this.movePrice();
this.timeout = setTimeout(this.checkOrder, this.checkInterval)
}
checkOrder() {
this.checking = true;
this.api.checkOrder(this.id, this.handleCheck);
}
handleCheck(err, result) {
if(this.cancelling || this.status === states.CANCELLED)
return;
this.checking = false;
if(err)
throw err;
if(result.open) {
if(result.filledAmount !== this.filledAmount) {
this.filledAmount = result.filledAmount;
// note: doc event API
this.emit('fill', this.filledAmount);
}
if(this.cancelling)
return this.cancel();
if(this.movingAmount)
return this.moveAmount();
if(this.movingPrice)
return this.movePrice();
this.timeout = setTimeout(this.checkOrder, this.checkInterval);
return;
}
if(!result.executed) {
// not open and not executed means it never hit the book
this.rejected();
return;
}
this.filled(this.price);
}
movePrice(price) {
if(this.completed)
return;
if(!price)
price = this.movePriceTo;
if(this.price === this.api.roundPrice(price))
// effectively nothing changed
return;
if(
this.status === states.SUBMITTED ||
this.status === states.MOVING ||
this.checking
) {
this.movePriceTo = price;
this.movingPrice = true;
return;
}
this.movingPrice = false;
this.price = this.api.roundPrice(price);
clearTimeout(this.timeout);
this.status = states.MOVING;
this.api.cancelOrder(this.id, (err, filled) => {
if(err)
throw err;
if(filled)
return this.filled(this.price);
this.submit({
side: this.side,
amount: this.amount,
price: this.price,
alreadyFilled: this.filled
});
});
}
moveAmount(amount) {
if(this.completed)
return;
if(!amount)
amount = this.moveAmountTo;
if(this.amount === this.api.roundAmount(amount))
// effectively nothing changed
return;
if(
this.status === states.SUBMITTED ||
this.status === states.MOVING ||
this.checking
) {
this.moveAmountTo = amount;
this.movingAmount = true;
return;
}
this.movingAmount = false;
this.amount = this.api.roundAmount(amount);
clearTimeout(this.timeout);
this.status = states.MOVING;
this.emitStatus();
this.api.cancelOrder(this.id, (err, filled) => {
if(err)
throw err;
if(filled)
return this.filled(this.price);
this.submit({
side: this.side,
amount: this.amount,
price: this.price,
alreadyFilled: this.filled
});
});
}
cancel() {
if(this.completed)
return;
if(
this.status === states.SUBMITTED ||
this.status === states.MOVING ||
this.checking
) {
this.cancelling = true;
return;
}
clearTimeout(this.timeout);
this.api.cancelOrder(this.id, (err, filled) => {
if(err)
throw err;
this.cancelling = false;
if(filled)
return this.filled(this.price);
this.status = states.CANCELLED;
this.emitStatus();
this.finish(false);
});
}
}
module.exports = LimitOrder;