diff --git a/cmd/trade.go b/cmd/trade.go index 303c6c27d..c47b3bf9e 100644 --- a/cmd/trade.go +++ b/cmd/trade.go @@ -172,7 +172,10 @@ func init() { deleteAllOffersAndExit(botConfig, client, sdex) } - timeController := plugins.MakeIntervalTimeController(time.Duration(botConfig.TickIntervalSeconds) * time.Second) + timeController := plugins.MakeIntervalTimeController( + time.Duration(botConfig.TickIntervalSeconds)*time.Second, + botConfig.MaxTickDelayMillis, + ) bot := trader.MakeBot( client, botConfig.AssetBase(), diff --git a/examples/configs/trader/sample_trader.cfg b/examples/configs/trader/sample_trader.cfg index 024a9d2f9..4849caa64 100644 --- a/examples/configs/trader/sample_trader.cfg +++ b/examples/configs/trader/sample_trader.cfg @@ -17,6 +17,9 @@ ISSUER_B="GBMMZMK2DC4FFP4CAI6KCVNCQ7WLO5A7DQU7EC7WGHRDQBZB763X4OQI" # how often you want the bot to run TICK_INTERVAL_SECONDS=300 +# randomized interval delay in millis +MAX_TICK_DELAY_MILLIS=0 + # how many continuous errors in each update cycle can the bot accept before it will delete all offers to protect its exposure. # this number has to be exceeded for all the offers to be deleted and any error will be counted only once per update cycle. # any time the bot completes a full run successfully this counter will be reset. diff --git a/plugins/intervalTimeController.go b/plugins/intervalTimeController.go index 9c8b52629..1fbd13e7b 100644 --- a/plugins/intervalTimeController.go +++ b/plugins/intervalTimeController.go @@ -2,6 +2,7 @@ package plugins import ( "log" + "math/rand" "time" "github.com/interstellar/kelp/api" @@ -9,12 +10,19 @@ import ( // IntervalTimeController provides a standard time interval type IntervalTimeController struct { - tickInterval time.Duration + tickInterval time.Duration + maxTickDelayMillis int64 + randGen *rand.Rand } // MakeIntervalTimeController is a factory method -func MakeIntervalTimeController(tickInterval time.Duration) api.TimeController { - return &IntervalTimeController{tickInterval: tickInterval} +func MakeIntervalTimeController(tickInterval time.Duration, maxTickDelayMillis int64) api.TimeController { + randGen := rand.New(rand.NewSource(time.Now().UnixNano())) + return &IntervalTimeController{ + tickInterval: tickInterval, + maxTickDelayMillis: maxTickDelayMillis, + randGen: randGen, + } } var _ api.TimeController = &IntervalTimeController{} @@ -31,5 +39,16 @@ func (t *IntervalTimeController) ShouldUpdate(lastUpdateTime time.Time, currentU 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()) + fixedDurationCatchup := time.Duration(t.tickInterval.Nanoseconds() - elapsedSinceUpdate.Nanoseconds()) + randomizedDelayMillis := t.makeRandomDelay() + + // if fixedDurationCatchup < 0 then we already have a built-in randomized delay because of the variable processing time consumed + return fixedDurationCatchup + randomizedDelayMillis +} + +func (t *IntervalTimeController) makeRandomDelay() time.Duration { + if t.maxTickDelayMillis > 0 { + return time.Duration(t.randGen.Int63n(t.maxTickDelayMillis)) * time.Millisecond + } + return time.Duration(0) * time.Millisecond } diff --git a/trader/config.go b/trader/config.go index b5c3e1277..a318cabda 100644 --- a/trader/config.go +++ b/trader/config.go @@ -20,6 +20,7 @@ type BotConfig struct { AssetCodeB string `valid:"-" toml:"ASSET_CODE_B"` IssuerB string `valid:"-" toml:"ISSUER_B"` TickIntervalSeconds int32 `valid:"-" toml:"TICK_INTERVAL_SECONDS"` + MaxTickDelayMillis int64 `valid:"-" toml:"MAX_TICK_DELAY_MILLIS"` DeleteCyclesThreshold int64 `valid:"-" toml:"DELETE_CYCLES_THRESHOLD"` FillTrackerSleepMillis uint32 `valid:"-" toml:"FILL_TRACKER_SLEEP_MILLIS"` HorizonURL string `valid:"-" toml:"HORIZON_URL"`