# MyTT 麦语言-通达信-同花顺指标实现 https://github.com/mpquant/MyTT # MyTT高级函数验证版本: https://github.com/mpquant/MyTT/blob/main/MyTT_plus.py # Python2老版本pandas特别的MyTT: https://github.com/mpquant/MyTT/blob/main/MyTT_python2.py # V2.1 2021-6-6 新增 BARSLAST函数 SLOPE,FORCAST线性回归预测函数 # V2.3 2021-6-13 新增 TRIX,DPO,BRAR,DMA,MTM,MASS,ROC,VR,ASI等指标 # V2.4 2021-6-27 新增 EXPMA,OBV,MFI指标, 改进SMA核心函数(核心函数彻底无循环) # V2.7 2021-11-21 修正 SLOPE,BARSLAST,函数,新加FILTER,LONGCROSS, # 感谢qzhjiang对SLOPE,SMA等函数的指正 # V2.8 2021-11-23 修正 FORCAST,WMA函数,欢迎qzhjiang,stanene,bcq加入社群,一起来完善myTT库 # V2.9 2021-11-29 新增 HHVBARS,LLVBARS,CONST, VALUEWHEN功能函数 # V2.92 2021-11-30 新增 BARSSINCEN函数,现在可以 pip install MyTT 完成安装 # V3.0 2021-12-04 改进 DMA函数支持序列,新增XS2 薛斯通道II指标 # V3.1 2021-12-19 新增 TOPRANGE,LOWRANGE一级函数 # V3.2 2023-04-04 新增 CR指标 # V3.3 2023-11-09 新增 SIN,COS,TAN序列处理的三角函数 # V4.0 2026-06-02 handsomejustin 新增 ZHUOYAO,BIAS_SIGNAL两个自创函数 # V4.1 2026-06-14 新增 SAR(抛物线转向), VWAP(成交量加权均价), AROON(阿隆指标); 注册 FK # 以下所有函数如无特别说明,输入参数S均为numpy序列或者列表list,N为整型int # 应用层1级函数完美兼容通达信或同花顺,具体使用方法请参考通达信 import numpy as np import pandas as pd # ------------------ 0级:核心工具函数 -------------------------------------------- def RD(N, D=3): return np.round(N, D) # 四舍五入取3位小数 def RET(S, N=1): return np.array(S)[-N] # 返回序列倒数第N个值,默认返回最后一个 def ABS(S): return np.abs(S) # 返回N的绝对值 def LN(S): return np.log(S) # 求底是e的自然对数, def POW(S, N): return np.power(S, N) # 求S的N次方 def SQRT(S): return np.sqrt(S) # 求S的平方根 def SIN(S): return np.sin(S) # 求S的正弦值(弧度) def COS(S): return np.cos(S) # 求S的余弦值(弧度) def TAN(S): return np.tan(S) # 求S的正切值(弧度) def MAX(S1, S2): return np.maximum(S1, S2) # 序列max def MIN(S1, S2): return np.minimum(S1, S2) # 序列min def IF(S, A, B): return np.where(S, A, B) # 序列布尔判断 return=A if S==True else B def REF(S, N=1): # 对序列整体下移动N,返回序列(shift后会产生NAN) return pd.Series(S).shift(N).values def DIFF(S, N=1): # 前一个值减后一个值,前面会产生nan return pd.Series(S).diff(N).values # np.diff(S)直接删除nan,会少一行 def STD(S, N): # 求序列的N日标准差,返回序列 return pd.Series(S).rolling(N).std(ddof=0).values def SUM(S, N): # 对序列求N天累计和,返回序列 N=0对序列所有依次求和 return pd.Series(S).rolling(N).sum().values if N > 0 else pd.Series(S).cumsum().values def CONST(S): # 返回序列S最后的值组成常量序列 return np.full(len(S), S[-1]) def HHV(S, N): # HHV(C, 5) 最近5天收盘最高价 return pd.Series(S).rolling(N).max().values def LLV(S, N): # LLV(C, 5) 最近5天收盘最低价 return pd.Series(S).rolling(N).min().values def HHVBARS(S, N): # 求N周期内S最高值到当前周期数, 返回序列 return pd.Series(S).rolling(N).apply(lambda x: np.argmax(x[::-1]), raw=True).values def LLVBARS(S, N): # 求N周期内S最低值到当前周期数, 返回序列 return pd.Series(S).rolling(N).apply(lambda x: np.argmin(x[::-1]), raw=True).values def MA(S, N): # 求序列的N日简单移动平均值,返回序列 return pd.Series(S).rolling(N).mean().values def EMA(S, N): # 指数移动平均,为了精度 S>4*N EMA至少需要120周期 alpha=2/(span+1) return pd.Series(S).ewm(span=N, adjust=False).mean().values def SMA(S, N, M=1): # 中国式的SMA,至少需要120周期才精确 (雪球180周期) alpha=1/(1+com) return pd.Series(S).ewm(alpha=M / N, adjust=False).mean().values # com=N-M/M def WMA(S, N): # 通达信S序列的N日加权移动平均 Yn = (1*X1+2*X2+3*X3+...+n*Xn)/(1+2+3+...+Xn) return ( pd.Series(S) .rolling(N) .apply(lambda x: x[::-1].cumsum().sum() * 2 / N / (N + 1), raw=True) .values ) def DMA(S, A): # 求S的动态移动平均,A作平滑因子,必须 0B & A>0 & B>=0 return np.array( pd.Series(S).rolling(A + 1).apply(lambda x: np.all(x[::-1][B:]), raw=True), dtype=bool ) # -- 1级:应用层函数(通过0级核心函数实现)使用方法请参考通达信 -------------------- def COUNT(S, N): # COUNT(CLOSE>O, N): 最近N天满足S_BOO的天数 True的天数 return SUM(S, N) def EVERY(S, N): # EVERY(CLOSE>O, 5) 最近N天是否都是True return IF(SUM(S, N) == N, True, False) def EXIST(S, N): # EXIST(CLOSE>3010, N=5) n日内是否存在一天大于3000点 return IF(SUM(S, N) > 0, True, False) def FILTER(S, N): # FILTER函数,S满足条件后,将其后N周期内的数据置为0, FILTER(C==H,5) for i in range(len(S)): S[i + 1 : i + 1 + N] = 0 if S[i] else S[i + 1 : i + 1 + N] return S # 例:FILTER(C==H,5) 涨停后,后5天不再发出信号 def BARSLAST(S): # 上一次条件成立到当前的周期, BARSLAST(C/REF(C,1)>=1.1) 上一次涨停到今天的天数 M = np.concatenate(([0], np.where(S, 1, 0))) for i in range(1, len(M)): M[i] = 0 if M[i] else M[i - 1] + 1 return M[1:] def BARSLASTCOUNT(S): # 统计连续满足S条件的周期数 by jqz1226 rt = np.zeros(len(S) + 1) # BARSLASTCOUNT(CLOSE>OPEN)表示统计连续收阳的周期数 for i in range(len(S)): rt[i + 1] = rt[i] + 1 if S[i] else rt[i + 1] return rt[1:] def BARSSINCEN(S, N): # N周期内第一次S条件成立到现在的周期数,N为常量 by jqz1226 return ( pd.Series(S) .rolling(N) .apply(lambda x: N - 1 - np.argmax(x) if np.argmax(x) or x[0] else 0, raw=True) .fillna(0) .values.astype(int) ) def CROSS( S1, S2 ): # 判断向上金叉穿越 CROSS(MA(C,5),MA(C,10)) 判断向下死叉穿越 CROSS(MA(C,10),MA(C,5)) return np.concatenate( ([False], np.logical_not((S1 > S2)[:-1]) & (S1 > S2)[1:]) ) # 不使用0级函数,移植方便 by jqz1226 def LONGCROSS( S1, S2, N ): # 两条线维持一定周期后交叉,S1在N周期内都小于S2,本周期从S1下方向上穿过S2时返回1,否则返回0 return np.array( np.logical_and(LAST(S1 < S2, N, 1), (S1 > S2)), dtype=bool ) # N=1时等同于CROSS(S1, S2) def VALUEWHEN(S, X): # 当S条件成立时,取X的当前值,否则取VALUEWHEN的上个成立时的X值 by jqz1226 return pd.Series(np.where(S, X, np.nan)).ffill().values def BETWEEN(S, A, B): # S处于A和B之间时为真。 包括 AS>B return ((A < S) & (S < B)) | ((A > S) & (S > B)) def TOPRANGE(S): # TOPRANGE(HIGH)表示当前最高价是近多少周期内最高价的最大值 by jqz1226 rt = np.zeros(len(S)) for i in range(1, len(S)): rt[i] = np.argmin(np.flipud(S[:i] < S[i])) return rt.astype("int") def LOWRANGE(S): # LOWRANGE(LOW)表示当前最低价是近多少周期内最低价的最小值 by jqz1226 rt = np.zeros(len(S)) for i in range(1, len(S)): rt[i] = np.argmin(np.flipud(S[:i] > S[i])) return rt.astype("int") # ------------------ 2级:技术指标函数(全部通过0级,1级函数实现) ------------------------------ def MACD(CLOSE, SHORT=12, LONG=26, M=9): # EMA的关系,S取120日,和雪球小数点2位相同 DIF = EMA(CLOSE, SHORT) - EMA(CLOSE, LONG) DEA = EMA(DIF, M) MACD = (DIF - DEA) * 2 return RD(DIF), RD(DEA), RD(MACD) def KDJ(CLOSE, HIGH, LOW, N=9, M1=3, M2=3): # KDJ指标 low_n = LLV(LOW, N) high_n = HHV(HIGH, N) high_low_diff = high_n - low_n # 避免除零:当最高价等于最低价时,RSV 应该为 50(中性) with np.errstate(divide="ignore", invalid="ignore"): rsv = (CLOSE - low_n) / high_low_diff * 100 rsv = np.where(high_low_diff == 0, 50, rsv) # 除零时返回 50 K = EMA(rsv, (M1 * 2 - 1)) D = EMA(K, (M2 * 2 - 1)) J = K * 3 - D * 2 return K, D, J def RSI(CLOSE, N=24): # RSI指标,和通达信小数点2位相同 DIF = CLOSE - REF(CLOSE, 1) abs_dif_sma = SMA(ABS(DIF), N) # 避免除零:当价格完全不变时,RSI 应该为 50(中性) with np.errstate(divide="ignore", invalid="ignore"): rsi_value = SMA(MAX(DIF, 0), N) / abs_dif_sma * 100 rsi_value = np.where(abs_dif_sma == 0, 50, rsi_value) # 除零时返回 50 return RD(rsi_value) def WR(CLOSE, HIGH, LOW, N=10, N1=6): # W&R 威廉指标 high_n = HHV(HIGH, N) low_n = LLV(LOW, N) high_low_diff = high_n - low_n with np.errstate(divide="ignore", invalid="ignore"): wr = (high_n - CLOSE) / high_low_diff * 100 wr = np.where(high_low_diff == 0, 50, wr) # 除零时返回 50 high_n1 = HHV(HIGH, N1) low_n1 = LLV(LOW, N1) high_low_diff1 = high_n1 - low_n1 with np.errstate(divide="ignore", invalid="ignore"): wr1 = (high_n1 - CLOSE) / high_low_diff1 * 100 wr1 = np.where(high_low_diff1 == 0, 50, wr1) # 除零时返回 50 return RD(wr), RD(wr1) def BIAS(CLOSE, L1=6, L2=12, L3=24): # BIAS乖离率 BIAS1 = (CLOSE - MA(CLOSE, L1)) / MA(CLOSE, L1) * 100 BIAS2 = (CLOSE - MA(CLOSE, L2)) / MA(CLOSE, L2) * 100 BIAS3 = (CLOSE - MA(CLOSE, L3)) / MA(CLOSE, L3) * 100 return RD(BIAS1), RD(BIAS2), RD(BIAS3) def BOLL(CLOSE, N=20, P=2): # BOLL指标,布林带 MID = MA(CLOSE, N) UPPER = MID + STD(CLOSE, N) * P LOWER = MID - STD(CLOSE, N) * P return RD(UPPER), RD(MID), RD(LOWER) def PSY(CLOSE, N=12, M=6): PSY = COUNT(CLOSE > REF(CLOSE, 1), N) / N * 100 PSYMA = MA(PSY, M) return RD(PSY), RD(PSYMA) def CCI(CLOSE, HIGH, LOW, N=14): TP = (HIGH + LOW + CLOSE) / 3 return (TP - MA(TP, N)) / (0.015 * AVEDEV(TP, N)) def ATR(CLOSE, HIGH, LOW, N=20): # 真实波动N日平均值 TR = MAX(MAX((HIGH - LOW), ABS(REF(CLOSE, 1) - HIGH)), ABS(REF(CLOSE, 1) - LOW)) return MA(TR, N) def BBI(CLOSE, M1=3, M2=6, M3=12, M4=20): # BBI多空指标 return (MA(CLOSE, M1) + MA(CLOSE, M2) + MA(CLOSE, M3) + MA(CLOSE, M4)) / 4 def DMI(CLOSE, HIGH, LOW, M1=14, M2=6): # 动向指标:结果和同花顺,通达信完全一致 TR = SUM(MAX(MAX(HIGH - LOW, ABS(HIGH - REF(CLOSE, 1))), ABS(LOW - REF(CLOSE, 1))), M1) HD = HIGH - REF(HIGH, 1) LD = REF(LOW, 1) - LOW DMP = SUM(IF((HD > 0) & (HD > LD), HD, 0), M1) DMM = SUM(IF((LD > 0) & (LD > HD), LD, 0), M1) PDI = DMP * 100 / TR MDI = DMM * 100 / TR ADX = MA(ABS(MDI - PDI) / (PDI + MDI) * 100, M2) ADXR = (ADX + REF(ADX, M2)) / 2 return PDI, MDI, ADX, ADXR def TAQ(HIGH, LOW, N): # 唐安奇通道(海龟)交易指标,大道至简,能穿越牛熊 UP = HHV(HIGH, N) DOWN = LLV(LOW, N) MID = (UP + DOWN) / 2 return UP, MID, DOWN def KTN(CLOSE, HIGH, LOW, N=20, M=10): # 肯特纳交易通道, N选20日,ATR选10日 MID = EMA((HIGH + LOW + CLOSE) / 3, N) ATRN = ATR(CLOSE, HIGH, LOW, M) UPPER = MID + 2 * ATRN LOWER = MID - 2 * ATRN return UPPER, MID, LOWER def TRIX(CLOSE, M1=12, M2=20): # 三重指数平滑平均线 TR = EMA(EMA(EMA(CLOSE, M1), M1), M1) TRIX = (TR - REF(TR, 1)) / REF(TR, 1) * 100 TRMA = MA(TRIX, M2) return TRIX, TRMA def VR(CLOSE, VOL, M1=26): # VR容量比率 LC = REF(CLOSE, 1) return SUM(IF(CLOSE > LC, VOL, 0), M1) / SUM(IF(CLOSE <= LC, VOL, 0), M1) * 100 def CR(CLOSE, HIGH, LOW, N=20): # CR价格动量指标 MID = REF(HIGH + LOW + CLOSE, 1) / 3 num = SUM(MAX(0, HIGH - MID), N) den = SUM(MAX(0, MID - LOW), N) with np.errstate(divide="ignore", invalid="ignore"): return np.where(den > 0, num / den * 100, 100) def EMV(HIGH, LOW, VOL, N=14, M=9): # 简易波动指标 VOLUME = MA(VOL, N) / VOL MID = 100 * (HIGH + LOW - REF(HIGH + LOW, 1)) / (HIGH + LOW) EMV = MA(MID * VOLUME * (HIGH - LOW) / MA(HIGH - LOW, N), N) MAEMV = MA(EMV, M) return EMV, MAEMV def DPO(CLOSE, M1=20, M2=10, M3=6): # 区间震荡线 DPO = CLOSE - REF(MA(CLOSE, M1), M2) MADPO = MA(DPO, M3) return DPO, MADPO def BRAR(OPEN, CLOSE, HIGH, LOW, M1=26): # BRAR-ARBR 情绪指标 AR = SUM(HIGH - OPEN, M1) / SUM(OPEN - LOW, M1) * 100 BR = SUM(MAX(0, HIGH - REF(CLOSE, 1)), M1) / SUM(MAX(0, REF(CLOSE, 1) - LOW), M1) * 100 return AR, BR def DFMA(CLOSE, N1=10, N2=50, M=10): # 平行线差指标 DIF = MA(CLOSE, N1) - MA(CLOSE, N2) DIFMA = MA(DIF, M) # 通达信指标叫DMA 同花顺叫新DMA return DIF, DIFMA def MTM(CLOSE, N=12, M=6): # 动量指标 MTM = CLOSE - REF(CLOSE, N) MTMMA = MA(MTM, M) return MTM, MTMMA def MASS(HIGH, LOW, N1=9, N2=25, M=6): # 梅斯线 MASS = SUM(MA(HIGH - LOW, N1) / MA(MA(HIGH - LOW, N1), N1), N2) MA_MASS = MA(MASS, M) return MASS, MA_MASS def ROC(CLOSE, N=12, M=6): # 变动率指标 ROC = 100 * (CLOSE - REF(CLOSE, N)) / REF(CLOSE, N) MAROC = MA(ROC, M) return ROC, MAROC def EXPMA(CLOSE, N1=12, N2=50): # EMA指数平均数指标 return EMA(CLOSE, N1), EMA(CLOSE, N2) def OBV(CLOSE, VOL): # 能量潮指标 return SUM(IF(CLOSE > REF(CLOSE, 1), VOL, IF(CLOSE < REF(CLOSE, 1), -VOL, 0)), 0) / 10000 def MFI(CLOSE, HIGH, LOW, VOL, N=14): # MFI指标是成交量的RSI指标 TYP = (HIGH + LOW + CLOSE) / 3 pos_mf = SUM(IF(TYP > REF(TYP, 1), TYP * VOL, 0), N) neg_mf = SUM(IF(TYP < REF(TYP, 1), TYP * VOL, 0), N) with np.errstate(divide="ignore", invalid="ignore"): V1 = np.where(neg_mf > 0, pos_mf / neg_mf, np.where(pos_mf > 0, np.inf, 0)) return 100 - (100 / (1 + V1)) def ASI(OPEN, CLOSE, HIGH, LOW, M1=26, M2=10): # 振动升降指标 LC = REF(CLOSE, 1) AA = ABS(HIGH - LC) BB = ABS(LOW - LC) CC = ABS(HIGH - REF(LOW, 1)) DD = ABS(LC - REF(OPEN, 1)) R = IF( (AA > BB) & (AA > CC), AA + BB / 2 + DD / 4, IF((BB > CC) & (BB > AA), BB + AA / 2 + DD / 4, CC + DD / 4), ) X = CLOSE - LC + (CLOSE - OPEN) / 2 + LC - REF(OPEN, 1) SI = 16 * X / R * MAX(AA, BB) ASI = SUM(SI, M1) ASIT = MA(ASI, M2) return ASI, ASIT def XSII(CLOSE, HIGH, LOW, N=102, M=7): # 薛斯通道II AA = MA((2 * CLOSE + HIGH + LOW) / 4, 5) # 最新版DMA才支持 2021-12-4 TD1 = AA * N / 100 TD2 = AA * (200 - N) / 100 CC = ABS((2 * CLOSE + HIGH + LOW) / 4 - MA(CLOSE, 20)) / MA(CLOSE, 20) DD = DMA(CLOSE, CC) TD3 = (1 + M / 100) * DD TD4 = (1 - M / 100) * DD return TD1, TD2, TD3, TD4 def ZHUOYAO(CLOSE, N1=120, N2=60, N3=20, M=10): # 捉妖大师指标:中长短线趋势共振 LONG1 = (CLOSE / REF(CLOSE, N1) - 1) * 100 # 120日涨跌幅 LONG = EMA(LONG1, M) # 长线 EXPMA(长线1,10) MID = (CLOSE / REF(CLOSE, N2) - 1) * 100 # 中线 60日涨跌幅 SHORT = (CLOSE / REF(CLOSE, N3) - 1) * 100 # 短线 20日涨跌幅 TREND = EMA(MID, M) # 趋势 EXPMA(中线,10) return RD(LONG), RD(MID), RD(SHORT), RD(TREND) def BIAS_SIGNAL(CLOSE, P=10, M=30): # 乖离率信号指标:M日乖离 + 短/长信号线趋势判断 X = (CLOSE - MA(CLOSE, M)) / MA(CLOSE, M) * 100 # M日乖离率 S_SMA = MA(X, P) # 短周期信号线 MA(X,P) X_LMA = MA(X, M) # 长周期信号线 MA(X,M) return RD(X), RD(S_SMA), RD(X_LMA) def FK(CLOSE): # FK趋势指标:快线EMA(2)与斜率外推慢线EMA(42)比较 fast = EMA(CLOSE, 2) slow = EMA(SLOPE(CLOSE, 21) * 20 + CLOSE, 42) return fast > slow def OUTPERFORM_20D(CLOSE, INDEX_CLOSE): # 20日相对强度:个股涨幅跑赢大盘返回1,否则返回0 stock_ret = (CLOSE - REF(CLOSE, 20)) / REF(CLOSE, 20) index_ret = (INDEX_CLOSE - REF(INDEX_CLOSE, 20)) / REF(INDEX_CLOSE, 20) return IF(stock_ret > index_ret, 1, 0) def SAR(HIGH, LOW, AF_STEP=0.02, AF_MAX=0.2): # 抛物线转向指标:基于 ATR 思想的动态止损位 HIGH = np.asarray(HIGH, dtype=float) LOW = np.asarray(LOW, dtype=float) n = len(HIGH) sar = np.full(n, np.nan) if n == 0: return sar # 初始假设上涨趋势:SAR 起点取首根低点,极值点取首根高点 bull = True af = AF_STEP ep = HIGH[0] sar[0] = LOW[0] for i in range(1, n): # 下一根 SAR = 前一根 SAR + AF * (EP - 前一根 SAR) new_sar = sar[i - 1] + af * (ep - sar[i - 1]) # SAR 不能进入前两根 K 线极值范围(Wilder 标准限制,避免 SAR 被价格穿越) prev2 = max(i - 2, 0) if bull: new_sar = min(new_sar, LOW[i - 1], LOW[prev2]) else: new_sar = max(new_sar, HIGH[i - 1], HIGH[prev2]) sar[i] = new_sar # 反转判断:上涨时 LOW 穿越止损位 / 下跌时 HIGH 穿越止损位 if bull and LOW[i] <= new_sar: bull = False sar[i] = ep # 反转点 SAR = 前极值点 ep = LOW[i] af = AF_STEP elif not bull and HIGH[i] >= new_sar: bull = True sar[i] = ep ep = HIGH[i] af = AF_STEP else: # 无反转,更新极值点和加速因子 if bull and HIGH[i] > ep: ep = HIGH[i] af = min(af + AF_STEP, AF_MAX) elif not bull and LOW[i] < ep: ep = LOW[i] af = min(af + AF_STEP, AF_MAX) return sar def VWAP(CLOSE, HIGH, LOW, VOL, N=20): # 成交量加权均价:N日滚动机构基准成本价 TP = (HIGH + LOW + CLOSE) / 3.0 # 典型价格 num = pd.Series(TP * VOL).rolling(N).sum().values den = pd.Series(VOL).rolling(N).sum().values with np.errstate(divide="ignore", invalid="ignore"): return np.where(den > 0, num / den, np.nan) def AROON(HIGH, LOW, N=25): # 阿隆指标:趋势启动时机识别(N周期内新高/新低距今多少根) # HHVBARS/LLVBARS 返回极值距今的周期数 up_bars = HHVBARS(HIGH, N) # N周期最高价距今周期数 down_bars = LLVBARS(LOW, N) # N周期最低价距今周期数 AROON_UP = (N - up_bars) / N * 100 # 越接近100=近期创新高=上涨动能强 AROON_DOWN = (N - down_bars) / N * 100 # 越接近100=近期创新低=下跌动能强 OSC = AROON_UP - AROON_DOWN # 震荡指标:正值多头,负值空头 return RD(AROON_UP), RD(AROON_DOWN), RD(OSC) # 望大家能提交更多指标和函数 https://github.com/mpquant/MyTT