package data import ( "context" "github.com/go-xorm/xorm" "github.com/shopspring/decimal" "matchmaking-system/internal/biz/structure" "matchmaking-system/internal/data/tradedeal/virtual" "matchmaking-system/internal/pkg/flags" "matchmaking-system/internal/pkg/logging/applogger" "matchmaking-system/internal/pkg/logging/common" "matchmaking-system/internal/pkg/setting" "matchmaking-system/internal/pkg/utils" "strings" "time" orders "matchmaking-system/internal/data/convert" models "matchmaking-system/internal/pkg/model" ) // GetBotSecondTradeList // // @Description: 秒合约订单列表 // @receiver uo // @param ctx // @param pageSize // @param pageCount // @param userId // @param status // @param state // @return []*models.BotContractSecTrade // @return int64 // @return error func (uo *userOrderRepo) GetBotSecondTradeList(pageSize, pageCount, userId, status int64) ([]*models.BotContractSecTrade, int64, error) { totalCount, err := uo.data.mysqlDB.Table(flags.BotContractSecTrade). Where("user_id = ?", userId). Where("status = ?", status). Count() if err != nil { return nil, 0, flags.ErrMySqlDB } if totalCount == 0 { return nil, 0, nil } var contractList []*models.BotContractSecTrade if err := uo.data.mysqlDB.Table(flags.BotContractSecTrade). Where("user_id = ?", userId). Where("status = ?", status). Limit(int(pageSize), int(pageCount)). Desc(GetOrderByStatusSort(status)). Find(&contractList); err != nil { return nil, 0, flags.ErrMySqlDB } var contractUpdateList []*models.BotContractSecTrade for _, value := range contractList { value.KeepDecimal = GetKeepDecimal(flags.ContractSystemSetUpKey, value.ContractId) contractUpdateList = append(contractUpdateList, value) } return contractUpdateList, totalCount, nil } // CreateSecondBotContractTrade // // @Description: 秒合约下单 // @receiver uo // @param ctx // @param userId // @param order // @return string // @return error func (uo *userOrderRepo) CreateSecondBotContractTrade(ctx context.Context, userId int64, order structure.ContractOrder) (string, error) { // 1、下单订阅 contractId := strings.ToUpper(order.ContractId) _, ok := second.SecondMapSymbol.Load(contractId) if ok { go func() { second.SecondMap <- []byte(contractId) }() } // 2、获取系统设置 system, err := GetContractSystemSetUp(contractId) if err != nil || system == nil { return flags.SetNull, flags.ErrContractOne } secondSystem, err := GetContractSystemSecond() if err != nil { return flags.SetNull, flags.ErrContractOne } order.System = system // 系统设置 order.Proportion = secondSystem // 比例设置 order.StopTime = time.Now().Add(time.Duration(order.Time) * time.Second) // 设置时间 // 3、获取交易币市价 //priceNew, err := GetDigitalCurrencyPrice(ctx, flags.Sd, contractId) //if len(priceNew) == 0 || err != nil { // applogger.Error("%v CreateSecondBotContractTrade.GetDigitalCurrencyPrice:%v", common.ErrSecond, err) // return flags.SetNull, flags.ErrPriceUpdate //} // 4、下单判定 // 下单判定设置(false无设置|true止盈止损) checkBool, stopWinPrice, stopLossPrice, err := ContractVoteStopType(order) if err != nil { applogger.Error("%v CreateSecondBotContractTrade.ContractVoteStopType:%v", common.ErrSecond, err) return flags.SetNull, err } // 下单判定设置(限价|市价) limitOrMarketPrice, err := uo.ContractVoteDealType(order) if err != nil { applogger.Error("%v CreateSecondBotContractTrade.ContractVoteDealType:%v", common.ErrSecond, err) return flags.SetNull, err } // 下单判定设置(买涨|买跌) if err = uo.VoteTradeType(order.TradeType, stopWinPrice, stopLossPrice, limitOrMarketPrice, checkBool); err != nil { applogger.Error("%v CreateSecondBotContractTrade.VoteTradeType:%v", common.ErrSecond, err) return flags.SetNull, err } // 5、下单(订单信息|资产信息) orderId, err := uo.SecondWriteDB(ctx, userId, order) if err != nil || len(orderId) == 0 { applogger.Error("%v CreateSecondBotContractTrade.ContractSecondWriteDB:%v", common.ErrSecond, err) return flags.SetNull, err } // 6、写入缓存列表(挂单缓存|持仓缓存),等待撮合计算 marketStatus, tallyCache, err := virtual.SecondCacheDeal(ctx, userId, orderId, limitOrMarketPrice.String(), order) if err != nil || tallyCache == nil { applogger.Error("%v CreateSecondBotContractTrade.ContractSecondCacheDeal:%v", common.ErrSecond, err) return flags.SetNull, err } // 7、市价下单开仓处理(订单信息|资产信息|手续费|返佣|资产详情记录) if marketStatus == setting.MarketSecondPosition { if err = SecondOpenPosition(ctx, uo.data.mysqlDB, userId, orderId, tallyCache.OpenPrice, order); err != nil { applogger.Error("%v CreateSecondBotContractTrade.ContractSecondOpenPosition:%v", common.ErrSecond, err) return flags.SetNull, err } } // 8、写入(挂单|持仓)缓存列表 if err = virtual.SecondPushAddCache(Reds, marketStatus, tallyCache); err != nil { applogger.Error("%v CreateSecondBotContractTrade.ContractSecondPushAddCache:%v", common.ErrSecond, err) return flags.SetNull, flags.ErrCacheDB } // 9、写入用户订单订阅缓存列表 orderIdKey := virtual.OrderIdListKey(setting.SecondSubscribe, userId) if err = virtual.SecondHashSetOrderId(Reds, orderIdKey, tallyCache); err != nil { applogger.Error("%v CreateSecondBotContractTrade.ContractSecondHashSetOrderId:%v", common.ErrSecond, err) return flags.SetNull, flags.ErrCacheDB } // 10、写入管理员订单订阅列表 if err = virtual.SecondHashSetOrderId(Reds, setting.AdminSecondSubscribe, tallyCache); err != nil { applogger.Error("%v CreateSecondBotContractTrade.ContractSecondHashSetOrderId:%v", common.ErrSecond, err) return flags.SetNull, flags.ErrCacheDB } return orderId, nil } // SecondWriteDB // // @Description: 秒合约下单处理 // @receiver uo // @param ctx // @param userId // @param order // @return string // @return error func (uo *userOrderRepo) SecondWriteDB(ctx context.Context, userId int64, order structure.ContractOrder) (string, error) { session := uo.data.mysqlDB.NewSession() defer session.Close() err := session.Begin() if err != nil { applogger.Error("%v ContractSecondWriteDB.NewSession:%v", common.ErrSecond, err) return flags.SetNull, flags.ErrMySqlDB } // 查询当前下单用户可用账户金额和冻结金额 var usable, frozen decimal.Decimal usable, frozen, _, err = uo.GetBotUserContractSec(session, userId, flags.BasicUnit) if err != nil { applogger.Error("%v ContractSecondWriteDB.GetBotUserContractSec:%v", common.ErrSecond, err) return flags.SetNull, flags.ErrMySqlDB } // 检查用户订单下单资产 if usable.IsZero() || usable.IsNegative() || frozen.IsNegative() { return flags.SetNull, flags.ErrPublicOne } // 写入订单信息 orderId, err := uo.VerifyBotContractSecTradeOrderId(session) if err != nil { applogger.Error("%v ContractSecondWriteDB.VerifyBotContractSecTradeOrderId:%v", common.ErrSecond, err) return flags.SetNull, err } if !flags.CheckSetting { applogger.Debug("下单可用金额:%v", usable) applogger.Debug("下单冻结金额:%v", frozen) applogger.Debug("下单ID:%v", orderId) applogger.Debug("下单杠杆:%v", order.PryNum) } botStockTrade := orders.BotContractSecTrade(ctx, userId, orderId, order) if err = uo.CreatBotContractSecTrade(session, botStockTrade); err != nil { applogger.Error("%v ContractSecondWriteDB.CreatBotContractSecTrade:%v", common.ErrSecond, err) return flags.SetNull, flags.ErrMySqlDB } // 转换下单信息 orderAmount := decimal.RequireFromString(order.OrderAmount) // 订单价格 earnestMoney := decimal.RequireFromString(order.EarnestMoney) // 保证金 if !flags.CheckSetting { applogger.Debug("下单订单价格:%v", orderAmount) applogger.Debug("下单保证金:%v", earnestMoney) } // 总金额下单判定 residue := usable.Sub(earnestMoney).Div(usable) if usable.Cmp(earnestMoney) < 0 || residue.Cmp(utils.DecimalsStrInt()) < 0 { return flags.SetNull, flags.ErrPublicOne } // (买涨|买跌)处理 usableNew := usable.Sub(earnestMoney) // 可用资产 frozenNew := frozen.Add(earnestMoney) // 冻结资产 // 检查用户订单下单资产 if usableNew.IsNegative() || usableNew.IsZero() { return flags.SetNull, flags.ErrPublicTow } if !flags.CheckSetting { applogger.Debug("下单后用户可用资产:%v", usableNew) applogger.Debug("下单后用户冻结资产:%v", frozenNew) } // 更新用户资产信息 userContractUSDT := orders.UpdateBotUserContractSec(ctx, usableNew.String(), frozenNew.String()) if err = uo.UpdateBotUserContractSec(session, userId, flags.BasicUnit, userContractUSDT); err != nil { applogger.Error("%v ContractSecondWriteDB.UpdateBotUserContractSec:%v", common.ErrSecond, err) return flags.SetNull, flags.ErrMySqlDB } if err = session.Commit(); err != nil { applogger.Error("%v ContractSecondWriteDB.Commit:%v", common.ErrSecond, err) return flags.SetNull, flags.ErrMySqlDB } return orderId, nil } // SecondOpenPosition // // @Description: 秒合约开仓处理 // @param ctx // @param db // @param userId // @param orderId // @param openPrice // @param order // @return error func SecondOpenPosition(ctx context.Context, db *xorm.EngineGroup, userId int64, orderId, openPrice string, order structure.ContractOrder) error { session := db.NewSession() defer session.Close() err := session.Begin() if err != nil { applogger.Error("%v ContractSecondOpenPosition.NewSession:%v", common.ErrSecond, err) return flags.ErrMySqlDB } // 查询当前下单用户可用账户金额和冻结金额 var usable, frozen decimal.Decimal usable, frozen, _, err = Uo.GetBotUserContractSec(session, userId, flags.BasicUnit) if err != nil || usable.IsNegative() || frozen.IsNegative() { applogger.Error("%v ContractSecondOpenPosition.GetBotUserContractSec:%v", common.ErrSecond, err) return flags.ErrPublicThree } if !flags.CheckSetting { applogger.Debug("下单可用金额:%v", usable) applogger.Debug("下单冻结金额:%v", frozen) } // 转换下单信息 earnestMoney := decimal.RequireFromString(order.EarnestMoney) // 保证金 if !flags.CheckSetting { applogger.Debug("下单保证金:%v", earnestMoney) } openOrderPrice := decimal.RequireFromString(openPrice) // 开仓价格 usableNew := usable // 可用资产 frozenNew := frozen.Sub(earnestMoney) // 冻结资产 if !flags.CheckSetting { applogger.Debug("开仓订单价格:%v", openOrderPrice) applogger.Debug("下单开仓总金额容差值:%v", earnestMoney) applogger.Debug("下单后用户可用资产:%v", usableNew) applogger.Debug("下单后用户冻结资产:%v", frozenNew) } // 检查用户平仓资产 if frozenNew.IsNegative() { return flags.ErrPublicThree } // 处理资产信息 userContractUSDT := orders.UpdateBotUserContractSec(ctx, usableNew.String(), frozenNew.String()) if err = Uo.UpdateBotUserContractSec(session, userId, flags.BasicUnit, userContractUSDT); err != nil { applogger.Error("%v ContractSecondOpenPosition.UpdateBotUserContractSec:%v", common.ErrSecond, err) return flags.ErrMySqlDB } // 处理订单信息(成交价|仓位|开仓时间|订单总金额|手续费|持仓状态) trade := orders.UpdateOpenBotContractSecTrade(ctx, openPrice, decimal.Zero.String(), earnestMoney.String(), decimal.Zero.String()) if err = Uo.UpdateBotContractSecTradeByOrderId(session, orderId, trade); err != nil { applogger.Error("%v ContractSecondOpenPosition.UpdateBotContractSecTradeByOrderId:%v", common.ErrSecond, err) return flags.ErrMySqlDB } // 更改交易日志记录方式 var list []models.BotUserContractSecLog qData1 := orders.CreatBotUserContractSecLog(ctx, userId, flags.TransferOut, flags.BasicUnit, NegativeValue(earnestMoney.String()), orderId) // 资金变动明细表(开仓保证金) list = append(list, qData1) // 批量写入数据信息 if err = Uo.CreatBotUserContractSecLogList(session, list); err != nil { applogger.Error("%v ContractSecondOpenPosition.CreatBotUserContractSecLogList:%v", common.ErrSecond, err) return flags.ErrMySqlDB } if err = session.Commit(); err != nil { applogger.Error("%v ContractSecondOpenPosition.Commit:%v", common.ErrSecond, err) return flags.ErrMySqlDB } return nil } // SecondClosingPosition // // @Description: 秒合约平仓处理 // @param ctx // @param db // @param orderId // @param price // @param order // @return error func SecondClosingPosition(ctx context.Context, db *xorm.EngineGroup, orderId, closePrice string, order structure.ContractOrder) error { session := db.NewSession() defer session.Close() err := session.Begin() if err != nil { applogger.Error("%v ContractSecondClosingPosition.NewSession:%v", common.ErrSecond, err) return flags.ErrMySqlDB } // 查询订单信息 tread, err := Uo.GetBotContractSecTradeByOrderId(session, orderId, flags.PositionStatus) if err != nil { applogger.Error("%v ContractSecondClosingPosition.GetBotContractSecTradeByOrderId:%v", common.ErrSecond, err) return flags.ErrMySqlDB } var userId int64 var dealPrice, earnestMoney decimal.Decimal var closeTime time.Time if tread != nil { userId = int64(tread.UserId) // 用户Id earnestMoney = decimal.RequireFromString(tread.EarnestMoney) // 开仓保证金 dealPrice = decimal.RequireFromString(tread.DealPrice) // 开仓价格 closeTime = tread.OpenTime.Add(time.Duration(tread.SecondTime) * time.Second) // 平仓时间 } // 查询当前下单用户可用账户金额和冻结金额 var usable, frozen decimal.Decimal usable, frozen, _, err = Uo.GetBotUserContractSec(session, userId, flags.BasicUnit) if err != nil || usable.IsNegative() || frozen.IsNegative() { applogger.Error("%v ContractSecondClosingPosition.GetBotUserContract:%v", common.ErrSecond, err) return flags.ErrContractFive } if !flags.CheckSetting { applogger.Debug("下单开仓用户Id:%v", userId) applogger.Debug("开仓下单开仓价格:%v,平仓价格:%v", dealPrice, closePrice) applogger.Debug("下单可用金额:%v", usable) applogger.Debug("下单冻结金额:%v", frozen) } // 判定准备 var orderStatus int var orderValue string checkBool := decimal.RequireFromString(closePrice).Cmp(dealPrice) // 判定平仓和开仓价格 proValue := order.Proportion[order.Time] // 设定值 proPrice := decimal.RequireFromString(proValue.Value).Div(decimal.RequireFromString(flags.DecimalOne)) // 设置定转化 if !flags.CheckSetting { applogger.Debug("平仓数据展示:%v,%v,%v", proValue, proPrice, earnestMoney.Mul(proPrice)) } // 已经实现盈亏计算公式(已平仓结算方式) // 买涨 = 平仓价格 > 开仓价格(盈利) 平仓价格 < 开仓价格(亏损) 平仓价格 = 开仓价格(不亏不盈) // 买跌 = 平仓价格 < 开仓价格(盈利) 平仓价格 > 开仓价格(亏损) 平仓价格 = 开仓价格(不亏不盈) var usableNew, frozenNew, resultPrice decimal.Decimal switch order.TradeType { case flags.TradeTypeBuy: // 买涨--->{盈利|亏损|不亏不盈} if checkBool == 1 { resultPrice = earnestMoney.Mul(proPrice).Add(earnestMoney) usableNew = usable.Add(resultPrice) orderStatus = flags.OrderSetOne orderValue = resultPrice.String() if !flags.CheckSetting { applogger.Debug("买涨盈利:%v", resultPrice) } } if checkBool == -1 { resultPrice = decimal.Zero usableNew = usable orderStatus = flags.OrderSetTwo orderValue = NegativeValue(earnestMoney.String()) if !flags.CheckSetting { applogger.Debug("买涨亏损:%v", resultPrice) } } if checkBool == 0 { resultPrice = earnestMoney usableNew = usable.Add(earnestMoney) orderStatus = flags.OrderSetZero orderValue = decimal.Zero.String() if !flags.CheckSetting { applogger.Debug("买涨平局:%v", resultPrice) } } case flags.TradeTypeSell: // 买跌--->{盈利|亏损|不亏不盈} if checkBool == -1 { resultPrice = earnestMoney.Mul(proPrice).Add(earnestMoney) usableNew = usable.Add(resultPrice) orderStatus = flags.OrderSetOne orderValue = resultPrice.String() if !flags.CheckSetting { applogger.Debug("买跌盈利:%v", resultPrice) } } if checkBool == 1 { resultPrice = decimal.Zero usableNew = usable orderStatus = flags.OrderSetTwo orderValue = NegativeValue(earnestMoney.String()) if !flags.CheckSetting { applogger.Debug("买跌亏损:%v", resultPrice) } } if checkBool == 0 { resultPrice = earnestMoney usableNew = usable.Add(earnestMoney) orderStatus = flags.OrderSetZero orderValue = decimal.Zero.String() if !flags.CheckSetting { applogger.Debug("买跌平局:%v", resultPrice) } } default: } frozenNew = frozen // 冻结资产 if !flags.CheckSetting { applogger.Debug("可用资产:%v", usableNew) applogger.Debug("冻结资产:%v", frozenNew) } // 平仓(处理资产表(资产)) userContractUSDT := orders.UpdateBotUserContractSec(ctx, usableNew.String(), frozenNew.String()) if err = Uo.UpdateBotUserContractSec(session, userId, flags.BasicUnit, userContractUSDT); err != nil { applogger.Error("%v ContractSecondClosingPosition.UpdateBotUserContractSec:%v", common.ErrSecond, err) return flags.ErrMySqlDB } // 平仓(处理订单信息(平仓价|保证金|平仓|订单总金额|平仓手续费|完成订单)) trade := orders.UpdateCloseBotContractSecTrade(ctx, closePrice, earnestMoney.String(), earnestMoney.String(), decimal.Zero.String(), orderValue, orderStatus, closeTime) if err = Uo.UpdateBotContractSecTradeByOrderId(session, orderId, trade); err != nil { applogger.Error("%v ContractSecondClosingPosition.UpdateBotContractSecTradeByOrderId:%v", common.ErrSecond, err) return flags.ErrMySqlDB } // 盈利-亏损不写入日志 if !resultPrice.IsZero() { // 平仓(资金变动明细表(正负盈亏)) var list []models.BotUserContractSecLog qData4 := orders.CreatBotUserContractSecLog(ctx, userId, flags.ChangeInto, flags.BasicUnit, resultPrice.String(), orderId) list = append(list, qData4) if err = Uo.CreatBotUserContractSecLogList(session, list); err != nil { applogger.Error("%v ContractSecondClosingPosition.CreatBotUserContractSecLogList:%v", common.ErrSecond, err) return flags.ErrMySqlDB } } if err = session.Commit(); err != nil { applogger.Error("%v ContractSecondClosingPosition.Commit:%v", common.ErrSecond, err) return flags.ErrMySqlDB } return nil } // GetBotContractSecTradeByOrderId // // @Description: // @receiver uo // @param ctx // @param session // @param orderId // @param status // @return *models.BotContractSecTrade // @return error func (uo *userOrderRepo) GetBotContractSecTradeByOrderId(session *xorm.Session, orderId string, status int) (*models.BotContractSecTrade, error) { var botContractTrade []models.BotContractSecTrade if err := session.Table(flags.BotContractSecTrade). Where("order_id = ?", orderId). Where("`status` = ?", status). Find(&botContractTrade); err != nil { return nil, err } for _, value := range botContractTrade { return &value, nil } return nil, nil } // GetBotUserContractSec // // @Description: // @receiver uo // @param ctx // @param session // @param userId // @param symbol // @return decimal.Decimal // @return decimal.Decimal // @return int // @return error func (uo *userOrderRepo) GetBotUserContractSec(session *xorm.Session, userId int64, symbol string) (decimal.Decimal, decimal.Decimal, int, error) { var contractUSDT []models.BotUserContractSec if err := session.Table(flags.BotUserContractSec). Where("user_id = ?", userId). Where("contract_id = ?", strings.ToUpper(symbol)). Find(&contractUSDT); err != nil { return decimal.Zero, decimal.Zero, 0, err } var usableNum, frozenNum decimal.Decimal for _, value := range contractUSDT { usableNum = decimal.RequireFromString(value.UsableNum) // 资产可用余额 frozenNum = decimal.RequireFromString(value.FrozenNum) // 资产冻结数量 } return usableNum, frozenNum, len(contractUSDT), nil } // VerifyBotContractSecTradeOrderId // // @Description: // @receiver uo // @param ctx // @param session // @return string // @return error func (uo *userOrderRepo) VerifyBotContractSecTradeOrderId(session *xorm.Session) (string, error) { var orderId string for { orderId = orders.CreateRandCodeOrder(10) var orderList []models.BotContractSecTrade err := session.Table(flags.BotContractSecTrade).Where("order_id = ?", orderId).Find(&orderList) if err != nil || len(orderList) > 0 { applogger.Error("%v VerifyBotContractTradeOrderId.Find:%v", common.ErrSecond, err) continue } break } return orderId, nil } // CreatBotContractSecTrade // // @Description: // @receiver uo // @param ctx // @param session // @param trade // @return error func (uo *userOrderRepo) CreatBotContractSecTrade(session *xorm.Session, trade models.BotContractSecTrade) error { _, err := session.Table(flags.BotContractSecTrade).Insert(&trade) if err != nil { return err } return nil } // UpdateBotUserContractSec // // @Description: // @receiver uo // @param ctx // @param session // @param userId // @param symbol // @param userContract // @return error func (uo *userOrderRepo) UpdateBotUserContractSec(session *xorm.Session, userId int64, symbol string, userContract models.BotUserContractSec) error { _, err := session.Table(flags.BotUserContractSec). Where("user_id = ?", userId). Where("contract_id = ?", symbol). Update(&userContract) if err != nil { return err } return nil } // UpdateBotContractSecTradeByOrderId // // @Description: // @receiver uo // @param ctx // @param session // @param orderId // @param trade // @return error func (uo *userOrderRepo) UpdateBotContractSecTradeByOrderId(session *xorm.Session, orderId string, trade models.BotContractSecTrade) error { _, err := session.Table(flags.BotContractSecTrade). Where("order_id = ?", orderId). Update(&trade) if err != nil { return err } return nil }