Skip to content

Commit

Permalink
refactor(structure): remove pkg directory, fix async issues in backte…
Browse files Browse the repository at this point in the history
…sting (#16)
  • Loading branch information
rodrigo-brito authored Sep 13, 2021
1 parent 50e8fa3 commit 9be8210
Show file tree
Hide file tree
Showing 66 changed files with 647 additions and 389 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@
/ninjabot
/ninjabot.db
/backtest.db
/data/
/dist/
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
run:
timeout: 1m
skip-dirs:
- example
- pkg/ent

linters:
enable:
- lll
Expand Down
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ before:
- go mod tidy

builds:
- main: ./cmd/cli
- main: ./cmd/ninjabot
id: "ninjabot"
binary: ninjabot
env:
Expand Down
12 changes: 6 additions & 6 deletions cmd/cli/cli.go → cmd/ninjabot/ninjabot.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"log"
"os"

"github.com/rodrigo-brito/ninjabot/pkg/data"
"github.com/rodrigo-brito/ninjabot/pkg/exchange"
"github.com/rodrigo-brito/ninjabot/download"
"github.com/rodrigo-brito/ninjabot/exchange"

"github.com/urfave/cli/v2"
)
Expand Down Expand Up @@ -66,20 +66,20 @@ func main() {
return err
}

var options []data.Option
var options []download.Option
if days := c.Int("days"); days > 0 {
options = append(options, data.WithDays(days))
options = append(options, download.WithDays(days))
}

start := c.Timestamp("start")
end := c.Timestamp("end")
if start != nil && end != nil && !start.IsZero() && !end.IsZero() {
options = append(options, data.WithInterval(*start, *end))
options = append(options, download.WithInterval(*start, *end))
} else if start != nil || end != nil {
log.Fatal("START and END must be informed together")
}

return data.NewDownloader(exc).Download(c.Context, c.String("pair"),
return download.NewDownloader(exc).Download(c.Context, c.String("pair"),
c.String("timeframe"), c.String("output"), options...)

},
Expand Down
4 changes: 2 additions & 2 deletions pkg/data/download.go → download/download.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package data
package download

import (
"context"
"encoding/csv"
"os"
"time"

"github.com/rodrigo-brito/ninjabot/pkg/service"
"github.com/rodrigo-brito/ninjabot/service"

log "github.com/sirupsen/logrus"
"github.com/xhit/go-str2duration/v2"
Expand Down
2 changes: 1 addition & 1 deletion pkg/data/download_test.go → download/download_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package data
package download

import (
"testing"
Expand Down
13 changes: 6 additions & 7 deletions examples/backtesting/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,24 @@ import (

"github.com/rodrigo-brito/ninjabot"
"github.com/rodrigo-brito/ninjabot/examples/strategies"
"github.com/rodrigo-brito/ninjabot/pkg/exchange"
"github.com/rodrigo-brito/ninjabot/pkg/model"
"github.com/rodrigo-brito/ninjabot/pkg/plot"
"github.com/rodrigo-brito/ninjabot/pkg/storage"
"github.com/rodrigo-brito/ninjabot/exchange"
"github.com/rodrigo-brito/ninjabot/plot"
"github.com/rodrigo-brito/ninjabot/storage"

log "github.com/sirupsen/logrus"
)

func main() {
ctx := context.Background()

settings := model.Settings{
settings := ninjabot.Settings{
Pairs: []string{
"BTCUSDT",
"ETHUSDT",
},
}

strategy := new(strategies.OCOSell)
strategy := new(strategies.CrossEMA)

csvFeed, err := exchange.NewCSVFeed(
strategy.Timeframe(),
Expand Down Expand Up @@ -63,7 +62,7 @@ func main() {
wallet,
strategy,
ninjabot.WithStorage(storage),
ninjabot.WithCandleSubscription(wallet),
ninjabot.WithPaperWallet(wallet),
ninjabot.WithCandleSubscription(chart),
ninjabot.WithOrderSubscription(chart),
ninjabot.WithLogLevel(log.WarnLevel),
Expand Down
9 changes: 4 additions & 5 deletions examples/binance/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import (

"github.com/rodrigo-brito/ninjabot"
"github.com/rodrigo-brito/ninjabot/examples/strategies"
"github.com/rodrigo-brito/ninjabot/pkg/exchange"
"github.com/rodrigo-brito/ninjabot/pkg/model"
"github.com/rodrigo-brito/ninjabot/exchange"
)

func main() {
Expand All @@ -21,12 +20,12 @@ func main() {
telegramUser, _ = strconv.Atoi(os.Getenv("TELEGRAM_USER"))
)

settings := model.Settings{
settings := ninjabot.Settings{
Pairs: []string{
"BTCUSDT",
"ETHUSDT",
},
Telegram: model.TelegramSettings{
Telegram: ninjabot.TelegramSettings{
Enabled: true,
Token: telegramToken,
Users: []int{telegramUser},
Expand All @@ -40,7 +39,7 @@ func main() {
}

// Initialize your strategy and bot
strategy := &strategies.CrossEMA{}
strategy := new(strategies.CrossEMA)
bot, err := ninjabot.NewBot(ctx, settings, binance, strategy)
if err != nil {
log.Fatalln(err)
Expand Down
16 changes: 10 additions & 6 deletions examples/paperwallet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import (

"github.com/rodrigo-brito/ninjabot"
"github.com/rodrigo-brito/ninjabot/examples/strategies"
"github.com/rodrigo-brito/ninjabot/pkg/exchange"
"github.com/rodrigo-brito/ninjabot/pkg/model"
"github.com/rodrigo-brito/ninjabot/pkg/storage"
"github.com/rodrigo-brito/ninjabot/exchange"
"github.com/rodrigo-brito/ninjabot/storage"

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

settings := model.Settings{
settings := ninjabot.Settings{
Pairs: []string{
"BTCUSDT",
"ETHUSDT",
"BNBUSDT",
"LTCUSDT",
},
Telegram: model.TelegramSettings{
Telegram: ninjabot.TelegramSettings{
Enabled: true,
Token: telegramToken,
Users: []int{telegramUser},
Expand All @@ -41,11 +40,13 @@ func main() {
log.Fatal(err)
}

// creating a storage to save trades
storage, err := storage.FromFile("backtest.db")
if err != nil {
log.Fatal(err)
}

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

// initializing my strategy
strategy := new(strategies.CrossEMA)

// initializer ninjabot
bot, err := ninjabot.NewBot(
ctx,
settings,
paperWallet,
strategy,
ninjabot.WithStorage(storage),
ninjabot.WithCandleSubscription(paperWallet),
ninjabot.WithPaperWallet(paperWallet),
)
if err != nil {
log.Fatalln(err)
Expand Down
18 changes: 8 additions & 10 deletions examples/strategies/emacross.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package strategies

import (
"github.com/rodrigo-brito/ninjabot/pkg/model"
"github.com/rodrigo-brito/ninjabot/pkg/service"
"github.com/rodrigo-brito/ninjabot"
"github.com/rodrigo-brito/ninjabot/service"

"github.com/markcheno/go-talib"
log "github.com/sirupsen/logrus"
)

type CrossEMA struct{}

func (e CrossEMA) Init(settings model.Settings) {}

func (e CrossEMA) Timeframe() string {
return "1d"
}
Expand All @@ -20,11 +18,11 @@ func (e CrossEMA) WarmupPeriod() int {
return 9
}

func (e CrossEMA) Indicators(df *model.Dataframe) {
func (e CrossEMA) Indicators(df *ninjabot.Dataframe) {
df.Metadata["ema9"] = talib.Ema(df.Close, 9)
}

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

Expand All @@ -36,11 +34,11 @@ func (e *CrossEMA) OnCandle(df *model.Dataframe, broker service.Broker) {
buyAmount := 4000.0
if quotePosition > buyAmount && df.Close.Crossover(df.Metadata["ema9"]) {
size := buyAmount / closePrice
_, err := broker.OrderMarket(model.SideTypeBuy, df.Pair, size)
_, err := broker.CreateOrderMarket(ninjabot.SideTypeBuy, df.Pair, size)
if err != nil {
log.WithFields(map[string]interface{}{
"pair": df.Pair,
"side": model.SideTypeBuy,
"side": ninjabot.SideTypeBuy,
"close": closePrice,
"asset": assetPosition,
"quote": quotePosition,
Expand All @@ -51,11 +49,11 @@ func (e *CrossEMA) OnCandle(df *model.Dataframe, broker service.Broker) {

if assetPosition*closePrice > 10 && // minimum tradable size
df.Close.Crossunder(df.Metadata["ema9"]) {
_, err := broker.OrderMarket(model.SideTypeSell, df.Pair, assetPosition)
_, err := broker.CreateOrderMarket(ninjabot.SideTypeSell, df.Pair, assetPosition)
if err != nil {
log.WithFields(map[string]interface{}{
"pair": df.Pair,
"side": model.SideTypeSell,
"side": ninjabot.SideTypeSell,
"close": closePrice,
"asset": assetPosition,
"quote": quotePosition,
Expand Down
10 changes: 4 additions & 6 deletions examples/strategies/ocosell.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package strategies

import (
"github.com/rodrigo-brito/ninjabot/pkg/model"
"github.com/rodrigo-brito/ninjabot/pkg/service"
"github.com/rodrigo-brito/ninjabot/model"
"github.com/rodrigo-brito/ninjabot/service"

"github.com/markcheno/go-talib"
log "github.com/sirupsen/logrus"
)

type OCOSell struct{}

func (e OCOSell) Init(settings model.Settings) {}

func (e OCOSell) Timeframe() string {
return "1d"
}
Expand Down Expand Up @@ -45,7 +43,7 @@ func (e *OCOSell) OnCandle(df *model.Dataframe, broker service.Broker) {
buyAmount := 4000.0
if quotePosition > buyAmount && df.Metadata["stoch"].Crossover(df.Metadata["stoch_signal"]) {
size := buyAmount / closePrice
_, err := broker.OrderMarket(model.SideTypeBuy, df.Pair, size)
_, err := broker.CreateOrderMarket(model.SideTypeBuy, df.Pair, size)
if err != nil {
log.WithFields(map[string]interface{}{
"pair": df.Pair,
Expand All @@ -57,7 +55,7 @@ func (e *OCOSell) OnCandle(df *model.Dataframe, broker service.Broker) {
}).Error(err)
}

_, err = broker.OrderOCO(model.SideTypeSell, df.Pair, size, closePrice*1.05, closePrice*0.95, closePrice*0.95)
_, err = broker.CreateOrderOCO(model.SideTypeSell, df.Pair, size, closePrice*1.05, closePrice*0.95, closePrice*0.95)
if err != nil {
log.WithFields(map[string]interface{}{
"pair": df.Pair,
Expand Down
12 changes: 7 additions & 5 deletions pkg/exchange/binance.go → exchange/binance.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"strconv"
"time"

"github.com/rodrigo-brito/ninjabot/pkg/model"
"github.com/rodrigo-brito/ninjabot/model"

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

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

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

func (b *Binance) OrderLimit(side model.SideType, symbol string, quantity float64, limit float64) (model.Order, error) {
func (b *Binance) CreateOrderLimit(side model.SideType, symbol string,
quantity float64, limit float64) (model.Order, error) {

err := b.validate(side, model.OrderTypeLimit, symbol, quantity, &limit)
if err != nil {
return model.Order{}, err
Expand Down Expand Up @@ -297,7 +299,7 @@ func (b *Binance) OrderLimit(side model.SideType, symbol string, quantity float6
}, nil
}

func (b *Binance) OrderMarket(side model.SideType, symbol string, quantity float64) (model.Order, error) {
func (b *Binance) CreateOrderMarket(side model.SideType, symbol string, quantity float64) (model.Order, error) {
err := b.validate(side, model.OrderTypeMarket, symbol, quantity, nil)
if err != nil {
return model.Order{}, err
Expand Down Expand Up @@ -329,7 +331,7 @@ func (b *Binance) OrderMarket(side model.SideType, symbol string, quantity float
}, nil
}

func (b *Binance) OrderMarketQuote(side model.SideType, symbol string, quantity float64) (model.Order, error) {
func (b *Binance) CreateOrderMarketQuote(side model.SideType, symbol string, quantity float64) (model.Order, error) {
err := b.validate(side, model.OrderTypeMarket, symbol, quantity, nil)
if err != nil {
return model.Order{}, err
Expand Down
9 changes: 7 additions & 2 deletions pkg/exchange/csvfeed.go → exchange/csvfeed.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"strconv"
"time"

"github.com/rodrigo-brito/ninjabot/pkg/model"
"github.com/rodrigo-brito/ninjabot/model"

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

return false, fmt.Errorf("invalid timeframe: 1y")
return false, fmt.Errorf("invalid timeframe: %s", targetTimeframe)
}

func (c *CSVFeed) resample(pair, sourceTimeframe, targetTimeframe string) error {
Expand Down Expand Up @@ -168,6 +168,11 @@ func (c *CSVFeed) resample(pair, sourceTimeframe, targetTimeframe string) error
candles = append(candles, candle)
}

// remove last candle if not complete
if !candles[len(candles)-1].Complete {
candles = candles[:len(candles)-1]
}

c.CandlePairTimeFrame[targetKey] = candles

return nil
Expand Down
Loading

0 comments on commit 9be8210

Please sign in to comment.