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

Ui/market watchers [WIP] #513

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3d96715
use ListManager for state
askmike Nov 27, 2016
a64f731
run UI server seperately in dev
askmike Nov 27, 2016
18712ac
moving frontend stuff around
askmike Nov 27, 2016
0fd9c00
use vuex to store state
askmike Nov 27, 2016
97077cb
early version market watcher
askmike Dec 1, 2016
d7e1246
fix routing to new watcher
askmike Dec 1, 2016
617ed04
use TOML files for configuration
askmike Dec 3, 2016
6e20f03
use new config structure everywhere
askmike Dec 3, 2016
67ae8ab
implement getCandles & hook up to API
askmike Dec 3, 2016
ea092c8
communicate candle data from child-process to frontend
askmike Dec 3, 2016
288a47e
use very simple backtest price chart in the singleWatcher
askmike Dec 3, 2016
f53a9c0
use candle data for marketwatcher statistics
askmike Dec 4, 2016
33dd4b6
add init leech market
askmike Dec 4, 2016
e49d35e
handle standby, fix #514
askmike Dec 4, 2016
2857836
dont mutate state in vuex store
askmike Dec 5, 2016
7ddee35
redraw market chart on new data
askmike Dec 5, 2016
5b65760
base market time on candle dates
askmike Dec 5, 2016
5b315ab
remove sqlite WAL, (kinda) fixes #437
askmike Dec 5, 2016
b0280f0
create live Gekko -> spawn leecher
askmike Dec 6, 2016
47fef95
relay candle messages when leeching
askmike Dec 9, 2016
d0a4195
use cp to relay trade messags
askmike Dec 9, 2016
a18f680
init strat runner
askmike Dec 9, 2016
92534c0
fix if check.. (need more coffee)
askmike Dec 9, 2016
ee491a9
add killGekko api endpoint
askmike Dec 9, 2016
c4edda4
watch for killed gekko
askmike Dec 9, 2016
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
7 changes: 7 additions & 0 deletions config/adapters/mongodb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
connectionString = "mongodb://mongodb/gekko"
path = "plugins/mongodb"
version = 0.1

[[dependencies]]
module = "mongojs"
version = "2.4.0"
7 changes: 7 additions & 0 deletions config/adapters/postgresql.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
connectionString = "postgres://user:pass@localhost:5432"
path = "plugins/postgresql"
version = 0.1

[[dependencies]]
module = "pg"
version = "6.1.0"
7 changes: 7 additions & 0 deletions config/adapters/sqlite.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dataDirectory = "history"
path = "plugins/sqlite"
version = 0.1

[[dependencies]]
module = "sqlite3"
version = "3.1.4"
8 changes: 8 additions & 0 deletions config/backtest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
batchSize = 50

# scan for available dateranges
daterange = "scan"
# or specify it like so:
# [daterange]
# from = "2015-01-01"
# to = "2016-01-01"
10 changes: 10 additions & 0 deletions config/general.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
debug = true

# what database should Gekko use?
adapter = 'sqlite'

[watch]
exchange = 'Poloniex'
currency = 'USDT'
asset = 'BTC'

3 changes: 1 addition & 2 deletions config/plugins/candleWriter.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
enabled = true
sqlite = "sqlite"
enabled = true
16 changes: 16 additions & 0 deletions config/plugins/pushbullet.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
enabled = false

# Send 'Gekko starting' message if true
sendMessageOnStart = true

# disable advice printout if it's soft
muteSoft = true

# your email, change it unless you are Azor Ahai
email = "[email protected]"

# your pushbullet API key
key = "xxx"

# will make Gekko messages start with [GEKKO]
tag = "[GEKKO]"
9 changes: 9 additions & 0 deletions config/plugins/trader.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
enabled = false

## NOTE: once you filled in the following
## never share this file with anyone!

key = ""
secret = ""
# this is only required at specific exchanges
username = ""
9 changes: 9 additions & 0 deletions config/plugins/tradingAdvisor.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
enabled = true

candleSize = 60
historySize = 25
method = "MACD"

[talib]
enabled = false
version = "1.0.2"
11 changes: 11 additions & 0 deletions core/budfox/candleManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var util = require(__dirname + '/../util');
var dirs = util.dirs();
var config = util.getConfig();
var log = require(dirs.core + 'log');
var cp = require(dirs.core + 'cp');

var CandleCreator = require(dirs.budfox + 'candleCreator');

Expand All @@ -20,6 +21,10 @@ var Manager = function() {

this.candleCreator
.on('candles', this.relayCandles);

this.messageFirstCandle = _.once(candle => {
cp.firstCandle(candle);
})
};

util.makeEventEmitter(Manager);
Expand All @@ -29,6 +34,12 @@ Manager.prototype.processTrades = function(tradeBatch) {

Manager.prototype.relayCandles = function(candles) {
this.emit('candles', candles);

if(!_.size(candles))
return;

this.messageFirstCandle(_.first(candles));
cp.lastCandle(_.last(candles));
}

module.exports = Manager;
31 changes: 19 additions & 12 deletions core/budfox/heart.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ var log = require(util.dirs().core + 'log');
var _ = require('lodash');
var moment = require('moment');

if(util.getConfig().watch.exchange === 'okcoin')
var TICKRATE = 2;
else
var TICKRATE = 20;

var Heart = function() {
this.lastTick = false;

_.bindAll(this);
}

Expand All @@ -18,22 +25,22 @@ Heart.prototype.pump = function() {
}

Heart.prototype.tick = function() {
if(this.lastTick) {
// make sure the last tick happened not to lang ago
// @link https://github.com/askmike/gekko/issues/514
if(this.lastTick < moment().unix() - TICKRATE * 3)
util.die('Failed to tick in time, see https://github.com/askmike/gekko/issues/514 for details', true);
}

this.lastTick = moment().unix();
this.emit('tick');
}

Heart.prototype.determineLiveTickRate = function() {
// TODO: fix
if(util.getConfig().watch.exchange === 'okcoin')
var seconds = 2;
else
var seconds = 20;

this.tickRate = +moment.duration(seconds, 's');
}

Heart.prototype.scheduleTicks = function() {
this.determineLiveTickRate();
setInterval(this.tick, this.tickRate);
setInterval(
this.tick,
+moment.duration(TICKRATE, 's')
);

// start!
_.defer(this.tick);
Expand Down
42 changes: 34 additions & 8 deletions core/cp.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// functions that emit information
// to the parent process.
// functions that emit data to the parent process.
//
// noops if this gekko instance is not a child process!

Expand All @@ -11,13 +10,40 @@ var moment = require('moment');

var ENV = util.gekkoEnv();

var message = (type, payload) => {
payload.type = type;
process.send(payload);
}

var cp = {
message: (type, payload) => {
payload.type = type;
process.send(payload);
},
update: latest => cp.message('update', { latest }),
startAt: startAt => cp.message('startAt', { startAt }),
// string like: '2016-12-03T22:23:00.000Z'
update: latest => message('update', { latest }),
startAt: startAt => message('startAt', { startAt }),

// object like:
//
// {
// start: '2016-12-03T22:23:00.000Z',
// open: 765,
// high: 765,
// low: 765,
// close: 765,
// vwp: 765,
// volume: 0,
// trades: 0
// }
lastCandle: lastCandle => message('lastCandle', { lastCandle }),
firstCandle: firstCandle => message('firstCandle', { firstCandle }),

// object like:
//
// {
// action: 'sell',
// price: 765,
// date: '2016-12-03T22:23:00.000Z',
// balance: 4242
// }
trade: trade => message('trade', { trade }),
}

if(ENV !== 'child-process') {
Expand Down
8 changes: 7 additions & 1 deletion core/markets/backtest.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var dirs = util.dirs();
var log = require(dirs.core + 'log');
var moment = require('moment');

var adapter = config.adapters[config.backtest.adapter];
var adapter = config[config.adapter];
var Reader = require(dirs.gekko + adapter.path + '/reader');
var daterange = config.backtest.daterange;

Expand All @@ -15,6 +15,12 @@ var from = moment.utc(daterange.from);
if(to <= from)
util.die('This daterange does not make sense.')

if(!from.isValid())
util.die('invalid `from`');

if(!to.isValid())
util.die('invalid `to`');

var Market = function() {

_.bindAll(this);
Expand Down
2 changes: 1 addition & 1 deletion core/markets/importer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var log = require(dirs.core + 'log');
var moment = require('moment');
var cp = require(dirs.core + 'cp');

var adapter = config.adapters[config.importer.adapter];
var adapter = config[config.adapter];
var daterange = config.importer.daterange;

var from = moment.utc(daterange.from);
Expand Down
103 changes: 103 additions & 0 deletions core/markets/leech.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// a leech market is "semi-realtime" and pulls out candles of a
// database (which is expected to be updated reguraly, like with a
// realtime market running in parralel).

const _ = require('lodash');
const moment = require('moment');

const util = require('../util');
const dirs = util.dirs();
const config = util.getConfig();

const cp = require(dirs.core + 'cp');

const adapter = config[config.adapter];
const Reader = require(dirs.gekko + adapter.path + '/reader');

const TICKINTERVAL = 20 * 1000; // 20 seconds

const exchanges = require(dirs.gekko + 'exchanges');
const exchange = _.find(exchanges, function(e) {
return e.slug === config.watch.exchange.toLowerCase();
});

if(!exchange)
util.die(`Unsupported exchange: ${config.watch.exchange.toLowerCase()}`)

const exchangeChecker = require(util.dirs().core + 'exchangeChecker');

const error = exchangeChecker.cantMonitor(config.watch);
if(error)
util.die(error, true);

if(config.market.from)
var fromTs = moment.utc(config.market.from).unix();
else
var fromTs = moment().startOf('minute').unix();


var Market = function() {

_.bindAll(this);

Readable.call(this, {objectMode: true});

this.reader = new Reader();
this.latestTs = fromTs;

setInterval(
this.get,
TICKINTERVAL
);
}

var Readable = require('stream').Readable;
Market.prototype = Object.create(Readable.prototype, {
constructor: { value: Market }
});

Market.prototype._read = _.once(function() {
this.get();
});

Market.prototype.get = function() {
var future = moment().add(1, 'minute').unix();

this.reader.get(
this.latestTs,
future,
'full',
this.processCandles
)
}

Market.prototype.processCandles = function(err, candles) {
var amount = _.size(candles);
if(amount === 0) {
// no new candles!
return;
}


// TODO:
// verify that the correct amount of candles was passed:
//
// if `this.latestTs` was at 10:00 and we receive 3 candles with the latest at 11:00
// we know we are missing 57 candles...

_.each(candles, function(c, i) {
c.start = moment.unix(c.start).utc();
this.push(c);
}, this);

this.sendStartAt(_.first(candles));
cp.lastCandle(_.last(candles));

this.latestTs = _.last(candles).start.unix() + 1;
}

Market.prototype.sendStartAt = _.once(function(candle) {
cp.firstCandle(candle);
});

module.exports = Market;
10 changes: 8 additions & 2 deletions core/pipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,14 @@ var pipeline = (settings) => {
prepareMarket
],
function() {
// load a market based on the mode
var Market = require(dirs.markets + mode);
// load a market based on the config (or fallback to mode)
let marketType;
if(config.market)
marketType = config.market.type;
else
marketType = mode;

var Market = require(dirs.markets + marketType);

var market = new Market(config);
var gekko = new GekkoStream(candleConsumers);
Expand Down
Loading