gitee.com/quant1x/engine@v1.8.4/factors/dataset_wide_old.go (about) 1 package factors 2 3 import ( 4 "gitee.com/quant1x/engine/cache" 5 "gitee.com/quant1x/engine/datasource/base" 6 "gitee.com/quant1x/exchange" 7 "gitee.com/quant1x/gotdx" 8 "gitee.com/quant1x/gotdx/proto" 9 "gitee.com/quant1x/gotdx/quotes" 10 "gitee.com/quant1x/gox/api" 11 "gitee.com/quant1x/gox/logger" 12 "gitee.com/quant1x/num" 13 "gitee.com/quant1x/pandas" 14 . "gitee.com/quant1x/pandas/formula" 15 "reflect" 16 "strconv" 17 ) 18 19 var ( 20 FBarsProtocolFields = []string{"Open", "Close", "High", "Low", "Vol", "Amount", "DateTime", "UpCount", "DownCount"} 21 FBarsRawFields = []string{"open", "close", "high", "low", "volume", "amount", "date", "up", "down"} 22 FBarsHalfFields = []string{"date", "open", "close", "high", "low", "volume", "amount", "up", "down", "open_volume", "open_turnz", "open_unmatched", "close_volume", "close_turnz", "close_unmatched", "inner_volume", "outer_volume", "inner_amount", "outer_amount"} 23 FBarsWideFields = []string{"date", "open", "close", "high", "low", "volume", "amount", "up", "down", "last_close", "change_rate", "open_volume", "open_turnz", "open_unmatched", "close_volume", "close_turnz", "close_unmatched", "inner_volume", "outer_volume", "inner_amount", "outer_amount"} 24 ) 25 26 // SetKLineOffset 设置K线数据调整回补天数 27 func SetKLineOffset(days int) { 28 if days <= 1 { 29 return 30 } 31 base.DataDaysDiff = days 32 } 33 34 // loadCacheKLine 加载K线 35 // 36 // 第2个参数, 是否前复权 37 func loadCacheKLine(code string, adjust ...bool) pandas.DataFrame { 38 // 默认不复权 39 qfq := false 40 if len(adjust) > 0 { 41 qfq = adjust[0] 42 } 43 filename := cache.WideFilename(code) 44 var df pandas.DataFrame 45 if !api.FileExist(filename) { 46 return df 47 } else { 48 df = pandas.ReadCSV(filename) 49 } 50 // 调整字段流程 51 { 52 // turnover_rate 改为 change_rate 53 df.SetName("turnover_rate", "change_rate") 54 } 55 56 fields := FBarsWideFields 57 df = df.Select(fields) 58 if df.Nrow() == 0 { 59 return df 60 } 61 if qfq { 62 xdxrs := base.GetCacheXdxrList(code) 63 for i := 0; i < len(xdxrs); i++ { 64 xdxr := xdxrs[i] 65 if xdxr.Category != 1 { 66 continue 67 } 68 xdxrDate := xdxr.Date 69 factor := xdxr.Adjust() 70 for j := 0; j < df.Nrow(); j++ { 71 m1 := df.IndexOf(j, true) 72 dt := m1["date"].(reflect.Value).String() 73 if dt > xdxrDate { 74 break 75 } 76 if dt < xdxrDate { 77 po := m1["open"].(reflect.Value) 78 po.SetFloat(factor(po.Float())) 79 pc := m1["close"].(reflect.Value) 80 pc.SetFloat(factor(pc.Float())) 81 ph := m1["high"].(reflect.Value) 82 ph.SetFloat(factor(ph.Float())) 83 pl := m1["low"].(reflect.Value) 84 pl.SetFloat(factor(pl.Float())) 85 } 86 plc := m1["last_close"].(reflect.Value) 87 plc.SetFloat(factor(plc.Float())) 88 if dt == xdxrDate { 89 break 90 } 91 } 92 } 93 } 94 return df 95 } 96 97 // GetCacheKLine 加载K线 98 // 99 // 第2个参数, 是否前复权 100 func GetCacheKLine(code string, adjust ...bool) pandas.DataFrame { 101 df := loadCacheKLine(code, adjust...) 102 if df.Nrow() == 0 { 103 return df 104 } 105 // 取出成交量序列 106 VOL := df.Col("volume") 107 DATES := df.Col("date") 108 lastDay := DATES.IndexOf(-1).(string) 109 total := df.Nrow() 110 // 计算5日均量 111 mv5 := MA(VOL, 5) 112 mav := REF(mv5, 1) 113 lb := VOL.Div(mav) 114 lb = lb.Apply2(func(idx int, v any) any { 115 if idx+1 < total { 116 return v 117 } else { 118 tmp := num.Any2DType(v) 119 ms := num.DType(exchange.Minutes(lastDay)) / float64(exchange.CN_TOTALFZNUM) 120 tmp /= ms 121 return tmp 122 } 123 }, true) 124 125 // 链接量比序列 126 oLB := pandas.NewSeriesWithType(pandas.SERIES_TYPE_DTYPE, "lb", lb.DTypes()) 127 oMV5 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_DTYPE, "mv5", mv5.Div(exchange.CN_DEFAULT_TOTALFZNUM).DTypes()) 128 vr := VOL.Div(REF(VOL, 1)) 129 oVR := pandas.NewSeriesWithType(pandas.SERIES_TYPE_DTYPE, "vr", vr.DTypes()) 130 CLOSE := df.Col("close") 131 chg5 := CLOSE.Div(REF(CLOSE, 5)).Sub(1.00).Mul(100) 132 chg10 := CLOSE.Div(REF(CLOSE, 10)).Sub(1.00).Mul(100) 133 oChg5 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "chg5", chg5.DTypes()) 134 oChg10 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "chg10", chg10.DTypes()) 135 ma5 := MA(CLOSE, 5) 136 ma10 := MA(CLOSE, 10) 137 ma20 := MA(CLOSE, 20) 138 oMA5 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "ma5", ma5.DTypes()) 139 oMA10 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "ma10", ma10.DTypes()) 140 oMA20 := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "ma20", ma20.DTypes()) 141 AMOUNT := df.Col("amount") 142 averagePrice := AMOUNT.Div(VOL) 143 oAP := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "ap", averagePrice.DTypes()) 144 df = df.Join(oLB, oMV5, oVR, oChg5, oChg10, oMA5, oMA10, oMA20, oAP) 145 return df 146 } 147 148 // GetKLineAll 获取日K线 149 func GetKLineAll(securityCode string, argv ...int) pandas.DataFrame { 150 kType := uint16(proto.KLINE_TYPE_RI_K) 151 if len(argv) == 1 { 152 kType = uint16(argv[0]) 153 } 154 securityCode = exchange.CorrectSecurityCode(securityCode) 155 tdxApi := gotdx.GetTdxApi() 156 startDate := exchange.MARKET_CH_FIRST_LISTTIME 157 // 默认是缓存中是已经复权的数据 158 dfCache := loadCacheKLine(securityCode) 159 isIndex := exchange.AssertIndexBySecurityCode(securityCode) 160 rawFields := FBarsProtocolFields 161 newFields := FBarsRawFields 162 // 尝试选择一次字段, 如果出现异常, 则清空dataframe, 重新下载 163 dfCache = dfCache.Select(FBarsWideFields) 164 if dfCache.Nrow() == 0 { 165 dfCache = pandas.DataFrame{} 166 } 167 var info *quotes.FinanceInfo 168 var err error 169 var klineDaysOffset = base.DataDaysDiff 170 if dfCache.Nrow() > 0 { 171 ds := dfCache.Col("date").Strings() 172 if klineDaysOffset > len(ds) { 173 klineDaysOffset = len(ds) 174 } 175 startDate = ds[len(ds)-klineDaysOffset] 176 } else { 177 info, err = tdxApi.GetFinanceInfo(securityCode) 178 if err != nil { 179 return dfCache 180 } 181 if info.IPODate > 0 { 182 startDate = strconv.FormatInt(int64(info.IPODate), 10) 183 startDate = exchange.FixTradeDate(startDate) 184 } 185 } 186 endDate := exchange.Today() 187 ts := exchange.TradeRange(startDate, endDate) 188 history := []quotes.SecurityBar{} 189 step := uint16(quotes.TDX_SECURITY_BARS_MAX) 190 total := uint16(len(ts)) 191 start := uint16(0) 192 hs := []quotes.SecurityBarsReply{} 193 for { 194 count := step 195 if total-start >= step { 196 count = step 197 } else { 198 count = total - start 199 } 200 var data *quotes.SecurityBarsReply 201 var err error 202 retryTimes := 0 203 for retryTimes < quotes.DefaultRetryTimes { 204 if isIndex { 205 data, err = tdxApi.GetIndexBars(securityCode, kType, start, count) 206 } else { 207 data, err = tdxApi.GetKLine(securityCode, kType, start, count) 208 } 209 if err == nil && data != nil { 210 break 211 } 212 retryTimes++ 213 } 214 if err != nil { 215 logger.Errorf("code=%s, error=%s", securityCode, err.Error()) 216 return pandas.DataFrame{} 217 } 218 hs = append(hs, *data) 219 if data.Count < count { 220 // 已经是最早的记录 221 // 需要排序 222 break 223 } 224 start += count 225 if start >= total { 226 break 227 } 228 } 229 hs = api.Reverse(hs) 230 startDate = exchange.FixTradeDate(startDate) 231 for _, v := range hs { 232 for _, row := range v.List { 233 dateTime := exchange.FixTradeDate(row.DateTime) 234 if dateTime < startDate { 235 continue 236 } 237 row.Vol = row.Vol * 100 238 history = append(history, row) 239 } 240 } 241 dfNew := pandas.LoadStructs(history) 242 dfNew = dfNew.Select(rawFields) 243 err = dfNew.SetNames(newFields...) 244 if err != nil { 245 return pandas.DataFrame{} 246 } 247 ds1 := dfNew.Col("date", true) 248 ds1.Apply2(func(idx int, v any) any { 249 date1 := v.(string) 250 dt, err := api.ParseTime(date1) 251 if err != nil { 252 return date1 253 } 254 return dt.Format(exchange.TradingDayDateFormat) 255 }, true) 256 // 补充昨日收盘和涨跌幅 257 dfNew = attachVolume(dfNew, securityCode) 258 dfNew = dfNew.Select(FBarsHalfFields) 259 if dfNew.Nrow() > 0 { 260 // 除权除息 261 xdxrs := base.GetCacheXdxrList(securityCode) 262 cacheLastDay := dfNew.Col("date").IndexOf(-1).(string) 263 for i := 0; i < len(xdxrs); i++ { 264 xdxr := xdxrs[i] 265 if xdxr.Category != 1 || xdxr.Date < startDate || xdxr.Date > cacheLastDay { 266 continue 267 } 268 xdxrDate := xdxr.Date 269 factor := xdxr.Adjust() 270 for j := 0; j < dfNew.Nrow(); j++ { 271 m1 := dfNew.IndexOf(j, true) 272 barCurrentDate := m1["date"].(reflect.Value).String() 273 if barCurrentDate > xdxrDate { 274 break 275 } 276 if barCurrentDate < xdxrDate { 277 po := m1["open"].(reflect.Value) 278 po.SetFloat(factor(po.Float())) 279 pc := m1["close"].(reflect.Value) 280 pc.SetFloat(factor(pc.Float())) 281 ph := m1["high"].(reflect.Value) 282 ph.SetFloat(factor(ph.Float())) 283 pl := m1["low"].(reflect.Value) 284 pl.SetFloat(factor(pl.Float())) 285 } 286 if barCurrentDate == xdxrDate { 287 break 288 } 289 } 290 } 291 } 292 dfCache = dfCache.Select(FBarsHalfFields) 293 df := dfCache.Subset(0, dfCache.Nrow()-klineDaysOffset) 294 if df.Nrow() > 0 { 295 df = df.Concat(dfNew) 296 } else { 297 df = dfNew 298 } 299 CLOSE := df.Col("close") 300 LAST := CLOSE.Shift(1) 301 rate := CLOSE.Sub(LAST).Div(LAST).Mul(100.00).DTypes() 302 303 lc := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "last_close", LAST.DTypes()) 304 tr := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "change_rate", rate) 305 df = df.Join(lc, tr) 306 df = df.Select(FBarsWideFields) 307 if df.Nrow() > 0 { 308 filename := cache.WideFilename(securityCode) 309 _ = df.WriteCSV(filename) 310 } 311 return df 312 } 313 314 // 附加成交量 315 func attachVolume(df pandas.DataFrame, securityCode string) pandas.DataFrame { 316 securityCode = exchange.CorrectSecurityCode(securityCode) 317 dates := df.Col("date").Strings() 318 if len(dates) == 0 { 319 return df 320 } 321 buyVolumes := []int64{} 322 sellVolumes := []int64{} 323 buyAmounts := []num.DType{} 324 sellAmounts := []num.DType{} 325 326 openVolumes := []int64{} 327 openTurnZ := []num.DType{} 328 openUnmatched := []int64{} 329 closeVolumes := []int64{} 330 closeTurnZ := []num.DType{} 331 closeUnmatched := []int64{} 332 for _, tradeDate := range dates { 333 tmp := base.CheckoutTransactionData(securityCode, tradeDate, true) 334 logger.Warnf("tick: code=%s, date=%s, rows=%d", securityCode, tradeDate, len(tmp)) 335 //summary := InflowCount(tmp, securityCode) 336 summary := CountInflow(tmp, securityCode, tradeDate) 337 buyVolumes = append(buyVolumes, summary.OuterVolume) 338 sellVolumes = append(sellVolumes, summary.InnerVolume) 339 buyAmounts = append(buyAmounts, summary.OuterAmount) 340 sellAmounts = append(sellAmounts, summary.InnerAmount) 341 342 openVolumes = append(openVolumes, summary.OpenVolume) 343 openTurnZ = append(openTurnZ, summary.OpenTurnZ) 344 openUnmatched = append(openUnmatched, summary.OpenUnmatched) 345 closeVolumes = append(closeVolumes, summary.CloseVolume) 346 closeTurnZ = append(closeTurnZ, summary.CloseTurnZ) 347 closeUnmatched = append(closeUnmatched, summary.CloseUnmatched) 348 } 349 // 调整字段名 350 bv := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "outer_volume", buyVolumes) 351 sv := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "inner_volume", sellVolumes) 352 ba := pandas.NewSeriesWithType(pandas.SERIES_TYPE_DTYPE, "outer_amount", buyAmounts) 353 sa := pandas.NewSeriesWithType(pandas.SERIES_TYPE_DTYPE, "inner_amount", sellAmounts) 354 355 // 新增字段 356 ov := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "open_volume", openVolumes) 357 ot := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "open_turnz", openTurnZ) 358 ou := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "open_unmatched", openUnmatched) 359 cv := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "close_volume", closeVolumes) 360 ct := pandas.NewSeriesWithType(pandas.SERIES_TYPE_FLOAT64, "close_turnz", closeTurnZ) 361 cu := pandas.NewSeriesWithType(pandas.SERIES_TYPE_INT64, "close_unmatched", closeUnmatched) 362 363 df = df.Join(bv, sv, ba, sa, ov, ot, ou, cv, ct, cu) 364 return df 365 }