// main.go package main import ( "compress/gzip" "encoding/json" "errors" "flag" "fmt" "io" "log" "net/http" _ "net/http/pprof" "os" "runtime" "strconv" "strings" "time" "tickserver/client" "tickserver/markinfo" "tickserver/server/market" ) const ( K_STYLE = iota T_STYLE ) type Options struct { Start int `json:"start"` End int `json:"end"` Total int `json:"total_size"` } type KCandle struct { X []int `json:"x"` Y [][5]float32 `json:"y"` } type KVolume struct { X []int `json:"x"` Y []float32 `json:"y"` } type KData struct { C string `json:"c"` P string `json:"p"` Action string `json:"action"` End int `json:"end"` //KOptions Options `json:"options"` Candles KCandle `json:"main"` Volumes KVolume `json:"volumes|||__ignore__"` } type TData struct { End int `json:"end"` Candles []client.Candle `json:"data"` } var saddr1 = flag.String("s1", "127.0.0.1:19528", "tick server address 1") //115.231.103.7 var saddr2 = flag.String("s2", "127.0.0.1:19528", "tick server address 2") //127.0.0.1 var saddr3 = flag.String("s3", "127.0.0.1:19529", "tick server address 3") //115.236.75.194 var saddr4 = flag.String("s4", "127.0.0.1:9090", "tick server address 4") //19528 9090 var clientDown *client.ClientSimple //var clientDownTmp *client.ClientSimple type Conf struct { Saddr1 string // Saddr2 string // Saddr3 string // Saddr4 string } func readConf() (*Conf, error) { f, err := os.Open("webproxys.json") if err != nil { return nil, err } defer f.Close() dec := json.NewDecoder(f) conf := &Conf{} err = dec.Decode(conf) if err != nil { return nil, err } return conf, nil } func connectServer() (err error) { clientDown, err = client.NewClientSimple(*saddr1, *saddr2, *saddr3, *saddr4, "./tmp") if err != nil { //log.Println("new client", err) return err } //clientDownTmp, err = client.NewClientSimple("114.215.207.24:19528", "114.215.207.24:19528", "114.215.207.24:19529", "114.215.207.24:9090", "./tmp") //if err != nil { //log.Println("new client", err) //return err //} return nil } func changeCandle(candle, candle2, candle3 client.Candle) client.Candle { newCandle := candle if newCandle.High >= newCandle.Low*1.05 || newCandle.High >= candle2.Low*1.05 || newCandle.High >= candle3.Low*1.05 { //log.Println(newCandle) var low float64 if newCandle.High >= newCandle.Low*1.05 { low = newCandle.Low } if newCandle.High >= candle2.Low*1.05 { low = candle2.Low } if newCandle.High >= candle3.Low*1.05 { low = candle3.Low } if newCandle.Open > newCandle.Close { newCandle.Open = low * 1.003 newCandle.Close = low * 1.002 } else { newCandle.Open = low * 1.002 newCandle.Close = low * 1.003 } newCandle.High = low * 1.005 if newCandle.Low != low { newCandle.Low = low * 1.001 } } return newCandle } func getData(symbol, period, action, count, out, cb, te, ts string, style int) (string, error) { iCount, err := strconv.Atoi(count) if action != "new" && err != nil { return "", errors.New("webserver doesn't support count") } if action == "new" { iCount = 1000 } periodId, ok := market.PeriodIdMap[period] if !ok { return "", errors.New("webserver doesn't support period") } periodIdOld := periodId if periodId == market.M15 || periodId == market.M30 { periodId = market.M5 if periodIdOld == market.M15 { iCount *= 3 } if periodIdOld == market.M30 { iCount *= 6 } } if periodId == market.H2 || periodId == market.H4 { periodId = market.H1 if periodIdOld == market.H2 { iCount *= 2 } if periodIdOld == market.H4 { iCount *= 4 } } if periodId == market.W1 || periodId == market.MN1 { periodId = market.D1 if periodIdOld == market.W1 { iCount *= 7 } if periodIdOld == market.MN1 { iCount *= 31 } } iCount++ //startTime := time.Now().UnixNano() //log.Println("step1", symbol, period, action, count, out, cb, te, ts) symbolU := strings.ToUpper(symbol) if len(symbolU) == 3 { symbolU += "CNY" } _, err = markinfo.SymbolId(symbolU) if err != nil { return "", err } var prefix string prefix = market.BtyPrefix myInsId := prefix + symbolU var iTE, iTS int64 var bufferedCandles []client.Candle bufferedCandles, _ = clientDown.GetLastCandles(myInsId, periodId, 0x7fffffff) bufferedCandles = truncateBuffer(bufferedCandles, periodId, myInsId) var downN int var downTS int64 switch action { case "init": if len(bufferedCandles) >= iCount { bufferedCandles = bufferedCandles[len(bufferedCandles)-iCount:] downN = 0 downTS = 0 } else { downN = -(iCount - len(bufferedCandles)) downTS = -1 } case "new": iTE, _ = strconv.ParseInt(te, 10, 64) iTE *= 1000 beginTimeBuffered := int64(0x7fffffff) if len(bufferedCandles) > 0 { beginTimeBuffered = bufferedCandles[0].Timestamp } for cindex := 0; cindex < len(bufferedCandles); cindex++ { if bufferedCandles[cindex].Timestamp >= iTE { bufferedCandles = bufferedCandles[cindex:] break } } if beginTimeBuffered <= iTE { downN = 0 downTS = 0 if len(bufferedCandles) >= iCount { bufferedCandles = bufferedCandles[len(bufferedCandles)-iCount:] } } else { if len(bufferedCandles) >= iCount { bufferedCandles = bufferedCandles[len(bufferedCandles)-iCount:] downN = 0 downTS = 0 } else { if len(bufferedCandles) == 0 { downN = iCount downTS = iTE } else { downN = -(iCount - len(bufferedCandles)) downTS = -1 } } } case "down": iTS, _ = strconv.ParseInt(ts, 10, 64) iTS *= 1000 for cindex := 0; cindex < len(bufferedCandles); cindex++ { if bufferedCandles[cindex].Timestamp > iTS { bufferedCandles = bufferedCandles[:cindex] break } } if len(bufferedCandles) >= iCount { bufferedCandles = bufferedCandles[len(bufferedCandles)-iCount:] downN = 0 downTS = 0 } else { if len(bufferedCandles) == 0 { downN = -iCount downTS = iTS } else { downN = -(iCount - len(bufferedCandles)) downTS = -1 } } } //log.Println("step2", len(bufferedCandles), downN, downTS) var filecandles []client.Candle _, filecandles, _ = clientDown.GetHistory(myInsId, periodId, downN, downTS) //log.Println("step3", len(filecandles)) candles := make([]client.Candle, len(bufferedCandles)+len(filecandles)) for findex := 0; findex <= 1; findex++ { if findex == 1 { for iBuf := 0; iBuf < len(bufferedCandles); iBuf++ { //bufferedCandles[iBuf] = changeCandle(bufferedCandles[iBuf], strings.ToUpper(period), strings.ToUpper(symbol)) candles[len(filecandles)+iBuf] = bufferedCandles[iBuf] } } else { if downN < 0 { for iFile := len(filecandles) - 1; iFile >= 0; iFile-- { //filecandles[iFile] = changeCandle(filecandles[iFile], strings.ToUpper(period), strings.ToUpper(symbol)) candles[len(filecandles)-iFile-1] = filecandles[iFile] } } else { for iFile := 0; iFile < len(filecandles); iFile++ { //filecandles[iFile] = changeCandle(filecandles[iFile], strings.ToUpper(period), strings.ToUpper(symbol)) candles[iFile] = filecandles[iFile] } } } } var tData TData var kData KData if K_STYLE == style { kData.C = symbol kData.P = period kData.Action = action } if len(candles) >= iCount { if T_STYLE == style { tData.End = 0 } if K_STYLE == style { kData.End = 0 } } else { if T_STYLE == style { tData.End = 1 } if K_STYLE == style { kData.End = 1 } } if periodIdOld == market.M15 || periodIdOld == market.M30 || periodIdOld == market.H2 || periodIdOld == market.H4 || periodIdOld == market.W1 || periodIdOld == market.MN1 { candles = makeCandle(candles, periodIdOld, myInsId) } var candleLast client.Candle for cindex := 0; cindex < len(candles); cindex++ { if candles[cindex].Timestamp < candleLast.Timestamp { continue } bduplicated := false if candles[cindex].Timestamp == candleLast.Timestamp { bduplicated = true } if action == "down" && candles[cindex].Timestamp > iTS { break } if action == "new" && candles[cindex].Timestamp < iTE { continue } if candles[cindex].Open == 0 || candles[cindex].High == 0 || candles[cindex].Low == 0 || candles[cindex].Close == 0 { candles[cindex] = candleLast } if bduplicated { if candleLast.RealVolums < candles[cindex].RealVolums { if T_STYLE == style { tData.Candles[len(tData.Candles)-1] = candles[cindex] } if K_STYLE == style { kData.Candles.X[len(kData.Candles.X)-1] = int(candles[cindex].Timestamp / 1000) kData.Candles.Y[len(kData.Candles.Y)-1] = [5]float32{float32(candles[cindex].Open), float32(candles[cindex].High), float32(candles[cindex].Low), float32(candles[cindex].Close), float32(candles[cindex].RealVolums)} kData.Volumes.X[len(kData.Volumes.X)-1] = int(candles[cindex].Timestamp / 1000) kData.Volumes.Y[len(kData.Volumes.Y)-1] = float32(candles[cindex].RealVolums) } candleLast = candles[cindex] } } else { if T_STYLE == style { tData.Candles = append(tData.Candles, candles[cindex]) } if K_STYLE == style { kData.Candles.X = append(kData.Candles.X, int(candles[cindex].Timestamp/1000)) kData.Candles.Y = append(kData.Candles.Y, [5]float32{float32(candles[cindex].Open), float32(candles[cindex].High), float32(candles[cindex].Low), float32(candles[cindex].Close), float32(candles[cindex].RealVolums)}) kData.Volumes.X = append(kData.Volumes.X, int(candles[cindex].Timestamp/1000)) kData.Volumes.Y = append(kData.Volumes.Y, float32(candles[cindex].RealVolums)) } candleLast = candles[cindex] } } var dataLen int if T_STYLE == style { dataLen = len(tData.Candles) } if K_STYLE == style { dataLen = len(kData.Candles.X) } if dataLen >= iCount { if T_STYLE == style { tData.Candles = tData.Candles[dataLen-(iCount-1):] } if K_STYLE == style { kData.Candles.X = kData.Candles.X[dataLen-(iCount-1):] kData.Candles.Y = kData.Candles.Y[dataLen-(iCount-1):] kData.Volumes.X = kData.Volumes.X[dataLen-(iCount-1):] kData.Volumes.Y = kData.Volumes.Y[dataLen-(iCount-1):] } } var b []byte if K_STYLE == style { b, err = json.Marshal(&kData) } if T_STYLE == style { b, err = json.Marshal(&tData) } if err != nil { return "", errors.New("marshal error") } var output string if out == "jsonp" { output = fmt.Sprintf("if (%s) %s(%s)\n", cb, cb, string(b)) } else { output = string(b) } return output, nil } func kdataHandler(w http.ResponseWriter, r *http.Request) { symbol := r.FormValue("c") period := r.FormValue("p") action := r.FormValue("action") count := r.FormValue("count") out := r.FormValue("out") cb := r.FormValue("callback") var ts, te string if action == "new" { te = r.FormValue("te") } if action == "down" { ts = r.FormValue("ts") } output, err := getData(symbol, period, action, count, out, cb, te, ts, K_STYLE) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if out != "jsonp" { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Add("Access-Control-Allow-Headers", "Content-Type") w.Header().Set("content-type", "application/json") } io.WriteString(w, output) } func tdataHandler(w http.ResponseWriter, r *http.Request) { symbol := r.FormValue("c") period := r.FormValue("p") action := r.FormValue("action") count := r.FormValue("count") out := r.FormValue("out") cb := r.FormValue("callback") var ts, te string if action == "new" { te = r.FormValue("te") } if action == "down" { ts = r.FormValue("ts") } output, err := getData(symbol, period, action, count, out, cb, te, ts, T_STYLE) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if out != "jsonp" { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Add("Access-Control-Allow-Headers", "Content-Type") w.Header().Set("content-type", "application/json") } io.WriteString(w, output) } func makeCandle(candles []client.Candle, periodIdOld int, insId string) []client.Candle { var candlestmp []market.Candle for _, v := range candles { candlestmp = append(candlestmp, market.Candle{ Timestamp: v.Timestamp, Open: v.Open, High: v.High, Low: v.Low, Close: v.Close, RealVolums: v.RealVolums, TickVolums: v.TickVolums, }) } r := market.NewCandleBuf(candlestmp) candlestmpnew, _ := market.ConvPeriod(r, insId, periodIdOld) candles = candles[0:0] for _, v := range candlestmpnew { candles = append(candles, client.Candle{ Timestamp: v.Timestamp, Open: v.Open, High: v.High, Low: v.Low, Close: v.Close, RealVolums: v.RealVolums, TickVolums: v.TickVolums, }) } return candles } func truncateBuffer(bufferedCandles []client.Candle, periodId int, insId string) []client.Candle { var filecandlelatesttime int64 tmpDownN := -2 var tmpfilecandles []client.Candle _, tmpfilecandles, _ = clientDown.GetHistory(insId, periodId, tmpDownN, -1) if len(tmpfilecandles) > 1 { filecandlelatesttime = tmpfilecandles[1].Timestamp /*if insId != "bty_BTYUSDT" && len(bufferedCandles) > 0 && bufferedCandles[0].Timestamp > tmpfilecandles[0].Timestamp { bufferedCandlesTmp := bufferedCandles[:] bufferedCandles = make([]client.Candle, len(bufferedCandlesTmp)) for timestampAdded := tmpfilecandles[0].Timestamp + int64(periodId*1000); timestampAdded < bufferedCandlesTmp[0].Timestamp; timestampAdded += int64(periodId * 1000) { candleAdded := bufferedCandlesTmp[0] candleAdded.Timestamp = timestampAdded candleAdded.TickVolums = 0 candleAdded.RealVolums = 0 bufferedCandles = append(bufferedCandles, candleAdded) } bufferedCandles = append(bufferedCandles, bufferedCandlesTmp...) }*/ var iPos int for iPos = 0; iPos < len(bufferedCandles); iPos++ { if bufferedCandles[iPos].Timestamp > filecandlelatesttime { break } } bufferedCandles = bufferedCandles[iPos:] } return bufferedCandles } func patchingByVolume(candles []client.Candle, periodId int, bAsc bool, bBuffered bool) []client.Candle { bNeeded := false for i := 0; i < len(candles); i++ { if candles[i].TickVolums == 0 { bNeeded = true break } } if bNeeded { var st, et int64 var posHuobi int //var posOkCoin int if bAsc { st = candles[0].Timestamp et = candles[len(candles)-1].Timestamp } else { st = candles[len(candles)-1].Timestamp et = candles[0].Timestamp } var candlesHuobi []client.Candle if bBuffered { candlesHuobi, _ = clientDown.GetLastCandles("huobi_BTCCNY", periodId, 0x7fffffff) //candlesOkCoin, _ = clientDown.GetLastCandles("btc_BTCCNY", periodId, 0x7fffffff) } else { _, candlesHuobi, _ = clientDown.GetHistoryEx("huobi_BTCCNY", periodId, st, et) //_, candlesOkCoin, _ = clientDown.GetHistoryEx("btc_BTCCNY", periodId, st, et) } if bAsc { posHuobi = 0 //posOkCoin = 0 } else { posHuobi = len(candlesHuobi) - 1 //posOkCoin = len(filecandlesOkCoin) - 1 } candlesTmp := candles[:] candles = candles[0:0] for i := 0; i < len(candlesTmp); i++ { if candlesTmp[i].TickVolums == 0 { count := 0 var candleHuobi client.Candle //var candleOkCoin client.Candle if bAsc { for j := posHuobi; j < len(candlesHuobi); j++ { if candlesHuobi[j].Timestamp == candlesTmp[i].Timestamp { posHuobi = j count++ candleHuobi = candlesHuobi[j] break } if candlesHuobi[j].Timestamp > candlesTmp[i].Timestamp { posHuobi = j break } } /*for k := posOkCoin; k < len(filecandlesOkCoin); k++ { if filecandlesOkCoin[k].Timestamp == filecandlesTmp[i].Timestamp { posOkCoin = k count++ candleOkCoin = filecandlesOkCoin[k] break } if filecandlesOkCoin[k].Timestamp > filecandlesTmp[i].Timestamp { posOkCoin = k break } }*/ } else { for j := posHuobi; j >= 0; j-- { if candlesHuobi[j].Timestamp == candlesTmp[i].Timestamp { posHuobi = j count++ candleHuobi = candlesHuobi[j] break } if candlesHuobi[j].Timestamp < candlesTmp[i].Timestamp { posHuobi = j break } } /*for k := posOkCoin; k >= 0; k-- { if filecandlesOkCoin[k].Timestamp == filecandlesTmp[i].Timestamp { posOkCoin = k count++ candleOkCoin = filecandlesOkCoin[k] break } if filecandlesOkCoin[k].Timestamp < filecandlesTmp[i].Timestamp { posOkCoin = k break } }*/ } if count > 0 { var candleComplex client.Candle count = 0 if candleHuobi.Open > 0 && candleHuobi.High > 0 && candleHuobi.Low > 0 && candleHuobi.Close > 0 { count++ candleComplex.Open += candleHuobi.Open candleComplex.High += candleHuobi.High candleComplex.Low += candleHuobi.Low candleComplex.Close += candleHuobi.Close } /*if candleOkCoin.Open > 0 && candleOkCoin.High > 0 && candleOkCoin.Low > 0 && candleOkCoin.Close > 0 { count++ candleComplex.Open += candleOkCoin.Open candleComplex.High += candleOkCoin.High candleComplex.Low += candleOkCoin.Low candleComplex.Close += candleOkCoin.Close }*/ //filecandles = append(filecandles, client.Candle{Timestamp: filecandlesTmp[i].Timestamp, Open: candleComplex.Open / float64(count), //High: candleComplex.High / float64(count), Low: candleComplex.Low / float64(count), //Close: candleComplex.Close / float64(count), RealVolums: (candleHuobi.TickVolums + candleOkCoin.TickVolums) * 150, TickVolums: (candleHuobi.TickVolums + candleOkCoin.TickVolums) / 2}) candleHuobi.RealVolums /= 1e6 candles = append(candles, candleHuobi) //log.Println("1", candleHuobi) } else { candles = append(candles, candlesTmp[i]) //log.Println("2", candlesTmp[i]) } } else { candles = append(candles, candlesTmp[i]) //log.Println("3", candlesTmp[i]) } } } return candles } func patchingByTime(candles []client.Candle, periodId int, bAsc bool) []client.Candle { bNeedAdd := false for i := 0; i < len(candles); i++ { if bAsc { if i > 0 && ((candles[i].Timestamp - candles[i-1].Timestamp) > int64(periodId*1000)) { bNeedAdd = true break } } else { if i > 0 && ((candles[i-1].Timestamp - candles[i].Timestamp) > int64(periodId*1000)) { bNeedAdd = true break } } } if bNeedAdd { candlesTmp := make([]client.Candle, len(candles)) for k, v := range candles { candlesTmp[k] = v } candles = candles[0:0] for i := 0; i < len(candlesTmp); i++ { if bAsc { if i > 0 && ((candlesTmp[i].Timestamp - candlesTmp[i-1].Timestamp) > int64(periodId*1000)) { for timestampAdded := candlesTmp[i-1].Timestamp + int64(periodId*1000); timestampAdded < candlesTmp[i].Timestamp; timestampAdded += int64(periodId * 1000) { candleAdded := candlesTmp[i-1] candleAdded.Timestamp = timestampAdded candleAdded.TickVolums = 0 candles = append(candles, candleAdded) } } } else { if i > 0 && ((candlesTmp[i-1].Timestamp - candlesTmp[i].Timestamp) > int64(periodId*1000)) { for timestampAdded := candlesTmp[i-1].Timestamp - int64(periodId*1000); timestampAdded > candlesTmp[i].Timestamp; timestampAdded -= int64(periodId * 1000) { candleAdded := candlesTmp[i-1] candleAdded.Timestamp = timestampAdded candleAdded.TickVolums = 0 candles = append(candles, candleAdded) } } } candles = append(candles, candlesTmp[i]) } } return candles } type gzipResponseWriter struct { io.Writer http.ResponseWriter } func (w gzipResponseWriter) Write(b []byte) (int, error) { return w.Writer.Write(b) } func (w gzipResponseWriter) Flush() { w.Writer.(*gzip.Writer).Flush() w.ResponseWriter.(http.Flusher).Flush() } func makeGzipHandler(fn http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { fn(w, r) return } w.Header().Set("Content-Encoding", "gzip") w.Header().Set("Content-Type", "text/javascript") gz := gzip.NewWriter(w) defer gz.Close() fn(gzipResponseWriter{Writer: gz, ResponseWriter: w}, r) } } func main() { runtime.GOMAXPROCS(runtime.NumCPU()) conf, err := readConf() if err != nil { flag.Parse() } else { *saddr1 = conf.Saddr1 *saddr2 = conf.Saddr2 *saddr3 = conf.Saddr3 *saddr4 = conf.Saddr4 } err = connectServer() if err != nil { log.Fatal("connect server", err) } s := &http.Server{ //Addr: ":3062", Addr: ":4062", //Addr: ":9062", ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } http.HandleFunc("/kdata", makeGzipHandler(kdataHandler)) http.HandleFunc("/tdata", makeGzipHandler(tdataHandler)) log.Fatal(s.ListenAndServeTLS("214341259320977.pem", "214341259320977.key")) //log.Fatal(s.ListenAndServe()) //log.Fatal(s.ListenAndServeTLS("licai20170620.pem", "licai20170620.key")) }