Skip to content

Commit

Permalink
Hot-reload strategies
Browse files Browse the repository at this point in the history
  • Loading branch information
notVitaliy committed Nov 28, 2020
1 parent 3a58432 commit f063035
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 20 deletions.
7 changes: 2 additions & 5 deletions src/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,6 @@ class Core {
const exchangeCore = this.exchangeCores.get(exchange)
const isRunning = exchangeCore.strategyIsRunning(symbol, strategy)

// @note(notVitaliy): Hot reloading the strategy will not preload it with trade data
// unless the symbol trade sync is restarted, which is not a good idea since another
// strategy might be using that same symbol. That will double load the other strategy.
if (isRunning) exchangeCore.strategyStop(symbol, strategy)
await StrategyStore.instance.save(strategyConfig)
exchangeCore.updateStrategy(strategyConfig)
Expand All @@ -171,7 +168,7 @@ class Core {
if (hasExchange !== true) return hasExchange

const exchangeCore = this.exchangeCores.get(exchange)
if (exchangeCore.strategyIsRunning(symbol, strategy)) exchangeCore.strategyStop(symbol, strategy)
if (exchangeCore.strategyIsRunning(symbol, strategy)) exchangeCore.strategyKill(symbol, strategy)

exchangeCore.deleteStrategy(symbol, strategy)
await StrategyStore.instance.delete(exchange, symbol, strategy)
Expand Down Expand Up @@ -251,7 +248,7 @@ class Core {
strategies.forEach(({ symbol, strategy }) => {
if (exchangeCore.syncIsRunning(symbol)) exchangeCore.syncStop(symbol)
if (exchangeCore.tickerIsRunning(symbol)) exchangeCore.tickerStop(symbol)
if (exchangeCore.strategyIsRunning(symbol, strategy)) exchangeCore.strategyStop(symbol, strategy)
if (exchangeCore.strategyIsRunning(symbol, strategy)) exchangeCore.strategyKill(symbol, strategy)
})

return exchange
Expand Down
10 changes: 8 additions & 2 deletions src/core/exchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ export class ExchangeCore {
const { symbol, strategy } = strategyConfig
if (!this.strategyCores.has(symbol)) this.strategyCores.set(symbol, new Map())

const strategyCore = new StrategyCore(this.exchangeProvider, strategyConfig)
this.strategyCores.get(symbol).set(strategy, strategyCore)
this.strategyCores.get(symbol).get(strategy).update(strategyConfig)
}

public deleteStrategy(symbol: string, strategy: string) {
Expand Down Expand Up @@ -102,6 +101,13 @@ export class ExchangeCore {
this.strategyCores.get(symbol).get(strategy).stop()
}

public strategyKill(symbol: string, strategy: string) {
if (!this.checkForStrategy(symbol, strategy)) return

// prettier-ignore
this.strategyCores.get(symbol).get(strategy).kill()
}

public strategyIsRunning(symbol: string, strategy: string) {
// prettier-ignore
if (!this.strategyCores.has(symbol) || !this.strategyCores.get(symbol).has(strategy)) return false
Expand Down
23 changes: 18 additions & 5 deletions src/core/strategy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { EventBusListener } from '@magic8bot/event-bus'

import { Signal } from '@m8bTypes'
import { eventBus, EVENT, StrategyConfig, Adjustment } from '@lib'

Expand All @@ -25,7 +23,7 @@ export class StrategyCore {
private orderEngine: OrderEngine
private lastSignal: Signal = null

private signalListener: EventBusListener<{ signal: Signal }>
private signalListener: () => void

private state: STRAT_STATE = STRAT_STATE.STOPPED

Expand All @@ -35,8 +33,10 @@ export class StrategyCore {
this.symbol = symbol
this.strategy = strategy

this.signalListener = eventBus.get(EVENT.STRAT_SIGNAL)(exchange)(symbol)(strategy).listen
this.signalListener(({ signal }) => this.onSignal(signal))
// @ts-ignore
this.signalListener = eventBus
.get(EVENT.STRAT_SIGNAL)(exchange)(symbol)(strategy)
.listen(({ signal }) => this.onSignal(signal))

this.baseStrategy = new (strategyLoader(strategy))(exchange, symbol, this.strategyConfig)

Expand Down Expand Up @@ -71,6 +71,19 @@ export class StrategyCore {
logger.info(`Stopping Strategy ${strategy}`)
}

public update(strategyConfig: StrategyConfig) {
this.baseStrategy.update(strategyConfig)
}

public kill() {
const { exchange, symbol, strategy } = this
const storeOpts = { exchange, symbol, strategy }

logger.debug(`Killing ${JSON.stringify(storeOpts)}`)
this.stop()
this.signalListener()
}

public async initStrategyWallet() {
await this.adjustStrategyWallet(null)
}
Expand Down
1 change: 1 addition & 0 deletions src/store/period.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export class PeriodStore {
public stop(storeOpts: StoreOpts) {
const idStr = this.makeIdStr(storeOpts)
this.periodStates.set(idStr, PERIOD_STATE.STOPPED)
clearTimeout(this.periodTimer.get(idStr))
}

public addTrade(idStr: string, trade: Trade) {
Expand Down
60 changes: 53 additions & 7 deletions src/strategy/strategies/aroon/aroon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { BaseStrategy, StrategyField } from '../base-strategy'
import { logger } from '../../../util'

export interface AroonOptions {
period?: string
periods: number
buyUpThreshold: number
buyDownThreshold: number
sellUpThreshold: number
sellDownThreshold: number
}

export interface AroonPeriod {
Expand All @@ -16,9 +20,51 @@ export interface AroonPeriod {
export class Aroon extends BaseStrategy<AroonOptions, void> {
public static description = ''

public static fields: StrategyField[] = []
public static fields: StrategyField[] = [
{
name: 'periods',
type: 'number',
prettyName: 'Lookback periods',
description: 'Number of periods to lookback.',
default: 60,
},
{
name: 'buyUpThreshold',
type: 'number',
prettyName: 'Buy up threshold',
description: 'Will send buy signal if AroonUp is above this value and buy down threshold is met.',
default: 50,
},
{
name: 'buyDownThreshold',
type: 'number',
prettyName: 'Buy down threshold',
description: 'Will send buy signal if AroonDown is below this value and buy up threshold is met.',
default: 50,
},
{
name: 'sellUpThreshold',
type: 'number',
prettyName: 'Sell up threshold',
description: 'Will send sell signal if AroonUp is below this value and sell down threshold is met.',
default: 50,
},
{
name: 'sellDownThreshold',
type: 'number',
prettyName: 'Sell down threshold',
description: 'Will send sell signal if AroonDown is above this value and sell up threshold is met.',
default: 50,
},
]

public options: AroonOptions = {}
public options: AroonOptions = {
periods: 60,
buyUpThreshold: 50,
buyDownThreshold: 50,
sellUpThreshold: 50,
sellDownThreshold: 50,
}

private periods: AroonPeriod[] = [
{
Expand All @@ -35,8 +81,8 @@ export class Aroon extends BaseStrategy<AroonOptions, void> {
public calculate(periods: PeriodItem[]) {
if (!periods.length) return

const up = AroonUp.calculate(periods, 60)
const down = AroonDown.calculate(periods, 60)
const up = AroonUp.calculate(periods, this.options.periods)
const down = AroonDown.calculate(periods, this.options.periods)

this.periods[0] = { up, down }
}
Expand All @@ -60,13 +106,13 @@ export class Aroon extends BaseStrategy<AroonOptions, void> {
private shouldBuy() {
const [{ up, down }] = this.periods

return up > 50 && down < 50
return up > this.options.buyUpThreshold && down < this.options.buyDownThreshold
}

private shouldSell() {
const [{ up, down }] = this.periods

return up < 50 && down > 50
return up < this.options.sellUpThreshold && down > this.options.sellDownThreshold
}

public newPeriod() {
Expand Down
9 changes: 9 additions & 0 deletions src/strategy/strategies/aroon/phenotypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { phenotypes } from '@util'

export class AroonPhenotypes {
public static periods = phenotypes.range0(5, 200)
public static buyUpThreshold = phenotypes.range0(0, 100)
public static buyDownThreshold = phenotypes.range0(0, 100)
public static sellUpThreshold = phenotypes.range0(0, 100)
public static sellDownThreshold = phenotypes.range0(0, 100)
}
6 changes: 5 additions & 1 deletion src/strategy/strategies/base-strategy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PeriodItem, EVENT, eventBus } from '@lib'
import { PeriodItem, EVENT, eventBus, StrategyConfig } from '@lib'
import { EventBusListener, EventBusEmitter } from '@magic8bot/event-bus'
import { SignalEvent, Signal } from '@m8bTypes'

Expand Down Expand Up @@ -101,6 +101,10 @@ export abstract class BaseStrategy<TOptions = any, TCalcResult = any> {
this.calcEmitter = eventBus.get(EVENT.STRAT_CALC)(exchange)(symbol)(this.name).emit
}

public update(strategyConfig: StrategyConfig) {
this.options = { ...this.options, strategyConfig }
}

/**
* This method should implement the Indicator calculation.
* It should not return anything. All states have to be saved within the strategy!
Expand Down

0 comments on commit f063035

Please sign in to comment.