1、合仓
This commit is contained in:
		| @ -7,6 +7,7 @@ import ( | ||||
| 	"go-admin/common/global" | ||||
| 	"go-admin/common/helper" | ||||
| 	models2 "go-admin/models" | ||||
| 	"go-admin/models/positiondto" | ||||
| 	"go-admin/pkg/utility" | ||||
| 	"go-admin/pkg/utility/snowflakehelper" | ||||
| 	"go-admin/services/binanceservice" | ||||
| @ -225,11 +226,7 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi | ||||
| 	var data models.LinePreOrder | ||||
| 	var list []models.LinePreOrder | ||||
| 	e.Orm.Model(&models.LinePreOrder{}).Where("id in ?", d.GetId()).Find(&list) | ||||
| 	//for _, order := range list { | ||||
| 	//if order.Status != "0" { | ||||
| 	//	return errors.New(fmt.Sprintf("订单id %d 已被触发 无法被删除", order.Id)) | ||||
| 	//} | ||||
| 	//} | ||||
|  | ||||
| 	db := e.Orm.Model(&data). | ||||
| 		Scopes( | ||||
| 			actions.Permission(data.TableName(), p), | ||||
| @ -245,6 +242,8 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi | ||||
| 		return errors.New("无权删除该数据") | ||||
| 	} | ||||
|  | ||||
| 	positions := map[string]positiondto.LinePreOrderPositioinDelReq{} | ||||
|  | ||||
| 	//删除的缓存 | ||||
| 	for _, order := range list { | ||||
| 		redisList := dto.PreOrderRedisList{ | ||||
| @ -272,6 +271,19 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi | ||||
| 			helper.DefaultRedis.LRem(listKey, string(marshal)) | ||||
| 		} | ||||
|  | ||||
| 		//会影响持仓的 | ||||
| 		removeSymbolKey := fmt.Sprintf("%v_%s_%s_%s_%v", order.ApiId, order.ExchangeType, order.Symbol, order.Site, order.SymbolType) | ||||
|  | ||||
| 		if _, ok := positions[removeSymbolKey]; !ok { | ||||
| 			positions[removeSymbolKey] = positiondto.LinePreOrderPositioinDelReq{ | ||||
| 				ApiId:        order.ApiId, | ||||
| 				Symbol:       order.Symbol, | ||||
| 				ExchangeType: order.ExchangeType, | ||||
| 				Side:         order.Site, | ||||
| 				SymbolType:   order.SymbolType, | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		binanceservice.MainClosePositionClearCache(order.Id, order.SymbolType) | ||||
|  | ||||
| 		ints = append(ints, order.Id) | ||||
| @ -280,6 +292,27 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi | ||||
| 	if len(ints) > 0 { | ||||
| 		e.Orm.Model(&models.LinePreOrder{}).Where("main_id >0 AND main_id in ?", ints).Unscoped().Delete(&models.LinePreOrder{}) | ||||
| 	} | ||||
|  | ||||
| 	//清理仓位缓存 | ||||
| 	for _, v := range positions { | ||||
| 		var count int64 | ||||
| 		e.Orm.Model(&models.LinePreOrder{}). | ||||
| 			Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6", | ||||
| 				v.ApiId, v.Side, v.Symbol, v.SymbolType, v.ExchangeType).Count(&count) | ||||
|  | ||||
| 		//没有已开仓的订单 直接清理仓位 | ||||
| 		if count == 0 { | ||||
| 			var key string | ||||
|  | ||||
| 			if v.SymbolType == 1 { | ||||
| 				key = fmt.Sprintf(rediskey.SpotPosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) | ||||
| 			} else { | ||||
| 				key = fmt.Sprintf(rediskey.FuturePosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) | ||||
| 			} | ||||
|  | ||||
| 			helper.DefaultRedis.DeleteString(key) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -323,11 +356,11 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP | ||||
|  | ||||
| 		//获取交易对 | ||||
| 		tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, key) | ||||
| 		orderCount := e.CheckRepeatOrder(req.SymbolType, id, req.Site, tradeSet.Coin) | ||||
| 		if orderCount > 0 { | ||||
| 			*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 该交易对已存在,请勿重复下单", id, req.Symbol)) | ||||
| 			continue | ||||
| 		} | ||||
| 		// orderCount := e.CheckRepeatOrder(req.SymbolType, id, req.Site, tradeSet.Coin) | ||||
| 		// if orderCount > 0 { | ||||
| 		// 	*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 该交易对已存在,请勿重复下单", id, req.Symbol)) | ||||
| 		// 	continue | ||||
| 		// } | ||||
| 		tickerPrice := utility.StrToDecimal(tradeSet.LastPrice) | ||||
| 		if tickerPrice.Equal(decimal.Zero) { //redis 没有这个值 | ||||
| 			*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 交易行情出错", id, req.Symbol)) | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| package rediskey | ||||
|  | ||||
| // 量化 | ||||
| const ( | ||||
| 	IPPositionCache     = "_IPPositionCache"        // IP 归属地缓存 | ||||
| 	AppLoginUserToken   = "_AppLoginUserToken_%d"   // App登录用户的Token {uid} | ||||
| @ -52,10 +53,18 @@ const ( | ||||
|  | ||||
| 	SpotAddPositionList    = "spot_add_position_list:%s"    //现货加仓待触发 {交易所code} | ||||
| 	FuturesAddPositionList = "futures_add_position_list:%s" //合约加仓待触发 {交易所code} | ||||
|  | ||||
| 	//现货持仓 {exchangeType,apiuserid,symbol,side} | ||||
| 	SpotPosition = "spot_position:%s:%v:%s_%s" | ||||
| 	//合约持仓 {exchangeType,apiuserid,symbol,side} | ||||
| 	FuturePosition = "future_position:%s:%v:%s_%s" | ||||
| 	//需要清理键值---------END----------------- | ||||
|  | ||||
| 	JobReOrderTrigger = "job_re_order_trigger" //定时取消限价并下市价锁 | ||||
| 	//定时取消限价并下市价锁 | ||||
| 	JobReOrderTrigger = "job_re_order_trigger" | ||||
| 	//现货持仓修改锁{apiId,symbol,side} | ||||
| 	SpotPositionLock = "spot_position_lock:%v:%s:%s" | ||||
| 	//合约持仓修改锁{apiId,symbol,side} | ||||
| 	FuturePositionLock = "future_position_lock:%v:%s:%s" | ||||
|  | ||||
| 	ListenAveLastSymbol = "listen_ave_last_symbol" // 监听最新交易对 | ||||
| 	AveRequestToken     = "ave_request_token"      // AVE请求token | ||||
|  | ||||
							
								
								
									
										33
									
								
								models/positiondto/cache.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								models/positiondto/cache.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| package positiondto | ||||
|  | ||||
| import "github.com/shopspring/decimal" | ||||
|  | ||||
| //持仓信息 | ||||
| type PositionDto struct { | ||||
| 	SymbolType   int             `json:"symbolType"`   //交易对类型 1-现货 2-合约 | ||||
| 	Side         string          `json:"side"`         //买卖方向 BUY SELL | ||||
| 	PositionSide string          `json:"positionSide"` //持仓方向 LONG SHORT | ||||
| 	Quantity     decimal.Decimal `json:"quantity"`     //总数量 | ||||
| 	TotalLoss    decimal.Decimal `json:"totalLoss"`    //总亏损 | ||||
| 	Symbol       string          `json:"symbol"`       //交易对 | ||||
| 	ApiId        int             `json:"apiId"`        //apiid | ||||
| 	LastPrice    decimal.Decimal `json:"lastPrice"`    //上一次成交价 | ||||
| } | ||||
|  | ||||
| type PositionAddReq struct { | ||||
| 	SymbolType   int             `json:"symbolType"`   //交易对类型 1-现货 2-合约 | ||||
| 	Side         string          `json:"side"`         //方向 | ||||
| 	Quantity     decimal.Decimal `json:"quantity"`     //总数量 | ||||
| 	Symbol       string          `json:"symbol"`       //交易对 | ||||
| 	ApiId        int             `json:"apiId"`        //apiid | ||||
| 	Price        decimal.Decimal `json:"price"`        //本次成交价 | ||||
| 	PositionSide string          `json:"positionSide"` //持仓方向 | ||||
| } | ||||
|  | ||||
| type LinePreOrderPositioinDelReq struct { | ||||
| 	SymbolType   int    `json:"symbolType"`   //交易对类型 1-现货 2-合约 | ||||
| 	Side         string `json:"side"`         //方向 | ||||
| 	Symbol       string `json:"symbol"`       //交易对 | ||||
| 	ApiId        int    `json:"apiId"`        //apiid | ||||
| 	ExchangeType string `json:"exchangeType"` //交易所类型 | ||||
| } | ||||
| @ -230,10 +230,8 @@ func (e SpotRestApi) OrderPlace(orm *gorm.DB, params OrderPlacementService) erro | ||||
| 			paramsMaps["stopPrice"] = params.StopPrice.String() | ||||
| 		} | ||||
| 	} | ||||
| 	var apiUserInfo DbModels.LineApiUser | ||||
|  | ||||
| 	err := orm.Model(&DbModels.LineApiUser{}).Where("id = ?", params.ApiId).Find(&apiUserInfo).Error | ||||
| 	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { | ||||
| 	apiUserInfo, err := GetApiInfo(params.ApiId) | ||||
| 	if apiUserInfo.Id == 0 { | ||||
| 		log.Errorf("api用户出错 err: %+v", err) | ||||
| 		return err | ||||
| 	} | ||||
| @ -273,6 +271,24 @@ func (e SpotRestApi) OrderPlace(orm *gorm.DB, params OrderPlacementService) erro | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // 循环取消 | ||||
| func (e SpotRestApi) CancelOpenOrdersLoop(orm *gorm.DB, req CancelOpenOrdersReq, retryCount int) error { | ||||
| 	err := e.CancelOpenOrders(orm, req) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		for x := 1; x < retryCount; x++ { | ||||
| 			err = e.CancelOpenOrders(orm, req) | ||||
| 			if err == nil { | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			time.Sleep(time.Duration(x) * 200 * time.Millisecond) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // CancelOpenOrders 撤销单一交易对下所有挂单 包括了来自订单列表的挂单 | ||||
| func (e SpotRestApi) CancelOpenOrders(orm *gorm.DB, req CancelOpenOrdersReq) error { | ||||
| 	if orm == nil { | ||||
| @ -285,19 +301,12 @@ func (e SpotRestApi) CancelOpenOrders(orm *gorm.DB, req CancelOpenOrdersReq) err | ||||
| 	params := map[string]string{ | ||||
| 		"symbol": req.Symbol, | ||||
| 	} | ||||
| 	var apiUserInfo DbModels.LineApiUser | ||||
| 	apiUserInfo, err := GetApiInfo(req.ApiId) | ||||
|  | ||||
| 	err = orm.Model(&DbModels.LineApiUser{}).Where("id = ?", req.ApiId).Find(&apiUserInfo).Error | ||||
| 	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { | ||||
| 	if apiUserInfo.Id == 0 { | ||||
| 		return fmt.Errorf("api_id:%d 交易对:%s api用户出错:%+v", apiUserInfo.Id, req.Symbol, err) | ||||
| 	} | ||||
| 	var client *helper.BinanceClient | ||||
|  | ||||
| 	if apiUserInfo.UserPass == "" { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) | ||||
| 	} else { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) | ||||
| 	} | ||||
| 	client := GetClient(&apiUserInfo) | ||||
| 	_, _, err = client.SendSpotAuth("/api/v3/openOrders", "DELETE", params) | ||||
| 	if err != nil { | ||||
| 		dataMap := make(map[string]interface{}) | ||||
| @ -327,13 +336,7 @@ func (e SpotRestApi) CancelOpenOrderByOrderSn(apiUserInfo DbModels.LineApiUser, | ||||
| 		"origClientOrderId": newClientOrderId, | ||||
| 		"recvWindow":        "10000", | ||||
| 	} | ||||
| 	var client *helper.BinanceClient | ||||
|  | ||||
| 	if apiUserInfo.UserPass == "" { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) | ||||
| 	} else { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) | ||||
| 	} | ||||
| 	client := GetClient(&apiUserInfo) | ||||
| 	_, code, err := client.SendSpotAuth("/api/v3/order", "DELETE", params) | ||||
| 	if err != nil || code != 200 { | ||||
| 		log.Error("取消现货委托失败 参数:", params) | ||||
|  | ||||
| @ -9,7 +9,9 @@ import ( | ||||
| 	"go-admin/common/global" | ||||
| 	"go-admin/common/helper" | ||||
| 	"go-admin/models" | ||||
| 	"go-admin/models/positiondto" | ||||
| 	"go-admin/pkg/utility" | ||||
| 	"go-admin/services/positionservice" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| @ -410,3 +412,64 @@ func cancelSymbolTakeAndStop(db *gorm.DB, mainId int, symbolType int) error { | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // 保存仓位信息 | ||||
| func savePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) positiondto.PositionDto { | ||||
| 	positionManage := positionservice.BinancePositionManagement{} | ||||
| 	positionManage.Orm = db | ||||
| 	positionReq := positiondto.PositionAddReq{ | ||||
| 		ApiId:      preOrder.ApiId, | ||||
| 		SymbolType: preOrder.SymbolType, | ||||
| 		Symbol:     preOrder.Symbol, | ||||
| 		Price:      utility.StrToDecimal(preOrder.Price), | ||||
| 		Side:       preOrder.Site, | ||||
| 		Quantity:   utility.StrToDecimal(preOrder.Num), | ||||
| 	} | ||||
|  | ||||
| 	switch { | ||||
| 	case preOrder.OrderType == 0 && preOrder.Site == "BUY", | ||||
| 		preOrder.OrderType != 0 && preOrder.Site == "SELL": | ||||
| 		positionReq.PositionSide = "LONG" | ||||
| 	case preOrder.OrderType == 0 && preOrder.Site == "SELL", | ||||
| 		preOrder.OrderType != 0 && preOrder.Site == "BUY": | ||||
| 		positionReq.PositionSide = "SHORT" | ||||
| 	} | ||||
|  | ||||
| 	//减仓单 数量为负 | ||||
| 	if preOrder.OrderType == 4 { | ||||
| 		positionReq.Quantity = positionReq.Quantity.Mul(decimal.NewFromInt(-1)) | ||||
| 	} | ||||
|  | ||||
| 	positionData, err := positionManage.SavePosition(&positionReq, global.EXCHANGE_BINANCE) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		logger.Error("保存持仓信息失败, 主单号:%s, 错误信息:%v", preOrder.OrderSn, err) | ||||
| 	} | ||||
| 	return positionData | ||||
| } | ||||
|  | ||||
| // 获取已开仓的主单id | ||||
| func getOpenPositionMainOrderId(db *gorm.DB, newId, apiId, symbolType int, exchangeType, symbol, side string) ([]DbModels.LinePreOrder, error) { | ||||
| 	mainOrders := make([]DbModels.LinePreOrder, 0) | ||||
|  | ||||
| 	if err := db.Model(&DbModels.LinePreOrder{}). | ||||
| 		Where("api_id =? AND status>4 AND status<7 AND symbol=? AND symbol_type =? AND side= ? AND exchange_type=? AND id!=?", | ||||
| 			apiId, symbol, symbolType, side, exchangeType, newId). | ||||
| 		Select("id", "order_sn").Find(&mainOrders).Error; err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return mainOrders, nil | ||||
| } | ||||
|  | ||||
| // 获取需要取消的订单号 | ||||
| func getOpenOrderSns(db *gorm.DB, mainIds []int) ([]string, error) { | ||||
| 	result := []string{} | ||||
|  | ||||
| 	//委托中的订单 | ||||
| 	if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id IN ? AND status=5", mainIds).Select("order_sn").Find(&result).Error; err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
| @ -467,8 +467,7 @@ func (e FutRestApi) OrderPlace(orm *gorm.DB, params FutOrderPlace) error { | ||||
| 	if err2 != nil { | ||||
| 		return err2 | ||||
| 	} | ||||
| 	var paramsMaps map[string]string | ||||
| 	paramsMaps = map[string]string{ | ||||
| 	paramsMaps := map[string]string{ | ||||
| 		"symbol":           params.Symbol, | ||||
| 		"side":             params.Side, | ||||
| 		"quantity":         params.Quantity.String(), | ||||
| @ -519,18 +518,11 @@ func (e FutRestApi) OrderPlace(orm *gorm.DB, params FutOrderPlace) error { | ||||
| 			paramsMaps["positionSide"] = "LONG" | ||||
| 		} | ||||
| 	} | ||||
| 	var apiUserInfo DbModels.LineApiUser | ||||
| 	err := orm.Model(&DbModels.LineApiUser{}).Where("id = ?", params.ApiId).Find(&apiUserInfo).Error | ||||
| 	apiUserInfo, err := GetApiInfo(params.ApiId) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	var client *helper.BinanceClient | ||||
|  | ||||
| 	if apiUserInfo.UserPass == "" { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) | ||||
| 	} else { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) | ||||
| 	} | ||||
| 	client := GetClient(&apiUserInfo) | ||||
| 	_, statusCode, err := client.SendFuturesRequestAuth("/fapi/v1/order", "POST", paramsMaps) | ||||
| 	if err != nil { | ||||
| 		var dataMap map[string]interface{} | ||||
| @ -662,12 +654,7 @@ func getSymbolHolde(e FutRestApi, apiInfo *DbModels.LineApiUser, symbol string, | ||||
| } | ||||
|  | ||||
| func (e FutRestApi) GetPositionV3(apiUserInfo *DbModels.LineApiUser, symbol string) ([]PositionRisk, error) { | ||||
| 	var client *helper.BinanceClient | ||||
| 	if apiUserInfo.UserPass == "" { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) | ||||
| 	} else { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) | ||||
| 	} | ||||
| 	client := GetClient(apiUserInfo) | ||||
| 	params := map[string]string{ | ||||
| 		"symbol":     symbol, | ||||
| 		"recvWindow": "5000", | ||||
| @ -736,12 +723,7 @@ func (e FutRestApi) ClosePosition(symbol string, orderSn string, quantity decima | ||||
| 		params["timeInForce"] = "GTC" | ||||
| 	} | ||||
|  | ||||
| 	var client *helper.BinanceClient | ||||
| 	if apiUserInfo.UserPass == "" { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) | ||||
| 	} else { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) | ||||
| 	} | ||||
| 	client := GetClient(&apiUserInfo) | ||||
|  | ||||
| 	resp, _, err := client.SendFuturesRequestAuth(endpoint, "POST", params) | ||||
| 	if err != nil { | ||||
| @ -781,12 +763,7 @@ func (e FutRestApi) CancelFutOrder(apiUserInfo DbModels.LineApiUser, symbol stri | ||||
| 		"symbol":            symbol,           //交易对 | ||||
| 		"origClientOrderId": newClientOrderId, //用户自定义订单号 | ||||
| 	} | ||||
| 	var client *helper.BinanceClient | ||||
| 	if apiUserInfo.UserPass == "" { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) | ||||
| 	} else { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) | ||||
| 	} | ||||
| 	client := GetClient(&apiUserInfo) | ||||
| 	_, _, err := client.SendFuturesRequestAuth(endpoint, "DELETE", params) | ||||
| 	if err != nil { | ||||
| 		var dataMap map[string]interface{} | ||||
| @ -815,12 +792,7 @@ func (e FutRestApi) CancelAllFutOrder(apiUserInfo DbModels.LineApiUser, symbol s | ||||
| 		"symbol":     symbol, //交易对 | ||||
| 		"recvWindow": "5000", | ||||
| 	} | ||||
| 	var client *helper.BinanceClient | ||||
| 	if apiUserInfo.UserPass == "" { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) | ||||
| 	} else { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) | ||||
| 	} | ||||
| 	client := GetClient(&apiUserInfo) | ||||
| 	_, _, err := client.SendFuturesRequestAuth(endpoint, "DELETE", params) | ||||
| 	if err != nil { | ||||
| 		var dataMap map[string]interface{} | ||||
| @ -854,12 +826,7 @@ func (e FutRestApi) CancelBatchFutOrder(apiUserInfo DbModels.LineApiUser, symbol | ||||
| 		"symbol":                symbol, //交易对 | ||||
| 		"origClientOrderIdList": string(marshal), | ||||
| 	} | ||||
| 	var client *helper.BinanceClient | ||||
| 	if apiUserInfo.UserPass == "" { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) | ||||
| 	} else { | ||||
| 		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) | ||||
| 	} | ||||
| 	client := GetClient(&apiUserInfo) | ||||
| 	_, code, err := client.SendFuturesRequestAuth(endpoint, "DELETE", params) | ||||
| 	if err != nil { | ||||
| 		log.Error("取消合约委托失败 参数:", params) | ||||
| @ -893,8 +860,8 @@ func (e FutRestApi) CalcSymbolExchangeAmt(symbol string, quoteSymbol string, tot | ||||
| 	tickerSymbol := helper.DefaultRedis.Get(rediskey.FutSymbolTicker).Val() | ||||
| 	tickerSymbolMaps := make([]dto.Ticker, 0) | ||||
| 	sonic.Unmarshal([]byte(tickerSymbol), &tickerSymbolMaps) | ||||
| 	var targetSymbol string | ||||
| 	targetSymbol = strings.Replace(symbol, quoteSymbol, "USDT", 1) //ETHBTC -》 ETHUSDT | ||||
|  | ||||
| 	targetSymbol := strings.Replace(symbol, quoteSymbol, "USDT", 1) //ETHBTC -》 ETHUSDT | ||||
| 	key := fmt.Sprintf("%s:%s", global.TICKER_FUTURES, targetSymbol) | ||||
| 	tradeSet, _ := helper.GetObjString[models.TradeSet](helper.DefaultRedis, key) | ||||
| 	var targetPrice decimal.Decimal | ||||
| @ -908,11 +875,6 @@ func (e FutRestApi) CalcSymbolExchangeAmt(symbol string, quoteSymbol string, tot | ||||
| 	return quantity | ||||
| } | ||||
|  | ||||
| // 加仓主账号 | ||||
| func (e FutRestApi) CoverAccountA(apiUserInfo DbModels.LineApiUser, symbol string) { | ||||
|  | ||||
| } | ||||
|  | ||||
| // GetFutSymbolLastPrice 获取现货交易对最新价格 | ||||
| func (e FutRestApi) GetFutSymbolLastPrice(targetSymbol string) (lastPrice decimal.Decimal) { | ||||
| 	key := fmt.Sprintf(global.TICKER_FUTURES, global.EXCHANGE_BINANCE, targetSymbol) | ||||
| @ -979,7 +941,7 @@ func (e FutRestApi) GetOrderByOrderSn(symbol, orderSn string, apiUserInfo DbMode | ||||
| } | ||||
|  | ||||
| /* | ||||
| 查询现货委托 | ||||
| 查询合约委托 | ||||
| */ | ||||
| // 根据订单号获取订单信息,如果获取失败,则进行重试 | ||||
| func (e FutRestApi) GetOrderByOrderSnLoop(symbol, ordersn string, apiUserInfo DbModels.LineApiUser, retryCount int) (order binancedto.BinanceFutureOrder, err error) { | ||||
|  | ||||
| @ -127,45 +127,51 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| 		logger.Errorf("handleReduceFilled 获取交易对设置失败,订单号:%s", preOrder.OrderSn) | ||||
| 		return | ||||
| 	} | ||||
| 	rate := utility.StringAsFloat(preOrder.Rate) | ||||
|  | ||||
| 	price := utility.StrToDecimal(preOrder.Price) | ||||
| 	parentOrder, err := GetOrderById(db, preOrder.Pid) | ||||
| 	// 100%减仓 终止流程 | ||||
| 	if rate >= 100 { | ||||
| 		//缓存 | ||||
| 		removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) | ||||
| 		removePosition(db, preOrder) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		logger.Errorf("handleReduceFilled 获取主单失败,订单号:%s", preOrder.OrderSn) | ||||
| 		return | ||||
| 	} | ||||
| 	parentPrice := utility.StrToDecimal(parentOrder.Price) | ||||
| 	num := utility.StrToDecimal(preOrder.Num) | ||||
| 	lossAmount := price.Sub(parentPrice).Abs().Mul(num).Truncate(int32(tradeSet.PriceDigit)) | ||||
|  | ||||
| 	if !strings.HasSuffix(preOrder.Symbol, "USDT") { | ||||
| 		tradeSetU, err := GetTradeSet(utility.ReplaceSuffix(preOrder.Symbol, preOrder.QuoteSymbol, ""), 1) | ||||
|  | ||||
| 		if err != nil { | ||||
| 			logger.Errorf("handleMainReduceFilled 获取币本位对应U交易对设置失败,订单号:%s", preOrder.OrderSn) | ||||
| 			return | ||||
| 		ids := []int{preOrder.MainId, preOrder.Pid} | ||||
| 		if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6", ids).Update("status", 9).Error; err != nil { | ||||
| 			logger.Info("100%减仓完毕,终结流程") | ||||
| 		} | ||||
|  | ||||
| 		lossAmount = lossAmount.Mul(utility.StrToDecimal(tradeSetU.LastPrice)).Truncate(int32(tradeSetU.PriceDigit)) | ||||
| 	} | ||||
|  | ||||
| 	if err := db.Model(&parentOrder).Where("loss_amount = ?", 0).Update("loss_amount", lossAmount).Error; err != nil { | ||||
| 		logger.Errorf("handleMainReduceFilled 更新亏损金额失败,订单号:%s", preOrder.OrderSn) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	positionData := savePosition(db, preOrder) | ||||
| 	orders := make([]models.LinePreOrder, 0) | ||||
| 	if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status = 0", preOrder.Id).Find(&orders).Error; err != nil { | ||||
| 		logger.Errorf("handleMainReduceFilled 获取待触发订单失败,订单号:%s", preOrder.OrderSn) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	orderExt := models.LinePreOrderExt{} | ||||
| 	totalNum := getFuturesPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) | ||||
| 	totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) | ||||
| 	price := utility.StrToDecimal(preOrder.Price).Truncate(int32(tradeSet.PriceDigit)) | ||||
| 	futApi := FutRestApi{} | ||||
| 	db.Model(&orderExt).Where("order_id =?", preOrder.Pid).First(&orderExt) | ||||
|  | ||||
| 	for _, v := range orders { | ||||
| 		if v.OrderType == 1 { | ||||
| 			//亏损大于0 重新计算比例 | ||||
| 			if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { | ||||
| 				percentag := positionData.TotalLoss.Div(totalNum).Div(price) | ||||
| 				percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) | ||||
| 				v.Rate = percentag.String() | ||||
| 				percentag = percentag.Div(decimal.NewFromInt(100)) | ||||
|  | ||||
| 				if positionData.PositionSide == "LONG" { | ||||
| 					v.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() | ||||
| 				} else { | ||||
| 					v.Price = price.Mul(decimal.NewFromInt(1).Sub(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			processFutTakeProfitOrder(db, futApi, v, totalNum) | ||||
| 		} else if v.OrderType == 2 { | ||||
| 			processFutStopLossOrder(db, v, utility.StrToDecimal(v.Price), totalNum) | ||||
| @ -275,7 +281,8 @@ func getFuturesPositionNum(apiUserInfo DbModels.LineApiUser, preOrder *DbModels. | ||||
|  | ||||
| // 平仓单成交 | ||||
| func handleClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| 	removeFutLossAndAddPosition(preOrder) | ||||
| 	removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) | ||||
| 	removePosition(db, preOrder) | ||||
|  | ||||
| 	futApi := FutRestApi{} | ||||
| 	apiUserInfo, _ := GetApiInfo(preOrder.ApiId) | ||||
| @ -295,7 +302,8 @@ func handleClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
|  | ||||
| // 止损单成交 | ||||
| func handleStopLoss(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| 	removeFutLossAndAddPosition(preOrder) | ||||
| 	removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) | ||||
| 	removePosition(db, preOrder) | ||||
|  | ||||
| 	spotApi := SpotRestApi{} | ||||
| 	apiUserInfo, _ := GetApiInfo(preOrder.ApiId) | ||||
| @ -319,7 +327,8 @@ func handleStopLoss(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
|  | ||||
| // 止盈单成交 | ||||
| func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| 	removeFutLossAndAddPosition(preOrder) | ||||
| 	removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) | ||||
| 	removePosition(db, preOrder) | ||||
|  | ||||
| 	futApi := FutRestApi{} | ||||
| 	apiUserInfo, _ := GetApiInfo(preOrder.ApiId) | ||||
| @ -338,7 +347,7 @@ func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| } | ||||
|  | ||||
| // 清除合约缓存 | ||||
| func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { | ||||
| func removeFutLossAndAddPosition(mainId int, orderSn string) { | ||||
| 	stoplossKey := fmt.Sprintf(rediskey.FuturesStopLossList, global.EXCHANGE_BINANCE) | ||||
| 	stoplossVal, _ := helper.DefaultRedis.GetAllList(stoplossKey) | ||||
| 	addPositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE) | ||||
| @ -352,11 +361,11 @@ func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { | ||||
| 	//止损缓存 | ||||
| 	for _, v := range stoplossVal { | ||||
| 		sonic.Unmarshal([]byte(v), &stoploss) | ||||
| 		if stoploss.MainId == preOrder.MainId { | ||||
| 		if stoploss.MainId == mainId { | ||||
| 			_, err := helper.DefaultRedis.LRem(stoplossKey, v) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", preOrder.OrderSn, err) | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", orderSn, err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @ -364,11 +373,11 @@ func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { | ||||
| 	//加仓缓存 | ||||
| 	for _, v := range addPositionVal { | ||||
| 		sonic.Unmarshal([]byte(v), &addPosition) | ||||
| 		if addPosition.MainId == preOrder.MainId { | ||||
| 		if addPosition.MainId == mainId { | ||||
| 			_, err := helper.DefaultRedis.LRem(addPositionKey, v) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", preOrder.OrderSn, err) | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", orderSn, err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @ -376,87 +385,172 @@ func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { | ||||
| 	//减仓缓存 | ||||
| 	for _, v := range reduceVal { | ||||
| 		sonic.Unmarshal([]byte(v), &reduce) | ||||
| 		if reduce.MainId == preOrder.MainId { | ||||
| 		if reduce.MainId == mainId { | ||||
| 			_, err := helper.DefaultRedis.LRem(reduceKey, v) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", preOrder.OrderSn, err) | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", orderSn, err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 主单成交 处理止盈止损订单 | ||||
| // preOrder 主单 | ||||
| // 处理主单成交,处理止盈、止损、减仓订单 | ||||
| func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { | ||||
| 	orders := []models.LinePreOrder{} | ||||
| 	tradeSet, _ := GetTradeSet(preOrder.Symbol, 1) | ||||
|  | ||||
| 	if tradeSet.Coin == "" { | ||||
| 		logger.Error("获取交易对失败") | ||||
| 	// 获取交易对配置和API信息 | ||||
| 	tradeSet, err := GetTradeSet(preOrder.Symbol, 1) | ||||
| 	if err != nil || tradeSet.Coin == "" { | ||||
| 		logger.Errorf("获取交易对配置失败, 回调订单号:%s, 错误信息: %v", preOrder.OrderSn, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if tradeSet.Coin == "" { | ||||
| 		logger.Errorf("获取交易对配置失败, 回调订单号:%s", preOrder.OrderSn) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if preOrder.OrderCategory == 3 { | ||||
| 		if err := db.Model(&DbModels.LinePreOrderStatus{}).Where("order_id = ?", preOrder.MainId).Update("add_position_status", 1).Error; err != nil { | ||||
| 			logger.Errorf("更新主单加仓状态失败, 主单号:%s, 错误信息:%v", preOrder.MainId, err) | ||||
| 		} | ||||
|  | ||||
| 		if err := cancelSymbolTakeAndStop(db, preOrder.MainId, preOrder.SymbolType); err != nil { | ||||
| 			logger.Errorf("取消止盈止损订单失败 orderSn:%s err:%v", preOrder.OrderSn, err) | ||||
| 		} | ||||
| 	} | ||||
| 	apiInfo, err := GetApiInfo(preOrder.ApiId) | ||||
| 	if apiInfo.Id == 0 { | ||||
| 		logger.Error("订单回调查询apiuserinfo失败 err:", err) | ||||
| 		return | ||||
| 	} | ||||
| 	if err := db.Model(&DbModels.LinePreOrder{}). | ||||
| 		Where("pid = ? AND order_type > 0 AND status = '0' ", preOrder.Id). | ||||
| 		Find(&orders).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { | ||||
| 		logger.Error("订单回调查询止盈止损单失败:", err) | ||||
| 	if err != nil || apiInfo.Id == 0 { | ||||
| 		logger.Errorf("订单回调查询apiuserinfo失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	futApi := FutRestApi{} | ||||
| 	num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet) | ||||
| 	num = num.Truncate(int32(tradeSet.AmountDigit)) | ||||
| 	// 处理主单加仓 | ||||
| 	if preOrder.OrderCategory == 3 { | ||||
| 		if err := handleMainOrderAddPosition(db, preOrder); err != nil { | ||||
| 			logger.Errorf("处理主单加仓失败, 主单号:%s, 错误信息: %v", preOrder.MainId, err) | ||||
| 			return | ||||
| 		} | ||||
| 	} else { | ||||
| 		// 处理其他主单逻辑 | ||||
| 		if err := cancelPositionOtherOrders(apiInfo, db, preOrder); err != nil { | ||||
| 			logger.Errorf("取消主单相关订单失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// 获取止盈止损订单 | ||||
| 	orders, err := getStopOrders(db, preOrder) | ||||
| 	if err != nil { | ||||
| 		logger.Errorf("查询止盈止损订单失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// 获取和保存持仓数据 | ||||
| 	positionData := savePosition(db, preOrder) | ||||
| 	orderExt := models.LinePreOrderExt{} | ||||
| 	num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit)) | ||||
|  | ||||
| 	// 更新订单数量并处理止盈、止损、减仓 | ||||
| 	for _, order := range orders { | ||||
| 		price := utility.StrToDecimal(order.Price).Truncate(int32(tradeSet.PriceDigit)) | ||||
| 		order.Price = price.String() | ||||
|  | ||||
| 		if order.OrderType == 4 { | ||||
| 			ext := DbModels.LinePreOrderExt{} | ||||
| 			db.Model(&ext).Where("order_id=?", preOrder.Id).Find(&ext) | ||||
| 		// 更新止盈止损订单数量 | ||||
| 		num = updateOrderQuantity(db, order, preOrder, num, tradeSet) | ||||
|  | ||||
| 			if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { | ||||
| 				num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) | ||||
| 				order.Num = num.String() | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if err := db.Model(&order).Update("num", num).Error; err != nil { | ||||
| 			logger.Errorf("修改止盈止损数量失败 订单号:%s err:%v", order.OrderSn, err) | ||||
| 		} | ||||
|  | ||||
| 		logger.Errorf("止盈止损 下单数量:%v", num) | ||||
| 		// 根据订单类型处理 | ||||
| 		switch order.OrderType { | ||||
| 		case 1: // 止盈 | ||||
| 			processFutTakeProfitOrder(db, futApi, order, num) | ||||
| 			//亏损大于0 重新计算比例 | ||||
| 			if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { | ||||
| 				percentag := positionData.TotalLoss.Div(num).Div(price) | ||||
| 				percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) | ||||
| 				order.Rate = percentag.String() | ||||
| 				percentag = percentag.Div(decimal.NewFromInt(100)) | ||||
|  | ||||
| 				if positionData.PositionSide == "LONG" { | ||||
| 					order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() | ||||
| 				} else { | ||||
| 					order.Price = price.Mul(decimal.NewFromInt(1).Sub(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			processFutTakeProfitOrder(db, FutRestApi{}, order, num) | ||||
| 		case 2: // 止损 | ||||
| 			processFutStopLossOrder(db, order, price, num) | ||||
| 		case 4: //减仓 | ||||
| 		case 4: // 减仓 | ||||
| 			processFutReduceOrder(order, price, num) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 处理主单加仓 | ||||
| func handleMainOrderAddPosition(db *gorm.DB, preOrder *models.LinePreOrder) error { | ||||
| 	// 更新加仓状态 | ||||
| 	if err := db.Model(&DbModels.LinePreOrderStatus{}). | ||||
| 		Where("order_id = ?", preOrder.MainId). | ||||
| 		Update("add_position_status", 1).Error; err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// 取消止盈止损订单 | ||||
| 	return cancelSymbolTakeAndStop(db, preOrder.MainId, preOrder.SymbolType) | ||||
| } | ||||
|  | ||||
| // 取消主单相关订单 | ||||
| func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, preOrder *models.LinePreOrder) error { | ||||
| 	mainOrders, err := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	mainIds := []int{} | ||||
| 	for _, mainOrder := range mainOrders { | ||||
| 		removeFutLossAndAddPosition(mainOrder.Id, mainOrder.OrderSn) | ||||
| 		mainIds = append(mainIds, mainOrder.Id) | ||||
| 	} | ||||
|  | ||||
| 	if len(mainIds) > 0 { | ||||
| 		orderSns, err := getOpenOrderSns(db, mainIds) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// 批量取消订单 | ||||
| 		orderArray := utility.SplitSlice(orderSns, 10) | ||||
| 		futApi := FutRestApi{} | ||||
| 		for _, item := range orderArray { | ||||
| 			err := futApi.CancelBatchFutOrder(apiUserInfo, preOrder.Symbol, item) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				logger.Errorf("批量取消订单失败 orderSns:%v", item) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // 获取止盈止损订单 | ||||
| func getStopOrders(db *gorm.DB, preOrder *models.LinePreOrder) ([]models.LinePreOrder, error) { | ||||
| 	var orders []models.LinePreOrder | ||||
| 	if err := db.Model(&DbModels.LinePreOrder{}). | ||||
| 		Where("pid = ? AND order_type > 0 AND status = '0' ", preOrder.Id). | ||||
| 		Find(&orders).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return orders, nil | ||||
| } | ||||
|  | ||||
| // 更新订单数量 | ||||
| func updateOrderQuantity(db *gorm.DB, order models.LinePreOrder, preOrder *models.LinePreOrder, num decimal.Decimal, tradeSet models2.TradeSet) decimal.Decimal { | ||||
| 	// 处理减仓比例 | ||||
| 	if order.OrderType == 4 { | ||||
| 		ext := DbModels.LinePreOrderExt{} | ||||
| 		if err := db.Model(&ext).Where("order_id=?", preOrder.Id).Find(&ext).Error; err != nil { | ||||
| 			logger.Errorf("查询减仓比例失败, 订单号:%s, 错误信息: %v", order.OrderSn, err) | ||||
| 		} | ||||
|  | ||||
| 		// 计算减仓数量 | ||||
| 		if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { | ||||
| 			num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) | ||||
| 			order.Num = num.String() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// 更新订单数量 | ||||
| 	if err := db.Model(&order).Update("num", num).Error; err != nil { | ||||
| 		logger.Errorf("修改止盈止损数量失败 订单号:%s err:%v", order.OrderSn, err) | ||||
| 	} | ||||
|  | ||||
| 	return num | ||||
| } | ||||
|  | ||||
| // 减仓单 | ||||
| func processFutReduceOrder(order DbModels.LinePreOrder, price, num decimal.Decimal) { | ||||
| 	key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE) | ||||
| @ -510,7 +604,8 @@ func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.Line | ||||
| 			logger.Error("合约止盈下单失败,更新状态失败:", order.OrderSn, " err:", err) | ||||
| 		} | ||||
| 	} else { | ||||
| 		if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil { | ||||
| 		if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id). | ||||
| 			Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate}).Error; err != nil { | ||||
| 			logger.Error("更新合约止盈单触发事件 ordersn:", order.OrderSn) | ||||
| 		} | ||||
|  | ||||
|  | ||||
| @ -154,3 +154,8 @@ func GetSymbolTriggerCount(db *gorm.DB, symbol string, apiId, symbolType int) (i | ||||
|  | ||||
| 	return count, nil | ||||
| } | ||||
|  | ||||
| //获取已开仓的 | ||||
| // func GetOpenedOrders(db *gorm.DB, apiId int, exchange, symbol, symbolType, side string) ([]models.LinePreOrder, error) { | ||||
|  | ||||
| // } | ||||
|  | ||||
| @ -83,13 +83,6 @@ func SpotOrderLock(db *gorm.DB, v *dto.PreOrderRedisList, item string, spotApi S | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		//判断是否有已触发交易对 | ||||
| 		count, _ := GetSymbolTriggerCount(db, v.Symbol, v.ApiId, 1) | ||||
|  | ||||
| 		if count > 0 { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		price, _ := decimal.NewFromString(v.Price) | ||||
| 		num, _ := decimal.NewFromString(preOrder.Num) | ||||
| 		params := OrderPlacementService{ | ||||
|  | ||||
| @ -11,7 +11,9 @@ import ( | ||||
| 	"go-admin/common/global" | ||||
| 	"go-admin/common/helper" | ||||
| 	models2 "go-admin/models" | ||||
| 	"go-admin/models/positiondto" | ||||
| 	"go-admin/pkg/utility" | ||||
| 	"go-admin/services/positionservice" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| @ -127,7 +129,8 @@ func handleOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderStatus | ||||
|  | ||||
| 	//主单止损回调 | ||||
| 	case preOrder.OrderType == 2 && orderStatus == 6: | ||||
| 		removeSpotLossAndAddPosition(preOrder) | ||||
| 		removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) | ||||
| 		removePosition(db, preOrder) | ||||
|  | ||||
| 		if err := db.Model(&DbModels.LinePreOrder{}).Where("id =?", preOrder.MainId).Update("status", 9).Error; err != nil { | ||||
| 			logger.Errorf("主单止损回调 订单号:%s 修改主单状态失败:%v", preOrder.OrderSn, err) | ||||
| @ -168,6 +171,8 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	orderExt := models.LinePreOrderExt{} | ||||
| 	positionData := savePosition(db, preOrder) | ||||
| 	orders := make([]models.LinePreOrder, 0) | ||||
| 	rate := utility.StringAsFloat(preOrder.Rate) | ||||
|  | ||||
| @ -177,7 +182,10 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
|  | ||||
| 	// 100%减仓 终止流程 | ||||
| 	if rate >= 100 { | ||||
| 		removeSpotLossAndAddPosition(preOrder) | ||||
| 		//缓存 | ||||
| 		removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) | ||||
| 		removePosition(db, preOrder) | ||||
|  | ||||
| 		ids := []int{preOrder.MainId, preOrder.Pid} | ||||
| 		if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6", ids).Update("status", 9).Error; err != nil { | ||||
| 			logger.Info("100%减仓完毕,终结流程") | ||||
| @ -185,7 +193,10 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	db.Model(&orderExt).Where("order_id =?", preOrder.Pid).Find(&orderExt) | ||||
| 	totalNum := getSpotPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) //getSpotTotalNum(apiUserInfo, preOrder, tradeSet) | ||||
| 	totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) | ||||
| 	price := utility.StrToDecimal(preOrder.Price) | ||||
|  | ||||
| 	if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status=0", preOrder.Id).Find(&orders).Error; err != nil { | ||||
| 		logger.Errorf("获取减仓单止盈止损失败 err:%v", err) | ||||
| @ -195,26 +206,24 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| 	spotApi := SpotRestApi{} | ||||
|  | ||||
| 	for index := range orders { | ||||
| 		orders[index].Num = totalNum.Truncate(int32(tradeSet.AmountDigit)).String() | ||||
| 		orders[index].Num = totalNum.String() | ||||
|  | ||||
| 		if orders[index].OrderType == 1 { | ||||
| 			//亏损大于0 重新计算比例 | ||||
| 			if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { | ||||
| 				percentag := positionData.TotalLoss.Div(totalNum).Div(price) | ||||
| 				percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) | ||||
| 				orders[index].Rate = percentag.String() | ||||
| 				percentag = percentag.Div(decimal.NewFromInt(100)) | ||||
| 				orders[index].Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() | ||||
| 			} | ||||
|  | ||||
| 			processTakeProfitOrder(db, spotApi, orders[index]) | ||||
| 		} else if orders[index].OrderType == 2 { | ||||
| 			processStopLossOrder(orders[index]) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//计算实际亏损 | ||||
| 	// if parentOrder.Price != "" { | ||||
| 	// 	parentPrice := utility.StrToDecimal(parentOrder.Price) | ||||
| 	// 	reduceNum := utility.StrToDecimal(preOrder.Num) | ||||
| 	// 	lossAmountU := price.Sub(parentPrice).Abs().Mul(reduceNum) //.Truncate(int32(tradeSet.PriceDigit)) | ||||
|  | ||||
| 	// 	if err := db.Model(&parentOrder).Update("loss_amount", lossAmountU).Error; err != nil { | ||||
| 	// 		logger.Errorf("修改主单实际亏损失败 订单号:%s err:%v", parentOrder.OrderSn, err) | ||||
| 	// 	} | ||||
| 	// } | ||||
|  | ||||
| 	//加仓待触发 | ||||
| 	addPositionOrder := DbModels.LinePreOrder{} | ||||
|  | ||||
| @ -376,7 +385,8 @@ func handleMainOrderClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	removeSpotLossAndAddPosition(preOrder) | ||||
| 	removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) | ||||
| 	removePosition(db, preOrder) | ||||
|  | ||||
| 	spotApi := SpotRestApi{} | ||||
| 	apiUserInfo, _ := GetApiInfo(preOrder.ApiId) | ||||
| @ -387,14 +397,14 @@ func handleMainOrderClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) | ||||
| 			ApiId:  preOrder.ApiId, | ||||
| 		} | ||||
| 		if err := spotApi.CancelOpenOrders(db, req); err != nil { | ||||
| 			logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) | ||||
| 			logger.Errorf("平仓单成功 取消其它订单失败 订单号:%s:", err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 止盈成交 | ||||
| func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| 	removeSpotLossAndAddPosition(preOrder) | ||||
| 	removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) | ||||
|  | ||||
| 	spotApi := SpotRestApi{} | ||||
| 	apiUserInfo, _ := GetApiInfo(preOrder.ApiId) | ||||
| @ -409,6 +419,8 @@ func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	removePosition(db, preOrder) | ||||
|  | ||||
| 	db.Transaction(func(tx *gorm.DB) error { | ||||
| 		ids := []int{preOrder.Pid, preOrder.MainId} | ||||
| 		if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6 AND order_type=0", ids).Update("status", 9).Error; err != nil { | ||||
| @ -428,7 +440,28 @@ func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
|  | ||||
| } | ||||
|  | ||||
| func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { | ||||
| // 移除仓位信息 | ||||
| func removePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| 	positionMangement := positionservice.BinancePositionManagement{} | ||||
| 	positionMangement.Orm = db | ||||
| 	positionDelReq := positiondto.LinePreOrderPositioinDelReq{ | ||||
| 		ApiId:        preOrder.ApiId, | ||||
| 		SymbolType:   preOrder.SymbolType, | ||||
| 		Symbol:       preOrder.Symbol, | ||||
| 		ExchangeType: preOrder.ExchangeType, | ||||
| 	} | ||||
|  | ||||
| 	if preOrder.Site == "BUY" { | ||||
| 		positionDelReq.Side = "SELL" | ||||
| 	} else { | ||||
| 		positionDelReq.Side = "BUY" | ||||
| 	} | ||||
|  | ||||
| 	positionMangement.RemovePosition(&positionDelReq) | ||||
| } | ||||
|  | ||||
| // 清理待加仓、待止损、待减仓缓存 | ||||
| func removeSpotLossAndAddPosition(mainId int, orderSn string) { | ||||
| 	stoplossKey := fmt.Sprintf(rediskey.SpotStopLossList, global.EXCHANGE_BINANCE) | ||||
| 	stoplossVal, _ := helper.DefaultRedis.GetAllList(stoplossKey) | ||||
| 	addPositionKey := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) | ||||
| @ -442,11 +475,11 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { | ||||
| 	//止损缓存 | ||||
| 	for _, v := range stoplossVal { | ||||
| 		sonic.Unmarshal([]byte(v), &stoploss) | ||||
| 		if stoploss.MainId == preOrder.MainId { | ||||
| 		if stoploss.MainId == mainId { | ||||
| 			_, err := helper.DefaultRedis.LRem(stoplossKey, v) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", preOrder.OrderSn, err) | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", orderSn, err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @ -454,11 +487,11 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { | ||||
| 	//加仓缓存 | ||||
| 	for _, v := range addPositionVal { | ||||
| 		sonic.Unmarshal([]byte(v), &addPosition) | ||||
| 		if addPosition.MainId == preOrder.MainId { | ||||
| 		if addPosition.MainId == mainId { | ||||
| 			_, err := helper.DefaultRedis.LRem(addPositionKey, v) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", preOrder.OrderSn, err) | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", orderSn, err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @ -466,11 +499,11 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { | ||||
| 	//减仓缓存 | ||||
| 	for _, v := range reduceVal { | ||||
| 		sonic.Unmarshal([]byte(v), &reduce) | ||||
| 		if reduce.MainId == preOrder.MainId { | ||||
| 		if reduce.MainId == mainId { | ||||
| 			_, err := helper.DefaultRedis.LRem(reduceKey, v) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", preOrder.OrderSn, err) | ||||
| 				logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", orderSn, err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @ -478,13 +511,31 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { | ||||
|  | ||||
| // 主单成交 | ||||
| func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { | ||||
| 	//修改持仓信息 | ||||
| 	positionData := savePosition(db, preOrder) | ||||
|  | ||||
| 	if preOrder.OrderCategory == 3 { | ||||
| 		if err := db.Model(&DbModels.LinePreOrderStatus{}).Where("order_id = ?", preOrder.MainId).Update("add_position_status", 1).Error; err != nil { | ||||
| 			logger.Errorf("更新主单加仓状态失败, 主单号:%s, 错误信息:%v", preOrder.MainId, err) | ||||
| 		} | ||||
| 	} else { | ||||
| 		mainOrders, _ := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) | ||||
|  | ||||
| 		if len(mainOrders) > 0 { | ||||
| 			for _, mainOrder := range mainOrders { | ||||
| 				removeSpotLossAndAddPosition(mainOrder.Id, mainOrder.OrderSn) | ||||
| 			} | ||||
|  | ||||
| 			spotApi := SpotRestApi{} | ||||
| 			err := spotApi.CancelOpenOrdersLoop(db, CancelOpenOrdersReq{ApiId: preOrder.ApiId, Symbol: preOrder.Symbol}, 4) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				logger.Errorf("取消未成交订单失败, 交易对:%s 主单号:%s, 错误信息:%v", preOrder.Symbol, preOrder.MainId, err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	processTakeProfitAndStopLossOrders(db, preOrder) | ||||
| 	processTakeProfitAndStopLossOrders(db, preOrder, &positionData) | ||||
| } | ||||
|  | ||||
| // 解析订单状态 | ||||
| @ -551,7 +602,8 @@ func updateOrderStatus(db *gorm.DB, preOrder *models.LinePreOrder, status int, r | ||||
|  | ||||
| // 主单成交 处理止盈止损订单 | ||||
| // preOrder 主单 | ||||
| func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder) { | ||||
| // positionData 持仓缓存信息 | ||||
| func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder, positionData *positiondto.PositionDto) { | ||||
| 	orders := []models.LinePreOrder{} | ||||
| 	tradeSet, _ := GetTradeSet(preOrder.Symbol, 0) | ||||
|  | ||||
| @ -581,7 +633,10 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	price := utility.StrToDecimal(preOrder.Price) | ||||
| 	spotApi := SpotRestApi{} | ||||
| 	orderExt := models.LinePreOrderExt{} | ||||
| 	db.Model(&orderExt).Where("order_id =?", preOrder.Id).First(&orderExt) | ||||
|  | ||||
| 	for _, order := range orders { | ||||
| 		order.Num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)).String() | ||||
| @ -601,6 +656,15 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd | ||||
|  | ||||
| 		switch order.OrderType { | ||||
| 		case 1: // 止盈 | ||||
| 			//亏损大于0 重新计算比例 | ||||
| 			if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { | ||||
| 				percentag := positionData.TotalLoss.Div(num).Div(price) | ||||
| 				percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) | ||||
| 				order.Rate = percentag.String() | ||||
| 				percentag = percentag.Div(decimal.NewFromInt(100)) | ||||
| 				order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() | ||||
| 			} | ||||
|  | ||||
| 			processTakeProfitOrder(db, spotApi, order) | ||||
| 		case 2: // 止损 | ||||
| 			processStopLossOrder(order) | ||||
| @ -686,7 +750,8 @@ func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LineP | ||||
| 			logger.Error("现货止盈下单失败,更新状态失败:", order.OrderSn, " err:", err) | ||||
| 		} | ||||
| 	} else { | ||||
| 		if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil { | ||||
| 		if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id). | ||||
| 			Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate}).Error; err != nil { | ||||
| 			logger.Error("更新现货止盈单触发事件 ordersn:", order.OrderSn) | ||||
| 		} | ||||
| 		if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status ='0'", order.Id). | ||||
|  | ||||
							
								
								
									
										120
									
								
								services/positionservice/position_management.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								services/positionservice/position_management.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | ||||
| package positionservice | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"go-admin/common/const/rediskey" | ||||
| 	"go-admin/common/helper" | ||||
| 	"go-admin/common/service" | ||||
| 	"go-admin/models/positiondto" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/bytedance/sonic" | ||||
| 	"github.com/go-admin-team/go-admin-core/logger" | ||||
| 	"github.com/shopspring/decimal" | ||||
| ) | ||||
|  | ||||
| type BinancePositionManagement struct { | ||||
| 	service.Service | ||||
| } | ||||
|  | ||||
| // 保存仓位信息 | ||||
| // return 仓位信息 | ||||
| // return 错误信息 | ||||
| func (e *BinancePositionManagement) SavePosition(data *positiondto.PositionAddReq, exchangeType string) (positiondto.PositionDto, error) { | ||||
| 	var key string | ||||
| 	result := positiondto.PositionDto{} | ||||
|  | ||||
| 	switch data.SymbolType { | ||||
| 	case 1: | ||||
| 		key = fmt.Sprintf(rediskey.SpotPosition, exchangeType, data.ApiId, data.Symbol, data.Side) | ||||
| 	case 2: | ||||
| 		key = fmt.Sprintf(rediskey.FuturePosition, exchangeType, data.ApiId, data.Symbol, data.Side) | ||||
| 	default: | ||||
| 		return result, fmt.Errorf("symbol type error") | ||||
| 	} | ||||
|  | ||||
| 	lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotPositionLock, data.ApiId, data.Symbol, data.Side), 200, 5, 200*time.Millisecond) | ||||
|  | ||||
| 	if ok, err := lock.AcquireWait(context.Background()); err != nil { | ||||
| 		logger.Debug("获取锁失败", err) | ||||
| 		return result, err | ||||
| 	} else if ok { | ||||
| 		defer lock.Release() | ||||
| 		val, _ := helper.DefaultRedis.GetString(key) | ||||
|  | ||||
| 		if val != "" { | ||||
| 			sonic.Unmarshal([]byte(val), &result) | ||||
| 		} | ||||
|  | ||||
| 		if result.Symbol == "" { | ||||
| 			result.Symbol = data.Symbol | ||||
| 			result.Side = data.Side | ||||
| 			result.ApiId = data.ApiId | ||||
| 			result.SymbolType = data.SymbolType | ||||
| 			result.PositionSide = data.PositionSide | ||||
| 		} | ||||
|  | ||||
| 		var totalLoss decimal.Decimal | ||||
|  | ||||
| 		if result.LastPrice.Cmp(decimal.Zero) > 0 { | ||||
| 			switch { | ||||
| 			//多 买入 | ||||
| 			case data.PositionSide == "LONG": | ||||
| 				totalLoss = result.LastPrice.Sub(data.Price).Abs().Mul(result.Quantity) | ||||
|  | ||||
| 			case data.PositionSide == "SHORT": | ||||
| 				totalLoss = data.Price.Sub(result.LastPrice).Abs().Mul(result.Quantity) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		result.LastPrice = data.Price | ||||
| 		result.TotalLoss = result.TotalLoss.Add(totalLoss) | ||||
| 		result.Quantity = data.Quantity.Add(result.Quantity) | ||||
|  | ||||
| 		dataVal, _ := sonic.MarshalString(result) | ||||
| 		if err := helper.DefaultRedis.SetString(key, dataVal); err != nil { | ||||
| 			logger.Errorf("保存仓位信息失败,val:%s err:%v", dataVal, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
| // 获取系统内仓位信息 | ||||
| func (e *BinancePositionManagement) GetPosition(apiId, symbolType int, exchangeTyp, symbol, side string) (positiondto.PositionDto, error) { | ||||
| 	result := positiondto.PositionDto{} | ||||
| 	var key string | ||||
|  | ||||
| 	switch symbolType { | ||||
| 	case 1: | ||||
| 		key = fmt.Sprintf(rediskey.SpotPosition, exchangeTyp, apiId, symbol, side) | ||||
| 	case 2: | ||||
| 		key = fmt.Sprintf(rediskey.FuturePosition, exchangeTyp, apiId, symbol, side) | ||||
| 	default: | ||||
| 		return result, fmt.Errorf("symbol type error") | ||||
| 	} | ||||
|  | ||||
| 	val, _ := helper.DefaultRedis.GetString(key) | ||||
| 	if val != "" { | ||||
| 		sonic.Unmarshal([]byte(val), &result) | ||||
| 	} | ||||
|  | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
| // 移除仓位信息 | ||||
| func (e *BinancePositionManagement) RemovePosition(req *positiondto.LinePreOrderPositioinDelReq) error { | ||||
| 	var key string | ||||
|  | ||||
| 	switch req.SymbolType { | ||||
| 	case 1: | ||||
| 		key = fmt.Sprintf(rediskey.SpotPosition, req.ExchangeType, req.ApiId, req.Symbol, req.Side) | ||||
| 	case 2: | ||||
| 		key = fmt.Sprintf(rediskey.FuturePosition, req.ExchangeType, req.ApiId, req.Symbol, req.Side) | ||||
| 	default: | ||||
| 		return fmt.Errorf("symbol type error") | ||||
| 	} | ||||
|  | ||||
| 	return helper.DefaultRedis.DeleteString(key) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user