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 }