|
| 1 | +package service |
| 2 | + |
| 3 | +import ( |
| 4 | + "binance-proxy/tool" |
| 5 | + "context" |
| 6 | + "sync" |
| 7 | + |
| 8 | + log "github.com/sirupsen/logrus" |
| 9 | + |
| 10 | + spot "github.com/adshao/go-binance/v2" |
| 11 | +) |
| 12 | + |
| 13 | +type TickerSrv struct { |
| 14 | + rw sync.RWMutex |
| 15 | + |
| 16 | + ctx context.Context |
| 17 | + cancel context.CancelFunc |
| 18 | + |
| 19 | + initCtx context.Context |
| 20 | + initDone context.CancelFunc |
| 21 | + |
| 22 | + si *symbolInterval |
| 23 | + ticker24hr *Ticker24hr |
| 24 | + bookTicker *BookTicker |
| 25 | +} |
| 26 | + |
| 27 | +type BookTicker struct { |
| 28 | + Symbol string `json:"symbol"` |
| 29 | + BidPrice string `json:"bidPrice"` |
| 30 | + BidQuantity string `json:"bidQty"` |
| 31 | + AskPrice string `json:"askPrice"` |
| 32 | + AskQuantity string `json:"askQty"` |
| 33 | +} |
| 34 | + |
| 35 | +type Ticker24hr struct { |
| 36 | + Symbol string `json:"symbol"` |
| 37 | + PriceChange string `json:"priceChange"` |
| 38 | + PriceChangePercent string `json:"priceChangePercent"` |
| 39 | + WeightedAvgPrice string `json:"weightedAvgPrice"` |
| 40 | + PrevClosePrice string `json:"prevClosePrice"` |
| 41 | + LastPrice string `json:"lastPrice"` |
| 42 | + LastQty string `json:"lastQty"` |
| 43 | + BidPrice string `json:"bidPrice"` |
| 44 | + AskPrice string `json:"askPrice"` |
| 45 | + OpenPrice string `json:"openPrice"` |
| 46 | + HighPrice string `json:"highPrice"` |
| 47 | + LowPrice string `json:"lowPrice"` |
| 48 | + Volume string `json:"volume"` |
| 49 | + QuoteVolume string `json:"quoteVolume"` |
| 50 | + OpenTime int64 `json:"openTime"` |
| 51 | + CloseTime int64 `json:"closeTime"` |
| 52 | + FirstID int64 `json:"firstId"` |
| 53 | + LastID int64 `json:"lastId"` |
| 54 | + Count int64 `json:"count"` |
| 55 | +} |
| 56 | + |
| 57 | +func NewTickerSrv(ctx context.Context, si *symbolInterval) *TickerSrv { |
| 58 | + s := &TickerSrv{si: si} |
| 59 | + s.ctx, s.cancel = context.WithCancel(ctx) |
| 60 | + s.initCtx, s.initDone = context.WithCancel(context.Background()) |
| 61 | + |
| 62 | + return s |
| 63 | +} |
| 64 | + |
| 65 | +func (s *TickerSrv) Start() { |
| 66 | + go func() { |
| 67 | + for d := tool.NewDelayIterator(); ; d.Delay() { |
| 68 | + s.rw.Lock() |
| 69 | + s.ticker24hr = nil |
| 70 | + s.bookTicker = nil |
| 71 | + s.rw.Unlock() |
| 72 | + |
| 73 | + ticker24hrDoneC, ticker24hrstopC, err := s.connectTicker24hr() |
| 74 | + if err != nil { |
| 75 | + log.Errorf("%s.Websocket 24hr ticker connect error!Error:%s", s.si, err) |
| 76 | + continue |
| 77 | + } |
| 78 | + |
| 79 | + bookDoneC, bookStopC, err := s.connectTickerBook() |
| 80 | + if err != nil { |
| 81 | + bookStopC <- struct{}{} |
| 82 | + log.Errorf("%s.Websocket book ticker connect error!Error:%s", s.si, err) |
| 83 | + continue |
| 84 | + } |
| 85 | + |
| 86 | + log.Debugf("%s.Websocket ticker connect success!", s.si) |
| 87 | + select { |
| 88 | + case <-s.ctx.Done(): |
| 89 | + bookStopC <- struct{}{} |
| 90 | + ticker24hrstopC <- struct{}{} |
| 91 | + return |
| 92 | + case <-bookDoneC: |
| 93 | + ticker24hrstopC <- struct{}{} |
| 94 | + case <-ticker24hrDoneC: |
| 95 | + bookStopC <- struct{}{} |
| 96 | + } |
| 97 | + |
| 98 | + log.Debugf("%s.Websocket book ticker or ticker 24hr disconnected!Reconnecting", s.si) |
| 99 | + } |
| 100 | + }() |
| 101 | +} |
| 102 | + |
| 103 | +func (s *TickerSrv) Stop() { |
| 104 | + s.cancel() |
| 105 | +} |
| 106 | + |
| 107 | +func (s *TickerSrv) connectTickerBook() (doneC, stopC chan struct{}, err error) { |
| 108 | + return spot.WsBookTickerServe(s.si.Symbol, s.wsHandlerBookTicker, s.errHandler) |
| 109 | +} |
| 110 | + |
| 111 | +func (s *TickerSrv) connectTicker24hr() (doneC, stopC chan struct{}, err error) { |
| 112 | + return spot.WsMarketStatServe(s.si.Symbol, s.wsHandlerTicker24hr, s.errHandler) |
| 113 | +} |
| 114 | + |
| 115 | +func (s *TickerSrv) GetTicker() *Ticker24hr { |
| 116 | + <-s.initCtx.Done() |
| 117 | + s.rw.RLock() |
| 118 | + defer s.rw.RUnlock() |
| 119 | + |
| 120 | + bidPrice := s.ticker24hr.BidPrice |
| 121 | + askPrice := s.ticker24hr.AskPrice |
| 122 | + if s.bookTicker != nil { |
| 123 | + bidPrice = s.bookTicker.BidPrice |
| 124 | + askPrice = s.bookTicker.AskPrice |
| 125 | + } |
| 126 | + |
| 127 | + return &Ticker24hr{ |
| 128 | + Symbol: s.ticker24hr.Symbol, |
| 129 | + PriceChange: s.ticker24hr.PriceChange, |
| 130 | + PriceChangePercent: s.ticker24hr.PriceChangePercent, |
| 131 | + WeightedAvgPrice: s.ticker24hr.WeightedAvgPrice, |
| 132 | + PrevClosePrice: s.ticker24hr.PrevClosePrice, |
| 133 | + LastPrice: s.ticker24hr.LastPrice, |
| 134 | + LastQty: s.ticker24hr.LastQty, |
| 135 | + BidPrice: bidPrice, |
| 136 | + AskPrice: askPrice, |
| 137 | + OpenPrice: s.ticker24hr.OpenPrice, |
| 138 | + HighPrice: s.ticker24hr.HighPrice, |
| 139 | + LowPrice: s.ticker24hr.LowPrice, |
| 140 | + Volume: s.ticker24hr.Volume, |
| 141 | + QuoteVolume: s.ticker24hr.QuoteVolume, |
| 142 | + OpenTime: s.ticker24hr.OpenTime, |
| 143 | + CloseTime: s.ticker24hr.CloseTime, |
| 144 | + FirstID: s.ticker24hr.FirstID, |
| 145 | + LastID: s.ticker24hr.LastID, |
| 146 | + Count: s.ticker24hr.Count, |
| 147 | + } |
| 148 | +} |
| 149 | + |
| 150 | +func (s *TickerSrv) wsHandlerBookTicker(event *spot.WsBookTickerEvent) { |
| 151 | + s.rw.Lock() |
| 152 | + defer s.rw.Unlock() |
| 153 | + |
| 154 | + s.bookTicker = &BookTicker{ |
| 155 | + Symbol: event.Symbol, |
| 156 | + BidPrice: event.BestBidPrice, |
| 157 | + BidQuantity: event.BestBidQty, |
| 158 | + AskPrice: event.BestAskPrice, |
| 159 | + AskQuantity: event.BestAskQty, |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +func (s *TickerSrv) wsHandlerTicker24hr(event *spot.WsMarketStatEvent) { |
| 164 | + s.rw.Lock() |
| 165 | + defer s.rw.Unlock() |
| 166 | + |
| 167 | + if s.ticker24hr == nil { |
| 168 | + defer s.initDone() |
| 169 | + } |
| 170 | + |
| 171 | + s.ticker24hr = &Ticker24hr{ |
| 172 | + Symbol: event.Symbol, |
| 173 | + PriceChange: event.PriceChange, |
| 174 | + PriceChangePercent: event.PriceChangePercent, |
| 175 | + WeightedAvgPrice: event.WeightedAvgPrice, |
| 176 | + PrevClosePrice: event.PrevClosePrice, |
| 177 | + LastPrice: event.LastPrice, |
| 178 | + LastQty: event.CloseQty, |
| 179 | + BidPrice: event.BidPrice, |
| 180 | + AskPrice: event.AskPrice, |
| 181 | + OpenPrice: event.OpenPrice, |
| 182 | + HighPrice: event.HighPrice, |
| 183 | + LowPrice: event.LowPrice, |
| 184 | + Volume: event.BaseVolume, |
| 185 | + QuoteVolume: event.QuoteVolume, |
| 186 | + OpenTime: event.OpenTime, |
| 187 | + CloseTime: event.CloseTime, |
| 188 | + FirstID: event.FirstID, |
| 189 | + LastID: event.LastID, |
| 190 | + Count: event.Count, |
| 191 | + } |
| 192 | +} |
| 193 | + |
| 194 | +func (s *TickerSrv) errHandler(err error) { |
| 195 | + log.Errorf("%s.Ticker websocket throw error!Error:%s", s.si, err) |
| 196 | +} |
0 commit comments