diff --git a/examples/backtesting/main.go b/examples/backtesting/main.go index 271f28de..7d147205 100644 --- a/examples/backtesting/main.go +++ b/examples/backtesting/main.go @@ -3,14 +3,14 @@ package main import ( "context" + log "github.com/sirupsen/logrus" + "github.com/rodrigo-brito/ninjabot" "github.com/rodrigo-brito/ninjabot/examples/strategies" "github.com/rodrigo-brito/ninjabot/exchange" "github.com/rodrigo-brito/ninjabot/plot" "github.com/rodrigo-brito/ninjabot/plot/indicator" "github.com/rodrigo-brito/ninjabot/storage" - - log "github.com/sirupsen/logrus" ) func main() { @@ -83,6 +83,7 @@ func main() { // connect bot feed (candle and orders) to the chart ninjabot.WithCandleSubscription(chart), ninjabot.WithOrderSubscription(chart), + ninjabot.WithLogLevel(log.WarnLevel), ) if err != nil { log.Fatal(err) diff --git a/readme.md b/readme.md index be4a8ac2..8f640950 100644 --- a/readme.md +++ b/readme.md @@ -49,38 +49,39 @@ go run examples/backtesting/main.go Output: ``` -INFO[2021-10-31 18:13] [SETUP] Using paper wallet -INFO[2021-10-31 18:13] [SETUP] Initial Portfolio = 10000.000000 USDT -+---------+--------+-----+------+--------+--------+----------+-----------+ -| PAIR | TRADES | WIN | LOSS | % WIN | PAYOFF | PROFIT | VOLUME | -+---------+--------+-----+------+--------+--------+----------+-----------+ -| BTCUSDT | 14 | 6 | 8 | 42.9 % | 5.929 | 13511.66 | 448030.05 | -| ETHUSDT | 9 | 6 | 3 | 66.7 % | 3.407 | 21748.41 | 407769.64 | -+---------+--------+-----+------+--------+--------+----------+-----------+ -| TOTAL | 23 | 12 | 11 | 52.2 % | 4.942 | 35260.07 | 855799.68 | -+---------+--------+-----+------+--------+--------+----------+-----------+ - --------------- -WALLET SUMMARY --------------- -0.000000 BTC = 0.000000 USDT -0.000000 ETH = 0.000000 USDT - -TRADING VOLUME -BTCUSDT = 448030.05 USDT -ETHUSDT = 407769.64 USDT - -45260.073493 USDT --------------- -START PORTFOLIO = 10000.00 USDT -FINAL PORTFOLIO = 45260.07 USDT -GROSS PROFIT = 35260.073493 USDT (352.60%) -MARKET (B&H) = 407.09% -MAX DRAWDOWN = -11.76 % -VOLUME = 855799.68 USDT -COSTS (0.001*V) = 855.80 USDT (ESTIMATION) --------------- +INFO[2022-10-16 16:34] [SETUP] Using paper wallet +INFO[2022-10-16 16:34] [SETUP] Initial Portfolio = 10000.000000 USDT ++---------+--------+-----+------+--------+--------+-----+----------+-----------+ +| PAIR | TRADES | WIN | LOSS | % WIN | PAYOFF | SQN | PROFIT | VOLUME | ++---------+--------+-----+------+--------+--------+-----+----------+-----------+ +| BTCUSDT | 14 | 6 | 8 | 42.9 % | 5.929 | 1.5 | 13511.66 | 448030.04 | +| ETHUSDT | 9 | 6 | 3 | 66.7 % | 3.407 | 1.3 | 21748.41 | 407769.64 | ++---------+--------+-----+------+--------+--------+-----+----------+-----------+ +| TOTAL | 23 | 12 | 11 | 52.2 % | 4.942 | 1.4 | 35260.07 | 855799.68 | ++---------+--------+-----+------+--------+--------+-----+----------+-----------+ + +-- FINAL WALLET -- +0.0000 BTC = 0.0000 USDT +0.0000 ETH = 0.0000 USDT +45260.0734 USDT + +----- RETURNS ----- +START PORTFOLIO = 10000.00 USDT +FINAL PORTFOLIO = 45260.07 USDT +GROSS PROFIT = 35260.073380 USDT (352.60%) +MARKET CHANGE (B&H) = 407.09% + +------ RISK ------- +MAX DRAWDOWN = -11.76 % + +------ VOLUME ----- +ETHUSDT = 407769.64 USDT +BTCUSDT = 448030.04 USDT +TOTAL = 855799.68 USDT +COSTS (0.001*V) = 855.80 USDT (ESTIMATION) +------------------- Chart available at http://localhost:8080 + ``` ### Plot result: diff --git a/storage/buntdb_test.go b/storage/buntdb_test.go index b4ba96ba..8fa060cb 100644 --- a/storage/buntdb_test.go +++ b/storage/buntdb_test.go @@ -11,7 +11,7 @@ func TestFromFile(t *testing.T) { file, err := os.CreateTemp(os.TempDir(), "*.db") require.NoError(t, err) defer func() { - os.Remove(file.Name()) + os.RemoveAll(file.Name()) }() db, err := FromFile(file.Name()) require.NoError(t, err) diff --git a/storage/sql.go b/storage/sql.go index d196cb18..d8032646 100644 --- a/storage/sql.go +++ b/storage/sql.go @@ -3,16 +3,25 @@ package storage import ( "time" - "github.com/rodrigo-brito/ninjabot/model" + "github.com/samber/lo" "gorm.io/gorm" + + "github.com/rodrigo-brito/ninjabot/model" ) type SQL struct { db *gorm.DB } -func FromSQL(dialector gorm.Dialector, opts ...gorm.Option) (Storage, error) { - db, err := gorm.Open(dialector, opts...) +// FromSQL creates a new SQL connections for orders storage. Example of usage: +// +// import "github.com/glebarez/sqlite" +// storage, err := storage.FromSQL(sqlite.Open("sqlite.db"), &gorm.Config{}) +// if err != nil { +// log.Fatal(err) +// } +func FromSQL(dialect gorm.Dialector, opts ...gorm.Option) (Storage, error) { + db, err := gorm.Open(dialect, opts...) if err != nil { return nil, err } @@ -21,6 +30,7 @@ func FromSQL(dialector gorm.Dialector, opts ...gorm.Option) (Storage, error) { if err != nil { return nil, err } + sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) sqlDB.SetConnMaxLifetime(time.Hour) @@ -35,11 +45,13 @@ func FromSQL(dialector gorm.Dialector, opts ...gorm.Option) (Storage, error) { }, nil } +// CreateOrder creates a new order in a SQL database func (s *SQL) CreateOrder(order *model.Order) error { result := s.db.Create(order) // pass pointer of data to Create return result.Error } +// UpdateOrder updates a given order func (s *SQL) UpdateOrder(order *model.Order) error { o := model.Order{ID: order.ID} s.db.First(&o) @@ -48,28 +60,21 @@ func (s *SQL) UpdateOrder(order *model.Order) error { return result.Error } +// Orders filter a list of orders given a filter func (s *SQL) Orders(filters ...OrderFilter) ([]*model.Order, error) { orders := make([]*model.Order, 0) - var os []model.Order - result := s.db.Find(&os) + result := s.db.Find(&orders) if result.Error != nil && result.Error != gorm.ErrRecordNotFound { return orders, nil } - for i := range os { - isFiltered := true + return lo.Filter(orders, func(order *model.Order, _ int) bool { for _, filter := range filters { - if ok := filter(os[i]); !ok { - isFiltered = false - break - } else { - isFiltered = true + if !filter(*order) { + return false } } - if isFiltered { - orders = append(orders, &os[i]) - } - } - return orders, nil + return true + }), nil } diff --git a/storage/sql_test.go b/storage/sql_test.go index f9238bd3..8a409ca4 100644 --- a/storage/sql_test.go +++ b/storage/sql_test.go @@ -4,16 +4,17 @@ import ( "os" "testing" + "gorm.io/gorm" + "github.com/glebarez/sqlite" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) func TestFromSQL(t *testing.T) { file, err := os.CreateTemp(os.TempDir(), "*.db") require.NoError(t, err) defer func() { - os.Remove(file.Name()) + os.RemoveAll(file.Name()) }() repo, err := FromSQL(sqlite.Open(file.Name()), &gorm.Config{}) diff --git a/storage/storage_test.go b/storage/storage_test.go index 7c56f5ce..9942b5a4 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -9,6 +9,7 @@ import ( ) func storageUseCase(repo Storage, t *testing.T) { + t.Helper() now := time.Now() firstOrder := &model.Order{