This repository has been archived by the owner on Feb 16, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
1 parent
085af56
commit a5a4e7d
Showing
4 changed files
with
341 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; | ||
}; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters