bbgo_origin/pkg/strategy/dca2/take_profit.go

91 lines
2.9 KiB
Go
Raw Normal View History

2023-12-07 06:38:13 +00:00
package dca2
import (
"context"
"fmt"
2023-12-07 06:38:13 +00:00
"github.com/c9s/bbgo/pkg/exchange/retry"
2023-12-07 06:38:13 +00:00
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/pkg/errors"
2023-12-07 06:38:13 +00:00
)
func (s *Strategy) placeTakeProfitOrders(ctx context.Context) error {
2024-03-12 06:29:36 +00:00
s.logger.Info("start placing take profit orders")
currentRound, err := s.collector.CollectCurrentRound(ctx)
if err != nil {
return errors.Wrap(err, "failed to place the take-profit order when collecting current round")
}
if len(currentRound.TakeProfitOrders) > 0 {
return fmt.Errorf("there is a take-profit order before placing the take-profit order, please check it and manually fix it")
}
trades, err := s.collector.CollectRoundTrades(ctx, currentRound)
if err != nil {
return errors.Wrap(err, "failed to place the take-profit order when collecting round trades")
}
2024-04-15 09:27:56 +00:00
roundPosition := types.NewPositionFromMarket(s.Market)
for _, trade := range trades {
2024-04-16 08:52:50 +00:00
s.logger.Infof("add trade into the position of this round %s", trade.String())
if trade.FeeProcessing {
return fmt.Errorf("failed to place the take-profit order because there is a trade's fee not ready")
}
2024-04-15 09:27:56 +00:00
roundPosition.AddTrade(trade)
}
2024-04-15 09:27:56 +00:00
s.logger.Infof("position of this round before place the take-profit order: %s", roundPosition.String())
2024-04-15 09:27:56 +00:00
order := generateTakeProfitOrder(s.Market, s.TakeProfitRatio, roundPosition, s.OrderGroupID)
// verify the volume of order
bals, err := retry.QueryAccountBalancesUntilSuccessfulLite(ctx, s.ExchangeSession.Exchange)
if err != nil {
return errors.Wrapf(err, "failed to query balance to verify")
}
bal, exist := bals[s.Market.BaseCurrency]
if !exist {
return fmt.Errorf("there is no %s in the balances %+v", s.Market.BaseCurrency, bals)
}
quantityDiff := bal.Available.Sub(order.Quantity)
if quantityDiff.Sign() < 0 {
return fmt.Errorf("the balance (%s) is not enough for the order (%s)", bal.String(), order.Quantity.String())
}
if quantityDiff.Compare(s.Market.MinQuantity) > 0 {
s.logger.Warnf("the diff between balance (%s) and the take-profit order (%s) is larger than min quantity %s", bal.String(), order.Quantity.String(), s.Market.MinQuantity.String())
}
2023-12-07 06:38:13 +00:00
createdOrders, err := s.OrderExecutor.SubmitOrders(ctx, order)
if err != nil {
return err
}
for _, createdOrder := range createdOrders {
s.logger.Info("SUBMIT TAKE PROFIT ORDER ", createdOrder.String())
}
return nil
}
2023-12-22 07:50:48 +00:00
func generateTakeProfitOrder(market types.Market, takeProfitRatio fixedpoint.Value, position *types.Position, orderGroupID uint32) types.SubmitOrder {
2023-12-07 06:38:13 +00:00
side := types.SideTypeSell
takeProfitPrice := market.TruncatePrice(position.AverageCost.Mul(fixedpoint.One.Add(takeProfitRatio)))
return types.SubmitOrder{
Symbol: market.Symbol,
Market: market,
Type: types.OrderTypeLimit,
Price: takeProfitPrice,
Side: side,
TimeInForce: types.TimeInForceGTC,
Quantity: position.GetBase().Abs(),
Tag: orderTag,
GroupID: orderGroupID,
}
}