package trade import ( "errors" "fmt" "log" "math" "git.apinb.com/bsm-sdk/core/utils" "git.apinb.com/quant/strategy/internal/models" ) func (bg *BitgetClient) OpenOrder_market(symbol, side, size string) error { // 上涨计算 // 判断:开仓锁,开仓信号 if IsLock(symbol, "OPEN."+side) { log.Println("103", "判断:开仓锁,开仓信号") return errors.New("开仓锁") } args := map[string]string{ "productType": "USDT-FUTURES", "symbol": symbol, "marginMode": "crossed", "marginCoin": "USDT", "orderType": "market", // 订单类型,limit: 限价单,market: 市价单 "size": size, "side": GetSide(side), // 下单方向: buy,long,多仓,买;sell,short,卖 "tradeSide": "open", // 开仓 "force": "fok", // ioc: 无法立即成交的部分就撤销,fok: 无法全部立即成交就撤销,gtc: 普通订单, 订单会一直有效,直到被成交或者取消,限价单limit时必填,若省略则默认为gtc "clientOid": GenClientId(), } resp, err := bg.OrderClient.PlaceOrder(args) if err != nil { log.Println("[ERROR] #001 OpenOrder", err) return err } log.Println("[INFO] OpenOrder", resp) // 加锁 SetLock(symbol, "OPEN."+side, 5) return nil } func (bg *BitgetClient) OpenOrder_limit(symbol, side, size, price string) error { // 上涨计算 // 判断:开仓锁,开仓信号 if IsLock(symbol, "OPEN."+side) { log.Println("103", "判断:开仓锁,开仓信号") return errors.New("开仓锁") } args := map[string]string{ "productType": "USDT-FUTURES", "symbol": symbol, "marginMode": "crossed", "marginCoin": "USDT", "orderType": "limit", // 订单类型,limit: 限价单,market: 市价单 "size": size, "side": GetSide(side), // 下单方向: buy,long,多仓,买;sell,short,卖 "tradeSide": "open", // 开仓 "force": "fok", // ioc: 无法立即成交的部分就撤销,fok: 无法全部立即成交就撤销,gtc: 普通订单, 订单会一直有效,直到被成交或者取消,限价单limit时必填,若省略则默认为gtc "price": price, "clientOid": GenClientId(), } resp, err := bg.OrderClient.PlaceOrder(args) if err != nil { log.Println("[ERROR] #001 OpenOrder", err) return err } log.Println("[INFO] OpenOrder", resp) // 加锁 SetLock(symbol, "OPEN."+side, 5) return nil } func (bg *BitgetClient) CloseOrder(symbol, side, size string) error { args := map[string]string{ "productType": "USDT-FUTURES", "symbol": symbol, "size": size, "side": GetSide(side), // 下单方向: buy,long,多仓,买;sell,short,卖 "tradeSide": "close", // 平仓 "marginMode": "crossed", "marginCoin": "USDT", "orderType": "market", } _, err := bg.OrderClient.PlaceOrder(args) if err != nil { log.Println("[ERROR] #004 CloseOrder", err) return err } return nil } // 网格动态平仓 func (bg *BitgetClient) Dynamic(scfg *models.StrategyConf, symbol string, qty, currentPrice float64, orders []*models.QuantOrders, closeingProfitRate float64) { // 利润率 <= 0时,亏损并没有达到加仓的百分比时,或是超过最大仓位的投入时,返回不做处理 newMaxMargin := AccountsAssets.Get("USDT").AccountEquity * 0.015 newMaxMargin = utils.FloatRound(newMaxMargin, 2) // 设置止盈率 for _, row := range orders { // 计算利润,计算回报率 profit, profitRate := bg.calProfitRate_V2(symbol, row.Side, float64(scfg.Leverage), row.OpenPrice, currentPrice, row.Volume) // 得到持仓与开仓数量的倍数 multiple := math.Round(row.Volume / qty) if multiple < 1 { multiple = 1 } // 根据倍数计算补仓百分比。 maxAddThreshold := scfg.AddThreshold * multiple * 1.2 if multiple > 3 { maxAddThreshold = scfg.AddThreshold * multiple * 1.6 } // cache key. CacheKey := fmt.Sprintf("%s_%s", row.Symbol, row.Side) // 网格计算 NewProfitCell := profitRate / scfg.DefCloseingGridPrice NewProfitCell = math.Ceil(NewProfitCell) // 打印日志 log.Println("Dynamic", row.Symbol, row.Side, "Volume:", row.Volume, "OpenPrice:", row.OpenPrice, "Profit:", profit, "ProfitRate", profitRate, "NewProfitCell", NewProfitCell, "closeingProfitRate", closeingProfitRate, "newMaxMargin", newMaxMargin, "maxAddThreshold", maxAddThreshold) // 止损处理 if scfg.StopLossOn && profitRate <= scfg.StopThreshold { log.Println("!!! Stop", CacheKey, "StopThreshold", scfg.StopThreshold, "profitRate", profitRate) // 平仓 bg.CloseOrder(row.Symbol, row.Side, utils.Float64ToString(row.Volume)) continue } if scfg.AddPositionOn && profitRate <= maxAddThreshold && row.MarginSize < newMaxMargin { if !IsLock(symbol, "OPEN."+row.Side) { qtyStr := utils.Float64ToString(qty) bg.OpenOrder_market(symbol, row.Side, qtyStr) SetLock(symbol, "OPEN."+row.Side, 5) continue } } // 读取缓存 lastProfitCell, exists := bg.LastUPL[CacheKey] if !exists { // 缓存处理: 缓存不存在 // 收益处理: 收益率达到默认收益率 if profitRate >= closeingProfitRate { log.Println("###", CacheKey, "profit", profit, "profitRate", profitRate, ">=", "closeingProfitRate", closeingProfitRate, "NewProfitCell", NewProfitCell, "newMaxMargin", newMaxMargin) bg.LastUPL[CacheKey] = NewProfitCell } } else { log.Println("$$$", CacheKey, "profit", profit, "profitRate", profitRate, ">", "closeingProfitRate", closeingProfitRate, "NewProfitCell", NewProfitCell, "lastProfitCell", lastProfitCell, "newMaxMargin", newMaxMargin) if profitRate >= closeingProfitRate { // 缓存处理: 存在缓存 // 判断: NewProfitCell < lastProfitCell 当前最新网格值 < 缓存的上一次网格值 if NewProfitCell < lastProfitCell { // 缓存的收益低于当前的CELL,则平仓。 log.Println("~~~", CacheKey, "profit:", profit, "ProfitRate", profitRate, " > CloseProfitRate", closeingProfitRate, "LastProfitCell", lastProfitCell, "< NewProfitCell", NewProfitCell) // 平仓 bg.CloseOrder(symbol, row.Side, utils.Float64ToString(row.Volume)) delete(bg.LastUPL, CacheKey) // 更新帐号资金 _, ac, err := bg.GetFuturesAccountBalance() if err != nil { log.Println("bg.GetFuturesAccountBalance", err.Error()) } else { AccountsAssets.SetData(ac) } } else { // 高于上次盈利CELL,更新缓存 log.Println("^^^ Up", CacheKey, "profit:", profit, "ProfitRate", profitRate, " > CloseProfitRate", closeingProfitRate, "ProfitCell", NewProfitCell) // 更新缓存 bg.LastUPL[CacheKey] = NewProfitCell } } } } } func (bg *BitgetClient) calProfitRate_V2(symbol, side string, leverage, avgPrice, currentPrice, volume float64) (profit, profitRate float64) { // 计算利润 switch side { case "LONG": profit = (currentPrice - avgPrice) * volume //Debug("CalculateProfit_Long", symbol, side, "AvgPrice:", avgPrice.String(), "CurrentPrice:", currentPrice.String(), "volume:", volume.String()) case "SHORT": //Debug("CalculateProfit_Short", symbol, side, "AvgPrice:", avgPrice.String(), "CurrentPrice:", currentPrice.String(), "volume:", volume.String()) profit = (avgPrice - currentPrice) * volume } // 计算回报率 if profit == 0 { return profit, profitRate } cost := avgPrice * volume actualInvestment := cost / leverage profitRate = profit / actualInvestment profitRate = utils.FloatRound(profitRate, 3) return profit, profitRate }