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

Commit

Permalink
Add balanced fill tracking to avoid unecessary re-randomization (#78),
Browse files Browse the repository at this point in the history
…closes #52
  • Loading branch information
Reidmcc authored and nikhilsaraf committed Dec 21, 2018
1 parent 6044c0b commit 0be414c
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 34 deletions.
17 changes: 11 additions & 6 deletions cmd/trade.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,16 +208,16 @@ func init() {
}()
}

strategyFillHandlers, e := strat.GetFillHandlers()
if e != nil {
log.Println()
log.Printf("problem encountered while instantiating the fill tracker: %s\n", e)
deleteAllOffersAndExit(botConfig, client, sdex)
}
if botConfig.FillTrackerSleepMillis != 0 {
fillTracker := plugins.MakeFillTracker(tradingPair, threadTracker, sdex, botConfig.FillTrackerSleepMillis)
fillLogger := plugins.MakeFillLogger()
fillTracker.RegisterHandler(fillLogger)
strategyFillHandlers, e := strat.GetFillHandlers()
if e != nil {
log.Println()
log.Printf("problem encountered while instantiating the fill tracker: %s\n", e)
deleteAllOffersAndExit(botConfig, client, sdex)
}
if strategyFillHandlers != nil {
for _, h := range strategyFillHandlers {
fillTracker.RegisterHandler(h)
Expand All @@ -234,6 +234,11 @@ func init() {
deleteAllOffersAndExit(botConfig, client, sdex)
}
}()
} else if strategyFillHandlers != nil && len(strategyFillHandlers) > 0 {
log.Println()
log.Printf("error: strategy has FillHandlers but fill tracking was disabled (set FILL_TRACKER_SLEEP_MILLIS to a non-zero value)\n")
// we want to delete all the offers and exit here because we don't want the bot to run if fill tracking isn't working
deleteAllOffersAndExit(botConfig, client, sdex)
}
// --- end initialization of services ---

Expand Down
91 changes: 63 additions & 28 deletions plugins/balancedLevelProvider.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package plugins

import (
"fmt"
"log"
"math/rand"
"time"
Expand All @@ -27,14 +28,21 @@ type balancedLevelProvider struct {
virtualBalanceBase float64 // virtual balance to use so we can smoothen out the curve
virtualBalanceQuote float64 // virtual balance to use so we can smoothen out the curve
orderConstraints *model.OrderConstraints
shouldRefresh bool // boolean for whether to generate levels, starts true

// precomputed before construction
randGen *rand.Rand

// uninitialized
lastLevels []api.Level // keeps the levels generated on the previous run to use if no offers were taken
}

// ensure it implements LevelProvider
var _ api.LevelProvider = &balancedLevelProvider{}

// ensure this implements api.FillHandler
var _ api.FillHandler = &balancedLevelProvider{}

// makeBalancedLevelProvider is the factory method
func makeBalancedLevelProvider(
spread float64,
Expand Down Expand Up @@ -69,8 +77,10 @@ func makeBalancedLevelProvider(
validateSpread(carryoverInclusionProbability)

randGen := rand.New(rand.NewSource(time.Now().UnixNano()))
shouldRefresh := true

return &balancedLevelProvider{
spread: spread,
spread: spread,
useMaxQuoteInTargetAmountCalc: useMaxQuoteInTargetAmountCalc,
minAmountSpread: minAmountSpread,
maxAmountSpread: maxAmountSpread,
Expand All @@ -84,6 +94,7 @@ func makeBalancedLevelProvider(
virtualBalanceQuote: virtualBalanceQuote,
orderConstraints: orderConstraints,
randGen: randGen,
shouldRefresh: shouldRefresh,
}
}

Expand All @@ -95,35 +106,19 @@ func validateSpread(spread float64) {

// GetLevels impl.
func (p *balancedLevelProvider) GetLevels(maxAssetBase float64, maxAssetQuote float64) ([]api.Level, error) {
_maxAssetBase := maxAssetBase + p.virtualBalanceBase
_maxAssetQuote := maxAssetQuote + p.virtualBalanceQuote
// represents the amount that was meant to be included in a previous level that we excluded because we skipped that level
amountCarryover := 0.0
levels := []api.Level{}
for i := int16(0); i < p.maxLevels; i++ {
level, e := p.getLevel(_maxAssetBase, _maxAssetQuote)
if e != nil {
return nil, e
}

// always update _maxAssetBase and _maxAssetQuote to account for the level we just calculated, ensures price moves across levels regardless of inclusion of prior levels
_maxAssetBase, _maxAssetQuote = updateAssetBalances(level, p.useMaxQuoteInTargetAmountCalc, _maxAssetBase, _maxAssetQuote)
if !p.shouldRefresh {
log.Println("no offers were taken, leave levels as they are")
return p.lastLevels, nil
}

// always take a spread off the amountCarryover
amountCarryoverSpread := p.getRandomSpread(p.minAmountCarryoverSpread, p.maxAmountCarryoverSpread)
amountCarryover *= (1 - amountCarryoverSpread)
levels, e := p.recomputeLevels(maxAssetBase, maxAssetQuote)
if e != nil {
return nil, fmt.Errorf("unable to generate new levels: %s", e)
}

if !p.shouldIncludeLevel(i) {
// accummulate targetAmount into amountCarryover
amountCarryover += level.Amount.AsFloat()
continue
}
p.lastLevels = levels
p.shouldRefresh = false

if p.shouldIncludeCarryover() {
level, amountCarryover = p.computeNewLevelWithCarryover(level, amountCarryover)
}
levels = append(levels, level)
}
return levels, nil
}

Expand Down Expand Up @@ -207,5 +202,45 @@ func (p *balancedLevelProvider) getLevel(maxAssetBase float64, maxAssetQuote flo

// GetFillHandlers impl
func (p *balancedLevelProvider) GetFillHandlers() ([]api.FillHandler, error) {
return nil, nil
return []api.FillHandler{p}, nil
}

// HandleFill impl
func (p *balancedLevelProvider) HandleFill(trade model.Trade) error {
log.Println("an offer was taken, levels will be recomputed")
p.shouldRefresh = true
return nil
}

func (p *balancedLevelProvider) recomputeLevels(maxAssetBase float64, maxAssetQuote float64) ([]api.Level, error) {
_maxAssetBase := maxAssetBase + p.virtualBalanceBase
_maxAssetQuote := maxAssetQuote + p.virtualBalanceQuote
// represents the amount that was meant to be included in a previous level that we excluded because we skipped that level
amountCarryover := 0.0
levels := []api.Level{}
for i := int16(0); i < p.maxLevels; i++ {
level, e := p.getLevel(_maxAssetBase, _maxAssetQuote)
if e != nil {
return nil, e
}

// always update _maxAssetBase and _maxAssetQuote to account for the level we just calculated, ensures price moves across levels regardless of inclusion of prior levels
_maxAssetBase, _maxAssetQuote = updateAssetBalances(level, p.useMaxQuoteInTargetAmountCalc, _maxAssetBase, _maxAssetQuote)

// always take a spread off the amountCarryover
amountCarryoverSpread := p.getRandomSpread(p.minAmountCarryoverSpread, p.maxAmountCarryoverSpread)
amountCarryover *= (1 - amountCarryoverSpread)

if !p.shouldIncludeLevel(i) {
// accummulate targetAmount into amountCarryover
amountCarryover += level.Amount.AsFloat()
continue
}

if p.shouldIncludeCarryover() {
level, amountCarryover = p.computeNewLevelWithCarryover(level, amountCarryover)
}
levels = append(levels, level)
}
return levels, nil
}

0 comments on commit 0be414c

Please sign in to comment.