Skip to content

Commit

Permalink
refactor(telegram): improve error handling (#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
RobertKwiatkowski authored Mar 27, 2022
1 parent d344e90 commit 16aa0b0
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 31 deletions.
7 changes: 5 additions & 2 deletions exchange/binance.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,17 @@ func (b *Binance) AssetsInfo(pair string) model.AssetInfo {
}

func (b *Binance) validate(pair string, quantity float64) error {

info, ok := b.assetsInfo[pair]
if !ok {
return ErrInvalidAsset
}

if quantity > info.MaxQuantity || quantity < info.MinQuantity {
return fmt.Errorf("%w: min: %f max: %f", ErrInvalidQuantity, info.MinQuantity, info.MaxQuantity)
return &OrderError{
Err: fmt.Errorf("%w: min: %f max: %f", ErrInvalidQuantity, info.MinQuantity, info.MaxQuantity),
Pair: pair,
Quantity: quantity,
}
}

return nil
Expand Down
10 changes: 10 additions & 0 deletions exchange/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ type Subscription struct {
consumer DataFeedConsumer
}

type OrderError struct {
Err error
Pair string
Quantity float64
}

func (o *OrderError) Error() string {
return fmt.Sprintf("order error: %v", o.Err)
}

type DataFeedConsumer func(model.Candle)

func NewDataFeed(exchange service.Exchange) *DataFeedSubscription {
Expand Down
21 changes: 16 additions & 5 deletions exchange/paperwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ import (
"sync"
"time"

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

log "github.com/sirupsen/logrus"

"github.com/rodrigo-brito/ninjabot/model"
"github.com/rodrigo-brito/ninjabot/service"
)

type assetInfo struct {
Expand Down Expand Up @@ -217,7 +216,11 @@ func (p *PaperWallet) Summary() {

func (p *PaperWallet) lockFunds(asset string, amount float64) error {
if value, ok := p.assets[asset]; !ok || value.Free < amount {
return ErrInsufficientFunds
return &OrderError{
Err: ErrInsufficientFunds,
Pair: asset,
Quantity: amount,
}
}
p.assets[asset].Free = p.assets[asset].Free - amount
p.assets[asset].Lock = p.assets[asset].Lock + amount
Expand Down Expand Up @@ -447,7 +450,11 @@ func (p *PaperWallet) createOrderMarket(side model.SideType, pair string, size f
asset, quote := SplitAssetQuote(pair)
if side == model.SideTypeSell {
if value, ok := p.assets[asset]; !ok || value.Free < size {
return model.Order{}, ErrInsufficientFunds
return model.Order{}, &OrderError{
Err: ErrInsufficientFunds,
Pair: pair,
Quantity: size,
}
}
if _, ok := p.assets[quote]; !ok {
p.assets[quote] = &assetInfo{}
Expand All @@ -456,7 +463,11 @@ func (p *PaperWallet) createOrderMarket(side model.SideType, pair string, size f
p.assets[quote].Free = p.assets[quote].Free + p.lastCandle[pair].Close*size
} else {
if value, ok := p.assets[quote]; !ok || value.Free < size*p.lastCandle[pair].Close {
return model.Order{}, ErrInsufficientFunds
return model.Order{}, &OrderError{
Err: ErrInsufficientFunds,
Pair: pair,
Quantity: size,
}
}
if _, ok := p.assets[asset]; !ok {
p.assets[asset] = &assetInfo{}
Expand Down
15 changes: 12 additions & 3 deletions exchange/paperwallet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ func TestPaperWallet_OrderLimit(t *testing.T) {
// try to buy again without funds
order, err = wallet.CreateOrderLimit(model.SideTypeBuy, "BTCUSDT", 1, 100)
require.Empty(t, order)
require.Equal(t, ErrInsufficientFunds, err)
require.Equal(t, &OrderError{
Err: ErrInsufficientFunds,
Pair: "USDT",
Quantity: 100}, err)

// try to sell and profit 100 USDT
order, err = wallet.CreateOrderLimit(model.SideTypeSell, "BTCUSDT", 1, 200)
Expand Down Expand Up @@ -138,7 +141,10 @@ func TestPaperWallet_OrderMarket(t *testing.T) {

// insufficient funds
order, err = wallet.CreateOrderMarket(model.SideTypeBuy, "BTCUSDT", 100)
require.Equal(t, ErrInsufficientFunds, err)
require.Equal(t, &OrderError{
Err: ErrInsufficientFunds,
Pair: "BTCUSDT",
Quantity: 100}, err)
require.Empty(t, order)

// sell
Expand Down Expand Up @@ -177,7 +183,10 @@ func TestPaperWallet_OrderOCO(t *testing.T) {

// insufficient funds
orders, err = wallet.CreateOrderOCO(model.SideTypeSell, "BTCUSDT", 1, 100, 40, 39)
require.Equal(t, ErrInsufficientFunds, err)
require.Equal(t, &OrderError{
Err: ErrInsufficientFunds,
Pair: "BTC",
Quantity: 1}, err)
require.Nil(t, orders)

// execute stop and cancel target
Expand Down
2 changes: 1 addition & 1 deletion ninjabot.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func (n *NinjaBot) backtestCandles() {
}

if err := progressBar.Add(1); err != nil {
log.Warningf("update progresbar fail: %s", err.Error())
log.Warningf("update progresbar fail: %v", err)
}
}
}
Expand Down
22 changes: 15 additions & 7 deletions notification/telegram.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package notification

import (
"errors"
"fmt"
"regexp"
"strconv"
Expand Down Expand Up @@ -250,7 +251,6 @@ func (t telegram) BuyHandle(m *tb.Message) {

order, err := t.orderController.CreateOrderMarketQuote(model.SideTypeBuy, pair, amount)
if err != nil {
log.Error(err)
return
}
log.Info("[TELEGRAM]: BUY ORDER CREATED: ", order)
Expand Down Expand Up @@ -290,15 +290,12 @@ func (t telegram) SellHandle(m *tb.Message) {
if command["percent"] != "" {
asset, _, err := t.orderController.Position(pair)
if err != nil {
log.Error(err)
t.OnError(err)
return
}

amount = amount * asset / 100.0
order, err := t.orderController.CreateOrderMarket(model.SideTypeSell, pair, amount)
if err != nil {
log.Error(err)
return
}
log.Info("[TELEGRAM]: SELL ORDER CREATED: ", order)
Expand All @@ -307,7 +304,6 @@ func (t telegram) SellHandle(m *tb.Message) {

order, err := t.orderController.CreateOrderMarketQuote(model.SideTypeSell, pair, amount)
if err != nil {
log.Error(err)
return
}
log.Info("[TELEGRAM]: SELL ORDER CREATED: ", order)
Expand Down Expand Up @@ -369,6 +365,18 @@ func (t telegram) OnOrder(order model.Order) {

func (t telegram) OnError(err error) {
title := "🛑 ERROR"
message := fmt.Sprintf("%s\n-----\n%s", title, err)
t.Notify(message)

var orderError *exchange.OrderError
if errors.As(err, &orderError) {
message := fmt.Sprintf(`%s
-----
Pair: %s
Quantity: %.4f
-----
%s`, title, orderError.Pair, orderError.Quantity, orderError.Err)
t.Notify(message)
return
}

t.Notify(fmt.Sprintf("%s\n-----\n%s", title, err))
}
26 changes: 13 additions & 13 deletions order/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,14 @@ func (c *Controller) processTrade(order *model.Order) {
// register order volume
c.Results[order.Pair].Volume += order.Price * order.Quantity

// calculate profit only for sell orders
// calculate profit only to sell orders
if order.Side != model.SideTypeSell {
return
}

profitValue, profit, err := c.calculateProfit(order)
if err != nil {
c.notifyError(fmt.Errorf("order/controller storage: %s", err))
c.notifyError(err)
return
}

Expand All @@ -231,7 +231,7 @@ func (c *Controller) updateOrders() {
model.OrderStatusTypePendingCancel,
))
if err != nil {
c.notifyError(fmt.Errorf("orderController/start: %s", err))
c.notifyError(err)
c.mtx.Unlock()
return
}
Expand All @@ -253,7 +253,7 @@ func (c *Controller) updateOrders() {
excOrder.ID = order.ID
err = c.storage.UpdateOrder(&excOrder)
if err != nil {
c.notifyError(fmt.Errorf("orderControler/update: %s", err))
c.notifyError(err)
continue
}

Expand Down Expand Up @@ -331,14 +331,14 @@ func (c *Controller) CreateOrderOCO(side model.SideType, pair string, size, pric
log.Infof("[ORDER] Creating OCO order for %s", pair)
orders, err := c.exchange.CreateOrderOCO(side, pair, size, price, stop, stopLimit)
if err != nil {
c.notifyError(fmt.Errorf("order/controller exchange: %s", err))
c.notifyError(err)
return nil, err
}

for i := range orders {
err := c.storage.CreateOrder(&orders[i])
if err != nil {
c.notifyError(fmt.Errorf("order/controller storage: %s", err))
c.notifyError(err)
return nil, err
}
go c.orderFeed.Publish(orders[i], true)
Expand All @@ -354,13 +354,13 @@ func (c *Controller) CreateOrderLimit(side model.SideType, pair string, size, li
log.Infof("[ORDER] Creating LIMIT %s order for %s", side, pair)
order, err := c.exchange.CreateOrderLimit(side, pair, size, limit)
if err != nil {
c.notifyError(fmt.Errorf("order/controller exchange: %s", err))
c.notifyError(err)
return model.Order{}, err
}

err = c.storage.CreateOrder(&order)
if err != nil {
c.notifyError(fmt.Errorf("order/controller storage: %s", err))
c.notifyError(err)
return model.Order{}, err
}
go c.orderFeed.Publish(order, true)
Expand All @@ -375,13 +375,13 @@ func (c *Controller) CreateOrderMarketQuote(side model.SideType, pair string, am
log.Infof("[ORDER] Creating MARKET %s order for %s", side, pair)
order, err := c.exchange.CreateOrderMarketQuote(side, pair, amount)
if err != nil {
c.notifyError(fmt.Errorf("order/controller exchange: %s", err))
c.notifyError(err)
return model.Order{}, err
}

err = c.storage.CreateOrder(&order)
if err != nil {
c.notifyError(fmt.Errorf("order/controller storage: %s", err))
c.notifyError(err)
return model.Order{}, err
}

Expand All @@ -399,13 +399,13 @@ func (c *Controller) CreateOrderMarket(side model.SideType, pair string, size fl
log.Infof("[ORDER] Creating MARKET %s order for %s", side, pair)
order, err := c.exchange.CreateOrderMarket(side, pair, size)
if err != nil {
c.notifyError(fmt.Errorf("order/controller exchange: %s", err))
c.notifyError(err)
return model.Order{}, err
}

err = c.storage.CreateOrder(&order)
if err != nil {
c.notifyError(fmt.Errorf("order/controller storage: %s", err))
c.notifyError(err)
return model.Order{}, err
}

Expand All @@ -429,7 +429,7 @@ func (c *Controller) Cancel(order model.Order) error {
order.Status = model.OrderStatusTypePendingCancel
err = c.storage.UpdateOrder(&order)
if err != nil {
c.notifyError(fmt.Errorf("order/controller storage: %s", err))
c.notifyError(err)
return err
}
log.Infof("[ORDER CANCELED] %s", order)
Expand Down

0 comments on commit 16aa0b0

Please sign in to comment.