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

Commit

Permalink
CoinFalcon Integration (#1599)
Browse files Browse the repository at this point in the history
* starting coinfalcon integration

* adding ticker and getTrades api

* getFees + basic getPortfolio

* getPortfolio done

* adding buy sell

* fixing retry

* callback given after non retry

* update sqlite3

* update coinfalcon api

* adding cancelOrder for coinfalcon

* decending fix

* fixing since param in getTrades - CoinFalcon

* fixing since issue with moment and adding importer

* add and check order

* fixing importer to go over 1h interval

* adding 522 code to retriable

* updating coinfalcon
  • Loading branch information
vivekmarakana authored and askmike committed Jan 13, 2018
1 parent 085af56 commit a5a4e7d
Show file tree
Hide file tree
Showing 4 changed files with 341 additions and 0 deletions.
260 changes: 260 additions & 0 deletions exchanges/coinfalcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
const moment = require('moment');
const util = require('../core/util');
const _ = require('lodash');
const log = require('../core/log');

const CoinFalcon = require('coinfalcon');

var Trader = function(config) {
_.bindAll(this);

if (_.isObject(config)) {
this.key = config.key;
this.secret = config.secret;
this.currency = config.currency.toUpperCase();
this.asset = config.asset.toUpperCase();
}

this.pair = this.asset + '-' + this.currency;
this.name = 'coinfalcon';

this.coinfalcon = new CoinFalcon.Client(this.key, this.secret);
};

var recoverableErrors = new RegExp(
/(SOCKETTIMEDOUT|TIMEDOUT|CONNRESET|CONNREFUSED|NOTFOUND|429|522)/
);

Trader.prototype.retry = function(method, args, error) {
var self = this;
// make sure the callback (and any other fn) is bound to Trader
_.each(args, function(arg, i) {
if (_.isFunction(arg)) {
args[i] = _.bind(arg, self);
}
});

log.debug('[CoinFalcon] ', this.name, "Retrying...");

if (!error || !error.message.match(recoverableErrors)) {
log.error('[CoinFalcon] ', this.name, 'returned an irrecoverable error');
_.each(args, function(arg, i) {
if (_.isFunction(arg)) {
arg(error, null);
return;
}
});
return;
}

var wait = +moment.duration(5, 'seconds');

// run the failed method again with the same arguments after wait
setTimeout(function() {
method.apply(self, args);
}, wait);
};

Trader.prototype.getTicker = function(callback) {
var success = function(res) {
callback(null, {bid: +res.data.bids[0].price, ask: +res.data.asks[0].price})
};

var failure = function(err) {
log.error('[CoinFalcon] error getting ticker', err);
callback(err, null);
};

var url = "markets/" + this.pair + "/orders?level=1"

this.coinfalcon.get(url).then(success).catch(failure);
};

Trader.prototype.getFee = function(callback) {
var fees = 0.25; // 0.25% for both sell & buy
callback(false, fees / 100);
};

Trader.prototype.getPortfolio = function(callback) {
var success = function(res) {
if (_.has(res, 'error')) {
var err = new Error(res.error);
callback(err, null);
} else {
var portfolio = res.data.map(function(account) {
return {
name: account.currency,
amount: parseFloat(account.available)
}
});
callback(null, portfolio);
}
};

var failure = function(err) {
log.error('[CoinFalcon] error getting portfolio', err);
callback(err, null);
}

this.coinfalcon.get('user/accounts').then(success).catch(failure);
};

Trader.prototype.addOrder = function(type, amount, price, callback) {
var args = _.toArray(arguments);

var success = function(res) {
if (_.has(res, 'error')) {
var err = new Error(res.error);
failure(err);
} else {
callback(false, res.data.id)
}
};

var failure = function(err) {
log.error('[CoinFalcon] unable to ' + type.toLowerCase(), err);
return this.retry(this.addOrder, args, err);
}.bind(this);

var payload = {
order_type: type,
operation_type: 'limit_order',
market: this.pair,
size: amount,
price: price
}

this.coinfalcon.post('user/orders', payload).then(success).catch(failure);
};

['buy', 'sell'].map(function(type) {
Trader.prototype[type] = function(amount, price, callback) {
this.addOrder(type, amount, price, callback);
};
});

Trader.prototype.getOrder = function(order, callback) {
var success = function(res) {
if (_.has(res, 'error')) {
var err = new Error(res.error);
failure(err);
} else {
var price = parseFloat(res.data.price);
var amount = parseFloat(res.data.size);
var date = moment(res.data.created_at);
callback(false, { price, amount, date });
}
};

var failure = function(err) {
log.error('[CoinFalcon] unable to getOrder', err);
callback(err, null);
}.bind(this);

this.coinfalcon.get('user/orders/' + order).then(success).catch(failure);
};

Trader.prototype.checkOrder = function(order, callback) {
var success = function(res) {
if (_.has(res, 'error')) {
var err = new Error(res.error);
failure(err);
} else {
var filled = res.data.status == "canceled" || res.data.status == "fulfilled";
callback(false, filled);
}
};

var failure = function(err) {
log.error('[CoinFalcon] unable to checkOrder', err);
callback(err, null);
}.bind(this);

this.coinfalcon.get('user/orders/' + order).then(success).catch(failure);
};

Trader.prototype.cancelOrder = function(order, callback) {
var args = _.toArray(arguments);
var success = function(res) {
if (_.has(res, 'error')) {
var err = new Error(res.error);
failure(err);
} else {
callback(false, res.data.id)
}
};

var failure = function(err) {
log.error('[CoinFalcon] unable to cancel', err);
return this.retry(this.cancelOrder, args, err);
}.bind(this);

this.coinfalcon.delete('user/orders?id=' + order).then(success).catch(failure);
};

Trader.prototype.getTrades = function(since, callback, descending) {
var args = _.toArray(arguments);

var success = function(res) {
var parsedTrades = [];
_.each(
res.data,
function(trade) {
parsedTrades.push({
tid: trade.id,
date: moment(trade.created_at).unix(),
price: parseFloat(trade.price),
amount: parseFloat(trade.size),
});
},
this
);

if (descending) {
callback(null, parsedTrades);
} else {
callback(null, parsedTrades.reverse());
}
}.bind(this);

var failure = function (err) {
err = new Error(err);
log.error('[CoinFalcon] error getting trades', err);
return this.retry(this.getTrades, args, err);
}.bind(this);

var url = "markets/" + this.pair + "/trades"

if (since) {
url += '?since_time=' + (_.isString(since) ? since : since.format());
}

this.coinfalcon.get(url).then(success).catch(failure);
};

Trader.getCapabilities = function () {
return {
name: 'CoinFalcon',
slug: 'coinfalcon',
currencies: ['EUR', 'BTC'],
assets: ['BTC', 'LTC', 'ETH', 'IOT', 'BCH'],
markets: [
// Euro pairs
{ pair: ['EUR', 'BTC'], minimalOrder: { amount: 0.0, unit: 'asset' } },
{ pair: ['EUR', 'ETH'], minimalOrder: { amount: 0.0, unit: 'asset' } },
// Bitcoin pairs
{ pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.0, unit: 'asset' } },
{ pair: ['BTC', 'LTC'], minimalOrder: { amount: 0.0, unit: 'asset' } },
{ pair: ['BTC', 'IOT'], minimalOrder: { amount: 0.0, unit: 'asset' } },
{ pair: ['BTC', 'BCH'], minimalOrder: { amount: 0.0, unit: 'asset' } }
],
requires: ['key', 'secret'],
providesHistory: 'date',
providesFullHistory: true,
tid: 'tid',
tradable: true,
forceReorderDelay: false
};
}

module.exports = Trader;
53 changes: 53 additions & 0 deletions importers/exchanges/coinfalcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const moment = require('moment');
const util = require('../../core/util.js');
const _ = require('lodash');
const log = require('../../core/log');

var config = util.getConfig();
var dirs = util.dirs();

var Fetcher = require(dirs.exchanges + 'coinfalcon');

util.makeEventEmitter(Fetcher);

var end = false;
var done = false;
var from = false;

var fetcher = new Fetcher(config.watch);

var fetch = () => {
fetcher.import = true;
log.debug('[CoinFalcon] Getting trades from: ', from);
fetcher.getTrades(from, handleFetch);
};

var handleFetch = (unk, trades) => {
if (trades.length > 0) {
var last = moment.unix(_.last(trades).date).utc();
var next = last.clone();
} else {
var next = from.clone().add(1, 'h');
log.debug('Import step returned no results, moving to the next 1h period');
}

if (from.add(1, 'h') >= end) {
fetcher.emit('done');

var endUnix = end.unix();
trades = _.filter(trades, t => t.date <= endUnix);
}

from = next.clone();
fetcher.emit('trades', trades);
};

module.exports = function(daterange) {
from = daterange.from.clone().utc();
end = daterange.to.clone().utc();

return {
bus: fetcher,
fetch: fetch,
};
};
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"btc-markets": "0.0.10",
"cexio": "0.0.x",
"co-fs": "^1.2.0",
"coinfalcon": "^1.0.3",
"commander": "^2.9.0",
"gdax": "^0.4.2",
"gekko": "0.0.9",
Expand Down

0 comments on commit a5a4e7d

Please sign in to comment.