This commit is contained in:
2026-01-09 15:48:31 +08:00
parent e32eabbf95
commit c8e189e9c7
28 changed files with 2795 additions and 0 deletions

219
trade/bitget_order.go Normal file
View File

@@ -0,0 +1,219 @@
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
}