126 lines
3.7 KiB
Go
126 lines
3.7 KiB
Go
package binanceservice
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
DbModels "go-admin/app/admin/models"
|
|
"go-admin/pkg/retryhelper"
|
|
|
|
"github.com/bytedance/sonic"
|
|
log "github.com/go-admin-team/go-admin-core/logger"
|
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
|
)
|
|
|
|
type FuturesResetV2 struct {
|
|
service.Service
|
|
}
|
|
|
|
// 带重试机制的合约下单
|
|
func (e *FuturesResetV2) OrderPlaceLoop(apiUserInfo *DbModels.LineApiUser, params FutOrderPlace) error {
|
|
opts := retryhelper.DefaultRetryOptions()
|
|
opts.RetryableErrFn = func(err error) bool {
|
|
if strings.Contains(err.Error(), "LOT_SIZE") {
|
|
return false
|
|
}
|
|
//重试
|
|
return true
|
|
}
|
|
|
|
err := retryhelper.Retry(func() error {
|
|
return e.OrderPlace(apiUserInfo, params)
|
|
}, opts)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 合约下单
|
|
func (e *FuturesResetV2) OrderPlace(apiUserInfo *DbModels.LineApiUser, params FutOrderPlace) error {
|
|
if err := params.CheckParams(); err != nil {
|
|
return err
|
|
}
|
|
|
|
orderType := strings.ToUpper(params.OrderType)
|
|
side := strings.ToUpper(params.Side)
|
|
|
|
paramsMaps := map[string]string{
|
|
"symbol": params.Symbol,
|
|
"side": side,
|
|
"type": orderType,
|
|
"newClientOrderId": params.NewClientOrderId,
|
|
"positionSide": params.PositionSide,
|
|
}
|
|
|
|
// 设置市价和限价等类型的参数
|
|
switch orderType {
|
|
case "LIMIT":
|
|
paramsMaps["price"] = params.Price.String()
|
|
paramsMaps["timeInForce"] = "GTC"
|
|
case "TAKE_PROFIT_MARKET":
|
|
paramsMaps["timeInForce"] = "GTC"
|
|
paramsMaps["stopprice"] = params.Profit.String()
|
|
paramsMaps["workingType"] = "MARK_PRICE"
|
|
|
|
if params.ClosePosition {
|
|
paramsMaps["closePosition"] = "true"
|
|
}
|
|
case "TAKE_PROFIT":
|
|
paramsMaps["price"] = params.Price.String()
|
|
paramsMaps["stopprice"] = params.Profit.String()
|
|
paramsMaps["timeInForce"] = "GTC"
|
|
case "STOP_MARKET":
|
|
paramsMaps["stopprice"] = params.StopPrice.String()
|
|
paramsMaps["workingType"] = "MARK_PRICE"
|
|
paramsMaps["timeInForce"] = "GTC"
|
|
|
|
if params.ClosePosition {
|
|
paramsMaps["closePosition"] = "true"
|
|
}
|
|
case "STOP":
|
|
paramsMaps["price"] = params.Price.String()
|
|
paramsMaps["stopprice"] = params.StopPrice.String()
|
|
paramsMaps["workingType"] = "MARK_PRICE"
|
|
paramsMaps["timeInForce"] = "GTC"
|
|
}
|
|
|
|
//不是平仓
|
|
if !params.ClosePosition {
|
|
paramsMaps["quantity"] = params.Quantity.String()
|
|
}
|
|
// 获取 API 信息和发送下单请求
|
|
client := GetClient(apiUserInfo)
|
|
_, statusCode, err := client.SendFuturesRequestAuth("/fapi/v1/order", "POST", paramsMaps)
|
|
|
|
if err != nil {
|
|
return parseOrderError(err, paramsMaps, statusCode, apiUserInfo)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 拆出的错误解析函数
|
|
func parseOrderError(err error, paramsMaps map[string]string, statusCode int, apiUserInfo *DbModels.LineApiUser) error {
|
|
var dataMap map[string]interface{}
|
|
|
|
if err2 := sonic.Unmarshal([]byte(err.Error()), &dataMap); err2 == nil {
|
|
if code, ok := dataMap["code"]; ok {
|
|
paramsVal, _ := sonic.MarshalString(¶msMaps)
|
|
log.Error("下单失败 参数:", paramsVal)
|
|
errContent := FutErrorMaps[code.(float64)]
|
|
if errContent == "" {
|
|
errContent, _ = dataMap["msg"].(string)
|
|
}
|
|
return fmt.Errorf("api_id:%d 交易对:%s 下单失败:%s", apiUserInfo.Id, paramsMaps["symbol"], errContent)
|
|
}
|
|
}
|
|
|
|
// 特殊处理保证金不足
|
|
if strings.Contains(err.Error(), "Margin is insufficient.") {
|
|
return fmt.Errorf("api_id:%d 交易对:%s 下单失败:%s", apiUserInfo.Id, paramsMaps["symbol"], FutErrorMaps[-2019])
|
|
}
|
|
|
|
return fmt.Errorf("api_id:%d 交易对:%s statusCode:%v 下单失败:%s", apiUserInfo.Id, paramsMaps["symbol"], statusCode, err.Error())
|
|
}
|