├── README.md ├── __init__.py ├── requirements.txt ├── trendline.py └── trendline_bak.py /README.md: -------------------------------------------------------------------------------- 1 | # stock_indictor 2 | 股票技术指标 3 | 4 | # trendline.py 5 | 文件中所有函数结果与同花顺校验过,并且全部通过numpy和pandas计算出来 6 | 7 | # trendline_bak.py 8 | 文件中函数不全面,只是有些函数写了两种计算方法,一般第一种算法是参照@liaocyintl 在tushare中写得,有些地方有修改,第二种方法其实就是trendline.py中呈现出来的。用作参考,方便理解 9 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzili/stock_indictor/191d102bbd7f2edfd513ea8d616d348fdf1a9bea/__init__.py -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | pandas 3 | tushare 4 | -------------------------------------------------------------------------------- /trendline.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | """ 4 | 股票技术指标接口 5 | Created on 2018/07/26 6 | @author: Wangzili 7 | @group : ** 8 | @contact: 446406177@qq.com 9 | 10 | 所有指标中参数df为通过get_k_data获取的股票数据 11 | """ 12 | import pandas as pd 13 | import numpy as np 14 | import itertools 15 | 16 | 17 | def ma(df, n=10): 18 | """ 19 | 移动平均线 Moving Average 20 | MA(N)=(第1日收盘价+第2日收盘价—+……+第N日收盘价)/N 21 | """ 22 | pv = pd.DataFrame() 23 | pv['date'] = df['date'] 24 | pv['v'] = df.close.rolling(n).mean() 25 | return pv 26 | 27 | 28 | def _ma(series, n): 29 | """ 30 | 移动平均 31 | """ 32 | return series.rolling(n).mean() 33 | 34 | 35 | def md(df, n=10): 36 | """ 37 | 移动标准差 38 | STD=S(CLOSE,N)=[∑(CLOSE-MA(CLOSE,N))^2/N]^0.5 39 | """ 40 | _md = pd.DataFrame() 41 | _md['date'] = df.date 42 | _md["md"] = df.close.rolling(n).std(ddof=0) 43 | return _md 44 | 45 | 46 | def _md(series, n): 47 | """ 48 | 标准差MD 49 | """ 50 | return series.rolling(n).std(ddof=0) # 有时候会用ddof=1 51 | 52 | 53 | def ema(df, n=12): 54 | """ 55 | 指数平均数指标 Exponential Moving Average 56 | 今日EMA(N)=2/(N+1)×今日收盘价+(N-1)/(N+1)×昨日EMA(N) 57 | EMA(X,N)=[2×X+(N-1)×EMA(ref(X),N]/(N+1) 58 | """ 59 | _ema = pd.DataFrame() 60 | _ema['date'] = df['date'] 61 | _ema['ema'] = df.close.ewm(ignore_na=False, span=n, min_periods=0, adjust=False).mean() 62 | return _ema 63 | 64 | 65 | def _ema(series, n): 66 | """ 67 | 指数平均数 68 | """ 69 | return series.ewm(ignore_na=False, span=n, min_periods=0, adjust=False).mean() 70 | 71 | 72 | def dma(df, n=10, m=50, k=10): 73 | """ 74 | 平均线差 DMA(10,50,10) 75 | DDD=MA(N1)-MA(N2) 76 | AMA= MA(DDD,M) 77 | 其中N1表示短期天数,N2表示长期天数,M表示AMA的天数 78 | """ 79 | _dma = pd.DataFrame() 80 | _dma['date'] = df['date'] 81 | _dma['ddd'] = _ma(df.close, n) - _ma(df.close, m) 82 | _dma['ama'] = _ma(_dma.ddd, k) 83 | return _dma 84 | 85 | 86 | def dmi(df, n=14, m=6): 87 | """ 88 | # 通达信计算方法 89 | TRZ: = EMA(MAX(MAX(HIGH - LOW, ABS(HIGH - REF(CLOSE, 1))), ABS(REF(CLOSE, 1) - LOW)), 7); 90 | HD: = HIGH - REF(HIGH, 1); 91 | LD: = REF(LOW, 1) - LOW; 92 | DMP: = EMA(IF(HD > 0 AND HD > LD, HD, 0), 7); 93 | DMM: = EMA(IF(LD > 0 AND LD > HD, LD, 0), 7); 94 | PDI: DMP * 100 / TRZ; 95 | MDI: DMM * 100 / TRZ; 96 | ADX: EMA(ABS(MDI - PDI) / (MDI + PDI) * 100, 7); 97 | ADXR: EMA(ADX, 7); 98 | tr = pd.Series(np.vstack([df.high - df.low, (df.high - df.close.shift()).abs(), (df.low - df.close.shift()).abs()]).max(axis=0)) 99 | trz = _ema(tr, n) 100 | _m = pd.DataFrame() 101 | _m['hd'] = df.high - df.high.shift() 102 | _m['ld'] = df.low.shift() - df.low 103 | _m['mp'] = _m.apply(lambda x: x.hd if x.hd > 0 and x.hd > x.ld else 0, axis=1) 104 | _m['mm'] = _m.apply(lambda x: x.ld if x.ld > 0 and x.hd < x.ld else 0, axis=1) 105 | _m['dmp'] = _ema(_m.mp, n) 106 | _m['dmm'] = _ema(_m.mm, n) 107 | _dmi = pd.DataFrame() 108 | _dmi['date'] = df.date 109 | _dmi['pdi'] = _m.dmp * 100 / trz 110 | _dmi['mdi'] = _m.dmm * 100 / trz 111 | _dmi['adx'] = _ema((_dmi.mdi - _dmi.pdi).abs() / (_dmi.mdi + _dmi.pdi) * 100, m) 112 | _dmi['adxr'] = _ema(_dmi.adx, m) 113 | return _dmi 114 | """ 115 | """ 116 | # 同花顺计算方式 117 | TR := SUM(MAX(MAX(HIGH-LOW,ABS(HIGH-REF(CLOSE,1))),ABS(LOW-REF(CLOSE,1))),N); 118 | HD: = HIGH - REF(HIGH, 1); 119 | LD: = REF(LOW, 1) - LOW; 120 | DMP:= SUM(IF(HD>0 AND HD>LD,HD,0),N); 121 | DMM:= SUM(IF(LD>0 AND LD>HD,LD,0),N); 122 | +DI: DMP*100/TR; 123 | -DI: DMM*100/TR; 124 | ADX: MA(ABS(-DI-+DI)/(-DI++DI)*100,M); 125 | ADXR:(ADX+REF(ADX,M))/2; 126 | """ 127 | tr = pd.Series(np.vstack([df.high - df.low, (df.high - df.close.shift()).abs(), (df.low - df.close.shift()).abs()]).max(axis=0)) 128 | trz = tr.rolling(n).sum() 129 | _m = pd.DataFrame() 130 | _m['hd'] = df.high - df.high.shift() 131 | _m['ld'] = df.low.shift() - df.low 132 | _m['mp'] = _m.apply(lambda x: x.hd if x.hd > 0 and x.hd > x.ld else 0, axis=1) 133 | _m['mm'] = _m.apply(lambda x: x.ld if x.ld > 0 and x.hd < x.ld else 0, axis=1) 134 | _m['dmp'] =_m.mp.rolling(n).sum() 135 | _m['dmm'] = _m.mm.rolling(n).sum() 136 | _dmi = pd.DataFrame() 137 | _dmi['date'] = df.date 138 | _dmi['pdi'] = _m.dmp * 100 / trz 139 | _dmi['mdi'] = _m.dmm * 100 / trz 140 | _dmi['adx'] = _ma((_dmi.mdi - _dmi.pdi).abs() / (_dmi.mdi + _dmi.pdi) * 100, m) 141 | _dmi['adxr'] = (_dmi.adx + _dmi.adx.shift(m)) / 2 142 | return _dmi 143 | 144 | 145 | def macd(df, n=12, m=26, k=9): 146 | """ 147 | 平滑异同移动平均线(Moving Average Convergence Divergence) 148 | 今日EMA(N)=2/(N+1)×今日收盘价+(N-1)/(N+1)×昨日EMA(N) 149 | DIFF= EMA(N1)- EMA(N2) 150 | DEA(DIF,M)= 2/(M+1)×DIF +[1-2/(M+1)]×DEA(REF(DIF,1),M) 151 | MACD(BAR)=2×(DIF-DEA) 152 | return: 153 | osc: MACD bar / OSC 差值柱形图 DIFF - DEM 154 | diff: 差离值 155 | dea: 讯号线 156 | """ 157 | _macd = pd.DataFrame() 158 | _macd['date'] = df['date'] 159 | _macd['diff'] = _ema(df.close, n) - _ema(df.close, m) 160 | _macd['dea'] = _ema(_macd['diff'], k) 161 | _macd['macd'] = _macd['diff'] - _macd['dea'] 162 | return _macd 163 | 164 | 165 | def kdj(df, n=9): 166 | """ 167 | 随机指标KDJ 168 | N日RSV=(第N日收盘价-N日内最低价)/(N日内最高价-N日内最低价)×100% 169 | 当日K值=2/3前1日K值+1/3×当日RSV=SMA(RSV,M1) 170 | 当日D值=2/3前1日D值+1/3×当日K= SMA(K,M2) 171 | 当日J值=3 ×当日K值-2×当日D值 172 | """ 173 | _kdj = pd.DataFrame() 174 | _kdj['date'] = df['date'] 175 | rsv = (df.close - df.low.rolling(n).min()) / (df.high.rolling(n).max() - df.low.rolling(n).min()) * 100 176 | _kdj['k'] = sma(rsv, 3) 177 | _kdj['d'] = sma(_kdj.k, 3) 178 | _kdj['j'] = 3 * _kdj.k - 2 * _kdj.d 179 | return _kdj 180 | 181 | 182 | def rsi(df, n=6): 183 | """ 184 | 相对强弱指标(Relative Strength Index,简称RSI n=6、12和24 185 | LC= REF(CLOSE,1) 186 | RSI=SMA(MAX(CLOSE-LC,0),N,1)/SMA(ABS(CLOSE-LC),N1,1)×100 187 | SMA(C,N,M)=M/N×今日收盘价+(N-M)/N×昨日SMA(N) 188 | """ 189 | # pd.set_option('display.max_rows', 1000) 190 | _rsi = pd.DataFrame() 191 | _rsi['date'] = df['date'] 192 | px = df.close - df.close.shift() 193 | px[px < 0] = 0 194 | _rsi['rsi'] = sma(px, n) / sma((df['close'] - df['close'].shift()).abs(), n) * 100 195 | # def tmax(x): 196 | # if x < 0: 197 | # x = 0 198 | # return x 199 | # _rsi['rsi'] = sma((df['close'] - df['close'].shift(1)).apply(tmax), n) / sma((df['close'] - df['close'].shift(1)).abs(), n) * 100 200 | return _rsi 201 | 202 | 203 | def stochrsi(df, n=12): 204 | """ 205 | 随机强弱指标(Relative Strength Index,简称RSI n=6、12和24 206 | Stochastic Relative Strength Index(Stoch RSI) 207 | https://zhuanlan.zhihu.com/p/55070261 208 | """ 209 | _stochrsi = pd.DataFrame() 210 | _stochrsi['date'] = df.date 211 | px = df.close - df.close.shift() 212 | px[px < 0] = 0 213 | rsi = sma(px, n) / sma((df.close - df.close.shift()).abs(), n) * 100 214 | lrsi = rsi.rolling(n).min() 215 | _stochrsi['stochrsi'] = (rsi - lrsi) / (rsi.rolling(n).max() - lrsi) * 100 216 | _stochrsi['fastk'] = sma(_stochrsi.stochrsi, 3) # or _ma ?????? 217 | _stochrsi['fastd'] = sma(_stochrsi.fastk, 3) # or _ma ?????? 218 | return _stochrsi 219 | 220 | 221 | def vrsi(df, n=6): 222 | """ 223 | 量相对强弱指标 224 | VRSI=SMA(最大值(成交量-REF(成交量,1),0),N,1)/SMA(ABS((成交量-REF(成交量,1),N,1)×100% 225 | """ 226 | _vrsi = pd.DataFrame() 227 | _vrsi['date'] = df['date'] 228 | px = df['volume'] - df['volume'].shift(1) 229 | px[px < 0] = 0 230 | _vrsi['vrsi'] = sma(px, n) / sma((df['volume'] - df['volume'].shift(1)).abs(), n) * 100 231 | return _vrsi 232 | 233 | 234 | def boll(df, n=26, k=2): 235 | """ 236 | 布林线指标BOLL boll(26,2) MID=MA(N) 237 | 标准差MD=根号[∑(CLOSE-MA(CLOSE,N))^2/N] 238 | UPPER=MID+k×MD 239 | LOWER=MID-k×MD 240 | """ 241 | _boll = pd.DataFrame() 242 | _boll['date'] = df.date 243 | _boll['mid'] = _ma(df.close, n) 244 | _mdd = _md(df.close, n) 245 | _boll['up'] = _boll.mid + k * _mdd 246 | _boll['low'] = _boll.mid - k * _mdd 247 | return _boll 248 | 249 | 250 | def bbiboll(df, n=10, k=3): 251 | """ 252 | BBI多空布林线 bbiboll(10,3) 253 | BBI={MA(3)+ MA(6)+ MA(12)+ MA(24)}/4 254 | 标准差MD=根号[∑(BBI-MA(BBI,N))^2/N] 255 | UPR= BBI+k×MD 256 | DWN= BBI-k×MD 257 | """ 258 | # pd.set_option('display.max_rows', 1000) 259 | _bbiboll = pd.DataFrame() 260 | _bbiboll['date'] = df.date 261 | _bbiboll['bbi'] = (_ma(df.close, 3) + _ma(df.close, 6) + _ma(df.close, 12) + _ma(df.close, 24)) / 4 262 | _bbiboll['md'] = _md(_bbiboll.bbi, n) 263 | _bbiboll['upr'] = _bbiboll.bbi + k * _bbiboll.md 264 | _bbiboll['dwn'] = _bbiboll.bbi - k * _bbiboll.md 265 | return _bbiboll 266 | 267 | 268 | def wr(df, n=14): 269 | """ 270 | 威廉指标 w&r 271 | WR=[最高值(最高价,N)-收盘价]/[最高值(最高价,N)-最低值(最低价,N)]×100% 272 | """ 273 | 274 | _wr = pd.DataFrame() 275 | _wr['date'] = df['date'] 276 | higest = df.high.rolling(n).max() 277 | _wr['wr'] = (higest - df.close) / (higest - df.low.rolling(n).min()) * 100 278 | return _wr 279 | 280 | 281 | def bias(df, n=12): 282 | """ 283 | 乖离率 bias 284 | bias=[(当日收盘价-12日平均价)/12日平均价]×100% 285 | """ 286 | _bias = pd.DataFrame() 287 | _bias['date'] = df.date 288 | _mav = df.close.rolling(n).mean() 289 | _bias['bias'] = (np.true_divide((df.close - _mav), _mav)) * 100 290 | # _bias["bias"] = np.vectorize(lambda x: round(Decimal(x), 4))(BIAS) 291 | return _bias 292 | 293 | 294 | def asi(df, n=5): 295 | """ 296 | 振动升降指标(累计震动升降因子) ASI # 同花顺给出的公式不完整就不贴出来了 297 | """ 298 | _asi = pd.DataFrame() 299 | _asi['date'] = df.date 300 | _m = pd.DataFrame() 301 | _m['a'] = (df.high - df.close.shift()).abs() 302 | _m['b'] = (df.low - df.close.shift()).abs() 303 | _m['c'] = (df.high - df.low.shift()).abs() 304 | _m['d'] = (df.close.shift() - df.open.shift()).abs() 305 | _m['r'] = _m.apply(lambda x: x.a + 0.5 * x.b + 0.25 * x.d if max(x.a, x.b, x.c) == x.a else ( 306 | x.b + 0.5 * x.a + 0.25 * x.d if max(x.a, x.b, x.c) == x.b else x.c + 0.25 * x.d 307 | ), axis=1) 308 | _m['x'] = df.close - df.close.shift() + 0.5 * (df.close - df.open) + df.close.shift() - df.open.shift() 309 | _m['k'] = np.maximum(_m.a, _m.b) 310 | _asi['si'] = 16 * (_m.x / _m.r) * _m.k 311 | _asi["asi"] = _ma(_asi.si, n) 312 | return _asi 313 | 314 | 315 | def vr_rate(df, n=26): 316 | """ 317 | 成交量变异率 vr or vr_rate 318 | VR=(AVS+1/2CVS)/(BVS+1/2CVS)×100 319 | 其中: 320 | AVS:表示N日内股价上涨成交量之和 321 | BVS:表示N日内股价下跌成交量之和 322 | CVS:表示N日内股价不涨不跌成交量之和 323 | """ 324 | _vr = pd.DataFrame() 325 | _vr['date'] = df['date'] 326 | _m = pd.DataFrame() 327 | _m['volume'] = df.volume 328 | _m['cs'] = df.close - df.close.shift(1) 329 | _m['avs'] = _m.apply(lambda x: x.volume if x.cs > 0 else 0, axis=1) 330 | _m['bvs'] = _m.apply(lambda x: x.volume if x.cs < 0 else 0, axis=1) 331 | _m['cvs'] = _m.apply(lambda x: x.volume if x.cs == 0 else 0, axis=1) 332 | _vr["vr"] = (_m.avs.rolling(n).sum() + 1 / 2 * _m.cvs.rolling(n).sum() 333 | ) / (_m.bvs.rolling(n).sum() + 1 / 2 * _m.cvs.rolling(n).sum()) * 100 334 | return _vr 335 | 336 | 337 | def vr(df, n=5): 338 | """ 339 | 开市后平均每分钟的成交量与过去5个交易日平均每分钟成交量之比 340 | 量比:=V/REF(MA(V,5),1); 341 | 涨幅:=(C-REF(C,1))/REF(C,1)*100; 342 | 1)量比大于1.8,涨幅小于2%,现价涨幅在0—2%之间,在盘中选股的 343 | 选股:量比>1.8 AND 涨幅>0 AND 涨幅<2; 344 | """ 345 | _vr = pd.DataFrame() 346 | _vr['date'] = df.date 347 | _vr['vr'] = df.volume / _ma(df.volume, n).shift(1) 348 | _vr['rr'] = (df.close - df.close.shift(1)) / df.close.shift(1) * 100 349 | return _vr 350 | 351 | 352 | def arbr(df, n=26): 353 | """ 354 | 人气意愿指标 arbr(26) 355 | N日AR=N日内(H-O)之和除以N日内(O-L)之和 356 | 其中,H为当日最高价,L为当日最低价,O为当日开盘价,N为设定的时间参数,一般原始参数日设定为26日 357 | N日BR=N日内(H-CY)之和除以N日内(CY-L)之和 358 | 其中,H为当日最高价,L为当日最低价,CY为前一交易日的收盘价,N为设定的时间参数,一般原始参数日设定为26日。 359 | """ 360 | _arbr = pd.DataFrame() 361 | _arbr['date'] = df.date 362 | _arbr['ar'] = (df.high - df.open).rolling(n).sum() / (df.open - df.low).rolling(n).sum() * 100 363 | _arbr['br'] = (df.high - df.close.shift(1)).rolling(n).sum() / (df.close.shift() - df.low).rolling(n).sum() * 100 364 | return _arbr 365 | 366 | 367 | def dpo(df, n=20, m=6): 368 | """ 369 | 区间震荡线指标 dpo(20,6) 370 | DPO=CLOSE-MA(CLOSE, N/2+1) 371 | MADPO=MA(DPO,M) 372 | """ 373 | _dpo = pd.DataFrame() 374 | _dpo['date'] = df['date'] 375 | _dpo['dpo'] = df.close - _ma(df.close, int(n / 2 + 1)) 376 | _dpo['dopma'] = _ma(_dpo.dpo, m) 377 | return _dpo 378 | 379 | 380 | def tema(df, n=20): 381 | """ 382 | h 一般取20或60 383 | TEMA三重指数移动平均线是帕特里克马洛于1994年开发的一个更平滑更快速的移动均线 384 | """ 385 | _tema = pd.DataFrame() 386 | _tema['date'] = df.date 387 | a1 = _ema(df.close, n) 388 | a2 = _ema(a1, n) 389 | a3 = _ema(a2, n) 390 | _tema['tema'] = (3 * a1 - 3 * a2 + a3) / _ma(df.close, n) 391 | return _tema 392 | 393 | 394 | def trix(df, n=12, m=20): 395 | """ 396 | 三重指数平滑平均 TRIX(12) 397 | TR= EMA(EMA(EMA(CLOSE,N),N),N),即进行三次平滑处理 398 | TRIX=(TR-昨日TR)/ 昨日TR×100 399 | TRMA=MA(TRIX,M) 400 | """ 401 | _trix = pd.DataFrame() 402 | _trix['date'] = df.date 403 | tr = _ema(_ema(_ema(df.close, n), n), n) 404 | _trix['trix'] = (tr - tr.shift()) / tr.shift() * 100 405 | _trix['trma'] = _ma(_trix.trix, m) 406 | return _trix 407 | 408 | 409 | def bbi(df): 410 | """ 411 | 多空指数 BBI(3,6,12,24) 412 | BBI=(3日均价+6日均价+12日均价+24日均价)/4 413 | """ 414 | _bbi = pd.DataFrame() 415 | _bbi['date'] = df['date'] 416 | _bbi['bbi'] = (_ma(df.close, 3) + _ma(df.close, 6) + _ma(df.close, 12) + _ma(df.close, 24)) / 4 417 | return _bbi 418 | 419 | 420 | def mtm(df, n=6, m=5): 421 | """ 422 | 动力指标 MTM(6,5) 423 | MTM(N日)=C-REF(C,N)式中,C=当日的收盘价,REF(C,N)=N日前的收盘价;N日是只计算交易日期,剔除掉节假日。 424 | MTMMA(MTM,N1)= MA(MTM,N1) 425 | N表示间隔天数,N1表示天数 426 | """ 427 | _mtm = pd.DataFrame() 428 | _mtm['date'] = df.date 429 | _mtm['mtm'] = df.close - df.close.shift(n) 430 | _mtm['mtmma'] = _ma(_mtm.mtm, m) 431 | return _mtm 432 | 433 | 434 | def obv(df): 435 | """ 436 | 能量潮 On Balance Volume 437 | 多空比率净额= [(收盘价-最低价)-(最高价-收盘价)] ÷( 最高价-最低价)×V # 同花顺貌似用的下面公式 438 | 主公式:当日OBV=前一日OBV+今日成交量 439 | 1.基期OBV值为0,即该股上市的第一天,OBV值为0 440 | 2.若当日收盘价>上日收盘价,则当日OBV=前一日OBV+今日成交量 441 | 3.若当日收盘价<上日收盘价,则当日OBV=前一日OBV-今日成交量 442 | 4.若当日收盘价=上日收盘价,则当日OBV=前一日OBV 443 | """ 444 | _obv = pd.DataFrame() 445 | _obv["date"] = df['date'] 446 | # tmp = np.true_divide(((df.close - df.low) - (df.high - df.close)), (df.high - df.low)) 447 | # _obv['obvv'] = tmp * df.volume 448 | # _obv["obv"] = _obv.obvv.expanding(1).sum() / 100 449 | _m = pd.DataFrame() 450 | _m['date'] = df.date 451 | _m['cs'] = df.close - df.close.shift() 452 | _m['v'] = df.volume 453 | _m['vv'] = _m.apply(lambda x: x.v if x.cs > 0 else (-x.v if x.cs < 0 else 0), axis=1) 454 | _obv['obv'] = _m.vv.expanding(1).sum() 455 | return _obv 456 | 457 | 458 | def cci(df, n=14): 459 | """ 460 | 顺势指标 461 | TYP:=(HIGH+LOW+CLOSE)/3 462 | CCI:=(TYP-MA(TYP,N))/(0.015×AVEDEV(TYP,N)) 463 | """ 464 | _cci = pd.DataFrame() 465 | _cci["date"] = df['date'] 466 | typ = (df.high + df.low + df.close) / 3 467 | _cci['cci'] = ((typ - typ.rolling(n).mean()) / 468 | (0.015 * typ.rolling(min_periods=1, center=False, window=n).apply( 469 | lambda x: np.fabs(x - x.mean()).mean()))) 470 | return _cci 471 | 472 | 473 | def priceosc(df, n=12, m=26): 474 | """ 475 | 价格振动指数 476 | PRICEOSC=(MA(C,12)-MA(C,26))/MA(C,12) * 100 477 | """ 478 | _c = pd.DataFrame() 479 | _c['date'] = df['date'] 480 | man = _ma(df.close, n) 481 | _c['osc'] = (man - _ma(df.close, m)) / man * 100 482 | return _c 483 | 484 | 485 | def sma(a, n, m=1): 486 | """ 487 | 平滑移动指标 Smooth Moving Average 488 | """ 489 | ''' # 方法一,此方法有缺陷 490 | _sma = [] 491 | for index, value in enumerate(a): 492 | if index == 0 or pd.isna(value) or np.isnan(value): 493 | tsma = 0 494 | else: 495 | # Y=(M*X+(N-M)*Y')/N 496 | tsma = (m * value + (n - m) * tsma) / n 497 | _sma.append(tsma) 498 | return pd.Series(_sma) 499 | ''' 500 | ''' # 方法二 501 | 502 | results = np.nan_to_num(a).copy() 503 | # FIXME this is very slow 504 | for i in range(1, len(a)): 505 | results[i] = (m * results[i] + (n - m) * results[i - 1]) / n 506 | # results[i] = ((n - 1) * results[i - 1] + results[i]) / n 507 | # return results 508 | ''' 509 | # b = np.nan_to_num(a).copy() 510 | # return ((n - m) * a.shift(1) + m * a) / n 511 | 512 | a = a.fillna(0) 513 | b = a.ewm(min_periods=0, ignore_na=False, adjust=False, alpha=m/n).mean() 514 | return b 515 | 516 | 517 | def dbcd(df, n=5, m=16, t=76): 518 | """ 519 | 异同离差乖离率 dbcd(5,16,76) 520 | BIAS=(C-MA(C,N))/MA(C,N) 521 | DIF=(BIAS-REF(BIAS,M)) 522 | DBCD=SMA(DIF,T,1) =(1-1/T)×SMA(REF(DIF,1),T,1)+ 1/T×DIF 523 | MM=MA(DBCD,5) 524 | """ 525 | _dbcd = pd.DataFrame() 526 | _dbcd['date'] = df.date 527 | man = _ma(df.close, n) 528 | _bias = (df.close - man) / man 529 | _dif = _bias - _bias.shift(m) 530 | _dbcd['dbcd'] = sma(_dif, t) 531 | _dbcd['mm'] = _ma(_dbcd.dbcd, n) 532 | return _dbcd 533 | 534 | 535 | def roc(df, n=12, m=6): 536 | """ 537 | 变动速率 roc(12,6) 538 | ROC=(今日收盘价-N日前的收盘价)/ N日前的收盘价×100% 539 | ROCMA=MA(ROC,M) 540 | ROC:(CLOSE-REF(CLOSE,N))/REF(CLOSE,N)×100 541 | ROCMA:MA(ROC,M) 542 | """ 543 | _roc = pd.DataFrame() 544 | _roc['date'] = df['date'] 545 | _roc['roc'] = (df.close - df.close.shift(n))/df.close.shift(n) * 100 546 | _roc['rocma'] = _ma(_roc.roc, m) 547 | return _roc 548 | 549 | 550 | def vroc(df, n=12): 551 | """ 552 | 量变动速率 553 | VROC=(当日成交量-N日前的成交量)/ N日前的成交量×100% 554 | """ 555 | _vroc = pd.DataFrame() 556 | _vroc['date'] = df['date'] 557 | _vroc['vroc'] = (df.volume - df.volume.shift(n)) / df.volume.shift(n) * 100 558 | return _vroc 559 | 560 | 561 | def cr(df, n=26): 562 | """ 能量指标 563 | CR=∑(H-PM)/∑(PM-L)×100 564 | PM:上一交易日中价((最高、最低、收盘价的均值) 565 | H:当天最高价 566 | L:当天最低价 567 | """ 568 | _cr = pd.DataFrame() 569 | _cr['date'] = df.date 570 | # pm = ((df['high'] + df['low'] + df['close']) / 3).shift(1) 571 | pm = (df[['high', 'low', 'close']]).mean(axis=1).shift(1) 572 | _cr['cr'] = (df.high - pm).rolling(n).sum()/(pm - df.low).rolling(n).sum() * 100 573 | return _cr 574 | 575 | 576 | def psy(df, n=12): 577 | """ 578 | 心理指标 PSY(12) 579 | PSY=N日内上涨天数/N×100 580 | PSY:COUNT(CLOSE>REF(CLOSE,1),N)/N×100 581 | MAPSY=PSY的M日简单移动平均 582 | """ 583 | _psy = pd.DataFrame() 584 | _psy['date'] = df.date 585 | p = df.close - df.close.shift() 586 | p[p <= 0] = np.nan 587 | _psy['psy'] = p.rolling(n).count() / n * 100 588 | return _psy 589 | 590 | 591 | def wad(df, n=30): 592 | """ 593 | 威廉聚散指标 WAD(30) 594 | TRL=昨日收盘价与今日最低价中价格最低者;TRH=昨日收盘价与今日最高价中价格最高者 595 | 如果今日的收盘价>昨日的收盘价,则今日的A/D=今日的收盘价-今日的TRL 596 | 如果今日的收盘价<昨日的收盘价,则今日的A/D=今日的收盘价-今日的TRH 597 | 如果今日的收盘价=昨日的收盘价,则今日的A/D=0 598 | WAD=今日的A/D+昨日的WAD;MAWAD=WAD的M日简单移动平均 599 | """ 600 | def dmd(x): 601 | if x.c > 0: 602 | y = x.close - x.trl 603 | elif x.c < 0: 604 | y = x.close - x.trh 605 | else: 606 | y = 0 607 | return y 608 | 609 | _wad = pd.DataFrame() 610 | _wad['date'] = df['date'] 611 | _ad = pd.DataFrame() 612 | _ad['trl'] = np.minimum(df.low, df.close.shift(1)) 613 | _ad['trh'] = np.maximum(df.high, df.close.shift(1)) 614 | _ad['c'] = df.close - df.close.shift() 615 | _ad['close'] = df.close 616 | _ad['ad'] = _ad.apply(dmd, axis=1) 617 | _wad['wad'] = _ad.ad.expanding(1).sum() 618 | _wad['mawad'] = _ma(_wad.wad, n) 619 | return _wad 620 | 621 | 622 | def mfi(df, n=14): 623 | """ 624 | 资金流向指标 mfi(14) 625 | MF=TYP×成交量;TYP:当日中价((最高、最低、收盘价的均值) 626 | 如果当日TYP>昨日TYP,则将当日的MF值视为当日PMF值。而当日NMF值=0 627 | 如果当日TYP<=昨日TYP,则将当日的MF值视为当日NMF值。而当日PMF值=0 628 | MR=∑PMF/∑NMF 629 | MFI=100-(100÷(1+MR)) 630 | """ 631 | _mfi = pd.DataFrame() 632 | _mfi['date'] = df.date 633 | _m = pd.DataFrame() 634 | _m['typ'] = df[['high', 'low', 'close']].mean(axis=1) 635 | _m['mf'] = _m.typ * df.volume 636 | _m['typ_shift'] = _m.typ - _m.typ.shift(1) 637 | _m['pmf'] = _m.apply(lambda x: x.mf if x.typ_shift > 0 else 0, axis=1) 638 | _m['nmf'] = _m.apply(lambda x: x.mf if x.typ_shift <= 0 else 0, axis=1) 639 | # _mfi['mfi'] = 100 - (100 / (1 + _m.pmf.rolling(n).sum() / _m.nmf.rolling(n).sum())) 640 | _m['mr'] = _m.pmf.rolling(n).sum() / _m.nmf.rolling(n).sum() 641 | _mfi['mfi'] = 100 * _m.mr / (1 + _m.mr) # 同花顺自己给出的公式和实际用的公式不一样,真操蛋,浪费两个小时时间 642 | return _mfi 643 | 644 | 645 | def pvt(df): 646 | """ 647 | pvt 量价趋势指标 pvt 648 | 如果设x=(今日收盘价—昨日收盘价)/昨日收盘价×当日成交量, 649 | 那么当日PVT指标值则为从第一个交易日起每日X值的累加。 650 | """ 651 | _pvt = pd.DataFrame() 652 | _pvt['date'] = df.date 653 | 654 | x = (df.close - df.close.shift(1)) / df.close.shift(1) * df.volume 655 | _pvt['pvt'] = x.expanding(1).sum() 656 | return _pvt 657 | 658 | 659 | def wvad(df, n=24, m=6): 660 | """ # 算法是对的,同花顺计算wvad用的n=6 661 | 威廉变异离散量 wvad(24,6) 662 | WVAD=N1日的∑ {(当日收盘价-当日开盘价)/(当日最高价-当日最低价)×成交量} 663 | MAWVAD=MA(WVAD,N2) 664 | """ 665 | _wvad = pd.DataFrame() 666 | _wvad['date'] = df.date 667 | # _wvad['wvad'] = (np.true_divide((df.close - df.open), (df.high - df.low)) * df.volume).rolling(n).sum() 668 | _wvad['wvad'] = (np.true_divide((df.close - df.open), (df.high - df.low)) * df.volume).rolling(n).sum() 669 | _wvad['mawvad'] = _ma(_wvad.wvad, m) 670 | return _wvad 671 | 672 | 673 | def cdp(df): 674 | """ 675 | 逆势操作 cdp 676 | CDP=(最高价+最低价+收盘价)/3 # 同花顺实际用的(H+L+2*c)/4 677 | AH=CDP+(前日最高价-前日最低价) 678 | NH=CDP×2-最低价 679 | NL=CDP×2-最高价 680 | AL=CDP-(前日最高价-前日最低价) 681 | """ 682 | _cdp = pd.DataFrame() 683 | _cdp['date'] = df.date 684 | # _cdp['cdp'] = (df.high + df.low + df.close * 2).shift(1) / 4 685 | _cdp['cdp'] = df[['high', 'low', 'close', 'close']].shift().mean(axis=1) 686 | _cdp['ah'] = _cdp.cdp + (df.high.shift(1) - df.low.shift()) 687 | _cdp['al'] = _cdp.cdp - (df.high.shift(1) - df.low.shift()) 688 | _cdp['nh'] = _cdp.cdp * 2 - df.low.shift(1) 689 | _cdp['nl'] = _cdp.cdp * 2 - df.high.shift(1) 690 | return _cdp 691 | 692 | 693 | def env(df, n=14): 694 | """ 695 | ENV指标 ENV(14) 696 | Upper=MA(CLOSE,N)×1.06 697 | LOWER= MA(CLOSE,N)×0.94 698 | """ 699 | _env = pd.DataFrame() 700 | _env['date'] = df.date 701 | _env['up'] = df.close.rolling(n).mean() * 1.06 702 | _env['low'] = df.close.rolling(n).mean() * 0.94 703 | return _env 704 | 705 | 706 | def mike(df, n=12): 707 | """ 708 | 麦克指标 mike(12) 709 | 初始价(TYP)=(当日最高价+当日最低价+当日收盘价)/3 710 | HV=N日内区间最高价 711 | LV=N日内区间最低价 712 | 初级压力线(WR)=TYP×2-LV 713 | 中级压力线(MR)=TYP+HV-LV 714 | 强力压力线(SR)=2×HV-LV 715 | 初级支撑线(WS)=TYP×2-HV 716 | 中级支撑线(MS)=TYP-HV+LV 717 | 强力支撑线(SS)=2×LV-HV 718 | """ 719 | _mike = pd.DataFrame() 720 | _mike['date'] = df.date 721 | typ = df[['high', 'low', 'close']].mean(axis=1) 722 | hv = df.high.rolling(n).max() 723 | lv = df.low.rolling(n).min() 724 | _mike['wr'] = typ * 2 - lv 725 | _mike['mr'] = typ + hv - lv 726 | _mike['sr'] = 2 * hv - lv 727 | _mike['ws'] = typ * 2 - hv 728 | _mike['ms'] = typ - hv + lv 729 | _mike['ss'] = 2 * lv - hv 730 | return _mike 731 | 732 | 733 | def vma(df, n=5): 734 | """ 735 | 量简单移动平均 VMA(5) VMA=MA(volume,N) 736 | VOLUME表示成交量;N表示天数 737 | """ 738 | _vma = pd.DataFrame() 739 | _vma['date'] = df.date 740 | _vma['vma'] = _ma(df.volume, n) 741 | return _vma 742 | 743 | 744 | def vmacd(df, qn=12, sn=26, m=9): 745 | """ 746 | 量指数平滑异同平均 vmacd(12,26,9) 747 | 今日EMA(N)=2/(N+1)×今日成交量+(N-1)/(N+1)×昨日EMA(N) 748 | DIFF= EMA(N1)- EMA(N2) 749 | DEA(DIF,M)= 2/(M+1)×DIF +[1-2/(M+1)]×DEA(REF(DIF,1),M) 750 | MACD(BAR)=2×(DIF-DEA) 751 | """ 752 | _vmacd = pd.DataFrame() 753 | _vmacd['date'] = df.date 754 | _vmacd['diff'] = _ema(df.volume, qn) - _ema(df.volume, sn) 755 | _vmacd['dea'] = _ema(_vmacd['diff'], m) # TODO: 不能用_vmacd.diff, 不知道为什么 756 | _vmacd['macd'] = (_vmacd['diff'] - _vmacd['dea']) 757 | return _vmacd 758 | 759 | 760 | def vosc(df, n=12, m=26): 761 | """ 762 | 成交量震荡 vosc(12,26) 763 | VOSC=(MA(VOLUME,SHORT)- MA(VOLUME,LONG))/MA(VOLUME,SHORT)×100 764 | """ 765 | _c = pd.DataFrame() 766 | _c['date'] = df['date'] 767 | _c['osc'] = (_ma(df.volume, n) - _ma(df.volume, m)) / _ma(df.volume, n) * 100 768 | return _c 769 | 770 | 771 | def tapi(df, n=6): 772 | """ # TODO: 由于get_k_data返回数据中没有amount,可以用get_h_data中amount,算法是正确的 773 | 加权指数成交值 tapi(6) 774 | TAPI=每日成交总值/当日加权指数=a/PI;A表示每日的成交金额,PI表示当天的股价指数即指收盘价 775 | """ 776 | _tapi = pd.DataFrame() 777 | # _tapi['date'] = df.date 778 | _tapi['tapi'] = df.amount / df.close 779 | _tapi['matapi'] = _ma(_tapi.tapi, n) 780 | return _tapi 781 | 782 | 783 | def vstd(df, n=10): 784 | """ 785 | 成交量标准差 vstd(10) 786 | VSTD=STD(Volume,N)=[∑(Volume-MA(Volume,N))^2/N]^0.5 787 | """ 788 | _vstd = pd.DataFrame() 789 | _vstd['date'] = df.date 790 | _vstd['vstd'] = df.volume.rolling(n).std(ddof=1) 791 | return _vstd 792 | 793 | 794 | def adtm(df, n=23, m=8): 795 | """ 796 | 动态买卖气指标 adtm(23,8) 797 | 如果开盘价≤昨日开盘价,DTM=0 798 | 如果开盘价>昨日开盘价,DTM=(最高价-开盘价)和(开盘价-昨日开盘价)的较大值 799 | 如果开盘价≥昨日开盘价,DBM=0 800 | 如果开盘价<昨日开盘价,DBM=(开盘价-最低价) 801 | STM=DTM在N日内的和 802 | SBM=DBM在N日内的和 803 | 如果STM > SBM,ADTM=(STM-SBM)/STM 804 | 如果STM < SBM , ADTM = (STM-SBM)/SBM 805 | 如果STM = SBM,ADTM=0 806 | ADTMMA=MA(ADTM,M) 807 | """ 808 | _adtm = pd.DataFrame() 809 | _adtm['date'] = df.date 810 | _m = pd.DataFrame() 811 | _m['cc'] = df.open - df.open.shift(1) 812 | _m['ho'] = df.high - df.open 813 | _m['ol'] = df.open - df.low 814 | _m['dtm'] = _m.apply(lambda x: max(x.ho, x.cc) if x.cc > 0 else 0, axis=1) 815 | _m['dbm'] = _m.apply(lambda x: x.ol if x.cc < 0 else 0, axis=1) 816 | _m['stm'] = _m.dtm.rolling(n).sum() 817 | _m['sbm'] = _m.dbm.rolling(n).sum() 818 | _m['ss'] = _m.stm - _m.sbm 819 | _adtm['adtm'] = _m.apply(lambda x: x.ss / x.stm if x.ss > 0 else (x.ss / x.sbm if x.ss < 0 else 0), axis=1) 820 | _adtm['adtmma'] = _ma(_adtm.adtm, m) 821 | return _adtm 822 | 823 | 824 | def mi(df, n=12): 825 | """ 826 | 动量指标 mi(12) 827 | A=CLOSE-REF(CLOSE,N) 828 | MI=SMA(A,N,1) 829 | """ 830 | _mi = pd.DataFrame() 831 | _mi['date'] = df.date 832 | _mi['mi'] = sma(df.close - df.close.shift(n), n) 833 | return _mi 834 | 835 | 836 | def micd(df, n=3, m=10, k=20): 837 | """ 838 | 异同离差动力指数 micd(3,10,20) 839 | MI=CLOSE-ref(CLOSE,1)AMI=SMA(MI,N1,1) 840 | DIF=MA(ref(AMI,1),N2)-MA(ref(AMI,1),N3) 841 | MICD=SMA(DIF,10,1) 842 | """ 843 | _micd = pd.DataFrame() 844 | _micd['date'] = df.date 845 | mi = df.close - df.close.shift(1) 846 | ami = sma(mi, n) 847 | dif = _ma(ami.shift(1), m) - _ma(ami.shift(1), k) 848 | _micd['micd'] = sma(dif, m) 849 | return _micd 850 | 851 | 852 | def rc(df, n=50): 853 | """ 854 | 变化率指数 rc(50) 855 | RC=收盘价/REF(收盘价,N)×100 856 | ARC=EMA(REF(RC,1),N,1) 857 | """ 858 | _rc = pd.DataFrame() 859 | _rc['date'] = df.date 860 | _rc['rc'] = df.close / df.close.shift(n) * 100 861 | _rc['arc'] = sma(_rc.rc.shift(), n) 862 | return _rc 863 | 864 | 865 | def rccd(df, n=59, m=21, k=28): 866 | """ # TODO: 计算结果错误和同花顺不同,检查不出来为什么 867 | 异同离差变化率指数 rate of change convergence divergence rccd(59,21,28) 868 | RC=收盘价/REF(收盘价,N)×100% 869 | ARC=EMA(REF(RC,1),N,1) 870 | DIF=MA(ref(ARC,1),N1)-MA MA(ref(ARC,1),N2) 871 | RCCD=SMA(DIF,N,1) 872 | """ 873 | _rccd = pd.DataFrame() 874 | _rccd['date'] = df.date 875 | rc = df.close / df.close.shift(n) * 100 876 | arc = sma(rc.shift(), n) 877 | dif = _ma(arc.shift(), m) - _ma(arc.shift(), k) 878 | _rccd['rccd'] = sma(dif, n) 879 | return _rccd 880 | 881 | 882 | def srmi(df, n=9): 883 | """ 884 | SRMIMI修正指标 srmi(9) 885 | 如果收盘价>N日前的收盘价,SRMI就等于(收盘价-N日前的收盘价)/收盘价 886 | 如果收盘价 0 else (x.cs/x.cp if x.cs < 0 else 0), axis=1) 896 | return _srmi 897 | 898 | 899 | def dptb(df, n=7): 900 | """ 901 | 大盘同步指标 dptb(7) 902 | DPTB=(统计N天中个股收盘价>开盘价,且指数收盘价>开盘价的天数或者个股收盘价<开盘价,且指数收盘价<开盘价)/N 903 | """ 904 | ind = ts.get_k_data("sh000001", start=df.date.iloc[0], end=df.date.iloc[-1]) 905 | sd = df.copy() 906 | sd.set_index('date', inplace=True) # 可能出现停盘等情况,所以将date设为index 907 | ind.set_index('date', inplace=True) 908 | _dptb = pd.DataFrame(index=df.date) 909 | q = ind.close - ind.open 910 | _dptb['p'] = sd.close - sd.open 911 | _dptb['q'] = q 912 | _dptb['m'] = _dptb.apply(lambda x: 1 if (x.p > 0 and x.q > 0) or (x.p < 0 and x.q < 0) else np.nan, axis=1) 913 | _dptb['jdrs'] = _dptb.m.rolling(n).count() / n 914 | _dptb.drop(columns=['p', 'q', 'm'], inplace=True) 915 | _dptb.reset_index(inplace=True) 916 | return _dptb 917 | 918 | 919 | def jdqs(df, n=20): 920 | """ 921 | 阶段强势指标 jdqs(20) 922 | JDQS=(统计N天中个股收盘价>开盘价,且指数收盘价<开盘价的天数)/(统计N天中指数收盘价<开盘价的天数) 923 | """ 924 | ind = ts.get_k_data("sh000001", start=df.date.iloc[0], end=df.date.iloc[-1]) 925 | sd = df.copy() 926 | sd.set_index('date', inplace=True) # 可能出现停盘等情况,所以将date设为index 927 | ind.set_index('date', inplace=True) 928 | _jdrs = pd.DataFrame(index=df.date) 929 | q = ind.close - ind.open 930 | _jdrs['p'] = sd.close - sd.open 931 | _jdrs['q'] = q 932 | _jdrs['m'] = _jdrs.apply(lambda x: 1 if (x.p > 0 and x.q < 0) else np.nan, axis=1) 933 | q[q > 0] = np.nan 934 | _jdrs['t'] = q 935 | _jdrs['jdrs'] = _jdrs.m.rolling(n).count() / _jdrs.t.rolling(n).count() 936 | _jdrs.drop(columns=['p', 'q', 'm', 't'], inplace=True) 937 | _jdrs.reset_index(inplace=True) 938 | return _jdrs 939 | 940 | 941 | def jdrs(df, n=20): 942 | """ 943 | 阶段弱势指标 jdrs(20) 944 | JDRS=(统计N天中个股收盘价<开盘价,且指数收盘价>开盘价的天数)/(统计N天中指数收盘价>开盘价的天数) 945 | """ 946 | ind = ts.get_k_data("sh000001", start=df.date.iloc[0], end=df.date.iloc[-1]) 947 | sd = df.copy() 948 | sd.set_index('date', inplace=True) 949 | ind.set_index('date', inplace=True) 950 | _jdrs = pd.DataFrame(index=df.date) 951 | q = ind.close - ind.open 952 | _jdrs['p'] = sd.close - sd.open 953 | _jdrs['q'] = q 954 | _jdrs['m'] = _jdrs.apply(lambda x: 1 if (x.p < 0 and x.q > 0) else np.nan, axis=1) 955 | q[q < 0] = np.nan 956 | _jdrs['t'] = q 957 | _jdrs['jdrs'] = _jdrs.m.rolling(n).count() / _jdrs.t.rolling(n).count() 958 | _jdrs.drop(columns=['p', 'q', 'm', 't'], inplace=True) 959 | _jdrs.reset_index(inplace=True) 960 | return _jdrs 961 | 962 | 963 | def zdzb(df, n=125, m=5, k=20): 964 | """ 965 | 筑底指标 zdzb(125,5,20) 966 | A=(统计N1日内收盘价>=前收盘价的天数)/(统计N1日内收盘价<前收盘价的天数) 967 | B=MA(A,N2) 968 | D=MA(A,N3) 969 | """ 970 | _zdzb = pd.DataFrame() 971 | _zdzb['date'] = df.date 972 | p = df.close - df.close.shift(1) 973 | q = p.copy() 974 | p[p < 0] = np.nan 975 | q[q >= 0] = np.nan 976 | _zdzb['a'] = p.rolling(n).count() / q.rolling(n).count() 977 | _zdzb['b'] = _zdzb.a.rolling(m).mean() 978 | _zdzb['d'] = _zdzb.a.rolling(k).mean() 979 | return _zdzb 980 | 981 | 982 | def atr(df, n=14): 983 | """ 984 | 真实波幅 atr(14) 985 | TR:MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW)) 986 | ATR:MA(TR,N) 987 | """ 988 | _atr = pd.DataFrame() 989 | _atr['date'] = df.date 990 | # _atr['tr'] = np.maximum(df.high - df.low, (df.close.shift(1) - df.low).abs()) 991 | # _atr['tr'] = np.maximum.reduce([df.high - df.low, (df.close.shift(1) - df.high).abs(), (df.close.shift(1) - df.low).abs()]) 992 | _atr['tr'] = np.vstack([df.high - df.low, (df.close.shift(1) - df.high).abs(), (df.close.shift(1) - df.low).abs()]).max(axis=0) 993 | _atr['atr'] = _atr.tr.rolling(n).mean() 994 | return _atr 995 | 996 | 997 | def mass(df, n=9, m=25): 998 | """ 999 | 梅丝线 mass(9,25) 1000 | AHL=MA((H-L),N1) 1001 | BHL= MA(AHL,N1) 1002 | MASS=SUM(AHL/BHL,N2) 1003 | H:表示最高价;L:表示最低价 1004 | """ 1005 | _mass = pd.DataFrame() 1006 | _mass['date'] = df.date 1007 | ahl = _ma((df.high - df.low), n) 1008 | bhl = _ma(ahl, n) 1009 | _mass['mass'] = (ahl / bhl).rolling(m).sum() 1010 | return _mass 1011 | 1012 | 1013 | def vhf(df, n=28): 1014 | """ 1015 | 纵横指标 vhf(28) 1016 | VHF=(N日内最大收盘价与N日内最小收盘价之前的差)/(N日收盘价与前收盘价差的绝对值之和) 1017 | """ 1018 | _vhf = pd.DataFrame() 1019 | _vhf['date'] = df.date 1020 | _vhf['vhf'] = (df.close.rolling(n).max() - df.close.rolling(n).min()) / (df.close - df.close.shift(1)).abs().rolling(n).sum() 1021 | return _vhf 1022 | 1023 | 1024 | def cvlt(df, n=10): 1025 | """ 1026 | 佳庆离散指标 cvlt(10) 1027 | cvlt=(最高价与最低价的差的指数移动平均-前N日的最高价与最低价的差的指数移动平均)/前N日的最高价与最低价的差的指数移动平均 1028 | """ 1029 | _cvlt = pd.DataFrame() 1030 | _cvlt['date'] = df.date 1031 | p = _ema(df.high.shift(n) - df.low.shift(n), n) 1032 | _cvlt['cvlt'] = (_ema(df.high - df.low, n) - p) / p * 100 1033 | return _cvlt 1034 | 1035 | 1036 | def up_n(df): 1037 | """ 1038 | 连涨天数 up_n 连续上涨天数,当天收盘价大于开盘价即为上涨一天 # 同花顺实际结果用收盘价-前一天收盘价 1039 | """ 1040 | _up = pd.DataFrame() 1041 | _up['date'] = df.date 1042 | p = df.close - df.close.shift() 1043 | p[p > 0] = 1 1044 | p[p < 0] = 0 1045 | m = [] 1046 | for k, g in itertools.groupby(p): 1047 | t = 0 1048 | for i in g: 1049 | if k == 0: 1050 | m.append(0) 1051 | else: 1052 | t += 1 1053 | m.append(t) 1054 | # _up['p'] = p 1055 | _up['up'] = m 1056 | return _up 1057 | 1058 | 1059 | def down_n(df): 1060 | """ 1061 | 连跌天数 down_n 连续下跌天数,当天收盘价小于开盘价即为下跌一天 1062 | """ 1063 | _down = pd.DataFrame() 1064 | _down['date'] = df.date 1065 | p = df.close - df.close.shift() 1066 | p[p > 0] = 0 1067 | p[p < 0] = 1 1068 | m = [] 1069 | for k, g in itertools.groupby(p): 1070 | t = 0 1071 | for i in g: 1072 | if k == 0: 1073 | m.append(0) 1074 | else: 1075 | t += 1 1076 | m.append(t) 1077 | _down['down'] = m 1078 | return _down 1079 | 1080 | 1081 | def elder(df, n=20): 1082 | """ 1083 | 艾达透视因子 观察多控指标 1084 | A = H - EMA(C, N) 1085 | B = L - EMA(C, N) 1086 | Elder = (A-B)/C 1087 | """ 1088 | _elder = pd.DataFrame() 1089 | _elder['date'] = df.date 1090 | a = df.high - _ema(df.close, n) 1091 | b = df.low - _ema(df.close, n) 1092 | _elder['elder'] = (a - b) / df.close 1093 | return _elder 1094 | 1095 | 1096 | """ --------------------------------------- 趋势性因子 -------------------------------------------""" 1097 | 1098 | 1099 | def acd(df, n=6): 1100 | """ 1101 | 收集派发因子 n=6或20 累积/派发线(Accumulation/Distribution Line,A/D或AC) 1102 | ACD指标将市场分为两股收集(买入)及派发(估出)的力量 1103 | """ 1104 | _acd = pd.DataFrame() 1105 | _acd['date'] = df.date 1106 | _m = pd.DataFrame() 1107 | _m['dif'] = df.close - df.close.shift() 1108 | _m['close'] = df.close 1109 | _m['low'] = np.minimum(df.low, df.low.shift()) 1110 | _m['high'] = np.maximum(df.high, df.high.shift()) 1111 | _m['cd'] = _m.apply(lambda x: x.close - x.low if x.dif > 0 else (x.close - x.high if x.dif < 0 else 0), axis=1) 1112 | _acd['acd'] = _m.cd.rolling(n).sum() 1113 | return _acd 1114 | 1115 | 1116 | def cop(df, n=11, m=14, h=10): 1117 | """ 1118 | 估波指标(Coppock 1119 | Curve)又称“估波曲线”,通过计算月度价格的变化速率的加权平均值来测量市场的动量,属于长线指标。 1120 | 1121 | 估波指标由Edwin 1122 | Sedgwick 1123 | Coppock于1962年提出,主要用于判断牛市的到来。该指标只能产生买进讯号。依估波指标买进股票后,应另外寻求其他指标来辅助卖出讯号。 1124 | """ 1125 | _cop = pd.DataFrame() 1126 | _cop['date'] = df.date 1127 | rc = (df.close - df.close.shift(n)) / df.close.shift(n) * 100 + (df.close - df.close.shift(m)) / df.close.shift(m) * 100 1128 | _cop['cop'] = _ema(rc, h) 1129 | return _cop 1130 | 1131 | 1132 | def join_frame(d1, d2, column='date'): 1133 | # 将两个DataFrame 按照datetime合并 1134 | return d1.join(d2.set_index(column), on=column) 1135 | 1136 | 1137 | if __name__ == "__main__": 1138 | import tushare as ts 1139 | # data = ts.get_k_data("000063", start="2017-05-01") 1140 | data = ts.get_k_data("002264", start="2017-05-01") 1141 | # print(data) 1142 | # maf = ma(data, n=[5, 10, 20]) 1143 | # 将均线合并到data中 1144 | # print(join_frame(data, maf)) 1145 | 1146 | # data = pd.DataFrame({"close": [1,2,3,4,5,6,7,8,9,0]}) 1147 | # print(ma(data)) 1148 | # mdf = md(data) 1149 | # print(md(data, n=26)) 1150 | # print(join_frame(data, mdf)) 1151 | # emaf = ema(data) 1152 | # print(ema(data, 5)) 1153 | # print(join_frame(data, emaf)) 1154 | # print(dma(data)) 1155 | # print(dmi(data)) 1156 | # print(macd(data)) 1157 | # print(kdj(data)) 1158 | # print(vrsi(data, 6)) 1159 | # print(boll(data)) 1160 | # print(bbiboll(data)) 1161 | # print(wr(data)) 1162 | # print(bias(data)) 1163 | # print(rsi(data)) 1164 | # print(asi(data)) 1165 | # print(vr_rate(data)) 1166 | # print(vr(data)) 1167 | # print(arbr(data)) 1168 | # print(dpo(data)) 1169 | # print(trix(data)) 1170 | # print(bbi(data)) 1171 | # print(ts.top_list(date="2019-01-17")) 1172 | # print(mtm(data)) 1173 | # print(obv(data)) 1174 | # print(cci(data)) 1175 | # print(priceosc(data)) 1176 | # print(dbcd(data)) 1177 | # print(roc(data)) 1178 | # print(vroc(data)) 1179 | # print(cr(data)) 1180 | # print(psy(data)) 1181 | # print(wad(data)) 1182 | # print(mfi(data)) 1183 | # print(pvt(data)) 1184 | # print(wvad(data)) 1185 | # print(cdp(data)) 1186 | # print(env(data)) 1187 | # print(mike(data)) 1188 | # print(vr(data)) 1189 | # print(vma(data)) 1190 | # print(vmacd(data)) 1191 | # print(vosc(data)) 1192 | # print(tapi(data)) 1193 | # print(vstd(data)) 1194 | # print(adtm(data)) 1195 | # print(mi(data)) 1196 | # print(micd(data)) 1197 | # print(rc(data)) 1198 | # print(rccd(data)) 1199 | # print(srmi(data)) 1200 | # print(dptb(data)) 1201 | # print(jdqs(data)) 1202 | # pd.set_option('display.max_rows', 1000) 1203 | # print(jdrs(data)) 1204 | # print(join_frame(data, jdrs(data))) 1205 | # print(data) 1206 | # print(zdzb(data)) 1207 | # print(atr(data)) 1208 | # print(mass(data)) 1209 | # print(vhf(data)) 1210 | # print(cvlt(data)) 1211 | # print(up_n(data)) 1212 | # print(down_n(data)) 1213 | # --------------------------------- 以下函数没有同花顺验证,慎重使用 -------------------------------- 1214 | # print(elder(data)) 1215 | # print(acd(data)) 1216 | # print(cop(data)) 1217 | # print(tema(data, n=20)) 1218 | print(stochrsi(data)) 1219 | # print(rsi(data)) 1220 | -------------------------------------------------------------------------------- /trendline_bak.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | """ 4 | 股票技术指标接口 5 | Created on 2018/07/26 6 | @author: Wangzili 7 | @group : ** 8 | @contact: 446406177@qq.com 9 | """ 10 | import pandas as pd 11 | import numpy as np 12 | from decimal import Decimal 13 | import itertools 14 | 15 | 16 | def ma(df, n=10, val_name="close"): 17 | """ 18 | 移动平均线 Moving Average 19 | :param df: pandas.DataFrame 20 | 通过 get_k_data 取得的股票数据 21 | :param n: int or list 22 | 移动平均线时长,时间单位根据df决定 23 | :param val_name: string 24 | 计算哪一列的列名,默认为 close 收盘值 25 | :return: 26 | pandas.DateFrame 27 | """ 28 | pv = pd.DataFrame() 29 | if isinstance(n, int): 30 | n = [n] 31 | pv['date'] = df['date'] 32 | for v in n: 33 | # 方法一 34 | # pv['v' + str(v)] = _ma(df, v, val_name) 35 | # 方法二 比方法大约快15倍 36 | pv['v' + str(v)] = df[val_name].rolling(v).mean() 37 | return pv 38 | 39 | 40 | def _ma(df, n, val_name): 41 | values = [] 42 | _mal = [] 43 | for index, row in df.iterrows(): 44 | values.append(row[val_name]) 45 | if len(values) > n: 46 | del values[0] 47 | if len(values) == n: 48 | # ma.append([row['date'], Decimal(np.average(values)).quantize(Decimal('0.00'))]) 49 | _mal.append(round(Decimal(np.average(values)), 2)) 50 | else: 51 | _mal.append(0) 52 | return _mal 53 | 54 | 55 | def md(df, n=10, val_name="close"): 56 | """ 57 | 移动标准差 58 | :param df: pandas.DataFrame 59 | 通过ts.get_k_data返回数据 60 | :param n: int 61 | 时长 62 | :param val_name: string 63 | 计算哪一列的列名,默认为 close 收盘值 64 | :return: pandas.DateFrame 65 | """ 66 | # 方法一 67 | ''' 68 | values = [] 69 | MD = [] 70 | _md = pd.DataFrame() 71 | _md['date'] = df['date'] 72 | for index, row in df.iterrows(): 73 | values.append(row[val_name]) 74 | if len(values) > n: 75 | del values[0] 76 | if len(values) == n: 77 | MD.append(round(Decimal(np.std(values)), 2)) 78 | else: 79 | MD.append(0) 80 | _md['md' + str(n)] = MD 81 | ''' 82 | # 方法二 83 | _md = pd.DataFrame() 84 | _md['date'] = df['date'] 85 | _md["md" + str(n)] = df[val_name].rolling(n).std(ddof=0) 86 | return _md 87 | 88 | 89 | def ema(df, n=12, val_name="close"): 90 | """ 91 | 指数平均数指标 Exponential Moving Average 92 | :param df: pandas.DataFrame 93 | 通过 get_h_data 取得的股票数据 94 | :param n: int 95 | 移动平均线时长,时间单位根据df决定 96 | :param val_name: string 97 | 计算哪一列的列名,默认为 close 收盘值 98 | :return _ema: pandas.DataFrame 99 | 100 | 今日EMA(N)=2/(N+1)×今日收盘价+(N-1)/(N+1)×昨日EMA(N) 101 | EMA(X,N)=[2×X+(N-1)×EMA(ref(X),N]/(N+1) 102 | """ 103 | EMA = [] 104 | _ema = pd.DataFrame() 105 | _ema['date'] = df['date'] 106 | for index, row in enumerate(df.itertuples(index=False)): 107 | if index == 0: 108 | today_ema = getattr(row, val_name) 109 | else: 110 | # Y=[2*X+(N-1)*Y’]/(N+1) 111 | today_ema = (2 * getattr(row, val_name) + (n - 1) * today_ema) / (n + 1) 112 | EMA.append(today_ema) 113 | _ema['ema' + str(n)] = EMA 114 | # _ema['ema' + str(n)] = df['close'].ewm(ignore_na=False, span=n, min_periods=0, adjust=False).mean() 115 | return _ema 116 | 117 | 118 | def macd(df, quick_n=12, slow_n=26, dem_n=9, val_name="close"): 119 | """ 120 | 平滑异同移动平均线(Moving Average Convergence Divergence) 121 | 今日EMA(N)=2/(N+1)×今日收盘价+(N-1)/(N+1)×昨日EMA(N) 122 | DIFF= EMA(N1)- EMA(N2) 123 | DEA(DIF,M)= 2/(M+1)×DIF +[1-2/(M+1)]×DEA(REF(DIF,1),M) 124 | MACD(BAR)=2×(DIF-DEA) 125 | :param df: 126 | :param quick_n: DIFF差离值中快速移动天数 127 | :param slow_n: DIFF差离值中慢速移动天数 128 | :param dem_n: DEM讯号线的移动天数 129 | :param val_name: 计算哪一列的列名,默认为 close 收盘值 130 | :return: 131 | _macd: pandas.DataFrame 132 | date: 日期 133 | osc: MACD bar / OSC 差值柱形图 DIFF - DEM 134 | diff: 差离值 135 | dem: 讯号线 136 | """ 137 | _macd = pd.DataFrame() 138 | _macd['date'] = df['date'] 139 | 140 | ema_quick = ema(df, quick_n, val_name) 141 | ema_slow = ema(df, slow_n, val_name) 142 | _macd['diff'] = ema_quick['ema' + str(quick_n)] - ema_slow['ema' + str(slow_n)] 143 | dem = ema(_macd, dem_n, "diff") 144 | _macd['dea'] = dem['ema' + str(dem_n)] 145 | _macd['macd'] = _macd['diff'] - _macd['dem'] 146 | 147 | return _macd 148 | 149 | 150 | def kdj(df, n=9): 151 | """ 152 | 随机指标KDJ 153 | :param df: pandas.DataFrame 154 | 通过 get_k_data 取得的股票数据 155 | :return: _kdj:pandas.DataFrame 156 | 包含 date, k, d, j 157 | """ 158 | # pd.set_option('display.max_rows', 1000) 159 | _kdj = pd.DataFrame() 160 | _kdj['date'] = df['date'] 161 | _k, _d, _j = [], [], [] 162 | last_k = last_d = 50 163 | for index, row in df.iterrows(): 164 | l = df['low'].loc[index - n + 1: index].min() 165 | h = df['high'].loc[index - n + 1: index].max() 166 | c = row["close"] 167 | 168 | rsv = (c - l) / (h - l) * 100 169 | k = (2 / 3) * last_k + (1 / 3) * rsv 170 | d = (2 / 3) * last_d + (1 / 3) * k 171 | j = 3 * k - 2 * d 172 | 173 | _k.append(round(Decimal(k), 2)) 174 | _d.append(round(Decimal(d), 2)) 175 | _j.append(round(Decimal(j), 2)) 176 | 177 | last_k, last_d = k, d 178 | _kdj['k'] = _k 179 | _kdj['d'] = _d 180 | _kdj['j'] = _j 181 | return _kdj 182 | 183 | 184 | def rsi(df, n=6, val_name="close"): 185 | """ 186 | 相对强弱指标(Relative Strength Index,简称RSI 187 | :param df: pandas.DataFrame 188 | 通过 get_k_data 取得的股票数据 189 | :param n: int 190 | 统计时长,时间单位根据data决定 191 | :param val_name: 192 | 计算哪一列的列名,默认为 close 收盘值 193 | :return: _rsi: pandas.DataFrame 194 | 包含 date, rsi 195 | """ 196 | # pd.set_option('display.max_rows', 1000) 197 | _rsi = pd.DataFrame() 198 | _rsi['date'] = df['date'] 199 | ''' 200 | # 方法一 201 | r = [] 202 | up = 0 203 | down = 0 204 | for index, row in enumerate(df.itertuples(index=False)): 205 | if index == 0: 206 | past_value = getattr(row, val_name) 207 | r.append(0) 208 | else: 209 | diff = getattr(row, val_name) - past_value 210 | if diff > 0: 211 | up = (up * (n - 1) + diff) / n 212 | down = down * (n - 1) / n 213 | else: 214 | up = up * (n - 1) / n 215 | down = (down * (n - 1) + abs(diff)) / n 216 | 217 | rsit = up / (down + up) * 100 218 | # r.append(round(Decimal(rsit), 2)) 219 | r.append(rsit) 220 | 221 | past_value = getattr(row, val_name) 222 | 223 | _rsi['rsi'] = r 224 | ''' 225 | # LC = REF(CLOSE, 1) 226 | # RSI = SMA(MAX(CLOSE - LC, 0), N, 1) / SMA(ABS(CLOSE - LC), N1, 1)×100 227 | # 方法二 228 | px = df['close'] - df['close'].shift(1) 229 | px[px < 0] = 0 230 | _rsi['rsi'] = sma(px, n, 1) / sma((df['close'] - df['close'].shift(1)).abs(), n, 1) * 100 231 | # 方法三 232 | # def tmax(x): 233 | # if x < 0: 234 | # x = 0 235 | # return x 236 | # _rsi['rsi'] = sma((df['close'] - df['close'].shift(1)).apply(tmax), n, 1) / sma((df['close'] - df['close'].shift(1)).abs(), n, 1) * 100 237 | return _rsi 238 | 239 | 240 | def vrsi(df, n=6): 241 | """ 242 | 量相对强弱指标 243 | VRSI=SMA(最大值(成交量-REF(成交量,1),0),N,1)/SMA(ABS((成交量-REF(成交量,1),N,1)×100% 244 | :param df: 245 | :param n: 246 | :return: 247 | """ 248 | _vrsi = pd.DataFrame() 249 | _vrsi['date'] = df['date'] 250 | px = df['volume'] - df['volume'].shift(1) 251 | px[px < 0] = 0 252 | _vrsi['vrsi'] = sma(px, n, 1) / sma((df['volume'] - df['volume'].shift(1)).abs(), n, 1) * 100 253 | return _vrsi 254 | 255 | 256 | def boll(df, n=20, k=2): 257 | """ 258 | 布林线指标BOLL boll(26,2) MID=MA(N) 259 | 标准差MD=根号[∑(CLOSE-MA(CLOSE,N))^2/N] 260 | UPPER=MID+k×MD 261 | LOWER=MID-k×MD 262 | """ 263 | _boll = pd.DataFrame() 264 | _boll['date'] = df.date 265 | _boll['mid'] = df.close.rolling(n).mean() 266 | _md = df.close.rolling(n).std(ddof=0) 267 | _boll['up'] = _boll.mid + k * _md 268 | _boll['low'] = _boll.mid - k * _md 269 | return _boll 270 | 271 | 272 | def bbiboll(df, n=10, k=3): 273 | """ 274 | BBI多空布林线 bbiboll(10,3) 275 | BBI={MA(3)+ MA(6)+ MA(12)+ MA(24)}/4 276 | 标准差MD=根号[∑(BBI-MA(BBI,N))^2/N] # 没搞明白什么意思 277 | UPR= BBI+k×MD 278 | DWN= BBI-k×MD 279 | """ 280 | pd.set_option('display.max_rows', 1000) 281 | _bbiboll = pd.DataFrame() 282 | _bbiboll['date'] = df.date 283 | _bbiboll['bbi'] = (df.close.rolling(3).mean() + df.close.rolling(6).mean() + 284 | df.close.rolling(12).mean() + df.close.rolling(24).mean()) / 4 285 | # _bbiboll['md'] = np.sqrt(np.square(_bbiboll.bbi - _bbiboll.bbi.rolling(n).mean()) / n) 286 | _bbiboll['md'] = _bbiboll.bbi.rolling(n).std(ddof=0) 287 | _bbiboll['upr'] = _bbiboll.bbi + k * _bbiboll.md 288 | _bbiboll['dwn'] = _bbiboll.bbi - k * _bbiboll.md 289 | return _bbiboll 290 | 291 | 292 | def wr(df, n=14): 293 | """ 294 | 威廉指标 w&r 295 | :param df: pandas.DataFrame 296 | 通过 get_k_data 取得的股票数据 297 | :param n: int 298 | 统计时长,时间单位根据data决定 299 | :return: pandas.DataFrame 300 | WNR: 威廉指标 301 | """ 302 | 303 | _wnr = pd.DataFrame() 304 | _wnr['date'] = df['date'] 305 | high_prices = [] 306 | low_prices = [] 307 | WNR = [] 308 | 309 | for index, row in data.iterrows(): 310 | high_prices.append(row["high"]) 311 | if len(high_prices) > n: 312 | del high_prices[0] 313 | low_prices.append(row["low"]) 314 | if len(low_prices) > n: 315 | del low_prices[0] 316 | 317 | highest = max(high_prices) 318 | lowest = min(low_prices) 319 | 320 | wnr = (highest - row["close"]) / (highest - lowest) * 100 321 | WNR.append(wnr) 322 | _wnr['wr' + str(n)] = WNR 323 | return _wnr 324 | 325 | 326 | def _ma_exact(df, n, val_name="close"): 327 | values = [] 328 | _mal = [] 329 | for index, row in df.iterrows(): 330 | values.append(row[val_name]) 331 | if len(values) > n: 332 | del values[0] 333 | _mal.append(np.average(values)) 334 | return _mal 335 | 336 | 337 | def _ma_array(arr, n): 338 | MA = [] 339 | values = [] 340 | for val in arr: 341 | values.append(val) 342 | if len(values) > n: 343 | del values[0] 344 | MA.append(np.average(values)) 345 | return np.asarray(MA) 346 | 347 | 348 | def bias(df, n=6): 349 | """ 350 | 乖离率 bias 351 | Parameters 352 | ------ 353 | df:pandas.DataFrame 354 | 通过 get_k_data 取得的股票数据 355 | n:int 356 | 统计时长,默认6 一般取值 6, 12, 24, 72 357 | return 358 | ------- 359 | BIAS:numpy.ndarray 360 | 乖离率指标 361 | 362 | """ 363 | 364 | _bias = pd.DataFrame() 365 | _bias['date'] = df['date'] 366 | c = df["close"] 367 | if isinstance(n, int): 368 | n = [n] 369 | for b in n: 370 | _mav = _ma_exact(df, b) 371 | # print(np.vectorize(lambda x: round(Decimal(x), 2))(c)) 372 | BIAS = (np.true_divide((c - _mav), _mav)) * 100 373 | _bias["bias" + str(b)] = np.vectorize(lambda x: round(Decimal(x), 4))(BIAS) 374 | return _bias 375 | 376 | 377 | def asi(df, n=5): 378 | """ 379 | 振动升降指标 ASI 380 | Parameters 381 | ------ 382 | df:pandas.DataFrame 383 | 通过 get_k_data 取得的股票数据 384 | n:int 385 | 统计时长,默认5 386 | return 387 | ------- 388 | ASI:numpy.ndarray 389 | 振动升降指标 390 | 391 | """ 392 | _si = [] 393 | for index, row in enumerate(df.itertuples(index=False)): 394 | if index == 0: 395 | _si.append(0) 396 | last_row = row 397 | else: 398 | a = abs(getattr(row, "high") - getattr(last_row, "close")) 399 | b = abs(getattr(row, "low") - getattr(last_row, "close")) 400 | c = abs(getattr(row, "high") - getattr(last_row, "low")) 401 | d = abs(getattr(last_row, "close") - getattr(last_row, "open")) 402 | 403 | e = getattr(row, 'close') - getattr(last_row, 'close') 404 | f = getattr(row, 'close') - getattr(row, 'open') 405 | g = getattr(last_row, 'close') - getattr(last_row, 'open') 406 | 407 | x = e + (1/2) * f + g 408 | k = max(a, b) 409 | 410 | if max(a, b, c) == a: 411 | r = a + (1/2) * b + (1/4) * d 412 | elif max(a, b, c) == b: 413 | r = b + (1/2) * a + (1/4) * d 414 | else: 415 | r = c + (1/4) * d 416 | l = 3 417 | SI = 50 * (x/r) * (k/l) 418 | _si.append(SI) 419 | _asi = pd.DataFrame() 420 | _asi['date'] = df['date'] 421 | if isinstance(n, int): 422 | n = [n] 423 | for a in n: 424 | _asi["asi" + str(a)] = _ma_array(_si, a) 425 | return _asi 426 | 427 | 428 | def vr_rate(df, n=26): 429 | """ 430 | 成交量变异率 vr or vr_rate 431 | VR=(AVS+1/2CVS)/(BVS+1/2CVS)×100 432 | 其中: 433 | AVS:表示N日内股价上涨成交量之和 434 | BVS:表示N日内股价下跌成交量之和 435 | CVS:表示N日内股价不涨不跌成交量之和 436 | """ 437 | ''' # 方法一 438 | VR = [] 439 | av, bv, cv = list(), list(), list() 440 | index_v = [] 441 | for index, row in enumerate(df.itertuples(index=False)): 442 | if index == 0: 443 | av.append(getattr(row, 'close')) 444 | index_v.append('av') 445 | else: 446 | if getattr(row, 'close') > pre_close: 447 | av.append(getattr(row, 'volume')) 448 | index_v.append('av') 449 | elif getattr(row, 'close') < pre_close: 450 | bv.append(getattr(row, 'volume')) 451 | index_v.append("bv") 452 | else: 453 | cv.append(getattr(row, 'volume')) 454 | index_v.append("cv") 455 | pre_close = getattr(row, 'close') 456 | if len(index_v) > n: 457 | (locals()[index_v.pop(0)]).pop(0) 458 | avs = sum(av) 459 | bvs = sum(bv) 460 | cvs = sum(cv) 461 | 462 | if bvs + (1 / 2) * cvs: 463 | vrd = (avs + (1 / 2) * cvs) / (bvs + (1 / 2) * cvs) * 100 464 | else: 465 | vrd = 0 466 | 467 | VR.append(vrd) 468 | _vr["vrr"] = VR 469 | ''' # 方法二 470 | _vr = pd.DataFrame() 471 | _vr['date'] = df['date'] 472 | _m = pd.DataFrame() 473 | _m['volume'] = df.volume 474 | _m['cs'] = df.close - df.close.shift(1) 475 | _m['avs'] = _m.apply(lambda x: x.volume if x.cs > 0 else 0, axis=1) 476 | _m['bvs'] = _m.apply(lambda x: x.volume if x.cs < 0 else 0, axis=1) 477 | _m['cvs'] = _m.apply(lambda x: x.volume if x.cs == 0 else 0, axis=1) 478 | _vr["vr"] = (_m.avs.rolling(n).sum() + 1 / 2 * _m.cvs.rolling(n).sum() 479 | ) / (_m.bvs.rolling(n).sum() + 1 / 2 * _m.cvs.rolling(n).sum()) * 100 480 | return _vr 481 | 482 | 483 | def vr(df, n=5): 484 | """ 485 | 开市后平均每分钟的成交量与过去5个交易日平均每分钟成交量之比 486 | 量比:=V/REF(MA(V,5),1); 487 | 涨幅:=(C-REF(C,1))/REF(C,1)*100; 488 | 1)量比大于1.8,涨幅小于2%,现价涨幅在0—2%之间,在盘中选股的 489 | 选股:量比>1.8 AND 涨幅>0 AND 涨幅<2; 490 | """ 491 | _vr = pd.DataFrame() 492 | _vr['date'] = df.date 493 | _vr['vr'] = df.volume / (df.volume.rolling(n).mean().shift(1)) 494 | _vr['rr'] = (df.close - df.close.shift(1)) / df.close.shift(1) * 100 495 | return _vr 496 | 497 | 498 | def arbr(df, n=26): 499 | """ 500 | AR 指标 BR指标 501 | Parameters 502 | ------ 503 | df:pandas.DataFrame 504 | 通过 get_k_data 取得的股票数据 505 | n:int 506 | 统计时长,默认26 507 | return 508 | ------- 509 | AR: 510 | AR指标 511 | BR: 512 | BR指标 513 | """ 514 | _arbr = pd.DataFrame() 515 | _arbr['date'] = df['date'] 516 | H, L, O, PC = np.array([0]), np.array([0]), np.array([0]), np.array([0]) 517 | AR, BR = np.array([0]), np.array([0]) 518 | for index, row in enumerate(df.itertuples(index=False)): 519 | if index == 0: 520 | last_row = row 521 | else: 522 | H = np.append(H, [getattr(row, "high")]) 523 | L = np.append(L, [getattr(row, 'low')]) 524 | O = np.append(O, [getattr(row, 'open')]) 525 | PC = np.append(PC, [getattr(last_row, "close")]) 526 | if len(H) > n: 527 | H = np.delete(H, 0) 528 | L = np.delete(L, 0) 529 | O = np.delete(O, 0) 530 | PC = np.delete(PC, 0) 531 | 532 | # ar = (np.sum(np.asarray(H) - np.asarray(O)) / sum(np.asarray(O) - np.asarray(L))) * 100 533 | ar = (np.sum(H - O) / sum(O - L)) * 100 534 | AR = np.append(AR, [ar]) 535 | br = (np.sum(H - PC) / sum(PC - L)) * 100 536 | BR = np.append(BR, [br]) 537 | 538 | last_row = row 539 | _arbr['ar'] = AR 540 | _arbr["br"] = BR 541 | 542 | return _arbr 543 | 544 | 545 | def dpo(df, n=20, m=6): 546 | """ 547 | 区间震荡线指标 DPO 548 | Parameters 549 | ------ 550 | df:pandas.DataFrame 551 | 通过 get_k_data 取得的股票数据 552 | n:int 553 | 统计时长,默认20 554 | m:int 555 | MADPO的参数M,默认6 556 | return 557 | ------- 558 | data 559 | DPO:numpy.ndarray 560 | DPO指标 561 | MADPO:numpy.ndarray 562 | MADPO指标 563 | 564 | """ 565 | _dpo = pd.DataFrame() 566 | _dpo['date'] = df['date'] 567 | DPO = df['close'] - np.asarray(_ma_exact(df, int(n / 2 + 1))) 568 | MADPO = _ma_array(DPO, m) 569 | _dpo['dop'] = DPO 570 | _dpo['dopma'] = MADPO 571 | return _dpo 572 | 573 | 574 | def _ema(arr, n=12): 575 | EMA = [] 576 | for index, a in enumerate(arr): 577 | if index == 0: 578 | t_ema = a 579 | else: 580 | # Y=[2*X+(N-1)*Y’]/(N+1) 581 | t_ema = (2 * a + (n - 1) * t_ema) / (n + 1) 582 | EMA.append(t_ema) 583 | return EMA 584 | 585 | 586 | def trix(df, n=12, m=20): 587 | """ 长期指标 588 | 三重指数平滑平均线 TRIX 589 | Parameters 590 | ------ 591 | df:pandas.DataFrame 592 | 通过 get_h_data 取得的股票数据 593 | n:int 594 | 统计时长,默认12 595 | m:int 596 | TRMA的参数M,默认20 597 | return 598 | ------- 599 | TRIX: 600 | trix指标 601 | TRMA: 602 | trix平均指标 603 | 604 | """ 605 | _trix = pd.DataFrame() 606 | _trix['date'] = df['date'] 607 | # 方法一 608 | trx = [] 609 | for index, row in enumerate(df.itertuples(index=False)): 610 | if index == 0: 611 | ax_last = getattr(row, 'close') 612 | bx_last = getattr(row, 'close') 613 | tx_last = getattr(row, 'close') 614 | trx.append(0) 615 | else: 616 | ax = (2 * getattr(row, "close") + (n - 1) * ax_last) / (n + 1) 617 | bx = (2 * ax + (n - 1) * bx_last) / (n + 1) 618 | tx = (2 * bx + (n - 1) * tx_last) / (n + 1) 619 | if tx_last != 0: 620 | trx.append((tx - tx_last) / tx_last * 100) 621 | else: 622 | trx.append(0) 623 | ax_last = ax 624 | bx_last = bx 625 | tx_last = tx 626 | # 方法二 627 | ''' 628 | trxi = _ema(_ema(_ema(df["close"], n), n), n) 629 | trx = [] 630 | for index, t in enumerate(trxi): 631 | if index in [0, 1]: 632 | trx.append(0) 633 | else: 634 | trx.append((t-trxi[index-1])/trxi[index-1]*100) 635 | ''' 636 | trma = _ma_array(trx, m) 637 | _trix["trix"] = trx 638 | _trix["trma"] = trma 639 | return _trix 640 | 641 | 642 | def bbi(df): 643 | """ 644 | Bull And Bearlndex 多空指标 645 | Parameters 646 | ------ 647 | df:pandas.DataFrame 648 | 通过 get_k_data 取得的股票数据 649 | return 650 | ------- 651 | BBI: 652 | BBI指标 653 | 654 | """ 655 | _bbi = pd.DataFrame() 656 | _bbi['date'] = df['date'] 657 | CS = [] 658 | BBI = [] 659 | for index, row in df.iterrows(): 660 | CS.append(row["close"]) 661 | 662 | if len(CS) < 24: 663 | BBI.append(row["close"]) 664 | else: 665 | bbi = np.average([np.average(CS[-3:]), np.average(CS[-6:]), np.average(CS[-12:]), np.average(CS[-24:])]) 666 | BBI.append(bbi) 667 | _bbi['bbi'] = BBI 668 | return _bbi 669 | 670 | 671 | def mtm(df, n=6): 672 | """ 673 | Momentum Index 动量指标 674 | Parameters 675 | ------ 676 | df:pandas.DataFrame 677 | 通过 get_k_data 取得的股票数据 678 | n:int 679 | 统计时长,默认6 680 | return 681 | ------- 682 | MTM: 683 | MTM动量指标 684 | 685 | MTM(N日)=C-REF(C,N)式中,C=当日的收盘价,REF(C,N)=N日前的收盘价;N日是只计算交易日期,剔除掉节假日。 686 | MTMMA(MTM,N1)= MA(MTM,N1) 687 | N表示间隔天数,N1表示天数 688 | """ 689 | _mtm = pd.DataFrame() 690 | _mtm['date'] = df['date'] 691 | MTM = [] 692 | CN = [] 693 | for index, row in enumerate(df.itertuples(index=False)): 694 | if index < n: 695 | MTM.append(0.) 696 | else: 697 | mtm = getattr(row, 'close') - CN[index - n] 698 | MTM.append(mtm) 699 | CN.append(getattr(row, 'close')) 700 | _mtm["mtm"] = MTM 701 | _mtm["mtmma"] = _ma_array(MTM, n - 1) 702 | return _mtm 703 | 704 | 705 | def obv(df): 706 | """ # TODO:与同花顺指标数据不匹配 707 | 能量潮 On Balance Volume 708 | 多空比率净额= [(收盘价-最低价)-(最高价-收盘价)] ÷( 最高价-最低价)×V 709 | """ 710 | _obv = pd.DataFrame() 711 | _obv["date"] = df['date'] 712 | tmp = np.true_divide(((df["close"] - df["low"]) - (df["high"] - df["close"])), (df["high"] - df["low"])) 713 | OBV = tmp * df["volume"] 714 | _obv["obv"] = OBV.expanding(1).sum() / 100 715 | return _obv 716 | 717 | 718 | def cci(df, n=14): 719 | """ 720 | 顺势指标 721 | :param df: pandas.DataFrame (get_k_data获取的数据) 722 | :param n: 统计时长,默认14 723 | :return: pandas.DataFrame 724 | 725 | 同花顺算法:TYP:=(HIGH+LOW+CLOSE)/3 726 | CCI:=(TYP-MA(TYP,N))/(0.015×AVEDEV(TYP,N)) 727 | """ 728 | _cci = pd.DataFrame() 729 | _cci["date"] = df['date'] 730 | df['typ'] = (df['high'] + df['low'] + df['close']) / 3 731 | _cci['cci'] = ((df['typ'] - df['typ'].rolling(n).mean()) / 732 | (0.015 * df['typ'].rolling(min_periods=1, center=False, window=n).apply( 733 | lambda x: np.fabs(x - x.mean()).mean()))) 734 | # _cci['ccci'] = ((df['typ'] - df['typ'].rolling(n).mean()) / 735 | # (0.015 * abs(df['typ'] - df['typ'].rolling(n).mean()).rolling(n).mean())) 736 | df.drop(columns=['typ'], inplace=True) 737 | return _cci 738 | 739 | 740 | def priceosc(df, n=12, m=26): 741 | """ 742 | 价格振动指数 743 | PRICEOSC=(MA(C,12)-MA(C,26))/MA(C,12) 744 | """ 745 | _c = pd.DataFrame() 746 | _c['date'] = df['date'] 747 | man = ma(df, n)['v' + str(n)] 748 | _c['osc'] = (man - ma(df, m)['v' + str(m)]) / man 749 | return _c 750 | 751 | 752 | def sma(a, n, m): 753 | """ 754 | 平滑移动指标 Smooth Moving Average 755 | """ 756 | _sma = [] 757 | for index, value in enumerate(a): 758 | if index == 0 or pd.isna(value) or np.isnan(value): 759 | tsma = 0 760 | else: 761 | # Y=(M*X+(N-M)*Y')/N 762 | tsma = (m * value + (n - m) * tsma) / n 763 | _sma.append(tsma) 764 | return np.asarray(_sma) 765 | 766 | 767 | def dbcd(df, n=5, m=16, t=76): 768 | """ 769 | 异同离差乖离率 dbcd(5,16,76) 770 | BIAS=(C-MA(C,N))/MA(C,N) 771 | DIF=(BIAS-REF(BIAS,M)) 772 | DBCD=SMA(DIF,T,1) =(1-1/T)×SMA(REF(DIF,1),T,1)+ 1/T×DIF 773 | MM=MA(DBCD,5) 774 | :param df: 775 | :param n: 776 | :param m: 777 | :return: 778 | """ 779 | _dbcd = pd.DataFrame() 780 | _dbcd['date'] = df['date'] 781 | man = ma(df, n)['v' + str(n)] 782 | _bias = (df['close'] - man) / man 783 | _dif = _bias - _bias.shift(m) 784 | _dbcd['dbcd'] = sma(_dif, t, 1) 785 | _dbcd['mm'] = _ma_array(_dbcd['dbcd'], n) 786 | return _dbcd 787 | 788 | 789 | def roc(df, n=12, m=6): 790 | """ 791 | :param df: 792 | :param n: 793 | :param m: 794 | :return 795 | 变动速率 roc(12,6) ROC=(今日收盘价-N日前的收盘价)/ N日前的收盘价×100% 796 | ROCMA=MA(ROC,M) 797 | ROC:(CLOSE-REF(CLOSE,N))/REF(CLOSE,N)×100 798 | ROCMA:MA(ROC,M) 799 | """ 800 | _roc = pd.DataFrame() 801 | _roc['date'] = df['date'] 802 | ''' 803 | # 方法一 804 | r = [] 805 | rn = [] 806 | for index, row in enumerate(df.itertuples(index=False)): 807 | close = getattr(row, 'close') 808 | if index < n: 809 | r.append(0) 810 | else: 811 | n_close = rn.pop(0) 812 | # r.append((getattr(row, 'close') - rn[index - n])/rn[index - n] * 100) 813 | r.append((close - n_close)/n_close * 100) 814 | rn.append(close) 815 | _roc['roc'] = r 816 | ''' 817 | _roc['roc'] = (df['close'] - df['close'].shift(n))/df['close'].shift(n) * 100 818 | _roc['rocma'] = _ma_array(_roc['roc'], m) 819 | return _roc 820 | 821 | 822 | def vroc(df, n=12): 823 | """ 824 | 量变动速率 VROC=(当日成交量-N日前的成交量)/ N日前的成交量×100% 825 | :param df: 826 | :param n: 827 | :return: 828 | """ 829 | _vroc = pd.DataFrame() 830 | _vroc['date'] = df['date'] 831 | _vroc['vroc'] = (df['volume'] - df['volume'].shift(n)) / df['volume'].shift(n) * 100 832 | return _vroc 833 | 834 | 835 | def cr(df, n=26): 836 | """ 能量指标 837 | CR=∑(H-PM)/∑(PM-L)×100 838 | PM:上一交易日中价((最高、最低、收盘价的均值) 839 | H:当天最高价 840 | L:当天最低价 841 | :param df: 842 | :param n: 843 | :return: 844 | """ 845 | _cr = pd.DataFrame() 846 | _cr['date'] = df['date'] 847 | # pm = ((df['high'] + df['low'] + df['close']) / 3).shift(1) 848 | pm = (df[['high', 'low', 'close']]).mean(axis=1).shift(1) 849 | _cr['cr'] = (df['high'] - pm).rolling(n).sum()/(pm - df['low']).rolling(n).sum() * 100 850 | return _cr 851 | 852 | 853 | def psy(df, n=12): 854 | """ 855 | 心理指标 PSY(12) 856 | PSY=N日内上涨天数/N×100 857 | PSY:COUNT(CLOSE>REF(CLOSE,1),N)/N×100 858 | MAPSY=PSY的M日简单移动平均 859 | :param df: pandas.DataFrame get_k_data数据 860 | :param n: 861 | :return: 862 | """ 863 | _psy = pd.DataFrame() 864 | _psy['date'] = df['date'] 865 | p = df['close'] - df['close'].shift() 866 | p[p <= 0] = np.nan 867 | _p = p.rolling(n).count() / n * 100 868 | _psy['psy'] = _p 869 | return _psy 870 | 871 | 872 | def wad(df, n=30): 873 | """ 874 | 威廉聚散指标 WAD(30) 875 | TRL=昨日收盘价与今日最低价中价格最低者;TRH=昨日收盘价与今日最高价中价格最高者 876 | 如果今日的收盘价>昨日的收盘价,则今日的A/D=今日的收盘价-今日的TRL 877 | 如果今日的收盘价<昨日的收盘价,则今日的A/D=今日的收盘价-今日的TRH 878 | 如果今日的收盘价=昨日的收盘价,则今日的A/D=0 879 | WAD=今日的A/D+昨日的WAD;MAWAD=WAD的M日简单移动平均 880 | """ 881 | def dmd(x): 882 | if x.c > 0: 883 | y = x.close - x.trl 884 | elif x.c < 0: 885 | y = x.close - x.trh 886 | else: 887 | y = 0 888 | return y 889 | 890 | _wad = pd.DataFrame() 891 | _wad['date'] = df['date'] 892 | ad = pd.DataFrame() 893 | ad['trl'] = np.minimum(df['low'], df['close'].shift(1)) 894 | ad['trh'] = np.maximum(df['high'], df['close'].shift(1)) 895 | ad['c'] = df['close'] - df['close'].shift() 896 | ad['close'] = df['close'] 897 | ad['ad'] = ad.apply(dmd, axis=1) 898 | _wad['wad'] = ad['ad'].expanding(1).sum() 899 | _wad['mawad'] = _ma_array(_wad['wad'], n) 900 | return _wad 901 | 902 | 903 | def mfi(df, n=14): 904 | """ 905 | 资金流向指标 mfi(14) 906 | MF=TYP×成交量;TYP:当日中价((最高、最低、收盘价的均值) 907 | 如果当日TYP>昨日TYP,则将当日的MF值视为当日PMF值。而当日NMF值=0 908 | 如果当日TYP<=昨日TYP,则将当日的MF值视为当日NMF值。而当日PMF值=0 909 | MR=∑PMF/∑NMF 910 | MFI=100-(100÷(1+MR)) 911 | """ 912 | _mfi = pd.DataFrame() 913 | _mfi['date'] = df['date'] 914 | _m = pd.DataFrame() 915 | _m['typ'] = df[['high', 'low', 'close']].mean(axis=1) 916 | _m['mf'] = _m['typ'] * df['volume'] 917 | _m['typ_shift'] = _m.typ - _m.typ.shift(1) 918 | _m['pmf'] = _m.apply(lambda x: x.mf if x.typ_shift > 0 else 0, axis=1) 919 | _m['nmf'] = _m.apply(lambda x: x.mf if x.typ_shift <= 0 else 0, axis=1) 920 | # _mfi['mfi'] = 100 - (100 / (1 + _m.pmf.rolling(n).sum() / _m.nmf.rolling(n).sum())) 921 | _m['mr'] = _m.pmf.rolling(n).sum() / _m.nmf.rolling(n).sum() 922 | _mfi['mfi'] = 100 * _m['mr'] / (1 + _m['mr']) # 同花顺自己给出的公式和实际用的公式不一样,真操蛋,浪费两个小时时间 923 | return _mfi 924 | 925 | 926 | def pvt(df): 927 | """ 928 | pvt 量价趋势指标 pvt 929 | 如果设x=(今日收盘价—昨日收盘价)/昨日收盘价×当日成交量, 930 | 那么当日PVT指标值则为从第一个交易日起每日X值的累加。 931 | """ 932 | _pvt = pd.DataFrame() 933 | _pvt['date'] = df.date 934 | 935 | x = (df.close - df.close.shift(1)) / df.close.shift(1) * df.volume 936 | _pvt['pvt'] = x.expanding(1).sum() 937 | return _pvt 938 | 939 | 940 | def wvad(df, n=24, m=6): 941 | """ # TODO: 与同花顺数据不匹配 942 | 威廉变异离散量 wvad(24,6) 943 | WVAD=N1日的∑ {(当日收盘价-当日开盘价)/(当日最高价-当日最低价)×成交量} 944 | MAWVAD=MA(WVAD,N2) 945 | """ 946 | _wvad = pd.DataFrame() 947 | _wvad['date'] = df.date 948 | _wvad['wvad'] = (np.true_divide((df.close - df.open), (df.high - df.low)) * df.volume).rolling(n).sum() 949 | _wvad['mawvad'] = _ma_array(_wvad['wvad'], m) 950 | return _wvad 951 | 952 | 953 | def cdp(df): 954 | """ 955 | 逆势操作 cdp 956 | CDP=(最高价+最低价+收盘价)/3 # 同花顺实际用的(H+L+2*c)/4 957 | AH=CDP+(前日最高价-前日最低价) 958 | NH=CDP×2-最低价 959 | NL=CDP×2-最高价 960 | AL=CDP-(前日最高价-前日最低价) 961 | """ 962 | _cdp = pd.DataFrame() 963 | _cdp['date'] = df.date 964 | # _cdp['cdp'] = (df.high + df.low + df.close * 2).shift(1) / 4 965 | _cdp['cdp'] = df[['high', 'low', 'close', 'close']].shift(1).mean(axis=1) 966 | _cdp['ah'] = _cdp.cdp + (df.high.shift(1) - df.low.shift(1)) 967 | _cdp['al'] = _cdp.cdp - (df.high.shift(1) - df.low.shift(1)) 968 | _cdp['nh'] = _cdp.cdp * 2 - df.low.shift(1) 969 | _cdp['nl'] = _cdp.cdp * 2 - df.high.shift(1) 970 | return _cdp 971 | 972 | 973 | def env(df, n=14): 974 | """ 975 | ENV指标 ENV(14) 976 | Upper=MA(CLOSE,N)×1.06 977 | LOWER= MA(CLOSE,N)×0.94 978 | """ 979 | _env = pd.DataFrame() 980 | _env['date'] = df.date 981 | _env['up'] = df.close.rolling(n).mean() * 1.06 982 | _env['low'] = df.close.rolling(n).mean() * 0.94 983 | return _env 984 | 985 | 986 | def mike(df, n=12): 987 | """ 988 | 麦克指标 mike(12) 989 | 初始价(TYP)=(当日最高价+当日最低价+当日收盘价)/3 990 | HV=N日内区间最高价 991 | LV=N日内区间最低价 992 | 初级压力线(WR)=TYP×2-LV 993 | 中级压力线(MR)=TYP+HV-LV 994 | 强力压力线(SR)=2×HV-LV 995 | 初级支撑线(WS)=TYP×2-HV 996 | 中级支撑线(MS)=TYP-HV+LV 997 | 强力支撑线(SS)=2×LV-HV 998 | """ 999 | _mike = pd.DataFrame() 1000 | _mike['date'] = df.date 1001 | typ = df[['high', 'low', 'close']].mean(axis=1) 1002 | hv = df.high.rolling(n).max() 1003 | lv = df.low.rolling(n).min() 1004 | _mike['wr'] = typ * 2 - lv 1005 | _mike['mr'] = typ + hv - lv 1006 | _mike['sr'] = 2 * hv - lv 1007 | _mike['ws'] = typ * 2 - hv 1008 | _mike['ms'] = typ - hv + lv 1009 | _mike['ss'] = 2 * lv - hv 1010 | return _mike 1011 | 1012 | 1013 | def vma(df, n=5): 1014 | """ 1015 | 量简单移动平均 VMA(5) VMA=MA(volume,N) 1016 | VOLUME表示成交量;N表示天数 1017 | """ 1018 | _vma = pd.DataFrame() 1019 | _vma['date'] = df.date 1020 | _vma['vma'] = df.volume.rolling(n).mean() 1021 | return _vma 1022 | 1023 | 1024 | def _ema_n(arr, n): 1025 | return arr.ewm(ignore_na=False, span=n, min_periods=0, adjust=False).mean() 1026 | 1027 | 1028 | def vmacd(df, qn=12, sn=26, m=9): 1029 | """ 1030 | 量指数平滑异同平均 vmacd(12,26,9) 1031 | 今日EMA(N)=2/(N+1)×今日成交量+(N-1)/(N+1)×昨日EMA(N) 1032 | DIFF= EMA(N1)- EMA(N2) 1033 | DEA(DIF,M)= 2/(M+1)×DIF +[1-2/(M+1)]×DEA(REF(DIF,1),M) 1034 | MACD(BAR)=2×(DIF-DEA) 1035 | """ 1036 | _vmacd = pd.DataFrame() 1037 | _vmacd['date'] = df.date 1038 | _vmacd['diff'] = _ema_n(df.volume, qn) - _ema_n(df.volume, sn) 1039 | _vmacd['dea'] = _ema_n(_vmacd['diff'], m) # TODO: 不能用_vmacd.diff, 不知道为什么 1040 | _vmacd['macd'] = (_vmacd['diff'] - _vmacd['dea']) 1041 | return _vmacd 1042 | 1043 | 1044 | def vosc(df, n=12, m=26): 1045 | """ 1046 | 成交量震荡 vosc(12,26) 1047 | VOSC=(MA(VOLUME,SHORT)- MA(VOLUME,LONG))/MA(VOLUME,SHORT)×100 1048 | """ 1049 | _c = pd.DataFrame() 1050 | _c['date'] = df['date'] 1051 | _c['osc'] = (df.volume.rolling(n).mean() - df.volume.rolling(m).mean()) / df.volume.rolling(n).mean() * 100 1052 | return _c 1053 | 1054 | 1055 | def tapi(df, n=6): 1056 | """ 1057 | 加权指数成交值 tapi(6) 1058 | TAPI=每日成交总值/当日加权指数=a/PI;A表示每日的成交金额,PI表示当天的股价指数即指收盘价 1059 | """ 1060 | 1061 | 1062 | def vstd(df, n=10): 1063 | """ 1064 | 成交量标准差 vstd(10) 1065 | VSTD=STD(Volume,N)=[∑(Volume-MA(Volume,N))^2/N]^0.5 1066 | """ 1067 | _vstd = pd.DataFrame() 1068 | _vstd['date'] = df.date 1069 | _vstd['vstd'] = df.volume.rolling(n).std(ddof=1) 1070 | return _vstd 1071 | 1072 | 1073 | def adtm(df, n=23, m=8): 1074 | """ 1075 | 动态买卖气指标 adtm(23,8) 1076 | 如果开盘价≤昨日开盘价,DTM=0 1077 | 如果开盘价>昨日开盘价,DTM=(最高价-开盘价)和(开盘价-昨日开盘价)的较大值 1078 | 如果开盘价≥昨日开盘价,DBM=0 1079 | 如果开盘价<昨日开盘价,DBM=(开盘价-最低价) 1080 | STM=DTM在N日内的和 1081 | SBM=DBM在N日内的和 1082 | 如果STM > SBM,ADTM=(STM-SBM)/STM 1083 | 如果STM < SBM , ADTM = (STM-SBM)/SBM 1084 | 如果STM = SBM,ADTM=0 1085 | ADTMMA=MA(ADTM,M) 1086 | """ 1087 | _adtm = pd.DataFrame() 1088 | _adtm['date'] = df.date 1089 | _m = pd.DataFrame() 1090 | _m['cc'] = df.open - df.open.shift(1) 1091 | _m['ho'] = df.high - df.open 1092 | _m['ol'] = df.open - df.low 1093 | _m['dtm'] = _m.apply(lambda x: max(x.ho, x.cc) if x.cc > 0 else 0, axis=1) 1094 | _m['dbm'] = _m.apply(lambda x: x.ol if x.cc < 0 else 0, axis=1) 1095 | _m['stm'] = _m.dtm.rolling(n).sum() 1096 | _m['sbm'] = _m.dbm.rolling(n).sum() 1097 | _m['ss'] = _m.stm - _m.sbm 1098 | _adtm['adtm'] = _m.apply(lambda x: x.ss / x.stm if x.ss > 0 else (x.ss / x.sbm if x.ss < 0 else 0), axis=1) 1099 | _adtm['adtmma'] = _adtm.adtm.rolling(m).mean() 1100 | return _adtm 1101 | 1102 | 1103 | def mi(df, n=12): 1104 | """ 1105 | 动量指标 mi(12) 1106 | A=CLOSE-REF(CLOSE,N) 1107 | MI=SMA(A,N,1) 1108 | """ 1109 | _mi = pd.DataFrame() 1110 | _mi['date'] = df.date 1111 | a = df.close - df.close.shift(n) 1112 | _mi['mi'] = sma(a, n, 1) 1113 | return _mi 1114 | 1115 | 1116 | def micd(df, n=3, m=10, k=20): 1117 | """ 1118 | 异同离差动力指数 micd(3,10,20) 1119 | MI=CLOSE-ref(CLOSE,1)AMI=SMA(MI,N1,1) 1120 | DIF=MA(ref(AMI,1),N2)-MA(ref(AMI,1),N3) 1121 | MICD=SMA(DIF,10,1) 1122 | """ 1123 | _micd = pd.DataFrame() 1124 | _micd['date'] = df.date 1125 | mi = df.close - df.close.shift(1) 1126 | ami = pd.Series(sma(mi, n, 1)) 1127 | dif = ami.shift(1).rolling(m).mean() - ami.shift(1).rolling(k).mean() 1128 | _micd['micd'] = sma(dif, 10, 1) 1129 | return _micd 1130 | 1131 | 1132 | def rc(df, n=50): 1133 | """ 1134 | 变化率指数 rc(50) 1135 | RC=收盘价/REF(收盘价,N)×100 1136 | ARC=EMA(REF(RC,1),N,1) 1137 | """ 1138 | _rc = pd.DataFrame() 1139 | _rc['date'] = df.date 1140 | _rc['rc'] = df.close / df.close.shift(n) * 100 1141 | _rc['arc'] = sma(_rc.rc.shift(1), n, 1) 1142 | return _rc 1143 | 1144 | 1145 | def rccd(df, n=59, m=21, k=28): 1146 | """ # TODO: 计算结果错误,稍后检查 1147 | 异同离差变化率指数 rccd(59,21,28) 1148 | RC=收盘价/REF(收盘价,N)×100% 1149 | ARC=EMA(REF(RC,1),N,1) 1150 | DIF=MA(ref(ARC,1),N1)-MA MA(ref(ARC,1),N2) 1151 | RCCD=SMA(DIF,N,1) 1152 | """ 1153 | _rccd = pd.DataFrame() 1154 | _rccd['date'] = df.date 1155 | rc = df.close / df.close.shift(n) * 100 1156 | arc = pd.Series(sma(rc.shift(1), n, 1)) 1157 | dif = arc.shift(1).rolling(m).mean() - arc.shift(1).rolling(k).mean() 1158 | _rccd['rc'] = rc 1159 | _rccd['arc'] = arc 1160 | _rccd['dif'] = dif 1161 | _rccd['rccd'] = sma(dif, n, 1) 1162 | return _rccd 1163 | 1164 | 1165 | def srmi(df, n=9): 1166 | """ 1167 | SRMIMI修正指标 srmi(9) 1168 | 如果收盘价>N日前的收盘价,SRMI就等于(收盘价-N日前的收盘价)/收盘价 1169 | 如果收盘价 0 else (x.cs/x.cp if x.cs < 0 else 0), axis=1) 1179 | return _srmi 1180 | 1181 | 1182 | def dptb(df, n=7): 1183 | """ 1184 | 大盘同步指标 dptb(7) 1185 | DPTB=(统计N天中个股收盘价>开盘价,且指数收盘价>开盘价的天数或者个股收盘价<开盘价,且指数收盘价<开盘价)/N 1186 | """ 1187 | ind = ts.get_k_data("sh000001", start=df.date.iloc[0], end=df.date.iloc[-1]) 1188 | sd = df.copy() 1189 | sd.set_index('date', inplace=True) # 可能出现停盘等情况,所以将date设为index 1190 | ind.set_index('date', inplace=True) 1191 | _dptb = pd.DataFrame(index=data.date) 1192 | q = ind.close - ind.open 1193 | _dptb['p'] = sd.close - sd.open 1194 | _dptb['q'] = q 1195 | _dptb['m'] = _dptb.apply(lambda x: 1 if (x.p > 0 and x.q > 0) or (x.p < 0 and x.q < 0) else np.nan, axis=1) 1196 | _dptb['jdrs'] = _dptb.m.rolling(n).count() / n 1197 | _dptb.drop(columns=['p', 'q', 'm'], inplace=True) 1198 | _dptb.reset_index(inplace=True) 1199 | return _dptb 1200 | 1201 | 1202 | def jdqs(df, n=20): 1203 | """ 1204 | 阶段强势指标 jdqs(20) 1205 | JDQS=(统计N天中个股收盘价>开盘价,且指数收盘价<开盘价的天数)/(统计N天中指数收盘价<开盘价的天数) 1206 | """ 1207 | ind = ts.get_k_data("sh000001", start=df.date.iloc[0], end=df.date.iloc[-1]) 1208 | sd = df.copy() 1209 | sd.set_index('date', inplace=True) # 可能出现停盘等情况,所以将date设为index 1210 | ind.set_index('date', inplace=True) 1211 | _jdrs = pd.DataFrame(index=data.date) 1212 | q = ind.close - ind.open 1213 | _jdrs['p'] = sd.close - sd.open 1214 | _jdrs['q'] = q 1215 | _jdrs['m'] = _jdrs.apply(lambda x: 1 if (x.p > 0 and x.q < 0) else np.nan, axis=1) 1216 | q[q > 0] = np.nan 1217 | _jdrs['t'] = q 1218 | _jdrs['jdrs'] = _jdrs.m.rolling(n).count() / _jdrs.t.rolling(n).count() 1219 | _jdrs.drop(columns=['p', 'q', 'm', 't'], inplace=True) 1220 | _jdrs.reset_index(inplace=True) 1221 | return _jdrs 1222 | 1223 | 1224 | def jdrs(df, n=20): 1225 | """ 1226 | 阶段弱势指标 jdrs(20) 1227 | JDRS=(统计N天中个股收盘价<开盘价,且指数收盘价>开盘价的天数)/(统计N天中指数收盘价>开盘价的天数) 1228 | """ 1229 | ind = ts.get_k_data("sh000001", start=df.date.iloc[0], end=df.date.iloc[-1]) 1230 | sd = df.copy() 1231 | sd.set_index('date', inplace=True) 1232 | ind.set_index('date', inplace=True) 1233 | _jdrs = pd.DataFrame(index=data.date) 1234 | q = ind.close - ind.open 1235 | _jdrs['p'] = sd.close - sd.open 1236 | _jdrs['q'] = q 1237 | _jdrs['m'] = _jdrs.apply(lambda x: 1 if (x.p < 0 and x.q > 0) else np.nan, axis=1) 1238 | q[q < 0] = np.nan 1239 | _jdrs['t'] = q 1240 | _jdrs['jdrs'] = _jdrs.m.rolling(n).count() / _jdrs.t.rolling(n).count() 1241 | _jdrs.drop(columns=['p', 'q', 'm', 't'], inplace=True) 1242 | _jdrs.reset_index(inplace=True) 1243 | return _jdrs 1244 | 1245 | 1246 | def zdzb(df, n=125, m=5, k=20): 1247 | """ 1248 | 筑底指标 zdzb(125,5,20) 1249 | A=(统计N1日内收盘价>=前收盘价的天数)/(统计N1日内收盘价<前收盘价的天数) 1250 | B=MA(A,N2) 1251 | D=MA(A,N3) 1252 | """ 1253 | _zdzb = pd.DataFrame() 1254 | _zdzb['date'] = df.date 1255 | p = df.close - df.close.shift(1) 1256 | q = p.copy() 1257 | p[p < 0] = np.nan 1258 | q[q >= 0] = np.nan 1259 | _zdzb['a'] = p.rolling(n).count() / q.rolling(n).count() 1260 | _zdzb['b'] = _zdzb.a.rolling(m).mean() 1261 | _zdzb['d'] = _zdzb.a.rolling(k).mean() 1262 | return _zdzb 1263 | 1264 | 1265 | def atr(df, n=14): 1266 | """ 1267 | 真实波幅 atr(14) 1268 | TR:MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW)) 1269 | ATR:MA(TR,N) 1270 | """ 1271 | _atr = pd.DataFrame() 1272 | _atr['date'] = df.date 1273 | # _atr['tr'] = np.maximum(df.high - df.low, (df.close.shift(1) - df.low).abs()) 1274 | # _atr['tr'] = np.maximum.reduce([df.high - df.low, (df.close.shift(1) - df.high).abs(), (df.close.shift(1) - df.low).abs()]) 1275 | _atr['tr'] = np.vstack([df.high - df.low, (df.close.shift(1) - df.high).abs(), (df.close.shift(1) - df.low).abs()]).max(axis=0) 1276 | _atr['atr'] = _atr.tr.rolling(n).mean() 1277 | return _atr 1278 | 1279 | 1280 | def mass(df, n=9, m=25): 1281 | """ 1282 | 梅丝线 mass(9,25) 1283 | AHL=MA((H-L),N1) 1284 | BHL= MA(AHL,N1) 1285 | MASS=SUM(AHL/BHL,N2) 1286 | H:表示最高价;L:表示最低价 1287 | """ 1288 | _mass = pd.DataFrame() 1289 | _mass['date'] = df.date 1290 | ahl = (df.high - df.low).rolling(n).mean() 1291 | bhl = ahl.rolling(n).mean() 1292 | _mass['mass'] = (ahl / bhl).rolling(m).sum() 1293 | return _mass 1294 | 1295 | 1296 | def vhf(df, n=28): 1297 | """ 1298 | 纵横指标 vhf(28) 1299 | VHF=(N日内最大收盘价与N日内最小收盘价之前的差)/(N日收盘价与前收盘价差的绝对值之和) 1300 | """ 1301 | _vhf = pd.DataFrame() 1302 | _vhf['date'] = df.date 1303 | _vhf['vhf'] = (df.close.rolling(n).max() - df.close.rolling(n).min()) / (df.close - df.close.shift(1)).abs().rolling(n).sum() 1304 | return _vhf 1305 | 1306 | 1307 | def cvlt(df, n=10): 1308 | """ 1309 | 佳庆离散指标 cvlt(10) 1310 | cvlt=(最高价与最低价的差的指数移动平均-前N日的最高价与最低价的差的指数移动平均)/前N日的最高价与最低价的差的指数移动平均 1311 | """ 1312 | _cvlt = pd.DataFrame() 1313 | _cvlt['date'] = df.date 1314 | p = _ema_n(df.high.shift(n) - df.low.shift(n), n) 1315 | _cvlt['cvlt'] = (_ema_n(df.high - df.low, n) - p) / p * 100 1316 | return _cvlt 1317 | 1318 | 1319 | def up_n(df): 1320 | """ 1321 | 连涨天数 up_n 连续上涨天数,当天收盘价大于开盘价即为上涨一天 # 同花顺实际结果用收盘价-前一天收盘价 1322 | """ 1323 | _up = pd.DataFrame() 1324 | _up['date'] = df.date 1325 | p = df.close - df.close.shift() 1326 | p[p > 0] = 1 1327 | p[p < 0] = 0 1328 | m = [] 1329 | for k, g in itertools.groupby(p): 1330 | t = 0 1331 | for i in g: 1332 | if k == 0: 1333 | m.append(0) 1334 | else: 1335 | t += 1 1336 | m.append(t) 1337 | # _up['p'] = p 1338 | _up['up'] = m 1339 | return _up 1340 | 1341 | 1342 | def down_n(df): 1343 | """ 1344 | 连跌天数 down_n 连续下跌天数,当天收盘价小于开盘价即为下跌一天 1345 | """ 1346 | _down = pd.DataFrame() 1347 | _down['date'] = df.date 1348 | p = df.close - df.close.shift() 1349 | p[p > 0] = 0 1350 | p[p < 0] = 1 1351 | m = [] 1352 | for k, g in itertools.groupby(p): 1353 | t = 0 1354 | for i in g: 1355 | if k == 0: 1356 | m.append(0) 1357 | else: 1358 | t += 1 1359 | m.append(t) 1360 | _down['down'] = m 1361 | return _down 1362 | 1363 | 1364 | def join_frame(d1, d2, column='date'): 1365 | # 将两个DataFrame 按照datetime合并 1366 | return d1.join(d2.set_index(column), on=column) 1367 | 1368 | 1369 | if __name__ == "__main__": 1370 | import tushare as ts 1371 | data = ts.get_k_data("000063", start="2018-03-01") 1372 | # data = ts.get_k_data("601138", start="2017-05-01") 1373 | 1374 | # maf = ma(data, n=[5, 10, 20]) 1375 | # 将均线合并到data中 1376 | # print(join_frame(data, maf)) 1377 | 1378 | # data = pd.DataFrame({"close": [1,2,3,4,5,6,7,8,9,0]}) 1379 | # print(ma(data)) 1380 | # mdf = md(data) 1381 | # print(md(data, n=26)) 1382 | # print(join_frame(data, mdf)) 1383 | # emaf = ema(data) 1384 | # print(ema(data, 5)) 1385 | # print(join_frame(data, emaf)) 1386 | # print(macd(data)) 1387 | # print(kdj(data)) 1388 | # print(vrsi(data, 6)) 1389 | # print(boll(data)) 1390 | # print(bbiboll(data)) 1391 | # print(join_frame(data, wr(data, n=14))) 1392 | # print(join_frame(data, bias(data, n=[6, 12, 24]))) 1393 | # print(join_frame(data, asi(data, n=[6, 12, 24]))) 1394 | # print(join_frame(data, vr_rate(data))) 1395 | # print(join_frame(data, arbr(data))) 1396 | # print(join_frame(data, dpo(data))) 1397 | # print(join_frame(data, trix(data))) 1398 | # print(join_frame(data, bbi(data))) 1399 | # print(ts.top_list(date="2018-12-20")) 1400 | # print(join_frame(data, mtm(data))) 1401 | # print(join_frame(data, obv(data))) 1402 | # print(join_frame(data, cci(data))) 1403 | # print(join_frame(data, priceosc(data))) 1404 | # print(dbcd(data)) 1405 | # print(roc(data)) 1406 | # print(vroc(data)) 1407 | # print(cr(data)) 1408 | # print(psy(data)) 1409 | # print(wad(data)) 1410 | # print(mfi(data)) 1411 | # print(pvt(data)) 1412 | # print(wvad(data)) 1413 | # print(cdp(data)) 1414 | # print(env(data)) 1415 | # print(mike(data)) 1416 | # print(vr(data)) 1417 | # print(vma(data)) 1418 | # print(vmacd(data)) 1419 | # print(vosc(data)) 1420 | # print(vstd(data)) 1421 | # print(adtm(data)) 1422 | # print(mi(data)) 1423 | # print(micd(data)) 1424 | # print(rc(data)) 1425 | # print(rccd(data)) 1426 | # print(srmi(data)) 1427 | # print(dptb(data)) 1428 | # print(jdqs(data)) 1429 | # pd.set_option('display.max_rows', 1000) 1430 | # print(jdrs(data)) 1431 | print(join_frame(data, jdrs(data))) 1432 | # print(data) 1433 | # print(zdzb(data)) 1434 | # print(atr(data)) 1435 | # print(mass(data)) 1436 | # print(vhf(data)) 1437 | # print(cvlt(data)) 1438 | # print(up_n(data)) 1439 | # print(down_n(data)) 1440 | --------------------------------------------------------------------------------