Skip to content
This repository has been archived by the owner on Feb 16, 2020. It is now read-only.

[WIP] Gekko broker #2118

Merged
merged 65 commits into from
May 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
7c31f9a
move exchange specific func into gekko/exchange
askmike Apr 16, 2018
29b0dde
reglue core exchange func
askmike Apr 16, 2018
afdbc5f
commeng on gekko broker
askmike Apr 16, 2018
306b1de
init limit and sticky order
askmike Apr 17, 2018
6f5fc27
add cancel logic to limit
askmike Apr 17, 2018
065ca16
add limit logic to sticky
askmike Apr 17, 2018
a0935bf
add cancel to sticky order
askmike Apr 17, 2018
0e301f7
export errors, clean up order amount, fix coinfalcon
askmike Apr 17, 2018
b1d854f
convert gekkoBroker into es6 class
askmike Apr 17, 2018
d8c7898
update gdax wrapper for sdk@0.7
askmike Apr 17, 2018
d3ae353
catch ticker err
askmike Apr 17, 2018
1ff9905
add partial order support to checkOrder
askmike Apr 18, 2018
bb854d2
add rejection state, distinct between filled & completed
askmike Apr 18, 2018
a3b1a34
init publish of npm package
askmike Apr 18, 2018
411932c
implement partial order support
askmike Apr 18, 2018
7fe3823
add moveLimit
askmike Apr 18, 2018
153c993
clean up empty orders
askmike Apr 18, 2018
8403a21
retry checkOrder
askmike Apr 18, 2018
5e716b1
refer to limit arg
askmike Apr 18, 2018
400002f
add moveAmount, improve async lock flow
askmike Apr 19, 2018
910d2c0
check whether public move amount & limit actually change anything
askmike Apr 19, 2018
2ef96f1
move docs to main gekko docs
askmike Apr 20, 2018
e778421
complete todo
askmike Apr 20, 2018
bcac599
rm completed state, add completed prop instead (true when filled, can…
askmike Apr 21, 2018
470d646
catch when the amount is moved below the filled amount
askmike Apr 21, 2018
1e1dc69
add generic submit to order
askmike Apr 21, 2018
eb4167b
update limit order to use updated base order
askmike Apr 21, 2018
ddf043a
catch more server errors (504|503|500|502)
askmike Apr 21, 2018
a15244a
add binance support
askmike Apr 22, 2018
dc69ff9
add binance dep
askmike Apr 22, 2018
a3b8976
add poloniex support to gekko broker
askmike Apr 22, 2018
c4e5c72
update cf to properly process resp
askmike Apr 23, 2018
b86de8c
fix loop over fill list in includes
askmike Apr 25, 2018
09b1936
actually move the amount at moveAmount
askmike Apr 29, 2018
5b03176
retry the correct method
askmike Apr 29, 2018
cb53200
move market gen files to gekko-broker
askmike May 3, 2018
006132a
update bitfinex to use dep v2.0.0
askmike May 3, 2018
618f708
update cancel cb signature on binance
askmike May 5, 2018
cc68d04
clean up wrappers
askmike May 7, 2018
eedb938
retry once on temp bfx errors
askmike May 7, 2018
a9ffe3d
allow errors to specify they `might` be temporary
askmike May 13, 2018
719289a
rename partial fill event to `fill`
askmike May 13, 2018
28f178f
rm debug log
askmike May 13, 2018
e3238bd
update polo to use retry lib
askmike May 14, 2018
a8ab37b
catch cloudflare html error pages
askmike May 14, 2018
e0d4a73
document exchange API error handling
askmike May 15, 2018
2700037
hookup sticky summary to exchange getOrders
askmike May 16, 2018
9da7bb4
[cf] handle cancel error: already filled
askmike May 16, 2018
8ecaa19
add retry prop to retryable error
askmike May 18, 2018
8e4ed39
rm gekko broker exchange deps from gekkos dependencies
askmike May 18, 2018
91a5ac5
improve the handling of edge race conditions
askmike May 20, 2018
9df53b5
temp use poloniex dep fork (waiting for upstream premasagar/poloniex.…
askmike May 20, 2018
93e0ac0
also fetch endpoint containing fee data
askmike May 20, 2018
56dc156
rm polo deps from main gekko
askmike May 20, 2018
3f9ed9e
handle polo responses where create/cancel might have executed (&temp …
askmike May 20, 2018
0fce8ae
catch 408
askmike May 20, 2018
13a06fd
look at all trades after completing an hour
askmike May 20, 2018
a3d5c89
always guard cb passed to polo dep wrapper
askmike May 20, 2018
0a0e373
update cf ref link
askmike May 21, 2018
6403aa8
use all trades to calc order result
askmike May 21, 2018
5c26546
[polo] also catch "internal error"
askmike May 21, 2018
1698d43
ignore last order if it was not filled
askmike May 22, 2018
6a1a1ba
force abort everything if ew are in the process of completing
askmike May 24, 2018
7898e63
use right prop for filled amount
askmike May 24, 2018
bfa98f3
Merge branch 'pre-v0.6' into gekko-trader
askmike May 28, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions core/error.js

This file was deleted.

28 changes: 0 additions & 28 deletions core/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ var path = require('path');
var fs = require('fs');
var semver = require('semver');
var program = require('commander');
var retry = require('retry');
var Errors = require('./error');

var startTime = moment();

Expand All @@ -17,19 +15,6 @@ var _gekkoEnv = false;

var _args = false;

var retryHelper = function(fn, options, callback) {
var operation = retry.operation(options);
operation.attempt(function(currentAttempt) {
fn(function(err, result) {
if (!(err instanceof Errors.AbortError) && operation.retry(err)) {
return;
}

callback(err ? err.message : null, result);
});
});
}

// helper functions
var util = {
getConfig: function() {
Expand Down Expand Up @@ -183,19 +168,6 @@ var util = {
getStartTime: function() {
return startTime;
},
retry: function(fn, callback) {
var options = {
retries: 5,
factor: 1.2,
minTimeout: 1 * 1000,
maxTimeout: 3 * 1000
};

retryHelper(fn, options, callback);
},
retryCustom: function(options, fn, callback) {
retryHelper(fn, options, callback);
},
}

// NOTE: those options are only used
Expand Down
24 changes: 17 additions & 7 deletions docs/extending/add_an_exchange.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,23 @@ This should create a buy / sell order at the exchange for [amount] of [asset] at

this.exchange.getOrder(order, callback);

The order will be something that the manager previously received via the `sell` or `buy` methods. The callback should have the parameters `err` and `order`. Order is an object with properties `price`, `amount` and `date`. Price is the (volume weighted) average price of all trades necesarry to execute the order. Amount is the amount of currency traded and Date is a moment object of the last trade part of this order.
Will only be called on orders that have been completed. The order will be something that the manager previously received via the `sell` or `buy` methods. The callback should have the parameters `err` and `order`. Order is an object with properties `price`, `amount` and `date`. Price is the (volume weighted) average price of all trades necesarry to execute the order. Amount is the amount of currency traded and Date is a moment object of the last trade part of this order.

### checkOrder

this.exchange.checkOrder(order, callback);

The order will be something that the manager previously received via the `sell` or `buy` methods. The callback should have the parameters `err` and `filled`. Filled is a boolean that is true when the order is already filled and false when it is not. Currently partially filled orders should be treated as not filled.
The order will be something that the manager previously received via the `sell` or `buy` methods. The callback should have the parameters `err` and a `result` object. The result object will have two or three properties:

- `open`: whether this order is currently in the orderbook.
- `completed`: whether this order has executed (filled completely).
- `filledAmount`: the amount of the order that has been filled. This property is only needed when both `open` is true and `completed` is false.

### cancelOrder

this.exchange.cancelOrder(order, callback);

The order will be something that the manager previously received via the `sell` or `buy` methods. The callback should have the parameterer `err`.
The order will be something that the manager previously received via the `sell` or `buy` methods. The callback should have the parameterers `err` and `filled`, `filled` last one should be true if the order was filled before it could be cancelled.

## Trading method's expectations

Expand All @@ -89,20 +93,26 @@ The trading method analyzes exchange data to determine what to do. The trading m

this.watcher.getTrades(since, callback, descending);


If since is truthy, Gekko requests as much trades as the exchange can give (up to ~10,000 trades, if the exchange supports more you can [create an importer](../features/importing.md)).

The callback expects an error and a `trades` object. Trades is an array of trade objects in chronological order (0 is older trade, 1 is newer trade). Each trade object needs to have:

- a `date` property (unix timestamp in either string or int)
- a `price` property (float) which represents the price in [currency] per 1 [asset]. `
- a `price` property (float) which represents the price in [currency] per 1 [asset].
- an `amount` proprty (float) which represent the amount of [asset].
- a `tid` property (float) which represents the tradeID.

## Error handling

It is the responsibility of the wrapper to handle errors and retry the call in case of a temporary error. Gekko exposes a retry helper you can use to implement an exponential backoff retry strategy. Your wrapper does need pass a proper error object explaining whether the call can be retried or not. If the error is fatal (for example private API calls with invalid API keys) the wrapper is expected to upstream this error. If the error is retryable (exchange is overloaded or a network error) an error should be passed with the property `notFatal` set to true. If the exchange replied with another error that might be temporary (for example an `Insufficiant funds` erorr right after Gekko canceled an order, which might be caused by the exchange not having updated the balance yet) the error object can be extended with an `retry` property indicating that the call can be retried for n times but after that the error should be considered fatal.

### Recompiling Gekko UI
For implementation refer to the bitfinex implementation, in a gist this is what a common flow looks like:

Once you added your exchange you can use it with Gekko! However if you want the new exchange to show up in the web interface you need to recompile the frontend (so your updated `exchanges.js` file is used by the webapp). [Read here](https://gekko.wizb.it/docs/internals/gekko_ui.html#Developing-for-the-Gekko-UI-frontend) how to do that.
- (async call) `exchange.buy`, then
- handle the response and normalize the error so the retry helper understands it, then
- the retry helper will determine whether the call needs to be retried, then
- based on the error it will retry (nonFatal or retry)
- if no error it will pass it to your handle function that normalizes the output

## Capabilities

Expand Down
65 changes: 65 additions & 0 deletions docs/gekko-broker/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Gekko Broker

Order execution library for bitcoin and crypto exchanges. This library is Gekko's execution engine for all live orders (simulated orders go through the paper trader, which is a separate module). This library is intended for developers of trading systems in need for advanced order types on multiple exchanges over a unified API.

## Introduction

This library makes it easy to do (advanced) orders all crypto exchanges where Gekko can live trade at. See the complete list here: https://gekko.wizb.it/docs/introduction/supported_exchanges.html

This library allows you to:

- Get basic market data
- ticker (BBO)
- orderbook (TODO)
- historical trades
- Get portfolio data
- Do an (advanced) order:
- Submit the order to the exchange
- Receive events about the status of the order:
- submitted
- open (accepted)
- partially filled
- rejected
- completed
- Mutate the order
- cancel
- amend
- move
- move limit
- Tracks orders submitted through the library.

## Status

Early WIP. All communication is via the REST APIs of exhanges. Not all exchanges are supported.

Currently supported exchanges:

- Binance
- GDAX
- Poloniex
- Coinfalcon

## Order types

This library aims to offer advanced order types, even on exchanges that do not natively support them by tracking the market and supplimenting native order support on specific exchanges.

WIP:

- Base orders
- Limit Order
- [Sticky Order](./sticky_order.md)

TODO:

- Base orders:
- Market Order
- Triggers:
- Stop
- If Touched (stop but opposite direction)

### TODO

- finish all exchange integrations that gekko supports
- finish all order types and triggers (under todo)
- implement websocket support (per exchange)
- use native move API calls wherever possible (poloniex)
45 changes: 45 additions & 0 deletions docs/gekko-broker/sticky_order.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Sticky Order

An advanced order that stays at the top of the book (until the optional limit). The order will automatically stick to the best BBO until the complete amount has been filled.

TODO:

- implement "overtake"
- implement fallback this order is alone at the top, some spread before everyone else
- finalize API
- add more events / ways to debug
- pull ticker data out of this order market data should flow from the broker (so we can easier move to at least public websocket streams).

## Example usage

const Broker = require('gekko-broker');

const gdax = new Broker({
currency: 'EUR',
asset: 'BTC',

exchange: 'gdax',

// Enables access to private endpoints.
// Needed to create orders and fetch portfolio
private: true,

key: 'x',
secret: 'y',
passphrase: 'z'
});

gdax.portfolio.setBalances(console.log);

const type = 'sticky';
const amount = 0.5;
const side = 'buy';
const limit = 6555;

const order = gdax.createOrder(type, side, amount, { limit });
order.on('statusChange', status => console.log(status));
order.on('filled', result => console.log(result));
order.on('completed', result => console.log(result));

order.moveAmount(1);
order.moveLimit(6666);
2 changes: 1 addition & 1 deletion docs/introduction/supported_exchanges.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ Gekko is able to directly communicate with the APIs of a number of exchanges. Ho
[22]: https://vip.bitcoin.co.id/
[23]: https://quadrigacx.com/
[24]: https://www.binance.com/?ref=11236330
[25]: https://coinfalcon.com/?ref=CFJSTEXJQFFE
[25]: https://coinfalcon.com/?ref=CFJSQBMXZZDS


3 changes: 3 additions & 0 deletions exchange/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Gekko Broker

see [the docs](../docs/gekko-broker/introduction.md).
34 changes: 10 additions & 24 deletions core/exchangeChecker.js → exchange/exchangeChecker.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@
var _ = require('lodash');
var fs = require('fs');
var util = require('./util');
var config = util.getConfig();
var dirs = util.dirs();
var moment = require('moment');

var Checker = function() {
_.bindAll(this);
}
const _ = require('lodash');
const fs = require('fs');
const moment = require('moment');
const errors = require('./exchangeErrors');

Checker.prototype.notValid = function(conf) {
if(conf.tradingEnabled)
return this.cantTrade(conf);
else
return this.cantMonitor(conf);
const Checker = function() {
_.bindAll(this);
}

Checker.prototype.getExchangeCapabilities = function(slug) {
var capabilities;

if(!fs.existsSync(dirs.exchanges + slug + '.js'))
util.die(`Gekko does not know exchange "${slug}"`);

var Trader = require(dirs.exchanges + slug);
capabilities = Trader.getCapabilities();
if(!fs.existsSync('./wrappers/' + slug + '.js'))
throw new errors.ExchangeError(`Gekko does not know exchange "${slug}"`);

return capabilities;
return require('./wrappers/' + slug).getCapabilities();
}

// check if the exchange is configured correctly for monitoring
Expand Down Expand Up @@ -56,7 +42,7 @@ Checker.prototype.cantMonitor = function(conf) {
if(!pair)
return 'Gekko does not support this currency/assets pair at ' + name;

// everyting okay
// everything is okay
return false;
}

Expand Down
42 changes: 42 additions & 0 deletions exchange/exchangeErrors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const _ = require('lodash');

const ExchangeError = function(message) {
_.bindAll(this);

this.name = "ExchangeError";
this.message = message;
}
ExchangeError.prototype = new Error();

const ExchangeAuthenticationError = function(message) {
_.bindAll(this);

this.name = "ExchangeAuthenticationError";
this.message = message;
}
ExchangeAuthenticationError.prototype = new Error();

const RetryError = function(message) {
_.bindAll(this);

this.name = "RetryError";
this.retry = 5;
this.message = message;
}
RetryError.prototype = new Error();

const AbortError = function(message) {
_.bindAll(this);

this.name = "AbortError";
this.message = message;
}
AbortError.prototype = new Error();

module.exports = {
ExchangeError,
ExchangeAuthenticationError,
RetryError,
AbortError
};

Loading