Skip to content

Commit

Permalink
Merge pull request #15 from askmike/develop
Browse files Browse the repository at this point in the history
Merge
  • Loading branch information
hiyan committed Aug 28, 2018
2 parents 441cb49 + 95d1873 commit e982c24
Show file tree
Hide file tree
Showing 24 changed files with 694 additions and 50 deletions.
4 changes: 2 additions & 2 deletions core/tools/candleLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const handleCandles = (err, data) => {
util.die('Encountered an error..')
}

if(_.size(data) && _.last(data).start >= toUnix)
if(_.size(data) && _.last(data).start >= toUnix || iterator.from.unix() >= toUnix)
DONE = true;

batcher.write(data);
Expand All @@ -97,4 +97,4 @@ const handleCandles = (err, data) => {

const handleBatchedCandles = candle => {
result.push(candle);
}
}
3 changes: 2 additions & 1 deletion core/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ var util = {
tools: ROOT + 'core/tools/',
workers: ROOT + 'core/workers/',
web: ROOT + 'web/',
config: ROOT + 'config/'
config: ROOT + 'config/',
broker: ROOT + 'exchange/'
}
},
inherit: function(dest, source) {
Expand Down
6 changes: 5 additions & 1 deletion core/workers/loadCandles/child.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ process.send('ready');
process.on('message', (m) => {
if(m.what === 'start')
start(m.config, m.candleSize, m.daterange);
});
});

process.on('disconnect', function() {
process.exit(0);
})
2 changes: 1 addition & 1 deletion core/workers/loadCandles/parent.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ module.exports = (config, callback) => {

// else we are done and have candles!
done(null, m);
child.kill('SIGINT');
this.disconnect();
});

child.on('exit', code => {
Expand Down
51 changes: 48 additions & 3 deletions docs/internals/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Note that all events from Gekko come from a plugin (with the exception of the `c

- [candle](#candle-event): Every time Gekko calculates a new one minute candle from the market.
- [stratWarmupCompleted](#stratWarmupCompleted-event): When the strategy is done warming up.
- [advice](#advice-event): Every time the trading strategy is fed a new candle.
- [advice](#advice-event): Every time the trading strategy has a new trading signal.
- [stratUpdate](#stratUpdate-event): Every time the strategy has processed a new strat candle.
- [stratNotification](#stratNotification-event): Every time the strategy emit new strategy notification.
- [tradeInitiated](#tradeInitiated-event): Every time a trading plugin (either the live trader or the paper trader) is going to start a new trade (buy or sell).
Expand All @@ -25,8 +25,11 @@ trader) has NOT acted on new advice (due to unsufficiant funds or a similar reas
- [portfolioValueChange](#portfolioValueChange-event): Every time value of the portfolio has changed.
- [performanceReport](#performanceReport-event): Every time the profit report was updated.
- [roundtrip](#roundtrip-event): Every time a new roundtrip has been completed.
- [triggerCreated](#triggerCreated-event): Every time a trader has created a new trigger.
- [triggerFired](#triggerFired-event): Every time a created trigger has fired.
- [triggerAborted](#triggerAborted-event): Every time a created trigger has been aborted due to new advice.

Beside those there are also two additional market events that are only emitted when Gekko is running in either realtime or importing mode (NOT during a backtest for performance reasons).
Beside those there are also two additional market events that are only emitted when Gekko is running in realtime mode (NOT during a backtest for performance reasons).

- [marketStart](#marketStart-event): Once, when the market just started.
- [marketUpdate](#marketUpdate-event): Whenever the market has fetched new raw market data.
Expand Down Expand Up @@ -123,7 +126,11 @@ and will start signaling advice.
{
recommendation: [position to take, either long or short],
date: [moment object of this advice],
id: [string identifying this unique trade]
id: [string identifying this unique trade],
trigger: {
type: [type of trigger, currently always "trailingStop"],
// optional parameters per type of trigger
}
}

### tradeInitiated event
Expand Down Expand Up @@ -297,6 +304,44 @@ and will start signaling advice.
profit: -0.2320439659276161,
}

### triggerCreated event

- What: A summary of a created trigger.
- When: After a buy advice that includes a stop.
- Subscribe: You can subscribe to this event by registering the `processTriggerCreated` method.
- Example:
{
id: [string identifying this trigger],
date: Moment<'2017-03-25 19:41:00'>,
type: type: "trailingStop",
properties: {
initialPrice: 100,
trail: 10
}
}

### triggerFired event

- What: A message indicating a created trigger has fired
- When: As soon as the trigger fired
- Subscribe: You can subscribe to this event by registering the `processTriggerFired` method.
- Example:
{
id: [string identifying this trigger],
date: Moment<'2017-03-25 19:41:00'>
}

### triggerAborted event

- What: A message indicating a created trigger has been aborted
- When: After an advice signal indicating a sell
- Subscribe: You can subscribe to this event by registering the `processTriggerAborted` method.
- Example:
{
id: [string identifying this trigger],
date: Moment<'2017-03-25 19:41:00'>
}

### marketStart event

- What: A moment object describing the first date of the market data.
Expand Down
16 changes: 12 additions & 4 deletions docs/strategies/creating_a_strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,21 @@ Most strategies need to warmup before the trading strategy can be started. For e

If you find out in the check function that you want to give new advice to the trader you can use the advice function:

this.advice('short');
// or
this.advice('long');
this.advice({
direction: 'long' // or short
trigger: { // ignored when direction is not "long"
type: 'trailingStop',
trailPercentage: 5
// or:
// trailValue: 100
}
});

The trigger is optional, if the direction is long and the trigger is specified as a trailingStop this will request the trader to create a trail stop trigger.

### candle variable

The following list of candle variables will be available when writing strategies:
The following list of candle variables will be available when writing strategies, they are part of the candle object which is given to your `update` and `check` functions (it's also accessable through `this.candle`).

- candle.close: the closing price of the candle
- candle.high: the highest price of the candle
Expand Down
12 changes: 12 additions & 0 deletions exchange/gekkoBroker.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ const errors = require('./exchangeErrors');
const Portfolio = require('./portfolioManager');
// const Market = require('./market');
const orders = require('./orders');
const Trigger = require('./trigger');
const exchangeUtils = require('./exchangeUtils');
const bindAll = exchangeUtils.bindAll;
const isValidOrder = exchangeUtils.isValidOrder;



class Broker {
constructor(config) {
this.config = config;
Expand Down Expand Up @@ -157,6 +160,15 @@ class Broker {

return order;
}

createTrigger({type, onTrigger, props}) {
return new Trigger({
api: this.api,
type,
onTrigger,
props
});
}
}

module.exports = Broker;
80 changes: 80 additions & 0 deletions exchange/trigger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// wraps around a low level trigger and feeds
// it live market data.

const _ = require('lodash');

const exchangeUtils = require('./exchangeUtils');
const bindAll = exchangeUtils.bindAll;

const triggers = require('./triggers');

// @param api: a gekko broker wrapper instance
// @param type: type of trigger to wrap
// @param props: properties to feed to trigger
class Trigger {
constructor({api, type, props, onTrigger}) {
this.onTrigger = onTrigger;
this.api = api;

this.isLive = true;

// note: we stay on the safe side and trigger
// as soon as the bid goes below trail.
this.tickerProp = 'bid';

if(!_.has(triggers, type)) {
throw new Error('Gekko Broker does not know trigger ' + type);
}

this.CHECK_INTERVAL = this.api.interval * 10;

bindAll(this);
this.trigger = new triggers[type]({
onTrigger: this.propogateTrigger,
...props
})

this.scheduleFetch();
}

scheduleFetch() {
this.timout = setTimeout(this.fetch, this.CHECK_INTERVAL);
}

fetch() {
if(!this.isLive) {
return;
}
this.api.getTicker(this.processTicker)
}

processTicker(err, ticker) {
if(!this.isLive) {
return;
}

if(err) {
return console.log('[GB/trigger] failed to fetch ticker:', err);
}

this.price = ticker[this.tickerProp];

this.trigger.updatePrice(this.price);
this.scheduleFetch();
}

cancel() {
this.isLive = false;
clearTimeout(this.timout);
}

propogateTrigger(payload) {
if(!this.isLive) {
return;
}
this.isLive = false;
this.onTrigger(payload);
}
}

module.exports = Trigger;
3 changes: 3 additions & 0 deletions exchange/triggers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const trailingStop = require('./trailingStop');

module.exports = { trailingStop };
62 changes: 62 additions & 0 deletions exchange/triggers/trailingStop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const EventEmitter = require('events');

// Note: as of now only supports trailing the price going up (after
// a buy), on trigger (when the price moves down) you should sell.


// @param initialPrice: initial price, preferably buy price
// @param trail: fixed offset from the price
// @param onTrigger: fn to call when the stop triggers
class TrailingStop extends EventEmitter {
constructor({trail, initialPrice, onTrigger}) {
super();

this.trail = trail;
this.isLive = true;
this.onTrigger = onTrigger;

this.previousPrice = initialPrice;
this.trailingPoint = initialPrice - this.trail;
}

updatePrice(price) {
if(!this.isLive) {
return;
}

if(price > this.trailingPoint + this.trail) {
this.trailingPoint = price - this.trail;
}

this.previousPrice = price;

if(price <= this.trailingPoint) {
this.trigger();
}
}

updateTrail(trail) {
if(!this.isLive) {
return;
}

this.trail = trail;
this.trailingPoint = this.previousPrice - this.trail;
// recheck whether moving the trail triggered.
this.updatePrice(this.previousPrice);
}

trigger() {
if(!this.isLive) {
return;
}

this.isLive = false;
if(this.onTrigger) {
this.onTrigger(this.previousPrice);
}
this.emit('trigger', this.previousPrice);
}
}

module.exports = TrailingStop;
1 change: 1 addition & 0 deletions exchange/wrappers/binance.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ Trader.prototype.isValidPrice = function(price) {
}

Trader.prototype.isValidLot = function(price, amount) {
console.log('isValidLot', this.market.minimalOrder.order, amount * price >= this.market.minimalOrder.order)
return amount * price >= this.market.minimalOrder.order;
}

Expand Down
22 changes: 15 additions & 7 deletions package-lock.json

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

Loading

0 comments on commit e982c24

Please sign in to comment.