|
@@ -1,880 +0,0 @@
|
|
-// Copyright 2013-2014 Fuzamei tech Ltd. All rights reserved.
|
|
|
|
-
|
|
|
|
-package tick
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
-#include <string.h>
|
|
|
|
-*/
|
|
|
|
-import "C"
|
|
|
|
-
|
|
|
|
-// 本文件实现大智慧数据源接口, 实时数据和历史数据的获取和保存
|
|
|
|
-
|
|
|
|
-import (
|
|
|
|
- "bytes"
|
|
|
|
- "compress/zlib"
|
|
|
|
- "container/list"
|
|
|
|
- "encoding/binary"
|
|
|
|
- "errors"
|
|
|
|
- "fmt"
|
|
|
|
- "io"
|
|
|
|
- "log"
|
|
|
|
- "net"
|
|
|
|
- "os"
|
|
|
|
- "strconv"
|
|
|
|
- "strings"
|
|
|
|
- "sync"
|
|
|
|
- "time"
|
|
|
|
- "unsafe"
|
|
|
|
- "tickserver/server/market"
|
|
|
|
-
|
|
|
|
- "golang.org/x/text/encoding/simplifiedchinese"
|
|
|
|
-)
|
|
|
|
-
|
|
|
|
-const (
|
|
|
|
- STOCK_PER_SERVER = 50
|
|
|
|
- FETCH_PER_MILLISECOND = 100
|
|
|
|
-)
|
|
|
|
-
|
|
|
|
-type RecvDataHeader struct {
|
|
|
|
- CheckSum int32
|
|
|
|
- EncodeMode byte
|
|
|
|
- Tmp [5]byte
|
|
|
|
- Msgid int16
|
|
|
|
- Size int16
|
|
|
|
- DePackSize int16
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// 公司资料原始数据
|
|
|
|
-type TdxStockInfo struct { // 初始化数据 29字节
|
|
|
|
- Code [6]byte //代码
|
|
|
|
- Rate int16 // 实时盘口中的成交量除去的除数?1手=n股?
|
|
|
|
- Name [8]byte //名称
|
|
|
|
- W1 int16 //w1 为5日平均量(用于量比计算)
|
|
|
|
- W2 int16
|
|
|
|
- PriceMag byte //小数点位数
|
|
|
|
- YClose float32 //昨收
|
|
|
|
- W3 int16
|
|
|
|
- W4 int16
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-//权息
|
|
|
|
-type QuanInfo struct {
|
|
|
|
- style byte
|
|
|
|
- day int32
|
|
|
|
- q1 float32
|
|
|
|
- q2 float32
|
|
|
|
- q3 float32
|
|
|
|
- q4 float32
|
|
|
|
- //style=1 (除权除息) (送现金,配股价,送股数,配股比例);
|
|
|
|
- //style=2 (送配股上市)
|
|
|
|
- //style=9 (转配股上市) (股本变化) Q1=前流通盘 Q2=前总股本 Q3=后流通盘 Q4=后总股本
|
|
|
|
- //style=3 (非流通股上市) 前流通盘 前总股本 后流通盘 后总股本
|
|
|
|
- // 3 送现金:3499.00 配股价:17468.00 送股数:4368.00 配股比例:17468.00
|
|
|
|
- // 权息日 类别 送转股 分红 配股 配股价 前流通盘 后流通盘 前总股本 后总股本
|
|
|
|
- //20120618 非流通股上市 3499.0 4368.0 17468.0 17468.0
|
|
|
|
- //style=5 (股本变化)前流通盘 前总股本 后流通盘 后总股本
|
|
|
|
- // 权息日 类别 送转股 分红 配股 配股价 前流通盘 后流通盘 前总股本 后总股本
|
|
|
|
- //20120316 股本变化 0.0 3499.0 0.0 17468.0
|
|
|
|
- //0 002663 Date:20120316 5 送现金: 0.00 配股价: 0.00 送股数:3499.00 配股比例:17468.00
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-type CaiWu struct {
|
|
|
|
- Mark byte
|
|
|
|
- code [6]byte
|
|
|
|
- LTG float32 //流通股数量
|
|
|
|
- t1 int16
|
|
|
|
- t2 int16
|
|
|
|
- day1 int32
|
|
|
|
- day2 int32
|
|
|
|
- zl [30]float32
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-type Stock struct {
|
|
|
|
- no int32 //no=mark*1000000+code;
|
|
|
|
- szOrsh byte
|
|
|
|
- quanlen int //权息长度
|
|
|
|
- gp TdxStockInfo
|
|
|
|
- quan [80]QuanInfo
|
|
|
|
- cw CaiWu
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// TdxDS实现了dataSource接口, 并对tdx的历史数据和实时数据保存
|
|
|
|
-type TdxDS struct {
|
|
|
|
- *DSBase
|
|
|
|
- conf *DsConf
|
|
|
|
- datetime uint32
|
|
|
|
- stocks map[string]*Stock
|
|
|
|
- servers []string
|
|
|
|
- serverlist *list.List
|
|
|
|
- symbolsGroup [][STOCK_PER_SERVER]string
|
|
|
|
- exsGroup [][STOCK_PER_SERVER]byte
|
|
|
|
- conn net.Conn
|
|
|
|
- instrumentUpdated bool
|
|
|
|
- goroutineNum int
|
|
|
|
- statusCh chan int
|
|
|
|
- mu sync.Mutex
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func init() {
|
|
|
|
- drivers[Tdx] = newTdxDS
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func newTdxDS(conf *DsConf) (DataSource, error) {
|
|
|
|
- tds := &TdxDS{
|
|
|
|
- DSBase: NewDsBase(conf),
|
|
|
|
- conf: conf,
|
|
|
|
- stocks: make(map[string]*Stock),
|
|
|
|
- serverlist: list.New(),
|
|
|
|
- instrumentUpdated: false,
|
|
|
|
- statusCh: make(chan int, 1),
|
|
|
|
- }
|
|
|
|
- var err error
|
|
|
|
- tds.servers, err = loadServers(conf.CfgFile)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- for _, v := range tds.servers {
|
|
|
|
- tds.serverlist.PushBack(v)
|
|
|
|
- }
|
|
|
|
- err = tds.getConn()
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- err = tds.getInstrument(0)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- err = tds.getInstrument(1)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- tds.conn.Close()
|
|
|
|
- i := 0
|
|
|
|
- var symbols [STOCK_PER_SERVER]string
|
|
|
|
- var exs [STOCK_PER_SERVER]byte
|
|
|
|
- for k, v := range tds.stocks {
|
|
|
|
- symbols[i] = k
|
|
|
|
- exs[i] = v.szOrsh
|
|
|
|
- i++
|
|
|
|
- if i >= STOCK_PER_SERVER {
|
|
|
|
- i = 0
|
|
|
|
- tds.symbolsGroup = append(tds.symbolsGroup, symbols)
|
|
|
|
- tds.exsGroup = append(tds.exsGroup, exs)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if i > 0 {
|
|
|
|
- for j := i; j < STOCK_PER_SERVER; j++ {
|
|
|
|
- symbols[j] = ""
|
|
|
|
- }
|
|
|
|
- tds.symbolsGroup = append(tds.symbolsGroup, symbols)
|
|
|
|
- tds.exsGroup = append(tds.exsGroup, exs)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return tds, nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (tds *TdxDS) Name() string {
|
|
|
|
- return Tdx
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (tds *TdxDS) Run() {
|
|
|
|
- log.Println("TdxDS.Run")
|
|
|
|
- //tds.stocks["000001"] = &Stock{}
|
|
|
|
- //tds.readTick(tds.conn, 0, "000001")
|
|
|
|
- //var symbols = [STOCK_PER_SERVER]string{"000716", "002216", "002329", "002481", "002495", "002507", "002570", "002626", "002650", "002661", "002719", "002732", "300146", "300149", "300381", "300401", "600073", "600186", "600298", "600305", "600419", "600429", "600597", "600866", "600872", "600873", "600887", "603020", "603288", "000019", "000557", "000568", "000596", "000729", "000752", "000799", "000848", "000858", "000869", "000929", "000995", "002304", "002387", "002461", "002646", "600059", "600084", "600090", "600132", "600197"}
|
|
|
|
- //var exs = [STOCK_PER_SERVER]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}
|
|
|
|
- //tds.readTicks(symbols, exs)
|
|
|
|
- go tds.updateInstruments()
|
|
|
|
- tds.fetchMarket()
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (tds *TdxDS) fetchMarket() {
|
|
|
|
- for i, v := range tds.symbolsGroup {
|
|
|
|
- go tds.readTicks(v, tds.exsGroup[i])
|
|
|
|
- tds.goroutineNum++
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (tds *TdxDS) updateInstruments() {
|
|
|
|
- var err error
|
|
|
|
- ticker := time.Tick(time.Second * 30)
|
|
|
|
- for t := range ticker {
|
|
|
|
- if t.Hour() == 7 && t.Minute() == 0 { // 7:00重新连接服务器和获得股票信息
|
|
|
|
- tds.getConn()
|
|
|
|
- err = tds.getInstrument(0)
|
|
|
|
- if err != nil {
|
|
|
|
- log.Println(err)
|
|
|
|
- }
|
|
|
|
- err = tds.getInstrument(1)
|
|
|
|
- if err != nil {
|
|
|
|
- log.Println(err)
|
|
|
|
- }
|
|
|
|
- tds.conn.Close()
|
|
|
|
- i := 0
|
|
|
|
- tds.symbolsGroup = nil
|
|
|
|
- tds.exsGroup = nil
|
|
|
|
- var symbols [STOCK_PER_SERVER]string
|
|
|
|
- var exs [STOCK_PER_SERVER]byte
|
|
|
|
- for k, v := range tds.stocks {
|
|
|
|
- symbols[i] = k
|
|
|
|
- exs[i] = v.szOrsh
|
|
|
|
- i++
|
|
|
|
- if i >= STOCK_PER_SERVER {
|
|
|
|
- i = 0
|
|
|
|
- tds.symbolsGroup = append(tds.symbolsGroup, symbols)
|
|
|
|
- tds.exsGroup = append(tds.exsGroup, exs)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if i > 0 {
|
|
|
|
- for j := i; j < STOCK_PER_SERVER; j++ {
|
|
|
|
- symbols[j] = ""
|
|
|
|
- }
|
|
|
|
- tds.symbolsGroup = append(tds.symbolsGroup, symbols)
|
|
|
|
- tds.exsGroup = append(tds.exsGroup, exs)
|
|
|
|
- }
|
|
|
|
- log.Println("updateInstruments begin")
|
|
|
|
- tds.instrumentUpdated = true
|
|
|
|
- for m := 0; m < tds.goroutineNum; m++ { //等待readTicks结束
|
|
|
|
- <-tds.statusCh
|
|
|
|
- }
|
|
|
|
- log.Println("updateInstruments end")
|
|
|
|
- tds.goroutineNum = 0
|
|
|
|
- tds.instrumentUpdated = false
|
|
|
|
- tds.fetchMarket()
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (tds *TdxDS) getTickConn() net.Conn {
|
|
|
|
- servernum := len(tds.servers)
|
|
|
|
- count := 0
|
|
|
|
- for {
|
|
|
|
- tds.mu.Lock()
|
|
|
|
- e := tds.serverlist.Front()
|
|
|
|
- server := tds.serverlist.Remove(e)
|
|
|
|
- tds.serverlist.PushBack(server)
|
|
|
|
- tds.mu.Unlock()
|
|
|
|
- count++
|
|
|
|
- if count > servernum {
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- conn, err := net.DialTimeout("tcp", server.(string), 5*time.Second)
|
|
|
|
- if err != nil {
|
|
|
|
- log.Println(server.(string), err)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- conn.(*net.TCPConn).SetDeadline(time.Now().Add(5 * time.Second))
|
|
|
|
-
|
|
|
|
- _, err = conn.Write([]byte("\x0C\x02\x18\x93\x00\x01\x03\x00\x03\x00\x0D\x00\x01"))
|
|
|
|
- if err != nil {
|
|
|
|
- log.Println(server.(string), err)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- _, err = readBuf(conn)
|
|
|
|
- if err != nil {
|
|
|
|
- log.Println(server.(string), err)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- //tds.datetime = binary.LittleEndian.Uint32(debuf[42:46])
|
|
|
|
- //log.Println("最后交易日:", tds.datetime)
|
|
|
|
- //log.Println("服务器名称:", decodeString(debuf[68:]))
|
|
|
|
-
|
|
|
|
- return conn
|
|
|
|
- }
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (tds *TdxDS) getConn() error {
|
|
|
|
- for _, v := range tds.servers {
|
|
|
|
- conn, err := net.DialTimeout("tcp", v, 5*time.Second)
|
|
|
|
- if err != nil {
|
|
|
|
- log.Println(v, err)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- conn.(*net.TCPConn).SetDeadline(time.Now().Add(5 * time.Second))
|
|
|
|
-
|
|
|
|
- _, err = conn.Write([]byte("\x0C\x02\x18\x93\x00\x01\x03\x00\x03\x00\x0D\x00\x01"))
|
|
|
|
- if err != nil {
|
|
|
|
- log.Println(v, err)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- debuf, err := readBuf(conn)
|
|
|
|
- if err != nil {
|
|
|
|
- log.Println(v, err)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- tds.datetime = binary.LittleEndian.Uint32(debuf[42:46])
|
|
|
|
- log.Println("最后交易日:", tds.datetime)
|
|
|
|
- log.Println("服务器名称:", decodeString(debuf[68:]))
|
|
|
|
-
|
|
|
|
- tds.conn = conn
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- return errors.New("no conn available")
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func decodeString(debuf []byte) string {
|
|
|
|
- var name []byte
|
|
|
|
- for i := 0; i < len(debuf); i++ {
|
|
|
|
- if int(debuf[i]) == 0 {
|
|
|
|
- name = debuf[0:i]
|
|
|
|
- break
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- trans := simplifiedchinese.GBK.NewDecoder()
|
|
|
|
- dst := make([]byte, 1024)
|
|
|
|
- nDst, _, err := trans.Transform(dst, name, true)
|
|
|
|
- if err != nil {
|
|
|
|
- panic(err)
|
|
|
|
- }
|
|
|
|
- return string(dst[0:nDst])
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (tds *TdxDS) getInstrument(szOrsh byte) error {
|
|
|
|
- log.Println("getInstrument", szOrsh)
|
|
|
|
- bb := []byte("\x0C\x0C\x18\x6C\x00\x01\x08\x00\x08\x00\x4E\x04\xFF\x00\x01\x02\x03\x04") //取得股票数量
|
|
|
|
- bb[12] = szOrsh //0深圳 1上海
|
|
|
|
- wlen := len(bb)
|
|
|
|
- buf := bytes.NewBuffer(bb[14:])
|
|
|
|
- buf.Reset()
|
|
|
|
- binary.Write(buf, binary.LittleEndian, &tds.datetime)
|
|
|
|
- wlen, err := tds.conn.Write(bb[:wlen])
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- debuf, err := readBuf(tds.conn)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- szcount := binary.LittleEndian.Uint16(debuf[:])
|
|
|
|
- //log.Println(wlen, szcount)
|
|
|
|
- trans := simplifiedchinese.GBK.NewDecoder()
|
|
|
|
- dst := make([]byte, 1024)
|
|
|
|
-
|
|
|
|
- var count uint16
|
|
|
|
- for count < szcount {
|
|
|
|
- bb11 := []byte("\x0C\x01\x18\x64\x01\x01\x06\x00\x06\x00\x50\x04\xFF\x00\xF2\xF3")
|
|
|
|
- bb11[12] = szOrsh
|
|
|
|
- wlen := len(bb11)
|
|
|
|
- buf := bytes.NewBuffer(bb11[14:])
|
|
|
|
- buf.Reset()
|
|
|
|
- binary.Write(buf, binary.LittleEndian, &count)
|
|
|
|
- wlen, err := tds.conn.Write(bb11[:wlen])
|
|
|
|
- if err != nil {
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- debuf, err := readBuf(tds.conn)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- n := binary.LittleEndian.Uint16(debuf[:])
|
|
|
|
- stockInfoSize := int(unsafe.Sizeof(TdxStockInfo{}))
|
|
|
|
- stockInfoSize = 29
|
|
|
|
- var stock Stock
|
|
|
|
- for j := 0; j < int(n); j++ {
|
|
|
|
- buf := bytes.NewBuffer(debuf[2+j*stockInfoSize:])
|
|
|
|
- binary.Read(buf, binary.LittleEndian, &stock.gp)
|
|
|
|
- codeStr := string(stock.gp.Code[:])
|
|
|
|
- no, _ := strconv.Atoi(codeStr)
|
|
|
|
- stock.no = int32(no)
|
|
|
|
- stock.szOrsh = szOrsh
|
|
|
|
- _, ok := tds.stocks[codeStr]
|
|
|
|
- if !ok {
|
|
|
|
- tds.stocks[codeStr] = &stock
|
|
|
|
- ins, ok1 := tds.insMap[int64(stock.no)]
|
|
|
|
- if !ok1 {
|
|
|
|
- exid := SHEX
|
|
|
|
- if szOrsh == 0 {
|
|
|
|
- exid = SZEX
|
|
|
|
- }
|
|
|
|
- priceInc := float64(1.0)
|
|
|
|
- for i := 0; i < int(stock.gp.PriceMag); i++ {
|
|
|
|
- priceInc /= 10
|
|
|
|
- }
|
|
|
|
- nDst, _, _ := trans.Transform(dst, stock.gp.Name[:], true)
|
|
|
|
- ins = &Instrument{
|
|
|
|
- Id: int64(stock.no),
|
|
|
|
- Name: string(dst[0:nDst]),
|
|
|
|
- Type: market.Securities,
|
|
|
|
- ExId: exid,
|
|
|
|
- PriceInc: priceInc,
|
|
|
|
- }
|
|
|
|
- tds.insMap[ins.Id] = ins
|
|
|
|
- //log.Println(ins)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- count++
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (tds *TdxDS) readTicks(symbols [STOCK_PER_SERVER]string, exs [STOCK_PER_SERVER]byte) {
|
|
|
|
- conn := tds.getTickConn()
|
|
|
|
- if conn == nil {
|
|
|
|
- log.Println("no conn available")
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- defer conn.Close()
|
|
|
|
-
|
|
|
|
- lastDataMap := make(map[string]string)
|
|
|
|
- for {
|
|
|
|
- if tds.instrumentUpdated {
|
|
|
|
- tds.statusCh <- 1
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- if !inTime() {
|
|
|
|
- time.Sleep(time.Second * 1)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- //start := time.Now().UnixNano()
|
|
|
|
- var bb [800]byte
|
|
|
|
- bb2 := []byte("\x0C\x01\x20\x63\x00\x02\x13\x00\x13\x00\x3E\x05\x05\x00\x00\x00\x00\x00\x00\x00\x01\x00")
|
|
|
|
- C.memcpy(unsafe.Pointer(&bb[0]), unsafe.Pointer(&bb2[0]), C.size_t(len(bb2)))
|
|
|
|
- i := len(bb2)
|
|
|
|
- for index := 0; index < STOCK_PER_SERVER; index++ {
|
|
|
|
- if "" == symbols[index] {
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- bb[i] = exs[index]
|
|
|
|
- i++
|
|
|
|
- tmpsymbol := []byte(symbols[index])
|
|
|
|
- C.memcpy(unsafe.Pointer(&bb[i]), unsafe.Pointer(&tmpsymbol[0]), 6)
|
|
|
|
- i += 6
|
|
|
|
- }
|
|
|
|
- bb[20] = byte((i - 22) / 7) //数量
|
|
|
|
- len := uint16(i) - 10
|
|
|
|
- binary.LittleEndian.PutUint16(bb[6:], len)
|
|
|
|
- binary.LittleEndian.PutUint16(bb[8:], len)
|
|
|
|
-
|
|
|
|
- _, err := conn.Write(bb[:i])
|
|
|
|
- if err != nil {
|
|
|
|
- log.Println("readTicks.Write", err)
|
|
|
|
- conn.Close()
|
|
|
|
- conn = nil
|
|
|
|
- for {
|
|
|
|
- conn = tds.getTickConn()
|
|
|
|
- if conn != nil {
|
|
|
|
- break
|
|
|
|
- } else {
|
|
|
|
- time.Sleep(5 * time.Second)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- debuf, err := readBuf(conn)
|
|
|
|
- if err != nil {
|
|
|
|
- log.Println("readTicks.Read", err)
|
|
|
|
- conn.Close()
|
|
|
|
- conn = nil
|
|
|
|
- for {
|
|
|
|
- conn = tds.getTickConn()
|
|
|
|
- if conn != nil {
|
|
|
|
- break
|
|
|
|
- } else {
|
|
|
|
- time.Sleep(5 * time.Second)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- n := binary.LittleEndian.Uint16(debuf[2:])
|
|
|
|
- if n < 1 {
|
|
|
|
- log.Println("readTicks.n no data fetched")
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- buf := debuf[4:]
|
|
|
|
- i = 0
|
|
|
|
- var mks []*Market
|
|
|
|
- for j := 0; j < int(n); j++ {
|
|
|
|
- //m := buf[i]
|
|
|
|
- var code [8]byte
|
|
|
|
- C.memcpy(unsafe.Pointer(&code[0]), unsafe.Pointer(&buf[i+1]), 6)
|
|
|
|
- symbol := string(code[:6])
|
|
|
|
-
|
|
|
|
- mk := &Market{}
|
|
|
|
- mk.Type = IntTdx
|
|
|
|
- mk.InsId, _ = strconv.ParseInt(string(code[:6]), 10, 64)
|
|
|
|
- dd := float64(100.0)
|
|
|
|
- i += 9
|
|
|
|
- startPos := i
|
|
|
|
- mk.Close = float64(TDXDecode(buf, i, &i)) / dd
|
|
|
|
- mk.LastPrice = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- mk.Open = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- mk.High = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- mk.Low = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- TDXDecode(buf, i, &i) //Time := TDXDecode(buf, i, &i)
|
|
|
|
- mk.Timestamp = time.Now().Unix() * 1000 //tds.ParseTime(Time)
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- mk.LastVolume = float64(TDXDecode(buf, i, &i))
|
|
|
|
- TDXDecode(buf, i, &i) //现量
|
|
|
|
- mk.AllAmount = float64(TDXGetDouble(buf, i, &i))
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
-
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
-
|
|
|
|
- var bid, ask PP
|
|
|
|
- bid[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- ask[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- bid[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- ask[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- mk.Bids = append(mk.Bids, bid)
|
|
|
|
- mk.Asks = append(mk.Asks, ask)
|
|
|
|
- bid[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- ask[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- bid[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- ask[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- mk.Bids = append(mk.Bids, bid)
|
|
|
|
- mk.Asks = append(mk.Asks, ask)
|
|
|
|
-
|
|
|
|
- bid[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- ask[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- bid[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- ask[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- mk.Bids = append(mk.Bids, bid)
|
|
|
|
- mk.Asks = append(mk.Asks, ask)
|
|
|
|
-
|
|
|
|
- bid[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- ask[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- bid[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- ask[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- mk.Bids = append(mk.Bids, bid)
|
|
|
|
- mk.Asks = append(mk.Asks, ask)
|
|
|
|
-
|
|
|
|
- bid[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- ask[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- bid[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- ask[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- mk.Bids = append(mk.Bids, bid)
|
|
|
|
- mk.Asks = append(mk.Asks, ask)
|
|
|
|
- i += 3
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- TDXGetInt16(buf, i, &i)
|
|
|
|
- //float speed=(float)(t/100.0);
|
|
|
|
- TDXGetInt16(buf, i, &i)
|
|
|
|
- endPos := i
|
|
|
|
- dataStr := string(buf[startPos:endPos])
|
|
|
|
- bsame := false
|
|
|
|
- lastData, ok := lastDataMap[symbol]
|
|
|
|
- if ok {
|
|
|
|
- if lastData == dataStr {
|
|
|
|
- bsame = true
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if !bsame {
|
|
|
|
- mks = append(mks, mk)
|
|
|
|
- }
|
|
|
|
- lastDataMap[symbol] = dataStr
|
|
|
|
- }
|
|
|
|
- for _, v := range mks {
|
|
|
|
- //if v.InsId == 1 {
|
|
|
|
- //log.Println("[readTicks]data trace")
|
|
|
|
- //}
|
|
|
|
- tds.Save(v)
|
|
|
|
- }
|
|
|
|
- //end := time.Now().UnixNano()
|
|
|
|
- //log.Println("time used:", end-start)
|
|
|
|
-
|
|
|
|
- time.Sleep(time.Millisecond * FETCH_PER_MILLISECOND)
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
-func (tds *TdxDS) readTick(conn net.Conn, szOrsh byte, symbol string) ([]*Market, error) {
|
|
|
|
- bb1 := []byte("\x0C\x01\x20\x63\x00\x02\x13\x00\x13\x00\x3E\x05\x05\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x30\x30\x30\x30\x30\x31")
|
|
|
|
- bb1[22] = szOrsh //市场0深圳 1上海
|
|
|
|
- bBuf := bytes.NewBuffer(bb1[23:])
|
|
|
|
- bBuf.Reset()
|
|
|
|
- binary.Write(bBuf, binary.LittleEndian, symbol[:6])
|
|
|
|
- _, err := conn.Write(bb1)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- debuf, err := readBuf(conn)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- n := binary.LittleEndian.Uint16(debuf[2:])
|
|
|
|
- if n < 1 {
|
|
|
|
- return nil, errors.New("no data")
|
|
|
|
- }
|
|
|
|
- var i int
|
|
|
|
- buf := debuf[4:]
|
|
|
|
- var mks []*Market
|
|
|
|
- for j := 0; j < int(n); j++ {
|
|
|
|
- //m := buf[i]
|
|
|
|
- var code [8]byte
|
|
|
|
- C.memcpy(unsafe.Pointer(&code[0]), unsafe.Pointer(&buf[i+1]), 6)
|
|
|
|
- //codeStr := string(code[:6])
|
|
|
|
- //_, ok := tds.stocks[codeStr]
|
|
|
|
- //if !ok {
|
|
|
|
- //log.Println("invalid code:", codeStr)
|
|
|
|
- //continue
|
|
|
|
- //}
|
|
|
|
- mk := &Market{}
|
|
|
|
- mk.Type = IntTdx
|
|
|
|
- mk.InsId, _ = strconv.ParseInt(symbol, 10, 64)
|
|
|
|
- dd := float64(100.0)
|
|
|
|
- i += 9
|
|
|
|
- mk.Close = float64(TDXDecode(buf, i, &i)) / dd
|
|
|
|
- mk.LastPrice = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- mk.Open = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- mk.High = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- mk.Low = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- mk.Timestamp = tds.ParseTime(TDXDecode(buf, i, &i))
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- mk.LastVolume = float64(TDXDecode(buf, i, &i))
|
|
|
|
- TDXDecode(buf, i, &i) //现量
|
|
|
|
- mk.AllAmount = float64(TDXGetDouble(buf, i, &i))
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- var bid, ask PP
|
|
|
|
- bid[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- ask[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- bid[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- ask[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- mk.Bids = append(mk.Bids, bid)
|
|
|
|
- mk.Asks = append(mk.Asks, ask)
|
|
|
|
- bid[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- ask[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- bid[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- ask[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- mk.Bids = append(mk.Bids, bid)
|
|
|
|
- mk.Asks = append(mk.Asks, ask)
|
|
|
|
- bid[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- ask[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- bid[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- ask[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- mk.Bids = append(mk.Bids, bid)
|
|
|
|
- mk.Asks = append(mk.Asks, ask)
|
|
|
|
- bid[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- ask[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- bid[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- ask[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- mk.Bids = append(mk.Bids, bid)
|
|
|
|
- mk.Asks = append(mk.Asks, ask)
|
|
|
|
- bid[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- ask[0] = mk.Close + float64(TDXDecode(buf, i, &i))/dd
|
|
|
|
- bid[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- ask[1] = float64(TDXDecode(buf, i, &i))
|
|
|
|
- mk.Bids = append(mk.Bids, bid)
|
|
|
|
- mk.Asks = append(mk.Asks, ask)
|
|
|
|
- i += 3
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- TDXDecode(buf, i, &i)
|
|
|
|
- TDXGetInt16(buf, i, &i)
|
|
|
|
- //speed := float32(t) / 100.0
|
|
|
|
- TDXGetInt16(buf, i, &i)
|
|
|
|
- mks = append(mks, mk)
|
|
|
|
- //log.Println(mk)
|
|
|
|
- }
|
|
|
|
- return mks, nil
|
|
|
|
-}*/
|
|
|
|
-
|
|
|
|
-func (tds *TdxDS) ParseTime(tdxTime int32) int64 {
|
|
|
|
- tdxTimeStr := fmt.Sprintf("%d", tdxTime)
|
|
|
|
- tdxTimeBytes := []byte(tdxTimeStr)
|
|
|
|
- if tdxTimeBytes[0] != '1' && tdxTimeBytes[0] != '2' {
|
|
|
|
- tdxTimeStr = "0" + tdxTimeStr
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- datetimeStr := strconv.Itoa(int(tds.datetime)) + tdxTimeStr
|
|
|
|
- var year, month, day, hour, minute, second, millisecond int
|
|
|
|
- fmt.Sscanf(datetimeStr, "%04d%02d%02d%02d%02d%02d%d", &year, &month, &day, &hour, &minute, &second, &millisecond)
|
|
|
|
- if second > 59 {
|
|
|
|
- second -= 60
|
|
|
|
- minute++
|
|
|
|
- }
|
|
|
|
- //log.Println(datetimeStr, tdxTimeStr, tdxTimeBytes, tdxTime, year, month, day, hour, minute, second, millisecond)
|
|
|
|
- t := time.Date(year, time.Month(month), day, hour, minute, second, 0, time.Local)
|
|
|
|
- //t, _ := time.Parse("2015090909090909", datetimeStr)
|
|
|
|
- return t.Unix() * 1000
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-//解包数据
|
|
|
|
-func TDXDecode(buf []byte, start int, next *int) int32 {
|
|
|
|
- var num uint32
|
|
|
|
- var num3, num2, num4, num5, num6, num7, num8 int32
|
|
|
|
- var cc byte
|
|
|
|
-
|
|
|
|
- for num2 < 0x20 {
|
|
|
|
- cc = buf[int32(start)+num2]
|
|
|
|
- num4 = int32(cc)
|
|
|
|
- num5 = (num4 & 0x80) / 0x80
|
|
|
|
- if num2 == 0 {
|
|
|
|
- num3 = 1 - (((num4 & 0x40) / 0x40) * 2)
|
|
|
|
- num6 = num4 & 0x3F
|
|
|
|
- num = uint32(int64(num) + int64(num6))
|
|
|
|
- } else if num2 == 1 {
|
|
|
|
- num7 = (num4 & 0x7F) * (2 << (uint64(num2)*6 - 1)) // power(2, num2 * 6));
|
|
|
|
- num = uint32(int64(num) + int64(num7))
|
|
|
|
- } else {
|
|
|
|
- num8 = (num4 & 0x7F) * (2 << (uint64(num2)*7 - 2)) // Power(2, (num2 * 7) - 1);
|
|
|
|
- num = uint32(int64(num) + int64(num8))
|
|
|
|
- }
|
|
|
|
- if num5 == 0 {
|
|
|
|
- num = uint32(int64(num) * int64(num3))
|
|
|
|
- break
|
|
|
|
- }
|
|
|
|
- num2++
|
|
|
|
- }
|
|
|
|
- *next = start + int(num2) + 1
|
|
|
|
- return int32(num)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-//读取16位数据
|
|
|
|
-func TDXGetInt16(buf []byte, start int, next *int) int16 {
|
|
|
|
- Num := binary.LittleEndian.Uint16(buf[start:])
|
|
|
|
- *next = start + 2
|
|
|
|
- return int16(Num)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-//读取32位数据
|
|
|
|
-func TDXGetInt32(buf []byte, start int, next *int) int32 {
|
|
|
|
- Num := binary.LittleEndian.Uint32(buf[start:])
|
|
|
|
- *next = start + 4
|
|
|
|
- return int32(Num)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-//读取浮点数据float
|
|
|
|
-func TDXGetDouble(buf []byte, start int, next *int) float32 {
|
|
|
|
- var Num float32
|
|
|
|
- bBuf := bytes.NewBuffer(buf[start:])
|
|
|
|
- binary.Read(bBuf, binary.LittleEndian, &Num)
|
|
|
|
- *next = start + 4
|
|
|
|
- return Num
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-//读取时间:HHMM
|
|
|
|
-func TDXGetTime(buf []byte, start int, next *int) int {
|
|
|
|
- i := TDXGetInt16(buf, start, next)
|
|
|
|
- mm := (i / 60)
|
|
|
|
- ss := (i % 60)
|
|
|
|
- if ss > 59 {
|
|
|
|
- ss = ss - 60
|
|
|
|
- mm++
|
|
|
|
- }
|
|
|
|
- ri := mm*100 + ss
|
|
|
|
- return int(ri)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func TDXGetDate(v int32, yy *int, mm *int, dd *int, hhh *int, mmm *int) {
|
|
|
|
- *yy = 2012
|
|
|
|
- *mm = 1
|
|
|
|
- *dd = 1
|
|
|
|
- *hhh = 9
|
|
|
|
- *mmm = 30
|
|
|
|
- if v > 21000000 {
|
|
|
|
- *yy = int(2004 + ((v & 0xF800) >> 11))
|
|
|
|
- d1 := v & 0x7FF
|
|
|
|
- *mm = int(d1 / 100)
|
|
|
|
- *dd = int(d1 % 100)
|
|
|
|
- d2 := v >> 16
|
|
|
|
- *hhh = int(d2 / 60)
|
|
|
|
- *mmm = int(d2 % 60)
|
|
|
|
- } else {
|
|
|
|
- *yy = int(v / 10000)
|
|
|
|
- *mm = (int(v) - *yy*10000) / 100
|
|
|
|
- *dd = int(v % 100)
|
|
|
|
- *hhh = 9
|
|
|
|
- *mmm = 30
|
|
|
|
- }
|
|
|
|
-} //解包数据
|
|
|
|
-
|
|
|
|
-func inTime() bool {
|
|
|
|
- t := time.Now()
|
|
|
|
- if t.Weekday() == time.Saturday || t.Weekday() == time.Sunday {
|
|
|
|
- return false
|
|
|
|
- }
|
|
|
|
- mm := t.Hour()*60 + t.Minute()
|
|
|
|
- for _, ti := range cffexTi {
|
|
|
|
- m1 := ti.st.hour*60 + ti.st.minute
|
|
|
|
- m2 := ti.et.hour*60 + ti.et.minute
|
|
|
|
- if mm >= m1 && mm <= m2 {
|
|
|
|
- return true
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return false
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func readBuf(conn net.Conn) ([]byte, error) {
|
|
|
|
- var head RecvDataHeader
|
|
|
|
- err := binary.Read(conn, binary.LittleEndian, &head)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- if head.CheckSum != 7654321 {
|
|
|
|
- return nil, errors.New("error checksum")
|
|
|
|
- }
|
|
|
|
- buf := make([]byte, int(head.Size))
|
|
|
|
- n, err := io.ReadFull(conn, buf)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- if int(head.Size) != n {
|
|
|
|
- return nil, errors.New("read size error")
|
|
|
|
- }
|
|
|
|
- //log.Println(head)
|
|
|
|
- var debuf []byte
|
|
|
|
- if (head.EncodeMode & 0x10) == 0x10 { //gzip compress
|
|
|
|
- reader, err := zlib.NewReader(bytes.NewBuffer(buf))
|
|
|
|
- defer reader.Close()
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- debuf = make([]byte, int(head.DePackSize))
|
|
|
|
- n, err := io.ReadFull(reader, debuf)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- if n != int(head.DePackSize) {
|
|
|
|
- return nil, errors.New("depack size error")
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- debuf = buf
|
|
|
|
- }
|
|
|
|
- return debuf, nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func loadServers(fname string) ([]string, error) {
|
|
|
|
- fp, err := os.Open(fname)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- defer fp.Close()
|
|
|
|
- fi, err := fp.Stat()
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- buf := make([]byte, fi.Size())
|
|
|
|
- n, err := io.ReadFull(fp, buf)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- if n != len(buf) {
|
|
|
|
- return nil, errors.New("can't read all data")
|
|
|
|
- }
|
|
|
|
- var realservers []string
|
|
|
|
- servers := strings.Split(string(buf), "\n")
|
|
|
|
- //去重
|
|
|
|
- serversMap := make(map[string]int)
|
|
|
|
- for _, server := range servers {
|
|
|
|
- _, ok := serversMap[server]
|
|
|
|
- if !ok {
|
|
|
|
- realservers = append(realservers, server)
|
|
|
|
- serversMap[server] = 0
|
|
|
|
- } else {
|
|
|
|
- log.Println("duplicate server:", server)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- log.Println("servers:", len(servers), "realservers:", len(realservers))
|
|
|
|
- return realservers, nil
|
|
|
|
-}
|
|
|