123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- // Copyright 2013-2014 Fuzamei tech Ltd. All rights reserved.
- package market
- // 本文件实现DataSource数据源的tick数据获取下载和保存
- import (
- "errors"
- "fmt"
- "io/ioutil"
- "log"
- "os"
- "path"
- "strings"
- "sync"
- "time"
- "unsafe"
- "tickserver/framework/base"
- "tickserver/framework/event"
- )
- var Debug bool = false // for debug
- type InsName interface {
- GetInsName(insId string) string
- }
- type BufCandleMaker struct {
- candleGenerators []*base.Candle
- ohlcs []base.Ohlc
- tbuf *tickBuf
- cbufs []*CandleBuf
- }
- // 数据源接口
- // lmax, easyforex, oanda, ctp以及未来的macoin等均实现此接口
- type DataSource interface {
- SubIns() *event.Event
- Run()
- GetCacheTicks(insId string) ([]Tick, error)
- GetCacheCandles(insId string, period int) ([]Candle, error)
- GetTimeList(insId, period, beginStr string) ([]string, error)
- SaveAllTicks()
- }
- type DSBase struct {
- db *MyDB
- dir string
- chMs chan *Market
- chMl chan *Market
- chId chan string
- cmu sync.Mutex // 保护camp
- cmap map[int]map[string]*CandleBuf // 缓存最新的Candle
- tmu sync.Mutex // 保护tmap
- tmap map[string]*tickBuf // 缓存最新的Tick
- cmmu sync.Mutex
- bufCandleMakersMap map[string]*BufCandleMaker
- saveCh chan string
- }
- func makeCandleMap() map[int]map[string]*CandleBuf {
- cmap := make(map[int]map[string]*CandleBuf)
- for k, _ := range basePeriodSet {
- cmap[k] = make(map[string]*CandleBuf)
- }
- return cmap
- }
- func NewDsBase(db *MyDB, dir string) *DSBase {
- dsb := &DSBase{
- db: db,
- dir: dir,
- chId: make(chan string, 1),
- chMs: make(chan *Market, 20480),
- chMl: make(chan *Market, 20480),
- cmap: makeCandleMap(),
- tmap: make(map[string]*tickBuf),
- bufCandleMakersMap: make(map[string]*BufCandleMaker),
- saveCh: make(chan string, 20480),
- }
- return dsb
- }
- func (dsb *DSBase) GetTimeList(insId, period, beginStr string) ([]string, error) {
- // log.Println("@@@: DSBase.GetLastCandles:", insId, period, n)
- // defer log.Println("###: DSBase.GetLastCandles:", insId, period, n)
- var year, month, day int
- fmt.Sscanf(beginStr, "%04d%02d%02d", &year, &month, &day)
- var timelist []string
- dir := path.Join(dsb.dir, insId, fmt.Sprint(year))
- for {
- if _, err := os.Stat(dir); os.IsNotExist(err) {
- year++
- if year > time.Now().Year() {
- break
- }
- dir = path.Join(dsb.dir, insId, fmt.Sprint(year))
- continue
- }
- tl := getTimeList(dir, period, beginStr)
- timelist = append(timelist, tl...)
- year++
- if year > time.Now().Year() {
- break
- }
- dir = path.Join(dsb.dir, insId, fmt.Sprint(year))
- }
- return timelist, nil
- }
- func getTimeList(dir, period, beginStr string) []string {
- var timelist []string
- suffix := fmt.Sprintf(".%s.gz", period)
- infos, _ := ioutil.ReadDir(dir)
- for i := 0; i < len(infos); i++ {
- name := infos[i].Name()
- if strings.HasSuffix(name, suffix) {
- time := strings.Split(name, ".")[0]
- if time >= beginStr {
- timelist = append(timelist, time)
- }
- }
- }
- return timelist
- }
- func (dsb *DSBase) GetCacheCandles(insId string, period int) ([]Candle, error) {
- // log.Println("@@@: DSBase.GetLastCandles:", insId, period, n)
- // defer log.Println("###: DSBase.GetLastCandles:", insId, period, n)
- buf, ok := dsb.getBuf(insId, period)
- if !ok {
- msg := fmt.Sprintf("GetLastCandles error: %s insId is NOT in fzm exchange", insId)
- return nil, errors.New(msg)
- }
- buf.Lock()
- defer buf.Unlock()
- return buf.Buf[:], nil
- }
- func (dsb *DSBase) GetCacheTicks(insId string) ([]Tick, error) {
- dsb.tmu.Lock()
- defer dsb.tmu.Unlock()
- buf, ok := dsb.tmap[insId]
- if !ok {
- msg := fmt.Sprintf("GetLastTicks error: %s insId is NOT in fzm exchange", insId)
- return nil, errors.New(msg)
- }
- buf.Lock()
- defer buf.Unlock()
- return buf.buf[:], nil
- }
- func (dsb *DSBase) getBuf(insId string, period int) (*CandleBuf, bool) {
- dsb.cmu.Lock()
- defer dsb.cmu.Unlock()
- bufMap, ok := dsb.cmap[period]
- if !ok {
- return nil, false
- }
- buf, ok := bufMap[insId]
- if !ok {
- return nil, false
- }
- return buf, true
- }
- // 删除不需要的insId
- func (dsb *DSBase) Del(insId string) {
- dsb.chId <- insId
- }
- func (dsb *DSBase) Save(m *Market) {
- select {
- case dsb.chMs <- m:
- default:
- //log.Println("@@@:Save:", m.InsId, m.LastPrice)
- }
- }
- func (dsb *DSBase) SaveL(m *Market) {
- dsb.chMl <- m
- //select {
- //case dsb.chMl <- m:
- //default:
- //log.Println("@@@:Save:", m.InsId, m.LastPrice)
- //}
- }
- type candleInfo struct {
- c Candle
- insId string
- period int
- }
- type readerInfo struct {
- r *candleBuffer
- insId string
- period int
- prev *Candle
- }
- func (dsb *DSBase) NewCandleMaker(insId string) *BufCandleMaker {
- // tick 缓存
- var tbuf *tickBuf
- var ok bool
- dsb.tmu.Lock()
- if tbuf, ok = dsb.tmap[insId]; !ok {
- tbuf = &tickBuf{}
- dsb.tmap[insId] = tbuf
- // log.Println("@@@: dsb.tmap", m.InsId)
- }
- dsb.tmu.Unlock()
- // candle 不同周期缓存
- var cbufs []*CandleBuf
- dsb.cmu.Lock()
- for _, period := range periodSet {
- if _, ok := dsb.cmap[period]; !ok {
- log.Fatal("_, ok := dsb.cmap[period] error")
- }
- var cbuf *CandleBuf
- if cbuf, ok = dsb.cmap[period][insId]; !ok {
- cbuf = &CandleBuf{}
- dsb.cmap[period][insId] = cbuf
- // log.Println("@@@: dsb.cmap", m.InsId, period)
- }
- cbufs = append(cbufs, cbuf)
- }
- dsb.cmu.Unlock()
- candleGenerators := make([]*base.Candle, len(periodSet))
- ohlcs := make([]base.Ohlc, len(periodSet))
- for i, period := range periodSet {
- candleGenerators[i], _ = base.NewCandle(period, 2, nil, 0)
- if strings.HasPrefix(insId, Ctp) {
- candleGenerators[i].Set(base.CANDLE_AUTOCOMPLETE_MAX, 1)
- }
- ohlcs[i] = base.Ohlc{}
- }
- return &BufCandleMaker{
- candleGenerators: candleGenerators[:],
- ohlcs: ohlcs[:],
- tbuf: tbuf,
- cbufs: cbufs,
- }
- }
- func (dsb *DSBase) SaveAllTicks() {
- dsb.tmu.Lock()
- defer dsb.tmu.Unlock()
- for i, v := range dsb.tmap {
- var saveTicks []Tick
- v.Lock()
- saveTicks = v.buf[:]
- v.buf = v.buf[0:0]
- v.Unlock()
- //log.Println("SaveAllTicks saving", i)
- SaveTickEx(dsb.dir, saveTicks, i, false)
- //if err != nil {
- //log.Println(fname, err)
- //}
- }
- }
- func (dsb *DSBase) SaveTicks() {
- for {
- insId := <-dsb.saveCh
- dsb.tmu.Lock()
- tbuf, ok := dsb.tmap[insId]
- dsb.tmu.Unlock()
- if ok {
- var saveTicks []Tick
- tbuf.Lock()
- if len(tbuf.buf) >= 2000 {
- saveTicks = tbuf.buf[:1000]
- tbuf.buf = tbuf.buf[1000:]
- }
- tbuf.Unlock()
- SaveTickEx(dsb.dir, saveTicks, insId, false)
- //if err != nil {
- //log.Println(fname, err)
- //}
- }
- }
- }
- func (dsb *DSBase) DoReadEx() error {
- go dsb.SaveTicks()
- for {
- var bLoaded bool
- var m *Market
- select {
- case m = <-dsb.chMs:
- bLoaded = false
- case m = <-dsb.chMl:
- bLoaded = true
- }
- var t *Tick
- if InsIdPrefix(m.InsId) == Lmax && !bLoaded {
- t = Market2TickByBid(m)
- } else {
- t = Market2Tick(m)
- }
- bcm, ok := dsb.bufCandleMakersMap[m.InsId]
- if !ok {
- bcm = dsb.NewCandleMaker(m.InsId)
- dsb.bufCandleMakersMap[m.InsId] = bcm
- }
- if !bLoaded {
- bSave := bcm.tbuf.add(t)
- if bSave {
- dsb.saveCh <- m.InsId
- }
- }
- for i, candleGenerator := range bcm.candleGenerators {
- tg := Tk2Tg(*t)
- num := candleGenerator.UpdateTick((*base.Tick)(unsafe.Pointer(&tg)))
- var candles []Candle
- if num == 0 {
- candleGenerator.Next(&bcm.ohlcs[i])
- ohlcGo := bcm.ohlcs[i].ToGOStruct()
- candles = append(candles, OhlcGo2Candle(ohlcGo))
- } else if num > 0 {
- for j := 0; j < num; j++ {
- candleGenerator.Next(&bcm.ohlcs[i])
- ohlcGo := bcm.ohlcs[i].ToGOStruct()
- candles = append(candles, OhlcGo2Candle(ohlcGo))
- }
- } else {
- //log.Println("tick error.")
- }
- for _, v := range candles {
- last := bcm.cbufs[i].Last()
- if last != nil && last.Timestamp == v.Timestamp {
- *last = v
- } else {
- bcm.cbufs[i].add(&v, periodSet[i])
- }
- }
- }
- }
- return nil
- }
- type tbuf struct {
- tb *tickBuf
- insId string
- }
- type cbuf struct {
- cb *CandleBuf
- insId string
- period int
- }
- var periodSet = []int{M1, M5, H1, D1}
- func SaveCandlesEx(dataDir, insId string, candles []Candle, period int, bTruncate bool) (string, error) {
- if len(candles) == 0 {
- return "", nil
- }
- t := time.Unix(candles[0].Timestamp/1000, 0)
- dir := path.Join(dataDir, insId)
- if period < D1 {
- dir = path.Join(dir, fmt.Sprint(t.Year()))
- }
- os.MkdirAll(dir, 0777)
- var bname string
- if period < D1 {
- bname = fmt.Sprintf("%04d%02d%02d.%s.gz", t.Year(), t.Month(), t.Day(), PeriodNameMap[period])
- } else {
- bname = fmt.Sprintf("%s.gz", PeriodNameMap[period])
- }
- fname := path.Join(dir, bname)
- if !bTruncate {
- candles, _ = combinEx(fname, candles)
- }
- // 新建并写入文件
- w, err := os.Create(fname)
- if err != nil {
- return "", errors.New("SaveCandles os.Create error:" + err.Error())
- }
- defer w.Close()
- err = ZipCBuf(w, candles)
- if err != nil {
- return "", errors.New("SaveCandles ZipCBuf error:" + err.Error())
- }
- return fname, nil
- }
- func SaveCandlesTmp(dataDir, insId string, candles []Candle, period int, bTruncate bool) (string, error) {
- if period == D1 {
- return saveCandlesTmp(dataDir, insId, candles, period, bTruncate)
- }
- if len(candles) == 0 {
- return "", nil
- }
- oneDay := int64(1000 * 3600 * 24)
- baseT := candles[0].Timestamp / oneDay
- begin := 0
- for k, v := range candles {
- tmpBaseT := v.Timestamp / oneDay
- if tmpBaseT != baseT {
- saveCandlesTmp(dataDir, insId, candles[begin:k], period, bTruncate)
- //log.Println(fname, err)
- begin = k
- baseT = tmpBaseT
- }
- }
- return saveCandlesTmp(dataDir, insId, candles[begin:], period, bTruncate)
- }
- func saveCandlesTmp(dataDir, insId string, candles []Candle, period int, bTruncate bool) (string, error) {
- if len(candles) == 0 {
- return "", nil
- }
- t := time.Unix(candles[0].Timestamp/1000, 0).UTC()
- dir := path.Join(dataDir, insId)
- if period < D1 {
- dir = path.Join(dir, fmt.Sprint(t.Year()))
- }
- os.MkdirAll(dir, 0777)
- var bname string
- if period < D1 {
- bname = fmt.Sprintf("%04d%02d%02d.%s.gz", t.Year(), t.Month(), t.Day(), PeriodNameMap[period])
- } else {
- bname = fmt.Sprintf("%s.gz", PeriodNameMap[period])
- }
- fname := path.Join(dir, bname)
- if period != D1 {
- tmpfname := fname
- fname = tmpfname + ".tmp"
- if _, err := os.Stat(fname); os.IsNotExist(err) {
- if _, err := os.Stat(tmpfname); err == nil {
- for err = os.Rename(tmpfname, fname); err != nil; err = os.Rename(tmpfname, fname) {
- time.Sleep(time.Second)
- }
- }
- }
- }
- if !bTruncate {
- candles, _ = combinEx(fname, candles)
- }
- // 新建并写入文件
- w, err := os.Create(fname)
- if err != nil {
- return "", errors.New("SaveCandles os.Create error:" + err.Error())
- }
- defer w.Close()
- err = ZipCBuf(w, candles)
- if err != nil {
- return "", errors.New("SaveCandles ZipCBuf error:" + err.Error())
- }
- //if period != D1 {
- //if _, err := os.Stat(tmpfname); os.IsNotExist(err) {
- //if _, err := os.Stat(fname); err == nil {
- //for err = os.Rename(fname, tmpfname); err != nil; err = os.Rename(fname, tmpfname) {
- //time.Sleep(time.Second)
- //}
- //}
- //}
- //}
- return fname, nil
- }
- func convCandles0(ticks []Tick, insId string, period int) ([]Candle, error) {
- r := NewTickBuf(ticks)
- return TickConvCandle(r, insId, period)
- }
- func convCandles1(candles []Candle, insId string, period int) ([]Candle, error) {
- r := NewCandleBuf(candles)
- return ConvPeriod(r, insId, period)
- }
|