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

CoinFalcon Integration #1599

Merged
merged 24 commits into from
Jan 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a80879d
Merge pull request #1451 from askmike/develop
askmike Dec 11, 2017
448900d
Merge pull request #1452 from askmike/develop
askmike Dec 11, 2017
5cc98eb
Fix typos in tradebot doc
alexleventer Dec 13, 2017
1766fb0
Add back "By YOU" text.
alexleventer Dec 13, 2017
2efdeaa
starting coinfalcon integration
vivekmarakana Dec 27, 2017
1b08a12
adding ticker and getTrades api
vivekmarakana Dec 27, 2017
ca57fef
getFees + basic getPortfolio
vivekmarakana Dec 27, 2017
4ee8246
getPortfolio done
vivekmarakana Dec 27, 2017
ad62e79
adding buy sell
vivekmarakana Dec 27, 2017
37f458c
fixing retry
vivekmarakana Dec 27, 2017
d5c0199
callback given after non retry
vivekmarakana Dec 27, 2017
f598737
update sqlite3
vivekmarakana Dec 27, 2017
8801f88
update coinfalcon api
vivekmarakana Dec 27, 2017
2bb9b70
adding cancelOrder for coinfalcon
vivekmarakana Dec 27, 2017
d995347
decending fix
vivekmarakana Dec 27, 2017
d1885dd
fixing since param in getTrades - CoinFalcon
vivekmarakana Dec 28, 2017
285b73f
fixing since issue with moment and adding importer
vivekmarakana Dec 28, 2017
02328cc
add and check order
vivekmarakana Dec 28, 2017
ea8f148
fixing importer to go over 1h interval
vivekmarakana Dec 28, 2017
b92631f
adding 522 code to retriable
vivekmarakana Dec 28, 2017
eb534c9
updating coinfalcon
vivekmarakana Dec 29, 2017
f055fbc
Merge branch 'feature/coinfalcon_integration' of github.com:vivekmara…
vivekmarakana Dec 29, 2017
f039f3c
Merge branch 'develop' into feature/coinfalcon_integration
askmike Jan 4, 2018
8dc68a9
Merge branch 'develop' into feature/coinfalcon_integration
askmike Jan 4, 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
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