Skip to content

Commit

Permalink
test: developing examples for optimize pkg
Browse files Browse the repository at this point in the history
  • Loading branch information
richklee committed May 25, 2022
1 parent 0d8cb61 commit 1b75083
Show file tree
Hide file tree
Showing 9 changed files with 4,397 additions and 9 deletions.
4 changes: 3 additions & 1 deletion broker/backtest/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func Example() {
asset := market.NewAsset("BTCUSD")

// Read a .csv file of historical prices (aka candlestick data)
file, _ := os.Open("prices.csv")
file, _ := os.Open("testdata/BTCUSDT-1h-2021-Q1.csv")
defer file.Close()
reader := market.NewCSVKlineReader(csv.NewReader(file))

Expand All @@ -52,5 +52,7 @@ func Example() {
trades, _, _ := dealer.ListTrades(context.Background(), nil)
equity := dealer.EquityHistory()
report := perf.NewPerformanceReport(trades, equity)

// Output: Your backtest return is 2974.54%
fmt.Printf("Your backtest return is %.2f%%", report.Portfolio.EquityReturn*100)
}
2,158 changes: 2,158 additions & 0 deletions broker/backtest/testdata/BTCUSDT-1h-2021-Q1.csv

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion internal/studyrun/readprices.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func ReadPricesFromConfig(config map[string]any) ([][]market.Kline, error) {
var prices [][]market.Kline
for _, v := range paths {
path := v.(string)
series, err := market.CSVKlineReadAllDir(path)
series, err := market.ReadKlinesFromCSV(path)
if err != nil {
return nil, err
}
Expand Down
5 changes: 3 additions & 2 deletions market/csvklinereadalldir.go → market/readklines.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (
"path/filepath"
)

// CSVKlineReadAllDir reads all the .csv files in a given directory into a slice of Klines.
func CSVKlineReadAllDir(path string) ([]Kline, error) {
// ReadKlinesFromCSV reads all the .csv files in a given directory or a single file into a slice of Klines.
// Wraps a CSVKlineReader for convenience. For finer grained memory management use the base kline reader.
func ReadKlinesFromCSV(path string) ([]Kline, error) {
var prices []Kline

err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
Expand Down
14 changes: 14 additions & 0 deletions market/readklines_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package market

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestReadKlinesFromCSV(t *testing.T) {

prices, err := ReadKlinesFromCSV("testdata/BTCUSDT-1h-2021-Q1.csv")
assert.NoError(t, err)
assert.Len(t, prices, 2158)
}
2,158 changes: 2,158 additions & 0 deletions market/testdata/BTCUSDT-1h-2021-Q1.csv

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion optimize/bruteoptimizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/gammazero/workerpool"
"github.com/thecolngroup/alphakit/broker"
"github.com/thecolngroup/alphakit/broker/backtest"
"github.com/thecolngroup/alphakit/market"
"github.com/thecolngroup/alphakit/perf"
"github.com/thecolngroup/alphakit/trader"
Expand Down Expand Up @@ -50,12 +51,13 @@ type bruteOptimizerJob struct {
MakeDealer broker.MakeSimulatedDealer
}

// NewBruteOptimizer creates a new BruteOptimizer instance.
// NewBruteOptimizer creates a new BruteOptimizer instance with sensible defaults.
// Call Prepare before Start to set up the study.
func NewBruteOptimizer() BruteOptimizer {
return BruteOptimizer{
SampleSplitPct: 0,
WarmupBarCount: 0,
MakeDealer: func() (broker.SimulatedDealer, error) { return backtest.NewDealer(), nil },
Ranker: SharpeRanker,
MaxWorkers: runtime.NumCPU(),
study: NewStudy(),
Expand Down
53 changes: 53 additions & 0 deletions optimize/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package optimize

import (
"context"
"fmt"

"github.com/thecolngroup/alphakit/market"
"github.com/thecolngroup/alphakit/trader/trend"
)

func Example() {
// Verbose error handling ommitted for brevity

// Identify the bot (algo) to optimize by supplying a factory function
// Here we're using the classic MA cross variant of trend bot
bot := trend.MakeCrossBotFromConfig

// Define the parameter space to optimize
// Param names must match those expected by the MakeBot function passed to optimizer
paramSpace := ParamMap{
"mafastlength": []float64{1, 10, 20, 30},
"maslowlength": []float64{30, 40, 50, 60},
"mmilength": []float64{200, 300},
}

// Read price samples to use for optimization
btcPriceSample, _ := market.ReadKlinesFromCSV("testdata/")
ethPriceSample, _ := market.ReadKlinesFromCSV("testdata/")
priceSamples := [][]market.Kline{btcPriceSample, ethPriceSample}

// Create a new brute style optimizer
optimizer := NewBruteOptimizer()
optimizer.SampleSplitPct = 0.5
optimizer.WarmupBarCount = 300
optimizer.MakeBot = bot

// Prepare the optimizer and get an estimate on the number of trials (backtests) required
trialCount, _ := optimizer.Prepare(paramSpace, priceSamples)
fmt.Printf("%d trials to run during optimization\n", trialCount)

// Start the optimization process and monitor with a receive channel
trials, _ := optimizer.Start(context.Background())
for range trials {
}

// Inspect the study to get the optimized param set and results
study := optimizer.Study()
optimaPSet := study.Validation[0]
optimaResult := study.ValidationResults[optimaPSet.ID]

// Output: Optima sharpe ratio is 0.0
fmt.Printf("Optima sharpe ratio is %.2f", optimaResult.Sharpe)
}
8 changes: 4 additions & 4 deletions trader/trend/makecrossbot.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ func MakeCrossBotFromConfig(config map[string]any) (trader.Bot, error) {

bot.Asset = market.NewAsset(util.ToString(config["asset"]))

bot.EnterLong = util.ToFloat(config["enterlong"])
bot.EnterShort = util.ToFloat(config["entershort"])
bot.ExitLong = util.ToFloat(config["exitlong"])
bot.ExitShort = util.ToFloat(config["exitshort"])
bot.EnterLong = util.NNZ(util.ToFloat(config["enterlong"]), 1.0)
bot.EnterShort = util.NNZ(util.ToFloat(config["entershort"]), -1.0)
bot.ExitLong = util.NNZ(util.ToFloat(config["exitlong"]), -1.0)
bot.ExitShort = util.NNZ(util.ToFloat(config["exitshort"]), 1.0)

maFastLength := util.ToInt(config["mafastlength"])
maSlowLength := util.ToInt(config["maslowlength"])
Expand Down

0 comments on commit 1b75083

Please sign in to comment.