220 lines
7.4 KiB
Go
220 lines
7.4 KiB
Go
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
|
||
}
|