Skip to content

Commit 9be8210

Browse files
refactor(structure): remove pkg directory, fix async issues in backtesting (#16)
1 parent 50e8fa3 commit 9be8210

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+647
-389
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,4 @@
1414
/ninjabot
1515
/ninjabot.db
1616
/backtest.db
17-
/data/
1817
/dist/

.golangci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
run:
2+
timeout: 1m
23
skip-dirs:
34
- example
45
- pkg/ent
6+
57
linters:
68
enable:
79
- lll

.goreleaser.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ before:
33
- go mod tidy
44

55
builds:
6-
- main: ./cmd/cli
6+
- main: ./cmd/ninjabot
77
id: "ninjabot"
88
binary: ninjabot
99
env:

cmd/cli/cli.go renamed to cmd/ninjabot/ninjabot.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import (
44
"log"
55
"os"
66

7-
"github.com/rodrigo-brito/ninjabot/pkg/data"
8-
"github.com/rodrigo-brito/ninjabot/pkg/exchange"
7+
"github.com/rodrigo-brito/ninjabot/download"
8+
"github.com/rodrigo-brito/ninjabot/exchange"
99

1010
"github.com/urfave/cli/v2"
1111
)
@@ -66,20 +66,20 @@ func main() {
6666
return err
6767
}
6868

69-
var options []data.Option
69+
var options []download.Option
7070
if days := c.Int("days"); days > 0 {
71-
options = append(options, data.WithDays(days))
71+
options = append(options, download.WithDays(days))
7272
}
7373

7474
start := c.Timestamp("start")
7575
end := c.Timestamp("end")
7676
if start != nil && end != nil && !start.IsZero() && !end.IsZero() {
77-
options = append(options, data.WithInterval(*start, *end))
77+
options = append(options, download.WithInterval(*start, *end))
7878
} else if start != nil || end != nil {
7979
log.Fatal("START and END must be informed together")
8080
}
8181

82-
return data.NewDownloader(exc).Download(c.Context, c.String("pair"),
82+
return download.NewDownloader(exc).Download(c.Context, c.String("pair"),
8383
c.String("timeframe"), c.String("output"), options...)
8484

8585
},

pkg/data/download.go renamed to download/download.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
package data
1+
package download
22

33
import (
44
"context"
55
"encoding/csv"
66
"os"
77
"time"
88

9-
"github.com/rodrigo-brito/ninjabot/pkg/service"
9+
"github.com/rodrigo-brito/ninjabot/service"
1010

1111
log "github.com/sirupsen/logrus"
1212
"github.com/xhit/go-str2duration/v2"

pkg/data/download_test.go renamed to download/download_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package data
1+
package download
22

33
import (
44
"testing"

examples/backtesting/main.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,24 @@ import (
66

77
"github.com/rodrigo-brito/ninjabot"
88
"github.com/rodrigo-brito/ninjabot/examples/strategies"
9-
"github.com/rodrigo-brito/ninjabot/pkg/exchange"
10-
"github.com/rodrigo-brito/ninjabot/pkg/model"
11-
"github.com/rodrigo-brito/ninjabot/pkg/plot"
12-
"github.com/rodrigo-brito/ninjabot/pkg/storage"
9+
"github.com/rodrigo-brito/ninjabot/exchange"
10+
"github.com/rodrigo-brito/ninjabot/plot"
11+
"github.com/rodrigo-brito/ninjabot/storage"
1312

1413
log "github.com/sirupsen/logrus"
1514
)
1615

1716
func main() {
1817
ctx := context.Background()
1918

20-
settings := model.Settings{
19+
settings := ninjabot.Settings{
2120
Pairs: []string{
2221
"BTCUSDT",
2322
"ETHUSDT",
2423
},
2524
}
2625

27-
strategy := new(strategies.OCOSell)
26+
strategy := new(strategies.CrossEMA)
2827

2928
csvFeed, err := exchange.NewCSVFeed(
3029
strategy.Timeframe(),
@@ -63,7 +62,7 @@ func main() {
6362
wallet,
6463
strategy,
6564
ninjabot.WithStorage(storage),
66-
ninjabot.WithCandleSubscription(wallet),
65+
ninjabot.WithPaperWallet(wallet),
6766
ninjabot.WithCandleSubscription(chart),
6867
ninjabot.WithOrderSubscription(chart),
6968
ninjabot.WithLogLevel(log.WarnLevel),

examples/binance/main.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import (
88

99
"github.com/rodrigo-brito/ninjabot"
1010
"github.com/rodrigo-brito/ninjabot/examples/strategies"
11-
"github.com/rodrigo-brito/ninjabot/pkg/exchange"
12-
"github.com/rodrigo-brito/ninjabot/pkg/model"
11+
"github.com/rodrigo-brito/ninjabot/exchange"
1312
)
1413

1514
func main() {
@@ -21,12 +20,12 @@ func main() {
2120
telegramUser, _ = strconv.Atoi(os.Getenv("TELEGRAM_USER"))
2221
)
2322

24-
settings := model.Settings{
23+
settings := ninjabot.Settings{
2524
Pairs: []string{
2625
"BTCUSDT",
2726
"ETHUSDT",
2827
},
29-
Telegram: model.TelegramSettings{
28+
Telegram: ninjabot.TelegramSettings{
3029
Enabled: true,
3130
Token: telegramToken,
3231
Users: []int{telegramUser},
@@ -40,7 +39,7 @@ func main() {
4039
}
4140

4241
// Initialize your strategy and bot
43-
strategy := &strategies.CrossEMA{}
42+
strategy := new(strategies.CrossEMA)
4443
bot, err := ninjabot.NewBot(ctx, settings, binance, strategy)
4544
if err != nil {
4645
log.Fatalln(err)

examples/paperwallet/main.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ import (
77

88
"github.com/rodrigo-brito/ninjabot"
99
"github.com/rodrigo-brito/ninjabot/examples/strategies"
10-
"github.com/rodrigo-brito/ninjabot/pkg/exchange"
11-
"github.com/rodrigo-brito/ninjabot/pkg/model"
12-
"github.com/rodrigo-brito/ninjabot/pkg/storage"
10+
"github.com/rodrigo-brito/ninjabot/exchange"
11+
"github.com/rodrigo-brito/ninjabot/storage"
1312

1413
log "github.com/sirupsen/logrus"
1514
)
@@ -21,14 +20,14 @@ func main() {
2120
telegramUser, _ = strconv.Atoi(os.Getenv("TELEGRAM_USER"))
2221
)
2322

24-
settings := model.Settings{
23+
settings := ninjabot.Settings{
2524
Pairs: []string{
2625
"BTCUSDT",
2726
"ETHUSDT",
2827
"BNBUSDT",
2928
"LTCUSDT",
3029
},
31-
Telegram: model.TelegramSettings{
30+
Telegram: ninjabot.TelegramSettings{
3231
Enabled: true,
3332
Token: telegramToken,
3433
Users: []int{telegramUser},
@@ -41,11 +40,13 @@ func main() {
4140
log.Fatal(err)
4241
}
4342

43+
// creating a storage to save trades
4444
storage, err := storage.FromFile("backtest.db")
4545
if err != nil {
4646
log.Fatal(err)
4747
}
4848

49+
// creating a paper wallet to simulate an exchange waller for fake operataions
4950
paperWallet := exchange.NewPaperWallet(
5051
ctx,
5152
"USDT",
@@ -54,14 +55,17 @@ func main() {
5455
exchange.WithDataFeed(binance),
5556
)
5657

58+
// initializing my strategy
5759
strategy := new(strategies.CrossEMA)
60+
61+
// initializer ninjabot
5862
bot, err := ninjabot.NewBot(
5963
ctx,
6064
settings,
6165
paperWallet,
6266
strategy,
6367
ninjabot.WithStorage(storage),
64-
ninjabot.WithCandleSubscription(paperWallet),
68+
ninjabot.WithPaperWallet(paperWallet),
6569
)
6670
if err != nil {
6771
log.Fatalln(err)

examples/strategies/emacross.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
package strategies
22

33
import (
4-
"github.com/rodrigo-brito/ninjabot/pkg/model"
5-
"github.com/rodrigo-brito/ninjabot/pkg/service"
4+
"github.com/rodrigo-brito/ninjabot"
5+
"github.com/rodrigo-brito/ninjabot/service"
66

77
"github.com/markcheno/go-talib"
88
log "github.com/sirupsen/logrus"
99
)
1010

1111
type CrossEMA struct{}
1212

13-
func (e CrossEMA) Init(settings model.Settings) {}
14-
1513
func (e CrossEMA) Timeframe() string {
1614
return "1d"
1715
}
@@ -20,11 +18,11 @@ func (e CrossEMA) WarmupPeriod() int {
2018
return 9
2119
}
2220

23-
func (e CrossEMA) Indicators(df *model.Dataframe) {
21+
func (e CrossEMA) Indicators(df *ninjabot.Dataframe) {
2422
df.Metadata["ema9"] = talib.Ema(df.Close, 9)
2523
}
2624

27-
func (e *CrossEMA) OnCandle(df *model.Dataframe, broker service.Broker) {
25+
func (e *CrossEMA) OnCandle(df *ninjabot.Dataframe, broker service.Broker) {
2826
closePrice := df.Close.Last(0)
2927
log.Info("New Candle = ", df.Pair, df.LastUpdate, closePrice)
3028

@@ -36,11 +34,11 @@ func (e *CrossEMA) OnCandle(df *model.Dataframe, broker service.Broker) {
3634
buyAmount := 4000.0
3735
if quotePosition > buyAmount && df.Close.Crossover(df.Metadata["ema9"]) {
3836
size := buyAmount / closePrice
39-
_, err := broker.OrderMarket(model.SideTypeBuy, df.Pair, size)
37+
_, err := broker.CreateOrderMarket(ninjabot.SideTypeBuy, df.Pair, size)
4038
if err != nil {
4139
log.WithFields(map[string]interface{}{
4240
"pair": df.Pair,
43-
"side": model.SideTypeBuy,
41+
"side": ninjabot.SideTypeBuy,
4442
"close": closePrice,
4543
"asset": assetPosition,
4644
"quote": quotePosition,
@@ -51,11 +49,11 @@ func (e *CrossEMA) OnCandle(df *model.Dataframe, broker service.Broker) {
5149

5250
if assetPosition*closePrice > 10 && // minimum tradable size
5351
df.Close.Crossunder(df.Metadata["ema9"]) {
54-
_, err := broker.OrderMarket(model.SideTypeSell, df.Pair, assetPosition)
52+
_, err := broker.CreateOrderMarket(ninjabot.SideTypeSell, df.Pair, assetPosition)
5553
if err != nil {
5654
log.WithFields(map[string]interface{}{
5755
"pair": df.Pair,
58-
"side": model.SideTypeSell,
56+
"side": ninjabot.SideTypeSell,
5957
"close": closePrice,
6058
"asset": assetPosition,
6159
"quote": quotePosition,

examples/strategies/ocosell.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
package strategies
22

33
import (
4-
"github.com/rodrigo-brito/ninjabot/pkg/model"
5-
"github.com/rodrigo-brito/ninjabot/pkg/service"
4+
"github.com/rodrigo-brito/ninjabot/model"
5+
"github.com/rodrigo-brito/ninjabot/service"
66

77
"github.com/markcheno/go-talib"
88
log "github.com/sirupsen/logrus"
99
)
1010

1111
type OCOSell struct{}
1212

13-
func (e OCOSell) Init(settings model.Settings) {}
14-
1513
func (e OCOSell) Timeframe() string {
1614
return "1d"
1715
}
@@ -45,7 +43,7 @@ func (e *OCOSell) OnCandle(df *model.Dataframe, broker service.Broker) {
4543
buyAmount := 4000.0
4644
if quotePosition > buyAmount && df.Metadata["stoch"].Crossover(df.Metadata["stoch_signal"]) {
4745
size := buyAmount / closePrice
48-
_, err := broker.OrderMarket(model.SideTypeBuy, df.Pair, size)
46+
_, err := broker.CreateOrderMarket(model.SideTypeBuy, df.Pair, size)
4947
if err != nil {
5048
log.WithFields(map[string]interface{}{
5149
"pair": df.Pair,
@@ -57,7 +55,7 @@ func (e *OCOSell) OnCandle(df *model.Dataframe, broker service.Broker) {
5755
}).Error(err)
5856
}
5957

60-
_, err = broker.OrderOCO(model.SideTypeSell, df.Pair, size, closePrice*1.05, closePrice*0.95, closePrice*0.95)
58+
_, err = broker.CreateOrderOCO(model.SideTypeSell, df.Pair, size, closePrice*1.05, closePrice*0.95, closePrice*0.95)
6159
if err != nil {
6260
log.WithFields(map[string]interface{}{
6361
"pair": df.Pair,

pkg/exchange/binance.go renamed to exchange/binance.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"strconv"
77
"time"
88

9-
"github.com/rodrigo-brito/ninjabot/pkg/model"
9+
"github.com/rodrigo-brito/ninjabot/model"
1010

1111
"github.com/adshao/go-binance/v2"
1212
log "github.com/sirupsen/logrus"
@@ -159,7 +159,7 @@ func (b *Binance) validate(side model.SideType, typ model.OrderType, symbol stri
159159
return nil
160160
}
161161

162-
func (b *Binance) OrderOCO(side model.SideType, symbol string,
162+
func (b *Binance) CreateOrderOCO(side model.SideType, symbol string,
163163
quantity, price, stop, stopLimit float64) ([]model.Order, error) {
164164

165165
// validate stop
@@ -263,7 +263,9 @@ func (b *Binance) formatQuantity(symbol string, value float64) string {
263263
return strconv.FormatFloat(value, 'f', precision, 64)
264264
}
265265

266-
func (b *Binance) OrderLimit(side model.SideType, symbol string, quantity float64, limit float64) (model.Order, error) {
266+
func (b *Binance) CreateOrderLimit(side model.SideType, symbol string,
267+
quantity float64, limit float64) (model.Order, error) {
268+
267269
err := b.validate(side, model.OrderTypeLimit, symbol, quantity, &limit)
268270
if err != nil {
269271
return model.Order{}, err
@@ -297,7 +299,7 @@ func (b *Binance) OrderLimit(side model.SideType, symbol string, quantity float6
297299
}, nil
298300
}
299301

300-
func (b *Binance) OrderMarket(side model.SideType, symbol string, quantity float64) (model.Order, error) {
302+
func (b *Binance) CreateOrderMarket(side model.SideType, symbol string, quantity float64) (model.Order, error) {
301303
err := b.validate(side, model.OrderTypeMarket, symbol, quantity, nil)
302304
if err != nil {
303305
return model.Order{}, err
@@ -329,7 +331,7 @@ func (b *Binance) OrderMarket(side model.SideType, symbol string, quantity float
329331
}, nil
330332
}
331333

332-
func (b *Binance) OrderMarketQuote(side model.SideType, symbol string, quantity float64) (model.Order, error) {
334+
func (b *Binance) CreateOrderMarketQuote(side model.SideType, symbol string, quantity float64) (model.Order, error) {
333335
err := b.validate(side, model.OrderTypeMarket, symbol, quantity, nil)
334336
if err != nil {
335337
return model.Order{}, err

pkg/exchange/csvfeed.go renamed to exchange/csvfeed.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"strconv"
1111
"time"
1212

13-
"github.com/rodrigo-brito/ninjabot/pkg/model"
13+
"github.com/rodrigo-brito/ninjabot/model"
1414

1515
"github.com/xhit/go-str2duration/v2"
1616
)
@@ -140,7 +140,7 @@ func isLastCandlePeriod(t time.Time, fromTimeframe, targetTimeframe string) (boo
140140
return next.Minute() == 0 && next.Hour()%24 == 0 && next.Weekday() == time.Sunday, nil
141141
}
142142

143-
return false, fmt.Errorf("invalid timeframe: 1y")
143+
return false, fmt.Errorf("invalid timeframe: %s", targetTimeframe)
144144
}
145145

146146
func (c *CSVFeed) resample(pair, sourceTimeframe, targetTimeframe string) error {
@@ -168,6 +168,11 @@ func (c *CSVFeed) resample(pair, sourceTimeframe, targetTimeframe string) error
168168
candles = append(candles, candle)
169169
}
170170

171+
// remove last candle if not complete
172+
if !candles[len(candles)-1].Complete {
173+
candles = candles[:len(candles)-1]
174+
}
175+
171176
c.CandlePairTimeFrame[targetKey] = candles
172177

173178
return nil

0 commit comments

Comments
 (0)