package selfContract import ( "encoding/json" "fmt" "github.com/shopspring/decimal" "math/rand" "time" "wss-pool/cmd/common" "wss-pool/cmd/websocketservice" "wss-pool/internal/gzip" red "wss-pool/internal/redis" "wss-pool/logging/applogger" "wss-pool/pkg/model/market" "wss-pool/pkg/model/stock" ) const ( volRand int = 1 //成交张数 随机最大值 maxRand float64 = 1000 //成交笔数 随机最大值 minRand float64 = 0.1 minLeastRand float64 = 1 quantity int = 60 //成交币 tradeNum int = 50 DepthNum int = 80 //深度数据只要 DepthMaxNum int = 300 //深度数据只要 defaultMaxStep float64 = 0.003 defaultMinStep float64 = 0.000001 ) var ( TotalAmount decimal.Decimal // 当前总成交量 TotalTradeTurnover decimal.Decimal //总成交额 TotalVol decimal.Decimal //总成交两张数 TotalCount decimal.Decimal //总成交笔数 oldAsks int64 oldBids int64 ) func ClearTotal() { TotalAmount = decimal.NewFromFloat(0) TotalTradeTurnover = decimal.NewFromFloat(0) TotalVol = decimal.NewFromFloat(0) TotalCount = decimal.NewFromFloat(0) } func CreateDepth(old, closePrice decimal.Decimal) { chStep6 := fmt.Sprintf("market-%s-depth-step6", SelfContractCode) //redisStep6, _ := red.Get_Cache_Data(chStep6) //随机生成单量 rand.New(rand.NewSource(time.Now().UnixNano())) tick := market.CtDepthTick{ Mrid: rand.Int63n(99999999999) + int64(99999), Id: common.TimeToNow(), Ts: common.TimeToNow(), } //卖单,买单 tick.Asks, tick.Bids = DepthStep(closePrice, old) //applogger.Info("asks ", tick.Asks, tick.Bids) CreateTradeDetail(tick) //升序 tick.Asks = insertionSort(tick.Asks) //降序 tick.Bids = quickSort(tick.Bids) res := market.SubscribeCtDepthResponse{ Channel: chStep6, Timestamp: common.TimeToNow(), Tick: &tick, } resByte, _ := json.Marshal(res) resStep6, _ := gzip.DecompressData(resByte) OneDayContractDepth(res) red.RedisClient.Publish(res.Channel, resStep6) } // 卖单 买单 func DepthStep(closePrice, old decimal.Decimal) ([][]decimal.Decimal, [][]decimal.Decimal) { data := make([][]decimal.Decimal, 0) datas := make([][]decimal.Decimal, 0) if old.Equal(closePrice) { return data, datas } amaxRand := float64(0) bmaxRand := float64(0) //跌 if old.GreaterThan(closePrice) { diff := old.Sub(closePrice) amaxRand, _ = diff.Div(old).Mul(decimal.NewFromFloat(maxRand)).Round(2).Float64() bmaxRand = amaxRand / float64(3) } else { diff := closePrice.Sub(old) bmaxRand, _ = diff.Div(old).Mul(decimal.NewFromFloat(maxRand)).Round(2).Float64() amaxRand = bmaxRand / float64(3) } asksMap := make(map[string]int) bidsMap := make(map[string]int) minRands := minRand if closePrice.LessThan(decimal.NewFromInt(int64(1))) { minRands = minLeastRand } for i := 0; i < DepthNum; i++ { item := make([]decimal.Decimal, 0) price := calculateMaxMinContractPrice(closePrice, false) if _, ok := asksMap[price.String()]; ok { continue } item = append(item, price) item = append(item, decimal.NewFromFloat(common.RandFloats(minRands, amaxRand))) data = append(data, item) asksMap[price.String()] = 1 if len(data) >= 20 { break } } for i := 0; i < DepthNum; i++ { item := make([]decimal.Decimal, 0) price := calculateMaxMinContractPrice(closePrice, true) if _, ok := bidsMap[price.String()]; ok { continue } item = append(item, price) item = append(item, decimal.NewFromFloat(common.RandFloats(minRands, bmaxRand))) datas = append(datas, item) bidsMap[price.String()] = 1 if len(datas) >= 20 { break } } return data, datas } // 生成一天聚合深度 func OneDayContractDepth(item market.SubscribeCtDepthResponse) { result := market.SubscribeCtDepthTempResponse{} result.Tick = &market.CtDepthTick{} title := fmt.Sprintf("market-%s-depth-step6", SelfContractCode) rand.New(rand.NewSource(time.Now().UnixNano())) result.Tick.Mrid = rand.Int63n(99999999999) + int64(99999) result.Tick.Id = common.TimeToNow() * 1000 result.Tick.Ts = common.TimeToNow() * 1000 result.Tick.Asks = insertionSort(item.Tick.Asks) result.Tick.Bids = quickSort(item.Tick.Bids) result.Channel = fmt.Sprintf("market.%s.depth.step6", SelfContractCode) resultJsons, _ := json.Marshal(result) red.Set_Cache_Value(title, string(resultJsons)) return //titleInfo := fmt.Sprintf("market-%s-depth-step6-info", SelfContractCode) //redisRes, _ := red.Get_Cache_Data(title) //if common.GetWeeHours() || redisRes == "" { // itemJson, _ := json.Marshal(item) // if err := red.Set_Cache_Value(title, string(itemJson)); err != nil { // applogger.Error(title, err) // } // //if err := red.Set_Cache_Value(titleInfo, string(itemJson)); err != nil { // // applogger.Error(title, err) // //} // return //} //result := market.SubscribeCtDepthTempResponse{} //if err := json.Unmarshal([]byte(redisRes), &result); err != nil { // applogger.Error("json err", err) // return //} //bids := make(map[decimal.Decimal]decimal.Decimal, 0) //asks := make(map[decimal.Decimal]decimal.Decimal, 0) //for _, v := range result.Tick.Bids { // bids[v[0]] = v[1] //} //for _, v := range result.Tick.Asks { // asks[v[0]] = v[1] //} //bidsTemp := make([][]decimal.Decimal, 0) //bidsAbandonMap := make(map[decimal.Decimal]int) //// fmt.Println("bids map" ,bids) //// fmt.Println("bids",result.Tick.Bids) //for _, v := range item.Tick.Bids { // value := make([]decimal.Decimal, 0) // if len(v) <= 0 { // continue // } // if num, ok := bids[v[0]]; !ok { // value = append(value, v[0]) // value = append(value, v[1].Add(num)) // bidsTemp = append(bidsTemp, value) // bidsAbandonMap[v[0]] = 1 // } else { // value = append(value, v[0]) // value = append(value, v[1]) // bidsTemp = append(bidsTemp, value) // } //} ////fmt.Println("bidsAb",bidsAbandonMap) //for key, val := range bids { // if _, ok := bidsAbandonMap[key]; !ok { // value := make([]decimal.Decimal, 0) // value = append(value, key) // value = append(value, val) // bidsTemp = append(bidsTemp, value) // } //} //asksTemp := make([][]decimal.Decimal, 0) //asksAbandonMap := make(map[decimal.Decimal]int) //for _, v := range item.Tick.Asks { // if len(v) <= 0 { // continue // } // value := make([]decimal.Decimal, 0) // if num, ok := asks[v[0]]; ok { // value = append(value, v[0]) // value = append(value, v[1].Add(num)) // asksAbandonMap[v[0]] = 1 //收集起来 // asksTemp = append(asksTemp, value) // } else { // value = append(value, v[0]) // value = append(value, v[1]) // asksTemp = append(asksTemp, value) // } //} //for key, val := range asks { // if _, ok := asksAbandonMap[key]; !ok { // value := make([]decimal.Decimal, 0) // value = append(value, key) // value = append(value, val) // asksTemp = append(asksTemp, value) // } //} //result.Channel = fmt.Sprintf("market.%s.depth.step6-info", SelfContractCode) //result.Tick.Asks = asksTemp //result.Tick.Bids = bidsTemp //result.Tick.Mrid = rand.Int63n(99999999999) + int64(99999) //result.Tick.Id = common.TimeToNow() * 1000 //result.Tick.Ts = common.TimeToNow() * 1000 ////resultJson, _ := json.Marshal(result) //////所有的买卖保存 ////if err := red.Set_Cache_Value(titleInfo, string(resultJson)); err != nil { //// applogger.Error(title, err) ////} ////推给前端数据 //if len(asksTemp) >= DepthNum { // asksTemp = asksTemp[:DepthNum] // //fmt.Println(len(asksTemp)) // //os.Exit(11) //} //if len(bidsTemp) >= DepthNum { // bidsTemp = bidsTemp[:DepthNum] //} ////result := market.SubscribeCtDepthTempResponse{} //result.Tick.Asks = insertionSort(asksTemp) //result.Tick.Bids = quickSort(bidsTemp) //result.Channel = fmt.Sprintf("market.%s.depth.step6", SelfContractCode) //resultJsons, _ := json.Marshal(result) //red.Set_Cache_Value(title, string(resultJsons)) //applogger.Info("OneDayContractDepth ", string(resultJsons)) } // 生成一天聚合行情 func OneDayDetailMerged(param market.SubscribeCtKlineResponse) { title := fmt.Sprintf("market-%s-detail-merged", SelfContractCode) chStep6 := fmt.Sprintf("market-%s-depth-step6", SelfContractCode) redisStep6, _ := red.Get_Cache_Data(chStep6) resultStep6 := market.SubscribeCtDepthResponse{} if err := json.Unmarshal([]byte(redisStep6), &resultStep6); err != nil { applogger.Error("OneDayContractDepth json err", err) return } result := market.SubscribeCtDetailResponse{} tick := &market.CtDetailTick{} result.Channel = title rand.Seed(time.Now().UnixNano()) var totalPriceAsks decimal.Decimal var totalNumAsks decimal.Decimal for _, v := range resultStep6.Tick.Asks { if len(v) <= 0 { continue } totalPriceAsks = totalPriceAsks.Add(v[0]) totalNumAsks = totalNumAsks.Add(v[1]) } if len(resultStep6.Tick.Asks) > 0 { value := make([]decimal.Decimal, 0) value = append(value, totalPriceAsks.Div(decimal.NewFromInt(int64(len(resultStep6.Tick.Asks)))).Round(digits)) value = append(value, totalNumAsks) tick.Asks = value } bidsTemp := make([][]decimal.Decimal, 0) var totalPriceBids decimal.Decimal var totalNumBids decimal.Decimal for _, v := range resultStep6.Tick.Bids { if len(v) <= 0 { continue } totalPriceBids = totalPriceBids.Add(v[0]) totalNumBids = totalNumBids.Add(v[1]) } if len(resultStep6.Tick.Bids) > 0 { value := make([]decimal.Decimal, 0) value = append(value, totalPriceBids.Div(decimal.NewFromInt(int64(len(resultStep6.Tick.Bids)))).Round(digits)) value = append(value, totalNumBids) bidsTemp = append(bidsTemp, value) tick.Bids = value } tick.Mrid = rand.Int63n(99999999999) + int64(99999) tick.Id = common.TimeToNow() tick.TradeTurnover = param.Tick.Rrade_Turnover tick.Count = param.Tick.Count.IntPart() tick.High = param.Tick.High tick.Open = param.Tick.Open tick.Vol = param.Tick.Vol tick.Close = param.Tick.Close tick.Low = param.Tick.Low tick.Amount = param.Tick.Amount result.Tick = tick result.Timestamp = common.TimeToNow() result.Channel = fmt.Sprintf("market.%s.detail.merged", SelfContractCode) resultJson, _ := json.Marshal(result) if err := red.Set_Cache_Value(title, string(resultJson)); err != nil { applogger.Error(title, err) } //applogger.Info("OneDayContractDepth ", string(resultJson)) } // 生成详情 func CreateTradeDetail(tick market.CtDepthTick) { res := market.SubscribeCtTradeDetailResponse{} res.Tick = &market.CtTradeDetailTick{} res.Tick.Data = make([]market.TradeDetail, 0) TotalCount = decimal.NewFromInt(0) //最近交易接口 tradeDetailAPI := make([]stock.MarketTrade, 0) for key, v := range tick.Asks { if key >= 1 { break } trade := v[1].Mul(FaceValue).Mul(v[0]) //每一笔成交张数 * 合约面值 * 成交价格 item := market.TradeDetail{ Amount: v[1], Ts: common.TimeToNow() * 1000, Id: common.TimeToNow() * 1000, Price: v[0], Direction: "sell", Quantity: trade.Div(v[0]).Round(digits), //成交币 TradeTurnover: trade, } //总成交额 //TotalTradeTurnover = TotalTradeTurnover.Add(trade) //总成交量 //TotalAmount = TotalAmount.Add(item.Quantity) //成交张数 //TotalVol = TotalVol.Add(v[1]) res.Tick.Data = append(res.Tick.Data, item) apiItem := stock.MarketTrade{ ID: common.TimeToNow() * 1000, OrderNumber: item.Amount.String(), DealPrice: item.Price.String(), OrderTime: common.TimeToNow() * 1000, TradeType: 2, TradeTurnover: trade, } tradeDetailAPI = append(tradeDetailAPI, apiItem) } res.Channel = fmt.Sprintf("market.%s.trade.detail", SelfContractCode) res.Timestamp = common.TimeToNow() * 1000 if len(res.Tick.Data) > 0 { // fmt.Println(res.Tick.Data) jsonMessage, _ := json.Marshal(websocketservice.Message{ ServersId: res.Channel, Content: res, Symbol: res.Channel}) //applogger.Info("CreateTradeDetail sell:", string(jsonMessage)) red.RedisClient.Publish(res.Channel, string(jsonMessage)) } //总成交笔数 //TotalCount = TotalCount.Add(decimal.NewFromInt(int64(len(tick.Asks)))) res = market.SubscribeCtTradeDetailResponse{} res.Tick = &market.CtTradeDetailTick{} res.Tick.Data = make([]market.TradeDetail, 0) res.Channel = fmt.Sprintf("market.%s.trade.detail", SelfContractCode) for key, v := range tick.Bids { if key >= 1 { break } trade := v[1].Mul(FaceValue).Mul(v[0]) //每一笔成交张数 * 合约面值 * 成交价格 item := market.TradeDetail{ Amount: v[1], //张数 Ts: common.TimeToNow() * 1000, Id: common.TimeToNow() * 1000, Price: v[0], Direction: "buy", Quantity: trade.Div(v[0]).Round(digits), //成交币 TradeTurnover: trade, } //总成交额 TotalTradeTurnover = TotalTradeTurnover.Add(trade) //总成交量 TotalAmount = TotalAmount.Add(item.Quantity) //成交张数 TotalVol = TotalVol.Add(v[1]) res.Tick.Data = append(res.Tick.Data, item) apiItem := stock.MarketTrade{ ID: common.TimeToNow() * 1000, OrderNumber: item.Amount.String(), DealPrice: item.Price.String(), OrderTime: common.TimeToNow() * 1000, TradeType: 1, TradeTurnover: trade, } tradeDetailAPI = append(tradeDetailAPI, apiItem) } res.Timestamp = common.TimeToNow() * 1000 //总成交笔数 TotalCount = TotalCount.Add(decimal.NewFromInt(int64(len(tick.Bids)))) if len(res.Tick.Data) > 0 { //fmt.Println(res.Tick.Data) jsonMessage, _ := json.Marshal(websocketservice.Message{ ServersId: res.Channel, Content: res, Symbol: res.Channel}) //applogger.Info("CreateTradeDetail buy:", string(jsonMessage)) go func(channel string, jsonMessage []byte) { time.Sleep(1 * time.Second) red.RedisClient.Publish(channel, string(jsonMessage)) }(res.Channel, jsonMessage) } //缓存 title := fmt.Sprintf("market-%s-trade-detail", SelfContractCode) var resultJson []byte //if len(tradeDetailAPI) < tradeNum { // item, _ := red.Get_Cache_Data(title) // //itemJson, _ := json.Marshal(item) // res := make([]stock.MarketTrade, 0) // json.Unmarshal([]byte(item), &res) // l := len(res) // res = append(res, tradeDetailAPI...) // if l > 0 { // res = res[l:] // } // resultJson, _ = json.Marshal(res) //} else { resultJson, _ = json.Marshal(tradeDetailAPI) // } if err := red.Set_Cache_Value(title, string(resultJson)); err != nil { applogger.Error(title, err) } // os.Exit(11111) // // applogger.Info(string(resultJson)) } func calculateMaxMinContractPrice(basePrice decimal.Decimal, isNegative bool) decimal.Decimal { rand.New(rand.NewSource(time.Now().UnixNano())) max := basePrice.Mul(decimal.NewFromFloat(rand.Float64()*defaultMinStep + rand.Float64()*(defaultMaxStep-defaultMinStep))).Round(digits) // fmt.Println(max) if isNegative { return basePrice.Sub(max).Round(digits) } return basePrice.Add(max).Round(digits) } // 买单不能高于当前价,卖但不能低于当前价 func randDepth(max, min int, closePrice, old decimal.Decimal) ([][]decimal.Decimal, [][]decimal.Decimal) { data := make([][]decimal.Decimal, 0) datas := make([][]decimal.Decimal, 0) //item := make([]decimal.Decimal, 0) //item = append(item, calculateMaxMinContractPrice(closePrice, false)) //item = append(item, decimal.NewFromInt(int64(max))) //data = append(data, item) //item = make([]decimal.Decimal, 0) //item = append(item, calculateMaxMinContractPrice(closePrice, true)) //item = append(item, decimal.NewFromInt(int64(min))) //datas = append(datas, item) //} //if old.GreaterThan(closePrice) { // //跌 // return data, datas //} //dataTemp := make([][]decimal.Decimal, 0) //datasTemp := make([][]decimal.Decimal, 0) //item = make([]decimal.Decimal, 0) //item = append(item, data[0][0]) //item = append(item, datas[0][1]) //dataTemp = append(dataTemp, item) //item = make([]decimal.Decimal, 0) //item = append(item, datas[0][0]) //item = append(item, data[0][1]) //datasTemp = append(datasTemp, item) return data, datas } // 升序 func insertionSort(nums [][]decimal.Decimal) [][]decimal.Decimal { n := len(nums) for i := 0; i < n-1; i++ { for j := 0; j < n-i-1; j++ { if nums[j][0].GreaterThan(nums[j+1][0]) { nums[j], nums[j+1] = nums[j+1], nums[j] } } } return nums } // 降序 func quickSort(nums [][]decimal.Decimal) [][]decimal.Decimal { n := len(nums) for i := 0; i < n-1; i++ { minIdx := i for j := i + 1; j < n; j++ { if nums[j][0].GreaterThan(nums[minIdx][0]) { minIdx = j } } nums[i], nums[minIdx] = nums[minIdx], nums[i] } return nums }