diff --git a/pkg/bbgo/order_execution.go b/pkg/bbgo/order_execution.go index b2ce44e56..b038630d9 100644 --- a/pkg/bbgo/order_execution.go +++ b/pkg/bbgo/order_execution.go @@ -144,7 +144,6 @@ func (e *BasicRiskControlOrderExecutor) SubmitOrders(ctx context.Context, orders e.Notify(":memo: Submitting %s %s %s order with quantity %s @ %s", o.Symbol, o.Side, o.Type, o.QuantityString, o.PriceString, o) } - return e.session.Exchange.SubmitOrders(ctx, formattedOrders...) } diff --git a/pkg/bbgo/order_processor.go b/pkg/bbgo/order_processor.go index c2241d4eb..00907cb02 100644 --- a/pkg/bbgo/order_processor.go +++ b/pkg/bbgo/order_processor.go @@ -1,11 +1,7 @@ package bbgo import ( - "context" - "github.com/pkg/errors" - - "github.com/c9s/bbgo/pkg/types" ) var ( @@ -17,118 +13,93 @@ var ( ErrAssetBalanceLevelTooHigh = errors.New("asset balance level too high") ) -// OrderProcessor does: -// - Check quote balance -// - Check and control the order amount -// - Adjust order amount due to the minAmount configuration and maxAmount configuration -// - Canonicalize the volume precision base on the given exchange -type OrderProcessor struct { - // balance control - MinQuoteBalance float64 `json:"minQuoteBalance"` - MaxAssetBalance float64 `json:"maxBaseAssetBalance"` - MinAssetBalance float64 `json:"minBaseAssetBalance"` - MaxOrderAmount float64 `json:"maxOrderAmount"` +/* + tradingCtx := p.OrderExecutor.Context + currentPrice := tradingCtx.CurrentPrice + market := order.Market + quantity := order.Quantity - // MinProfitSpread is used when submitting sell orders, it check if there the selling can make the profit. - MinProfitSpread float64 `json:"minProfitSpread"` + tradingCtx.Lock() + defer tradingCtx.Unlock() + switch order.Side { + case types.SideTypeBuy: - Exchange types.Exchange `json:"-"` -} + if balance, ok := tradingCtx.Balances[market.QuoteCurrency]; ok { + if balance.Available < p.MinQuoteBalance { + return errors.Wrapf(ErrQuoteBalanceLevelTooLow, "quote balance level is too low: %s < %s", + types.USD.FormatMoneyFloat64(balance.Available), + types.USD.FormatMoneyFloat64(p.MinQuoteBalance)) + } -func (p *OrderProcessor) Submit(ctx context.Context, order types.SubmitOrder) error { - /* - tradingCtx := p.OrderExecutor.Context - currentPrice := tradingCtx.CurrentPrice - market := order.Market - quantity := order.Quantity - - tradingCtx.Lock() - defer tradingCtx.Unlock() - - switch order.Side { - case types.SideTypeBuy: - - if balance, ok := tradingCtx.Balances[market.QuoteCurrency]; ok { - if balance.Available < p.MinQuoteBalance { - return errors.Wrapf(ErrQuoteBalanceLevelTooLow, "quote balance level is too low: %s < %s", - types.USD.FormatMoneyFloat64(balance.Available), - types.USD.FormatMoneyFloat64(p.MinQuoteBalance)) - } - - if baseBalance, ok := tradingCtx.Balances[market.BaseCurrency]; ok { - if util.NotZero(p.MaxAssetBalance) && baseBalance.Available > p.MaxAssetBalance { - return errors.Wrapf(ErrAssetBalanceLevelTooHigh, "asset balance level is too high: %f > %f", baseBalance.Available, p.MaxAssetBalance) - } - } - - available := math.Max(0.0, balance.Available-p.MinQuoteBalance) - - if available < market.MinAmount { - return errors.Wrapf(ErrInsufficientQuoteBalance, "insufficient quote balance: %f < min amount %f", available, market.MinAmount) - } - - quantity = adjustQuantityByMinAmount(quantity, currentPrice, market.MinAmount*1.01) - quantity = adjustQuantityByMaxAmount(quantity, currentPrice, available) - amount := quantity * currentPrice - if amount < market.MinAmount { - return fmt.Errorf("amount too small: %f < min amount %f", amount, market.MinAmount) + if baseBalance, ok := tradingCtx.Balances[market.BaseCurrency]; ok { + if util.NotZero(p.MaxAssetBalance) && baseBalance.Available > p.MaxAssetBalance { + return errors.Wrapf(ErrAssetBalanceLevelTooHigh, "asset balance level is too high: %f > %f", baseBalance.Available, p.MaxAssetBalance) } } - case types.SideTypeSell: + available := math.Max(0.0, balance.Available-p.MinQuoteBalance) - if balance, ok := tradingCtx.Balances[market.BaseCurrency]; ok { - if util.NotZero(p.MinAssetBalance) && balance.Available < p.MinAssetBalance { - return errors.Wrapf(ErrAssetBalanceLevelTooLow, "asset balance level is too low: %f > %f", balance.Available, p.MinAssetBalance) - } + if available < market.MinAmount { + return errors.Wrapf(ErrInsufficientQuoteBalance, "insufficient quote balance: %f < min amount %f", available, market.MinAmount) + } - quantity = adjustQuantityByMinAmount(quantity, currentPrice, market.MinNotional*1.01) - - available := balance.Available - quantity = math.Min(quantity, available) - if quantity < market.MinQuantity { - return errors.Wrapf(ErrInsufficientAssetBalance, "insufficient asset balance: %f > minimal quantity %f", available, market.MinQuantity) - } - - notional := quantity * currentPrice - if notional < tradingCtx.Market.MinNotional { - return fmt.Errorf("notional %f < min notional: %f", notional, market.MinNotional) - } - - // price tick10 - // 2 -> 0.01 -> 0.1 - // 4 -> 0.0001 -> 0.001 - tick10 := math.Pow10(-market.PricePrecision + 1) - minProfitSpread := math.Max(p.MinProfitSpread, tick10) - estimatedFee := currentPrice * 0.0015 * 2 // double the fee - targetPrice := currentPrice - estimatedFee - minProfitSpread - - stockQuantity := tradingCtx.StockManager.Stocks.QuantityBelowPrice(targetPrice) - if math.Round(stockQuantity*1e8) == 0.0 { - return fmt.Errorf("profitable stock not found: target price %f, profit spread: %f", targetPrice, minProfitSpread) - } - - quantity = math.Min(quantity, stockQuantity) - if quantity < market.MinLot { - return fmt.Errorf("quantity %f less than min lot %f", quantity, market.MinLot) - } - - notional = quantity * currentPrice - if notional < tradingCtx.Market.MinNotional { - return fmt.Errorf("notional %f < min notional: %f", notional, market.MinNotional) - } + quantity = adjustQuantityByMinAmount(quantity, currentPrice, market.MinAmount*1.01) + quantity = adjustQuantityByMaxAmount(quantity, currentPrice, available) + amount := quantity * currentPrice + if amount < market.MinAmount { + return fmt.Errorf("amount too small: %f < min amount %f", amount, market.MinAmount) } } - order.Quantity = quantity - order.QuantityString = market.FormatVolume(quantity) - */ + case types.SideTypeSell: - createdOrders, err := p.Exchange.SubmitOrders(ctx, order) - _ = createdOrders - return err -} + if balance, ok := tradingCtx.Balances[market.BaseCurrency]; ok { + if util.NotZero(p.MinAssetBalance) && balance.Available < p.MinAssetBalance { + return errors.Wrapf(ErrAssetBalanceLevelTooLow, "asset balance level is too low: %f > %f", balance.Available, p.MinAssetBalance) + } + + quantity = adjustQuantityByMinAmount(quantity, currentPrice, market.MinNotional*1.01) + + available := balance.Available + quantity = math.Min(quantity, available) + if quantity < market.MinQuantity { + return errors.Wrapf(ErrInsufficientAssetBalance, "insufficient asset balance: %f > minimal quantity %f", available, market.MinQuantity) + } + + notional := quantity * currentPrice + if notional < tradingCtx.Market.MinNotional { + return fmt.Errorf("notional %f < min notional: %f", notional, market.MinNotional) + } + + // price tick10 + // 2 -> 0.01 -> 0.1 + // 4 -> 0.0001 -> 0.001 + tick10 := math.Pow10(-market.PricePrecision + 1) + minProfitSpread := math.Max(p.MinProfitSpread, tick10) + estimatedFee := currentPrice * 0.0015 * 2 // double the fee + targetPrice := currentPrice - estimatedFee - minProfitSpread + + stockQuantity := tradingCtx.StockManager.Stocks.QuantityBelowPrice(targetPrice) + if math.Round(stockQuantity*1e8) == 0.0 { + return fmt.Errorf("profitable stock not found: target price %f, profit spread: %f", targetPrice, minProfitSpread) + } + + quantity = math.Min(quantity, stockQuantity) + if quantity < market.MinLot { + return fmt.Errorf("quantity %f less than min lot %f", quantity, market.MinLot) + } + + notional = quantity * currentPrice + if notional < tradingCtx.Market.MinNotional { + return fmt.Errorf("notional %f < min notional: %f", notional, market.MinNotional) + } + } + } + + order.Quantity = quantity + order.QuantityString = market.FormatVolume(quantity) +*/ func adjustQuantityByMinAmount(quantity float64, currentPrice float64, minAmount float64) float64 { // modify quantity for the min amount diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go index 58f2237cb..13ddfce25 100644 --- a/pkg/cmd/run.go +++ b/pkg/cmd/run.go @@ -24,7 +24,6 @@ import ( "github.com/c9s/bbgo/pkg/notifier/slacknotifier" "github.com/c9s/bbgo/pkg/slack/slacklog" "github.com/c9s/bbgo/pkg/types" - ) var errSlackTokenUndefined = errors.New("slack token is not defined.") @@ -117,7 +116,7 @@ func runConfig(ctx context.Context, userConfig *bbgo.Config) error { log.AddHook(slacklog.NewLogHook(slackToken, conf.ErrorChannel)) } - log.Infof("adding slack notifier...") + log.Infof("adding slack notifier with default channel: %s", conf.DefaultChannel) var notifier = slacknotifier.New(slackToken, conf.DefaultChannel) trader.AddNotifier(notifier) } diff --git a/pkg/notifier/slacknotifier/slack.go b/pkg/notifier/slacknotifier/slack.go index c4ad4c7f4..0688e4682 100644 --- a/pkg/notifier/slacknotifier/slack.go +++ b/pkg/notifier/slacknotifier/slack.go @@ -20,7 +20,8 @@ type Notifier struct { type NotifyOption func(notifier *Notifier) func New(token, channel string, options ...NotifyOption) *Notifier { - var client = slack.New(token, slack.OptionDebug(true)) + // var client = slack.New(token, slack.OptionDebug(true)) + var client = slack.New(token) notifier := &Notifier{ channel: channel,