package trade import ( "context" "fmt" "log" "math" "git.apinb.com/bsm-sdk/core/utils" "git.apinb.com/quant/strategy/internal/impl" "git.apinb.com/quant/strategy/internal/models" "github.com/adshao/go-binance/v2/futures" ) const ( OrderSide_Long string = "BUY" OrderSide_Short string = "SELL" ) func (bn *BinanceClient) AppendOrder(symbol string, side string, quantity string) { // 判断:开仓锁,开仓信号 if IsLock(symbol, "OPEN."+side) { Error("103", "判断:开仓锁,开仓信号") return } var res *futures.CreateOrderResponse var err error // 做多 if side == "LONG" { bid, _, err := bn.BookTicker(symbol) if err != nil { Error("113", "BookTicker", err.Error()) return } res, err = bn.CreateFuturesLimitOrder( symbol, quantity, "BUY", bid, futures.PositionSideTypeLong, ) } if side == "SHORT" { _, ask, err := bn.BookTicker(symbol) if err != nil { Error("113", "BookTicker", err.Error()) return } res, err = bn.CreateFuturesLimitOrder( symbol, quantity, "SELL", ask, futures.PositionSideTypeShort, ) } if err != nil { Error("104", "CreateFuturesOrder_Limit_Binance", err.Error()) return } if res != nil { Debug("Order Result", utils.Int642String(res.OrderID)) SetLock(symbol, "OPEN."+side, 3) } } // CreateOrder 创建订单 // symbol: 交易对 // side: 买卖方向: BUY 做多 / SELL 做空 // positionSide: 持仓方向: LONG 多仓 / SHORT 空仓 // quantity: 数量 // orderType: 订单类型:LIMIT, MARKET func (bn *BinanceClient) CreateFuturesMarketOrder(symbol, quantity, side string, positionSide futures.PositionSideType) (res *futures.CreateOrderResponse, err error) { if IsLock(symbol, "OPEN."+side) { return nil, fmt.Errorf("symbol %s side %s is lock", symbol, side) } srv := bn.Futures.NewCreateOrderService().Symbol(symbol).Side(futures.SideType(side)).PositionSide(positionSide).Quantity(quantity).Type(futures.OrderTypeMarket) result, err := srv.Do(context.Background()) if err != nil { Error("120", "CreateOrder:", err.Error()) return nil, err } SetLock(symbol, "OPEN."+side, 3) Info("[SUCCESS] Create Order, OrderID:", utils.Int642String(result.OrderID)) return result, nil } // CreateOrder 创建订单 // symbol: 交易对 // side: 买卖方向: BUY 做多 / SELL 做空 // positionSide: 持仓方向: LONG 多仓 / SHORT 空仓 // quantity: 数量 // orderType: 订单类型:LIMIT, MARKET func (bn *BinanceClient) CreateFuturesLimitOrder(symbol, quantity, side, price string, positionSide futures.PositionSideType) (res *futures.CreateOrderResponse, err error) { if IsLock(symbol, "OPEN."+side) { return nil, fmt.Errorf("symbol %s side %s is lock", symbol, side) } srv := bn.Futures.NewCreateOrderService().Symbol(symbol).Side(futures.SideType(side)).PositionSide(positionSide).Quantity(quantity).Type(futures.OrderTypeLimit).Price(price).TimeInForce(futures.TimeInForceTypeGTX) extra := map[string]any{ // "priceMatch": "OPPONENT_5", } result, err := srv.Do(context.Background(), futures.WithExtraForm(extra)) if err != nil { Error("120", "CreateOrder:", err.Error()) return nil, err } SetLock(symbol, "OPEN."+side, 3) Info("[SUCCESS] Create Order, OrderID:", utils.Int642String(result.OrderID)) return result, nil } func (bn *BinanceClient) CloseFuturesMarketOrder(symbol, orderType, quantity string, positionSide futures.PositionSideType) (res *futures.CreateOrderResponse, err error) { Debug("CloseFuturesOrder_Binance", symbol, orderType, quantity, string(positionSide)) var side string if positionSide == futures.PositionSideTypeLong { side = OrderSide_Short } else { side = OrderSide_Long } result, err := bn.Futures.NewCreateOrderService().Symbol(symbol).Side(futures.SideType(side)).PositionSide(positionSide).Type(futures.OrderType(orderType)).Quantity(quantity).Do(context.Background()) if err != nil { Error("121", "CloseOrder:", err.Error()) return nil, err } // 锁定仓位 SetLock(symbol, "CLOSE."+string(positionSide), 3) Info("[SUCCESS] Close Order, OrderID:", utils.Int642String(result.OrderID)) return res, nil } func (bn *BinanceClient) CloseFuturesLimitOrder(symbol, quantity, price string, positionSide futures.PositionSideType) (res *futures.CreateOrderResponse, err error) { Debug("CloseFuturesOrder_Limit_Binance", symbol, string(positionSide), price, quantity) var side string if positionSide == futures.PositionSideTypeLong { side = OrderSide_Short } else { side = OrderSide_Long } result, err := bn.Futures.NewCreateOrderService().Symbol(symbol).Side(futures.SideType(side)).PositionSide(positionSide).Quantity(quantity).Type(futures.OrderTypeLimit).Price(price).TimeInForce(futures.TimeInForceTypeGTX).Do(context.Background()) if err != nil { Error("121", "CloseOrder:", err.Error()) return nil, err } Info("[SUCCESS] Close Order, OrderID:", utils.Int642String(result.OrderID)) return res, nil } func (bn *BinanceClient) QuickMarketClose(PlanKeyName, symbol string, side string) { Debug(PlanKeyName, "Quick closeing") orders, _ := GetPositions(PlanKeyName, symbol) if len(orders) == 0 { return } volume := utils.Float64ToString(orders[0].Volume) bn.CloseFuturesMarketOrder(symbol, "MARKET", volume, futures.PositionSideType(side)) SetLock(symbol, "CLOSE."+side, 5) //content := fmt.Sprintf("快速平仓 %s %s Volume:%s", symbol, side, volume) //models.WriteSimpleLog(models.LogType_Close, p.Strategy.KeyName, content) } // 网格动态平仓 func (bn *BinanceClient) Dynamic(p *Spec, symbol string, currentPrice float64, orders []*models.QuantOrders, closeingProfitRate, qty float64) { // 最小持仓保证金 equity := AccountsAssets.Get("USDT").AccountEquity newMargin := utils.FloatRound(equity*p.StrategyConf.MarginMultipleByBalance, 1) if newMargin < p.StrategyConf.Margin { newMargin = p.StrategyConf.Margin } // 最大保证金:账户保证金 * 0.2(20%) / 交易币对数 = 最大持仓保证金 newMaxMargin := math.Ceil(equity * 0.2 / float64(len(p.AllowSymbols))) // 设置止盈率 for _, row := range orders { // 计算利润,计算回报率 profit, profitRate := calProfitRate_V2(symbol, row.Side, float64(p.StrategyConf.Leverage), row.OpenPrice, currentPrice, row.Volume) profit = utils.FloatRound(profit, 3) profitRate = utils.FloatRound(profitRate, 3) // 开仓保证金计算 if qty <= 0 { qty = QtyBalByFloat( currentPrice, newMargin, p.StrategyConf.Leverage, p.SymbolsSetting[symbol], ) } quantity := utils.Float64ToString(qty) // cache key. CacheKey := fmt.Sprintf("%s_%s", row.Symbol, row.Side) // 网格计算 NewProfitCell := profitRate / p.StrategyConf.DefCloseingGridPrice NewProfitCell = math.Ceil(NewProfitCell) log.Println("Dynamic", row.Symbol, row.Side, "Profit:", profit, "ProfitRate", profitRate, "NewProfitCell", NewProfitCell, "closeingProfitRate", closeingProfitRate) // 增持处理,持仓的利润达到设定的增持仓位百分比 if p.StrategyConf.AddPositionOn && profitRate <= p.StrategyConf.AddThreshold && row.MarginSize < newMaxMargin { // 增持处理,持仓的利润达到设定的增持仓位百分比 newAddThreshold := p.StrategyConf.AddThreshold if row.Volume > (qty * 1.95) { newAddThreshold = newAddThreshold * 2 } if profitRate <= newAddThreshold { if !IsLock(symbol, "OPEN."+row.Side) { bn.AppendOrder(symbol, row.Side, quantity) SetLock(symbol, "OPEN."+row.Side, 5) continue } } } // 收益处理: 收益率没达到默认收益率,跳过 // 以下是收益率》=默认收益率的逻辑的业务逻辑 // 读取缓存 lastProfitCell, exists := bn.LastUPL[CacheKey] if !exists { // 缓存处理: 缓存不存在 // 收益处理: 收益率达到默认收益率 if profitRate >= closeingProfitRate { log.Println("###", CacheKey, "profit", profit, "profitRate", profitRate, ">=", "closeingProfitRate", closeingProfitRate, "NewProfitCell", NewProfitCell) bn.LastUPL[CacheKey] = NewProfitCell } } else { log.Println("$$$", CacheKey, "profit", profit, "profitRate", profitRate, ">", "closeingProfitRate", closeingProfitRate, "NewProfitCell", NewProfitCell, "lastProfitCell", lastProfitCell) if profitRate >= closeingProfitRate { // 缓存处理: 存在缓存 // 判断: NewProfitCell < lastProfitCell 当前最新网格值 < 缓存的上一次网格值 if NewProfitCell < lastProfitCell { // 缓存的收益低于当前的CELL,则平仓。 log.Println("~~~", CacheKey, "profit:", profit, "ProfitRate", profitRate, " > CloseProfitRate", closeingProfitRate, "LastProfitCell", lastProfitCell, "< NewProfitCell", NewProfitCell) // 平仓 bn.CloseFuturesMarketOrder(symbol, "MARKET", utils.Float64ToString(row.Volume), futures.PositionSideType(row.Side)) delete(bn.LastUPL, CacheKey) } else { // 高于上次盈利CELL,更新缓存 log.Println("^^^ Up", CacheKey, "profit:", profit, "ProfitRate", profitRate, " > CloseProfitRate", closeingProfitRate, "ProfitCell", NewProfitCell) // 更新缓存 bn.LastUPL[CacheKey] = NewProfitCell } } } } } func CreateSignal(identity, signalHex, orderId string) { impl.DBService.Create(&models.QuantSignal{ Identity: identity, Md5Hex: signalHex, OrderId: orderId, }) }