Meetup Go BH. Set, 2021
www.brito.com.br
/rodrigo-brito
@RodrigoFBrito
Mestre em Ciência da Computação
Gerente de desenvolvimento
Entusiasta de criptomoedas
Smart Contracts
Memes
Privacidade
Exchange
Poxa Rodrigo, mas eu não sou um robô...
COMPRA
VENDE
COMPRA
VENDE
Alerta - URSO
Perder 10 reais
Probabilidade: 60%
Ganhar 30 reais
Probabilidade: 40%
Perder 20 reais
Probabilidade: 50%
Ganhar 20 reais
Probabilidade: 50%
Dataset CSV
Carteira Virtual
Carteira Virtual
Interface
Interface
Composição
type Feeder interface {
// Load historical data
CandlesByPeriod(...) ([]Candle, error)
// Real-time feed
CandlesSubscription(...) (chan Candle, chan error)
}
type Broker interface {
Account() (model.Account, error)
Position(symbol string) (asset, quote float64, err error)
Order(symbol string, id int64) (Order, error)
CreateOrderOCO(...) ([]Order, error)
CreateOrderLimit(...) (Order, error)
CreateOrderMarket(...) (model.Order, error)
CreateOrderMarketQuote(...) (Order, error)
Cancel(...) error
}
type Exchange interface {
Feeder // Binance, CSV, SQLite, etc
Broker // Binance, Virtual Wallet, etc
}
type NinjaBot struct {
storage Storage
settings Settings
exchange Exchange
strategy Strategy
}
type Option func(*NinjaBot)
func NewBot(ctx context.Context, e Exchange, s Strategy, options ...Option) (*NinjaBot, error) {
...
for _, option := range options {
option(bot)
}
...
}
// Without customization
bot, err := ninjabot.NewBot(ctx, binance, strategy)
if err != nil {
log.Error(err)
}
// With options
bot, err := ninjabot.NewBot(
ctx,
binance,
strategy,
ninjabot.WithCustomStorage(storage),
ninjabot.WithLogLevel(log.WarnLevel),
ninjabot.WithNotifier(telegram),
)
if err != nil {
log.Error(err)
}
type OrderSubscriber interface {
OnOrder(Order)
}
type CandleSubscriber interface {
OnCandle(Candle)
}
func WithOrderSubscription(subscriber OrderSubscriber) Option {
return func(bot *NinjaBot) {
bot.SubscribeOrder(subscriber)
}
}
func (n *NinjaBot) SubscribeOrder(subscriptions ...OrderSubscriber) {
for _, pair := range n.settings.Pairs {
for _, s := range subscriptions {
n.orderFeed.Subscribe(pair, s.OnOrder)
}
}
}
ccandle := make(chan model.Candle)
cerr := make(chan error)
go func() {
retry := &backoff.Backoff{ Min: 100 * time.Millisecond, Max: time.Second }
for {
done, _, err := binance.WsKlineServe(pair, period, func(event *binance.WsKlineEvent) {
retry.Reset()
ccandle <- CandleFromWsKline(symbol, event.Kline)
}, func(err error) {
cerr <- err
})
if err != nil {
cerr <- err
close(cerr)
close(ccandle)
return
}
select {
case <-ctx.Done():
close(cerr)
close(ccandle)
return
case <-done:
time.Sleep(retry.Duration())
}
}
}()
github.com/adshao/go-binance
type Strategy interface {
Timeframe() string
WarmupPeriod() int
Indicators(dataframe *model.Dataframe)
OnCandle(*model.Dataframe, service.Broker)
}
type Series []float64
type Dataframe struct {
Pair string
Close Series
Open Series
High Series
Low Series
Volume Series
Time []time.Time
LastUpdate time.Time
// Custom user metadata
Metadata map[string]Series
}
func (e CrossEMA) WarmupPeriod() int {
return 9
}
func (e CrossEMA) Indicators(df *ninjabot.Dataframe) {
df.Metadata["ema9"] = talib.Ema(df.Close, 9)
}
func (e *CrossEMA) OnCandle(df *ninjabot.Dataframe, broker service.Broker) {
if df.Close.Crossover(df.Metadata["ema9"]) {
_, err := broker.CreateOrderMarketQuote(ninjabot.SideTypeBuy, df.Pair, 100)
if err != nil {
log.Error(err)
}
}
if df.Close.Crossunder(df.Metadata["ema9"]) {
_, err := broker.CreateOrderMarket(ninjabot.SideTypeSell, df.Pair, 100)
if err != nil {
log.Error(err)
}
}
}
Live Code
@RodrigoFBrito
github.com/rodrigo-brito
brito.com.br