diff --git a/pkg/bbgo/order_processor.go b/pkg/bbgo/order_processor.go index 4d203fee7..b3b129dd1 100644 --- a/pkg/bbgo/order_processor.go +++ b/pkg/bbgo/order_processor.go @@ -14,6 +14,18 @@ var ( ErrAssetBalanceLevelTooHigh = errors.New("asset balance level too high") ) +// AdjustQuantityByMaxAmount adjusts the quantity to make the amount greater than the given minAmount +func AdjustQuantityByMaxAmount(quantity, currentPrice, maxAmount fixedpoint.Value) fixedpoint.Value { + // modify quantity for the min amount + amount := currentPrice.Mul(quantity) + if amount > maxAmount { + ratio := maxAmount.Div(amount) + quantity = quantity.Mul(ratio) + } + + return quantity +} + // AdjustQuantityByMinAmount adjusts the quantity to make the amount greater than the given minAmount func AdjustQuantityByMinAmount(quantity, currentPrice, minAmount fixedpoint.Value) fixedpoint.Value { // modify quantity for the min amount diff --git a/pkg/bbgo/twap_order_executor.go b/pkg/bbgo/twap_order_executor.go index db4199cd5..97de2fd96 100644 --- a/pkg/bbgo/twap_order_executor.go +++ b/pkg/bbgo/twap_order_executor.go @@ -22,6 +22,7 @@ type TwapExecution struct { StopPrice fixedpoint.Value NumOfTicks int UpdateInterval time.Duration + DeadlineTime time.Time market types.Market marketDataStream types.Stream @@ -79,7 +80,7 @@ func (e *TwapExecution) getSideBook() (pvs types.PriceVolumeSlice, err error) { return pvs, err } -func (e *TwapExecution) newBestPriceMakerOrder() (orderForm types.SubmitOrder, err error) { +func (e *TwapExecution) newBestPriceOrder() (orderForm types.SubmitOrder, err error) { book := e.orderBook.Get() sideBook, err := e.getSideBook() @@ -166,9 +167,34 @@ func (e *TwapExecution) newBestPriceMakerOrder() (orderForm types.SubmitOrder, e } minNotional := fixedpoint.NewFromFloat(e.market.MinNotional) - orderAmount := newPrice.Mul(orderQuantity) - if orderAmount <= minNotional { - orderQuantity = AdjustQuantityByMinAmount(orderQuantity, newPrice, minNotional) + orderQuantity = AdjustQuantityByMinAmount(orderQuantity, newPrice, minNotional) + + switch e.Side { + case types.SideTypeSell: + // check base balance for sell, try to sell as more as possible + if b, ok := e.Session.Account.Balance(e.market.BaseCurrency); ok { + orderQuantity = fixedpoint.Min(b.Available, orderQuantity) + } + + case types.SideTypeBuy: + // check base balance for sell, try to sell as more as possible + if b, ok := e.Session.Account.Balance(e.market.QuoteCurrency); ok { + orderQuantity = AdjustQuantityByMaxAmount(orderQuantity, newPrice, b.Available) + } + } + + if e.DeadlineTime != emptyTime { + now := time.Now() + if now.After(e.DeadlineTime) { + orderForm = types.SubmitOrder{ + Symbol: e.Symbol, + Side: e.Side, + Type: types.OrderTypeMarket, + Quantity: restQuantity.Float64(), + Market: e.market, + } + return orderForm, nil + } } orderForm = types.SubmitOrder{ @@ -264,7 +290,7 @@ func (e *TwapExecution) updateOrder(ctx context.Context) error { e.cancelActiveOrders(ctx) } - orderForm, err := e.newBestPriceMakerOrder() + orderForm, err := e.newBestPriceOrder() if err != nil { return err } diff --git a/pkg/cmd/orders.go b/pkg/cmd/orders.go index 4a1420114..ecc267ba6 100644 --- a/pkg/cmd/orders.go +++ b/pkg/cmd/orders.go @@ -187,6 +187,16 @@ var executeOrderCmd = &cobra.Command{ return err } + deadlineDuration, err := cmd.Flags().GetDuration("deadline") + if err != nil { + return err + } + + var deadlineTime time.Time + if deadlineDuration > 0 { + deadlineTime = time.Now().Add(deadlineDuration) + } + environ := bbgo.NewEnvironment() if err := environ.ConfigureExchangeSessions(userConfig); err != nil { return err @@ -213,6 +223,7 @@ var executeOrderCmd = &cobra.Command{ StopPrice: stopPrice, NumOfTicks: numOfPriceTicks, UpdateInterval: updateInterval, + DeadlineTime: deadlineTime, } if err := execution.Run(executionCtx); err != nil { @@ -334,6 +345,7 @@ func init() { executeOrderCmd.Flags().String("slice-quantity", "", "slice quantity") executeOrderCmd.Flags().String("stop-price", "0", "stop price") executeOrderCmd.Flags().Duration("update-interval", time.Second*10, "order update time") + executeOrderCmd.Flags().Duration("deadline", 0, "deadline of the order execution") executeOrderCmd.Flags().Int("price-ticks", 0, "the number of price tick for the jump spread, default to 0") RootCmd.AddCommand(listOrdersCmd)