initial
This commit is contained in:
274
trade/binance_order.go
Normal file
274
trade/binance_order.go
Normal file
@@ -0,0 +1,274 @@
|
||||
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,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user