You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
301 lines
8.8 KiB
301 lines
8.8 KiB
2 months ago
|
package selfMarketSpot
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"github.com/shopspring/decimal"
|
||
|
"math/rand"
|
||
|
"time"
|
||
|
"wss-pool/cmd/common"
|
||
|
"wss-pool/cmd/websocketservice"
|
||
|
red "wss-pool/internal/redis"
|
||
|
"wss-pool/logging/applogger"
|
||
|
"wss-pool/pkg/model/market"
|
||
|
"wss-pool/pkg/model/stock"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
maxRand float64 = 2000 //成交笔数 随机最大值
|
||
|
minRand float64 = 0.1
|
||
|
minLeastRand float64 = 1
|
||
|
DepthNum int = 80 //深度数据只要
|
||
|
DepthMaxNum int = 300 //深度数据只要
|
||
|
defaultMaxStep float64 = 0.003
|
||
|
defaultMinStep float64 = 0.000001
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
TotalAmount decimal.Decimal // 当前总成交量
|
||
|
TotalCount decimal.Decimal //总成交笔数
|
||
|
oldAsks int64
|
||
|
oldBids int64
|
||
|
)
|
||
|
|
||
|
func ClearTotal() {
|
||
|
TotalAmount = decimal.NewFromFloat(0)
|
||
|
TotalCount = decimal.NewFromFloat(0)
|
||
|
}
|
||
|
|
||
|
func CreateDepth(old, closePrice decimal.Decimal) {
|
||
|
chStep0 := fmt.Sprintf("market-%s-depth-step0", SelfSymbol)
|
||
|
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: chStep0,
|
||
|
Timestamp: common.TimeToNow(),
|
||
|
Tick: &tick,
|
||
|
}
|
||
|
jsonMessage, _ := json.Marshal(websocketservice.Message{
|
||
|
ServersId: res.Channel,
|
||
|
Content: res,
|
||
|
Symbol: res.Channel})
|
||
|
OneDayContractDepth(res)
|
||
|
red.RedisClient.Publish(res.Channel, string(jsonMessage))
|
||
|
|
||
|
}
|
||
|
|
||
|
// 卖单 买单
|
||
|
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-step0", SelfSymbol)
|
||
|
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.step0", SelfSymbol)
|
||
|
resultJsons, _ := json.Marshal(result)
|
||
|
red.Set_Cache_Value(title, string(resultJsons))
|
||
|
}
|
||
|
|
||
|
// TODO: 生成 聚合行情/市场概要
|
||
|
func OneDayDetailMerged(param market.SubscribeCandlestickResponse) {
|
||
|
result := market.TickerWebsocketResponse{}
|
||
|
tick := &market.TickR{}
|
||
|
tick.Count = int(param.Tick.Count)
|
||
|
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.ticker", SelfSymbol)
|
||
|
jsonMessage, _ := json.Marshal(websocketservice.Message{
|
||
|
ServersId: result.Channel,
|
||
|
Content: result,
|
||
|
Symbol: result.Channel})
|
||
|
red.RedisClient.Publish(result.Channel, string(jsonMessage))
|
||
|
res := market.TickerWebsocketResponses{}
|
||
|
title := fmt.Sprintf("market-%s-detail-merged", SelfSymbol)
|
||
|
res.Tick = tick
|
||
|
res.Channel = title
|
||
|
jsonMessages, _ := json.Marshal(res)
|
||
|
if err := red.Set_Cache_Value(title, string(jsonMessages)); err != nil {
|
||
|
applogger.Error(title, err)
|
||
|
}
|
||
|
//applogger.Info("subscribeTicker data,ServersId:%v,Sender:%v,Content:%v-%v", result.Channel, result.Tick, result.Data)
|
||
|
//applogger.Info("OneDayContractDepth ", string(resultJson))
|
||
|
}
|
||
|
|
||
|
// 生成详情
|
||
|
func CreateTradeDetail(tick market.CtDepthTick) {
|
||
|
res := market.SubscribeTradeResponse{}
|
||
|
res.Tick = &market.TickTrade{}
|
||
|
res.Tick.Data = make([]market.Trade, 0)
|
||
|
TotalCount = decimal.NewFromInt(0)
|
||
|
//最近交易接口
|
||
|
tradeDetailAPI := make([]stock.MarketTrade, 0)
|
||
|
for key, v := range tick.Asks {
|
||
|
if key >= 1 {
|
||
|
break
|
||
|
}
|
||
|
item := market.Trade{
|
||
|
Amount: v[1],
|
||
|
Timestamp: common.TimeToNow() * 1000,
|
||
|
TradeId: common.TimeToNow() * 1000,
|
||
|
Price: v[0],
|
||
|
Direction: "sell",
|
||
|
}
|
||
|
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,
|
||
|
}
|
||
|
tradeDetailAPI = append(tradeDetailAPI, apiItem)
|
||
|
}
|
||
|
res.Channel = fmt.Sprintf("market.%s.trade.detail", SelfSymbol)
|
||
|
res.Timestamp = common.TimeToNow() * 1000
|
||
|
if len(res.Tick.Data) > 0 {
|
||
|
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))
|
||
|
}
|
||
|
|
||
|
res = market.SubscribeTradeResponse{}
|
||
|
res.Tick = &market.TickTrade{}
|
||
|
res.Tick.Data = make([]market.Trade, 0)
|
||
|
res.Channel = fmt.Sprintf("market.%s.trade.detail", SelfSymbol)
|
||
|
for key, v := range tick.Bids {
|
||
|
if key >= 1 {
|
||
|
break
|
||
|
}
|
||
|
item := market.Trade{
|
||
|
Amount: v[1], //CHEN
|
||
|
Timestamp: common.TimeToNow() * 1000,
|
||
|
TradeId: common.TimeToNow() * 1000,
|
||
|
Price: v[0],
|
||
|
Direction: "buy",
|
||
|
}
|
||
|
//总成交量
|
||
|
TotalAmount = TotalAmount.Add(item.Amount)
|
||
|
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,
|
||
|
}
|
||
|
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", SelfSymbol)
|
||
|
var resultJson []byte
|
||
|
resultJson, _ = json.Marshal(tradeDetailAPI)
|
||
|
if err := red.Set_Cache_Value(title, string(resultJson)); err != nil {
|
||
|
applogger.Error(title, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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 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
|
||
|
}
|