gitee.com/quant1x/engine@v1.8.4/datasource/base/tdx_kline_basic.go (about)

     1  package base
     2  
     3  import (
     4  	"gitee.com/quant1x/engine/cache"
     5  	"gitee.com/quant1x/exchange"
     6  	"gitee.com/quant1x/gotdx"
     7  	"gitee.com/quant1x/gotdx/proto"
     8  	"gitee.com/quant1x/gotdx/quotes"
     9  	"gitee.com/quant1x/gox/api"
    10  	"gitee.com/quant1x/gox/logger"
    11  )
    12  
    13  var (
    14  	// DataDaysDiff 日期差异偏移量
    15  	DataDaysDiff = 1
    16  )
    17  
    18  // KLine 日K线基础结构
    19  type KLine struct {
    20  	Date     string  `name:"日期" dataframe:"date"`       // 日期
    21  	Open     float64 `name:"开盘" dataframe:"open"`       // 开盘价
    22  	Close    float64 `name:"收盘" dataframe:"close"`      // 收盘价
    23  	High     float64 `name:"最高" dataframe:"high"`       // 最高价
    24  	Low      float64 `name:"最低" dataframe:"low"`        // 最低价
    25  	Volume   float64 `name:"成交量(股)" dataframe:"volume"` // 成交量
    26  	Amount   float64 `name:"成交额(元)" dataframe:"amount"` // 成交金额
    27  	Up       int     `name:"上涨/外盘" dataframe:"up"`      // 上涨家数
    28  	Down     int     `name:"下跌/内盘" dataframe:"down"`    // 下跌家数
    29  	Datetime string  `name:"时间" dataframe:"datetime"`   // 时间
    30  }
    31  
    32  // LoadBasicKline 加载基础K线
    33  func LoadBasicKline(securityCode string) []KLine {
    34  	filename := cache.KLineFilename(securityCode)
    35  	var klines []KLine
    36  	_ = api.CsvToSlices(filename, &klines)
    37  	return klines
    38  }
    39  
    40  // UpdateAllBasicKLine 更新全部日K线基础数据并保存文件
    41  func UpdateAllBasicKLine(securityCode string) []KLine {
    42  	// 1. 确定本地有效数据最后1条数据作为拉取数据的开始日期
    43  	startDate := exchange.MARKET_CN_FIRST_DATE
    44  	securityCode = exchange.CorrectSecurityCode(securityCode)
    45  	isIndex := exchange.AssertIndexBySecurityCode(securityCode)
    46  	cacheKLines := LoadBasicKline(securityCode)
    47  	kLength := len(cacheKLines)
    48  	var klineDaysOffset = DataDaysDiff
    49  	if kLength > 0 {
    50  		if klineDaysOffset > kLength {
    51  			klineDaysOffset = kLength
    52  		}
    53  		startDate = cacheKLines[kLength-klineDaysOffset].Date
    54  	} else {
    55  		//f10 := flash.GetL5F10(securityCode)
    56  		//if f10 != nil && len(f10.IpoDate) > 0 {
    57  		//	startDate = f10.IpoDate
    58  		//	startDate = trading.FixTradeDate(startDate)
    59  		//}
    60  	}
    61  	// 2. 确定结束日期
    62  	endDate := exchange.Today()
    63  	ts := exchange.TradeRange(startDate, endDate)
    64  	history := make([]quotes.SecurityBar, 0)
    65  	step := uint16(quotes.TDX_SECURITY_BARS_MAX)
    66  	total := uint16(len(ts))
    67  	start := uint16(0)
    68  	hs := make([]quotes.SecurityBarsReply, 0)
    69  	kType := uint16(proto.KLINE_TYPE_RI_K)
    70  	tdxApi := gotdx.GetTdxApi()
    71  	// 3. 拉取数据
    72  	for {
    73  		count := step
    74  		if total-start >= step {
    75  			count = step
    76  		} else {
    77  			count = total - start
    78  		}
    79  		var data *quotes.SecurityBarsReply
    80  		var err error
    81  		retryTimes := 0
    82  		for retryTimes < quotes.DefaultRetryTimes {
    83  			if isIndex {
    84  				data, err = tdxApi.GetIndexBars(securityCode, kType, start, count)
    85  			} else {
    86  				data, err = tdxApi.GetKLine(securityCode, kType, start, count)
    87  			}
    88  			if err == nil && data != nil {
    89  				break
    90  			}
    91  			retryTimes++
    92  		}
    93  		if err != nil {
    94  			logger.Errorf("code=%s, error=%s", securityCode, err.Error())
    95  			return []KLine{}
    96  		}
    97  		hs = append(hs, *data)
    98  		if data.Count < count {
    99  			// 已经是最早的记录
   100  			// 需要排序
   101  			break
   102  		}
   103  		start += count
   104  		if start >= total {
   105  			break
   106  		}
   107  	}
   108  	// 4. 由于K线数据,每次获取数据是从后往前获取, 所以这里需要反转历史数据的切片
   109  	hs = api.Reverse(hs)
   110  	startDate = exchange.FixTradeDate(startDate)
   111  	// 5. 调整成交量, 单位从手改成股, vol字段 * 100
   112  	for _, v := range hs {
   113  		for _, row := range v.List {
   114  			dateTime := exchange.FixTradeDate(row.DateTime)
   115  			if dateTime < startDate {
   116  				continue
   117  			}
   118  			row.Vol = row.Vol * 100
   119  			history = append(history, row)
   120  		}
   121  	}
   122  	// 6. k线数据转换成KLine结构
   123  	var newKLines []KLine
   124  	for _, v := range history {
   125  		date := exchange.FixTradeDate(v.DateTime)
   126  		kline := KLine{
   127  			Date:     date,
   128  			Open:     v.Open,
   129  			Close:    v.Close,
   130  			High:     v.High,
   131  			Low:      v.Low,
   132  			Volume:   v.Vol,
   133  			Amount:   v.Amount,
   134  			Up:       int(v.UpCount),
   135  			Down:     int(v.DownCount),
   136  			Datetime: v.DateTime,
   137  		}
   138  		newKLines = append(newKLines, kline)
   139  	}
   140  	// 7. 前复权
   141  	calculatePreAdjustedStockPrice(securityCode, newKLines, startDate)
   142  	// 8. 拼接缓存和新增的数据
   143  	var klines []KLine
   144  	// 8.1 先截取本地缓存的数据
   145  	if kLength > klineDaysOffset {
   146  		klines = cacheKLines[:kLength-klineDaysOffset]
   147  	}
   148  	// 8.2 拼接新增的数据
   149  	if len(klines) > 0 {
   150  		klines = append(klines, newKLines...)
   151  	} else {
   152  		klines = newKLines
   153  	}
   154  	// 9. 刷新缓存文件
   155  	if len(klines) > 0 {
   156  		UpdateCacheKLines(securityCode, klines)
   157  		fname := cache.KLineFilename(securityCode)
   158  		_ = api.SlicesToCsv(fname, klines)
   159  	}
   160  	return klines
   161  }
   162  
   163  // 计算前复权
   164  // startDate 表示已经除权的日期
   165  func calculatePreAdjustedStockPrice(securityCode string, kLines []KLine, startDate string) {
   166  	rows := len(kLines)
   167  	if rows == 0 {
   168  		return
   169  	}
   170  	// 复权之前, 假定当前缓存之中的数据都是复权过的数据
   171  	// 那么就应该只拉取缓存最后1条记录之后的除权除息记录进行复权
   172  	// 前复权adjust
   173  	dividends := GetCacheXdxrList(securityCode)
   174  	cacheLastDay := kLines[rows-1].Date
   175  	for i := 0; i < len(dividends); i++ {
   176  		xdxr := dividends[i]
   177  		if xdxr.Category != 1 || xdxr.Date < startDate || xdxr.Date > cacheLastDay {
   178  			// 忽略非除权信息以及除权数据在新数据之前的除权记录
   179  			continue
   180  		}
   181  		xdxrDate := xdxr.Date
   182  		factor := xdxr.Adjust()
   183  		for j := 0; j < rows; j++ {
   184  			kl := &kLines[j]
   185  			barCurrentDate := kl.Date
   186  			if barCurrentDate > xdxrDate {
   187  				break
   188  			}
   189  			if barCurrentDate < xdxrDate {
   190  				kl.Open = factor(kl.Open)
   191  				kl.Close = factor(kl.Close)
   192  				kl.High = factor(kl.High)
   193  				kl.Low = factor(kl.Low)
   194  				// 成交量复权
   195  				// 1. 计算均价线
   196  				maPrice := kl.Amount / kl.Volume
   197  				// 2. 均价线复权
   198  				maPrice = factor(maPrice)
   199  				// 3. 以成交金额为基准, 用复权均价线计算成交量
   200  				kl.Volume = kl.Amount / maPrice
   201  			}
   202  			if barCurrentDate == xdxrDate {
   203  				break
   204  			}
   205  		}
   206  	}
   207  }