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

Commit

Permalink
more accurate and customizable clock tick intervals
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilsaraf committed Nov 15, 2018
1 parent c5d26bc commit cd33d91
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 35 deletions.
13 changes: 13 additions & 0 deletions api/timeController.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package api

import "time"

// TimeController controls the update loop for the bot
type TimeController interface {
// ShouldUpdate defines when to enter the bot's update cycle
// lastUpdateTime will never start off as the zero value
ShouldUpdate(lastUpdateTime time.Time, currentUpdateTime time.Time) bool

// SleepTime computes how long we want to sleep before the next call to ShouldUpdate
SleepTime(lastUpdateTime time.Time, currentUpdateTime time.Time) time.Duration
}
3 changes: 2 additions & 1 deletion cmd/trade.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,15 @@ func init() {
deleteAllOffersAndExit(botConfig, client, sdex)
}

timeController := plugins.MakeIntervalTimeController(time.Duration(botConfig.TickIntervalSeconds) * time.Second)
bot := trader.MakeBot(
client,
botConfig.AssetBase(),
botConfig.AssetQuote(),
botConfig.TradingAccount(),
sdex,
strat,
botConfig.TickIntervalSeconds,
timeController,
threadTracker,
fixedIterations,
dataKey,
Expand Down
35 changes: 35 additions & 0 deletions plugins/intervalTimeController.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package plugins

import (
"log"
"time"

"github.com/lightyeario/kelp/api"
)

// IntervalTimeController provides a standard time interval
type IntervalTimeController struct {
tickInterval time.Duration
}

// MakeIntervalTimeController is a factory method
func MakeIntervalTimeController(tickInterval time.Duration) api.TimeController {
return &IntervalTimeController{tickInterval: tickInterval}
}

var _ api.TimeController = &IntervalTimeController{}

// ShouldUpdate impl
func (t *IntervalTimeController) ShouldUpdate(lastUpdateTime time.Time, currentUpdateTime time.Time) bool {
elapsedSinceUpdate := currentUpdateTime.Sub(lastUpdateTime)
shouldUpdate := elapsedSinceUpdate >= t.tickInterval
log.Printf("intervalTimeController tickInterval=%s, shouldUpdate=%v, elapsedSinceUpdate=%s\n", t.tickInterval, shouldUpdate, elapsedSinceUpdate)
return shouldUpdate
}

// SleepTime impl
func (t *IntervalTimeController) SleepTime(lastUpdateTime time.Time, currentUpdateTime time.Time) time.Duration {
// use time till now as opposed to currentUpdateTime because we want the start of the clock cycle to be synchronized
elapsedSinceUpdate := time.Since(lastUpdateTime)
return time.Duration(t.tickInterval.Nanoseconds() - elapsedSinceUpdate.Nanoseconds())
}
80 changes: 46 additions & 34 deletions trader/trader.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ const maxLumenTrust float64 = math.MaxFloat64

// Trader represents a market making bot, which is composed of various parts include the strategy and various APIs.
type Trader struct {
api *horizon.Client
assetBase horizon.Asset
assetQuote horizon.Asset
tradingAccount string
sdex *plugins.SDEX
strat api.Strategy // the instance of this bot is bound to this strategy
tickIntervalSeconds int32
threadTracker *multithreading.ThreadTracker
fixedIterations *uint64
dataKey *model.BotKey
alert api.Alert
api *horizon.Client
assetBase horizon.Asset
assetQuote horizon.Asset
tradingAccount string
sdex *plugins.SDEX
strat api.Strategy // the instance of this bot is bound to this strategy
timeController api.TimeController
threadTracker *multithreading.ThreadTracker
fixedIterations *uint64
dataKey *model.BotKey
alert api.Alert

// uninitialized
maxAssetA float64
Expand All @@ -49,43 +49,55 @@ func MakeBot(
tradingAccount string,
sdex *plugins.SDEX,
strat api.Strategy,
tickIntervalSeconds int32,
timeController api.TimeController,
threadTracker *multithreading.ThreadTracker,
fixedIterations *uint64,
dataKey *model.BotKey,
alert api.Alert,
) *Trader {
return &Trader{
api: api,
assetBase: assetBase,
assetQuote: assetQuote,
tradingAccount: tradingAccount,
sdex: sdex,
strat: strat,
tickIntervalSeconds: tickIntervalSeconds,
threadTracker: threadTracker,
fixedIterations: fixedIterations,
dataKey: dataKey,
alert: alert,
api: api,
assetBase: assetBase,
assetQuote: assetQuote,
tradingAccount: tradingAccount,
sdex: sdex,
strat: strat,
timeController: timeController,
threadTracker: threadTracker,
fixedIterations: fixedIterations,
dataKey: dataKey,
alert: alert,
}
}

// Start starts the bot with the injected strategy
func (t *Trader) Start() {
log.Println("----------------------------------------------------------------------------------------------------")
var lastUpdateTime time.Time

for {
log.Println("----------------------------------------------------------------------------------------------------")
t.update()
if t.fixedIterations != nil {
*t.fixedIterations = *t.fixedIterations - 1
if *t.fixedIterations <= 0 {
log.Printf("finished requested number of iterations, waiting for all threads to finish...\n")
t.threadTracker.Wait()
log.Printf("...all threads finished, stopping bot update loop\n")
return
currentUpdateTime := time.Now()
if lastUpdateTime.IsZero() || t.timeController.ShouldUpdate(lastUpdateTime, currentUpdateTime) {
t.update()
if t.fixedIterations != nil {
*t.fixedIterations = *t.fixedIterations - 1
if *t.fixedIterations <= 0 {
log.Printf("finished requested number of iterations, waiting for all threads to finish...\n")
t.threadTracker.Wait()
log.Printf("...all threads finished, stopping bot update loop\n")
return
}
}

// wait for any goroutines from the current update to finish so we don't have inconsistent state reads
t.threadTracker.Wait()
log.Println("----------------------------------------------------------------------------------------------------")
lastUpdateTime = currentUpdateTime
}
log.Printf("sleeping for %d seconds...\n", t.tickIntervalSeconds)
time.Sleep(time.Duration(t.tickIntervalSeconds) * time.Second)

sleepTime := t.timeController.SleepTime(lastUpdateTime, currentUpdateTime)
log.Printf("sleeping for %s...\n", sleepTime)
time.Sleep(sleepTime)
}
}

Expand Down

0 comments on commit cd33d91

Please sign in to comment.