“ 快速高效学会python的终极奥义:过、抄、仿、改、调、看、练、创、悟”
正文开始前,首先还是声明下
1)我也是半路出家非科班出身,可能代码的规范性和优雅性不够强,大佬们求轻拍;然后今天的文档也写的很清楚,就不用录制视频啦~~
2)技术分析有完整的流派和体系,网上课程也很多,各种玄学看线大师的各种研究,这里只单纯讨论代码怎么写,不涉及到策略怎么构建(我也不太懂技术分析),大家学会了也可以自己去写;
3)本文的例子来源于万矿,然后为了方便大家查文档,特意把外文文档的中文翻译版本搬运到公众号了,大家可以手动复制粘贴,有什么技术指标找到下面几篇公众号的内容然后检索即可,由于作者不是我,也没有标注原创;
技术分析
量化本身实际上是分很多个流派的,个人把它分为三大类
第一类是基本面流派,一般包括宏观轮动和多因子(barra那套),一般做宏观对冲的或者基本面起家的股票的会比较喜欢;其优点在于•经济逻辑较强;•对事件的解释性较强;•比较容易给出一套逻辑来判断某种状态下股价被低估。其缺点在于•不容易判断被低估价值的股票价格何时或者以怎样的过程恢复到其对应的价值;•对知期行情的判断缺乏指导性;
其二就是市场行为流派,主要其实分为两块,一块是趋势,一块是反转,这两种的思路是不同的,趋势策略主要基于动量效应,其假设在前一段时间强势的股票,在未来一段时间继续保持强势,那么对应的策略就是寻找前期强势的股票,判断它将继续强势后买入持有,而反转策略恰好相反,其基于价值回归的理论,认为前一段时间弱的股票,未来一段时间会变强,那么策略就是寻找前期弱势的股票,判断它将出现逆转后买入持有,一个左侧一个右侧;总结来看,是对历史已发生的行情进行统计,得出指标来推断将来的行情。优点是可运用于长期或短期的判断,并且依据的信息是己经发生的可靠信息,可操作性最强。缺点是由于它利用的主要是已发生的信息,因此总是存在滞后性。
第三类是大类资产配置模型,我把它单独的放在一个流派里面,因为它是涉及到多个资产的,主要就是风险平价,均值方差,bl等~
一般来说,市场行为流派的技术指标比较多,所以本节也是主要探讨第二类量化模型~~

    
趋势流派背后的行为金融学理论

因而趋势追踪的思想是对于一种资产来说,当向上突破重要的压力位后可能意味着一波大的上涨趋势行情的到来,或者向下突破某重要的阻力位后,可能意味着一波大的下跌行情的到来。因而试图寻找大的趋势波段的到来,并且在突破的时候进行建仓或者平仓操作,以期获得大的波段收益。
反转流派背后的行为金融学理论

反转思想将资产的回报拆分为三块
α   ——偏离市场的回报,代表提前预制的偏离;
β_m—— 随着市场总体涨落带来的市场回报;
ε    ——残差,代表无法提前预知的相对于市场回报的差异。

以上介绍了技术分析的大的理论背景和灵魂思路,但是实际上在学习技术分析的时候,可以看到其流派还是相当多,有看第几波的有直接计算量价指标的,各种理论和指标层出不穷,自己造轮子实在是太辛苦,因此今天引入一个技术分析非常厉害的包包,叫talib,这个包包可分为10个子板块:Overlap Studies(重叠指标),Momentum Indicators(动量指标),Volume Indicators(交易量指标),Cycle Indicators(周期指标),Price Transform(价格变换),Volatility Indicators(波动率指标),Pattern Recognition(模式识别),Statistic Functions(统计函数),Math Transform(数学变换)和Math Operators(数学运算),基本满足大家的各类探索欲望,但是确实内容很多,所以我把文档分成了三个微信链接,供大家查找学习~~
当然也有很多用机器学习和模式识别来搞技术分析的哈,下面的章节有机会会介绍,今天就主要集中在talib~~
举个栗子
(本例来源于万矿小编,具体代码可以上去自行下载运行哈~)
首先导入包包
from WindPy import *import numpy as npimport pandas as pdimport talib as taimport matplotlibimport matplotlib.pyplot as pltimport matplotlib.patches as patchesw.start()data = w.wsd("IF.CFE", "open,high,low,close,volume", "2017-02-01", "2017-04-30")     data = pd.DataFrame(data.Data, columns=data.Times, index=data.Fields).T
技术指标有两种基本类型
1)叠加:使用与价格相同的比例的技术指标被绘制在股票图表的价格上方。例子包括移动平均线(moving averages)和布林带(Bollinger Bands)。
2)震荡(动量):在价格图的上方或下方绘制了在局部最小值和最大值之间波动的技术指标。示例包括随机振荡器(Stochastic Oscillator),MACD或RSI。
依次来介绍下,首先是叠加
1.1 常用移动平均线(Moving Average)
移动平均线(Moving Average),为将一段交易日收盘价求均值形成的线,是最常用的技术指标之一。之后从简单移动平均线,还衍生出了诸如指数移动均线,加权移动均线等其他均线。
    
| 类型 | 代码 | 计算方法 | 
|---|
| N日简单移动平均线 | ta.SMA(close,timeperiod=N) | N日移动平均线 = N日收盘价之和/N | 
| N日指数移动均线 | ta.EMA(close,timeperiod=N) | N日指数移动均线 = 平滑系数∗(当日收盘价−昨日计算的N日收盘均值)" role="presentation" style=" box-sizing: border-box; flex-shrink: 0; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;  ">平滑系数∗(当日收盘价−昨日计算的𝑁日收盘均值)平滑系数∗(当日收盘价−昨日计算的N日收盘均值)+昨日计算的N日收盘均值,平滑系数=2/(N+1)" role="presentation" style=" box-sizing: border-box; flex-shrink: 0; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;  ">2/(𝑁+1)2/(N+1) | 
| N日加权移动均线 | ta.WMA(close,timeperiod=N) | 
    
N日加权移动均线 = C1∗1+C2∗2+...+Cn∗N" role="presentation" style=" box-sizing: border-box; flex-shrink: 0; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;  ">𝐶1∗1+𝐶2∗2+...+𝐶𝑛∗𝑁C1∗1+C2∗2+...+Cn∗N /" role="presentation" style=" box-sizing: border-box; flex-shrink: 0; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;  ">// (1+2+3+...+N)" role="presentation" style=" box-sizing: border-box; flex-shrink: 0; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;  ">(1+2+3+...+𝑁)(1+2+3+...+N) | 
1.2 布林线(Bollinger Band)
*
    
布林线(Bollinger Band),由三条轨道线组成,其中上下两条线分别可以看成是价格的压力线和支撑线,在两条线之间是一条价格平均线,一般情况价格线在由上下轨道组成的带状区间游走,而且随价格的变化而自动调整轨道的位置。当波带变窄时,激烈的价格波动有可能随即产生;若高低点穿越带边线时,立刻又回到波带内,则会有回档产生。*
| 类型 | 计算方法 | 代码 | 
|---|
| 中间线 | 过去20交易日收盘价均线 | ta.BBANDS(close, timeperiod=5, nbdevup=2, nbdevdn=2, matype=0) | 
| Upper线 | 中间线+2*过去20交易日收盘价的标准差 | ta.BBANDS(close, timeperiod=5, nbdevup=2, nbdevdn=2, matype=0) | 
| Down线 | 中间线-2*过去20交易日收盘价的标准差 | ta.BBANDS(close, timeperiod=5, nbdevup=2, nbdevdn=2, matype=0) | 
def mkt_plot(quotes, sec):           # quotes:行情数据-Dateframe类型    sec:标题        __color_balck__= '#0F0F0F'    __color_green__= '#00FFFF'    __color_yellow__ = '#EE9A00'    __color_purple__ = '#9900CC'    __linewidth__ = 2
    fig = plt.figure(figsize=(11,5))
    ax1 = fig.add_axes([0, 1, 1, 1])                   ax1.set_title(sec,  fontsize=15)
    ax1.grid(True, axis='y')    ax1.set_xlim(-1, len(quotes)+1)
    for i in range(len(quotes)):        close_price,open_price = quotes.ix[i, 'CLOSE'], quotes.ix[i, 'OPEN']        high_price, low_price = quotes.ix[i, 'HIGH'], quotes.ix[i, 'LOW']        trade_date = quotes.index[i]
        if close_price > open_price:#画阳线            ax1.add_patch(patches.Rectangle((i-0.2, open_price), 0.4, close_price-open_price, fill=False, color='r'))            ax1.plot([i, i], [low_price, open_price], 'r')            ax1.plot([i, i], [close_price, high_price], 'r')        else:#画阴线            ax1.add_patch(patches.Rectangle((i-0.2, open_price), 0.4, close_price-open_price, color='g'))            ax1.plot([i, i], [low_price, high_price], color='g')    ax1.set_title("Price", fontsize=15, loc='left', color='r')    #设置x轴标签    ax1.set_xticks(range(0,len(quotes),5))#位置    ax1.set_xticklabels([(quotes.index[i]).strftime('%Y-%m-%d') for i in ax1.get_xticks()] , rotation=20)#标签内容    #布林带         upperband, middleband, lowerband = ta.BBANDS(np.array(quotes['CLOSE']), timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)     #计算布林带    ax1.plot(upperband, color=__color_yellow__, linewidth=__linewidth__, label='Upper Band')    ax1.plot(middleband, color='b', linewidth=__linewidth__, label='Middle Band')    ax1.plot(lowerband, color=__color_purple__, linewidth=__linewidth__, label='Lower Band')    #图例    ax1.legend(loc='Upper Left')
    return figmkt_plot(data,'IF.CFE')

万矿中基于布林带的策略(我之前写过量化模型看利率的时候,第二部分也有用到这个指标,用在10年国债上,效果一般,可能是布林带的大小定义的问题)


2.1 指数平滑异同移动平均线(MACD)
MACD的原理是运用短期(快速)和长期(慢速)移动平均线聚合和分散的征兆加以双重平滑运算,用来研判买进与卖出的时机,在股市中这一指标有较大的实际意义。根据移动平均线的特性,
    
在一段持续的涨势中短期移动平均线和长期移动平均线之间的距离将愈拉愈远,两者间的乖离越来越大,涨势如果趋向缓慢,两者间的距离也必然缩小,甚至互相交叉,发出卖出信号。同样,在持续的跌势中,短期线在长期线之下,相互之间的距离越来越远,如果跌势减缓,两者之间的距离也将缩小,最后交叉发出买入信号。
计算方法:
差离值DIF = 短期EMA - 中长期EMA;差离平均值DEA = 差离值DIF的N日移动平均值
DIF向上突破DEA时为买进信号;DIF向下跌破DEA时为卖出信号



代码:
macd, macdsignal, macdhist = ta.MACD(close, fastperiod=6, slowperiod=12, signalperiod=9)参数6,12,9分别为短期EMA的窗宽 中长期EMA窗宽 以及计算DEA的窗宽 macd, macdsignal, machhist分别为上面所说的DIF DEA 和DIF-DEA
关于这个指标用的可太广泛了,我刚接触的时候非常的困惑,一直听什么金叉死叉,然后后来用了这个包包之后,就去测试了下,发现用这个指标算下来概率是50%。。。跟你抛硬币是一样的胜率,应该是我没领会这指标真正的内涵,一定是这样~~然后这个指标还有各种高深的用法,我反正是不太懂的,有技术大拿也可以代码实现一下~~
2.2 随机指数(Stochastics / KDJ)
KDJ指标根据统计学原理,通过一个特定的周期(常为9日、9周等)内出现过的最高价、最低价及最后一个计算周期的收盘价及这三者之间的比例关系,来计算最后一个计算周期的未成熟随机值RSV,然后根据平滑移动平均线的方法来计算K值、D值与J值,并绘成曲线图来研判股票走势。
计算方法:
N日RSV = (第N日收盘价-N日内最低价)/(N日内最高价-N日内最低价)∗∗100
第N日K值 = 2/3∗∗N-1日K值 + 1/3∗∗第N日RSV,第N日D值 = 2/3∗∗N-1日D值 + 1/3∗∗第N日K值,第N-1日无K或D值,则可用50代替,第N日J值 = 3∗∗第N日K值 - 2∗∗第N日D值(talib不自动计算J值)
这里计算K值,D值用的3日的EMA,可以改变参数来调整
代码:
slowk, slowd = ta.STOCH(high, low, close, fastk_period=5, slowk_period=3, slowk_matype=0, slowd_period=3, slowd_matype=0) #  fastk_period为计算RSV的周期N slowk_period为N日RSV的EMA计算周期 slowd_period为指数滑动平均计算D值时的计算周期 slowk为快速确认线 而slowd为慢速主干线 #  fastk_period为计算RSV的周期N slowk_period为N日RSV的EMA计算周期   slowd_period为指数滑动平均计算D值时的计算周期 #slowk为快速确认线  而slowd为慢速主干 一般20以下 超卖区 一般80以上 超买区
策略:
当K从下向上穿过D时,可认为是金叉,为买入信号,但为了谨慎再加一个条件:金叉的位置处于超卖区(即K D值都在20以下);类似的,当K从上向下穿过D时,可认为是死叉,为卖出信号,同时也加一个条件:死叉的位置处于超买区(即K D值都在80以上)


3.1 交易量指标介绍
交易量指标作为量价指标的一部分,在技术分析往往辅助判断价格的走势(和价格指标一起使用),在TALib中交易量指标只有3个,汇总如下。
| 类型 | 中文释义 | 代码 | 计算说明 | 
|---|
| Chaikin A/D Line | 收集派发线 | 
    
ta.AD(high, low, close, volume) | 今日的A/D值 = 昨天的A/D值+(收盘价位置常数×成交量) 收盘价位置常数=((收盘价-最低价)-(最高价-收盘价))/(最高价-收盘价) | 
| Chaikin A/D Oscillator | 收集派发震荡指标 | ta.ADOSC(high, low, close, volume, fastperiod=3, slowperiod=10) | 收集派发线的3日EMA值与10日EMA值的差值 | 
| On Balance Volume | 能量潮指标 | ta.OBV(close, volume) | 今日OBV = 昨日OBV+sgn×今天的成交量,sgn是符号函数,sgn=+1 今日收盘价≥昨日收盘价;sgn=-1 今日收盘价 | 
3.2 交易量指标应用
引入收集派发线,结合KDJ和AD线,来构造策略。策略先利用AD线来确认趋势,再用KDJ指标来择时。具体来说,当AD线上升说明交易者在收集该股票,同时若股价也上升,则价格上升趋势被确认,再在KDJ指标下的K值大于D值时较大持仓;当AD线下降说明交易者在派发该股票,同时若股价也下降,则价格下降趋势被确认,再在KDJ指标下的K值小于D值时较小持仓。

4.1 波动性指标介绍
波动性指标,用于衡量价格的波动情况,辅助判断趋势改变的可能性,市场的交易氛围,也可以利用波动性指标来帮助止损止盈,在TALib中波动性指标只有3个,汇总如下。
| 类型 | 中文释义 | 代码 | 计算说明 | 
|---|
| Average True Range | 平均真实波动范围 | ta.ATR(high, low, close, timeperiod=14) | 当前交易日的最高价与最低价间的波幅,前一交易日收盘价与当个交易日最高价间的波幅,前一交易日收盘价与当个交易日最低价间的波幅,这三者中的最大值为真实波幅,有了真实波幅后,一段时间的真实波幅平均值就是ATR | 
| Normalized Average True Range | 正态化平均真实波动范围 | ta.NATR(high, low, close, timeperiod=14) | 正态化的ATR,注意正态化后的范围 | 
| True Range | 真实波动幅度 | ta.TRANGE(high, low, close) | 前交易日的最高价与最低价间的波幅,前一交易日收盘价与当个交易日最高价间的波幅,前一交易日收盘价与当个交易日最低价间的波幅,这三者中的最大值为真实波幅 | 
一般辅助止损,当当天收盘价较前一天下降超过0.95*TR则清仓。虽然策略的收益效果还是不太理想,但是相比于系列一种的布林带策略,使用TR止损后已经大大改善了该策略的风险控制。

    
在TALib中模式识别函数非常多,对应了技术分析中很多量价形态和模式,按模式期限汇总如下。 在这一节中,有些术语先在这里说明,x日K线模式指的是x交易日周期的K线模式,上影线指的是最高价-max(开盘价,收盘价)的长度,下影线指的是最低价-min(开盘价,收盘价)的长度,向上跳空指的是T日最低价大于T-1日最高价,向下跳空指的是T日最高价小于T-1日最低价。
在模式识别函数的输出中,0表示该日无这一模式,100表示在改日识别了这一模式,-100表示在改日识别了这一模式的反向模式。
按照一日k线形态为例,与此类似的还有多日k线~~
| 类型 | 中文释义 | 代码 | 简介说明 | 
|---|
| Closing Marubozu | 收盘缺影线 | ta.CDLCLOSINGMARUBOZU(open, high, low, close) | 一日K线模式,以阳线为例,最低价低于开盘价,收盘价等于最高价,预示着趋势持续 | 
| Doji | 十字 | ta.CDLDOJI(open, high, low, close) | 一日K线模式,开盘价与收盘价基本相同 | 
| Doji Star | 十字星 | ta.CDLDOJISTAR(open, high, low, close) | 一日K线模式,开盘价与收盘价基本相同,上下影线不会很长,预示着当前趋势反转 | 
| Dragonfly Doji | 蜻蜓十字/T形十字 | ta.CDLDRAGONFLYDOJI(open, high, low, close) | 一日K线模式,开盘后价格一路走低,之后收复,收盘价与开盘价相同,预示趋势反转 | 
| Gravestone Doji | 墓碑十字/倒T十字 | ta.CDLGRAVESTONEDOJI(open, high, low, close) | 一日K线模式,开盘价与收盘价相同,上影线长,无下影线,预示底部反转 | 
| Hammer | 锤头 | ta.CDLHAMMER(open, high, low, close) | 一日K线模式,实体较短,无上影线,下影线大于实体长度两倍,处于下跌趋势底部,预示反转 | 
| Hanging Man | 上吊线 | ta.CDLHANGINGMAN(open, high, low, close) | 一日K线模式,形状与锤子类似,处于上升趋势的顶部,预示着趋势反转 | 
| Inverted Hammer | 
    
倒锤头 | ta.CDLINVERTEDHAMMER(open, high, low, close) | 一日K线模式,上影线较长,长度为实体2倍以上,无下影线,在下跌趋势底部,预示着趋势反转 | 
| Long Legged Doji | 长脚十字 | ta.CDLLONGLEGGEDDOJI(open, high, low, close) | 一日K线模式,开盘价与收盘价相同居当日价格中部,上下影线长,表达市场不确定性 | 
| Long Line Candle | 长蜡烛 | ta.CDLLONGLINE(open, high, low, close) | 一日K线模式,K线实体长,无上下影线 | 
| Marubozu | 光头光脚/缺影线 | ta.CDLMARUBOZU(open, high, low, close) | 一日K线模式,上下两头都没有影线的实体,阴线预示着熊市持续或者牛市反转,阳线相反 | 
| Rickshaw Man | 黄包车夫 | ta.CDLRICKSHAWMAN(open, high, low, close) | 一日K线模式,与长腿十字线类似,若实体正好处于价格振幅中点,称为黄包车夫 | 
| Separating Lines | 分离线 | ta.CDLSEPARATINGLINES(open, high, low, close) | 一日K线模式,上影线至少为实体长度两倍,没有下影线,预示着股价下跌 | 
| Shooting Star | 射击之星 | ta.CDLSHOOTINGSTAR(open, high, low, close) | 一日K线模式,上影线至少为实体长度两倍,没有下影线,预示着股价下跌 | 
| Short Line Candle | 短蜡烛 | ta.CDLSHORTLINE(open, high, low, close) | 一日K线模式,实体短,无上下影线 | 
| Spinning Top | 纺锤 | ta.CDLSPINNINGTOP(open, high, low, close) | 一日K线,实体小 | 
    
| Takuri (Dragonfly Doji with very long lower shadow) | 探水竿 | ta.CDLTAKURI(open, high, low, close) | 一日K线模式,大致与蜻蜓十字相同,下影线长度长 | 
如果恰好电脑里面没有装talib(保存下来随时抄)
import numpyimport pandas as pdimport math as m
#Moving Average  def MA(df, n):    MA = pd.Series(pd.rolling_mean(df['Close'], n), name = 'MA_' + str(n))    df = df.join(MA)    return df
#Exponential Moving Average  def EMA(df, n):    EMA = pd.Series(pd.ewma(df['Close'], span = n, min_periods = n - 1), name = 'EMA_' + str(n))    df = df.join(EMA)    return df
#Momentumdef MOM(df, n):    M = pd.Series(df['Close'].diff(n), name = 'Momentum_' + str(n))    df = df.join(M)    return df
#Rate of Changedef ROC(df, n):    M = df['Close'].diff(n - 1)    N = df['Close'].shift(n - 1)    ROC = pd.Series(M / N, name = 'ROC_' + str(n))    df = df.join(ROC)    return df
#Average True Rangedef ATR(df, n):    i = 0    TR_l = [0]    while i < df.index[-1]:        TR = max(df.get_value(i + 1, 'High'), df.get_value(i, 'Close')) - min(df.get_value(i + 1, 'Low'), df.get_value(i, 'Close'))        TR_l.append(TR)        i = i + 1    TR_s = pd.Series(TR_l)    ATR = pd.Series(pd.ewma(TR_s, span = n, min_periods = n), name = 'ATR_' + str(n))    df = df.join(ATR)    return df
#Bollinger Bandsdef BBANDS(df, n):    MA = pd.Series(pd.rolling_mean(df['Close'], n))    MSD = pd.Series(pd.rolling_std(df['Close'], n))    b1 = 4 * MSD / MA    B1 = pd.Series(b1, name = 'BollingerB_' + str(n))    df = df.join(B1)    b2 = (df['Close'] - MA + 2 * MSD) / (4 * MSD)    B2 = pd.Series(b2, name = 'Bollinger%b_' + str(n))    df = df.join(B2)    return df
#Pivot Points, Supports and Resistances  def PPSR(df):      PP = pd.Series((df['High'] + df['Low'] + df['Close']) / 3)      R1 = pd.Series(2 * PP - df['Low'])      S1 = pd.Series(2 * PP - df['High'])  
    
    R2 = pd.Series(PP + df['High'] - df['Low'])      S2 = pd.Series(PP - df['High'] + df['Low'])      R3 = pd.Series(df['High'] + 2 * (PP - df['Low']))      S3 = pd.Series(df['Low'] - 2 * (df['High'] - PP))      psr = {'PP':PP, 'R1':R1, 'S1':S1, 'R2':R2, 'S2':S2, 'R3':R3, 'S3':S3}      PSR = pd.DataFrame(psr)      df = df.join(PSR)      return df
#Stochastic oscillator %K  def STOK(df):    SOk = pd.Series((df['Close'] - df['Low']) / (df['High'] - df['Low']), name = 'SO%k')    df = df.join(SOk)    return df
# Stochastic Oscillator, EMA smoothing, nS = slowing (1 if no slowing)def STO(df,  nK, nD, nS=1):      SOk = pd.Series((df['Close'] - df['Low'].rolling(nK).min()) / (df['High'].rolling(nK).max() - df['Low'].rolling(nK).min()), name = 'SO%k'+str(nK))      SOd = pd.Series(SOk.ewm(ignore_na=False, span=nD, min_periods=nD-1, adjust=True).mean(), name = 'SO%d'+str(nD))      SOk = SOk.ewm(ignore_na=False, span=nS, min_periods=nS-1, adjust=True).mean()      SOd = SOd.ewm(ignore_na=False, span=nS, min_periods=nS-1, adjust=True).mean()      df = df.join(SOk)      df = df.join(SOd)      return df
# Stochastic Oscillator, SMA smoothing, nS = slowing (1 if no slowing)def STO(df, nK, nD,  nS=1):    SOk = pd.Series((df['Close'] - df['Low'].rolling(nK).min()) / (df['High'].rolling(nK).max() - df['Low'].rolling(nK).min()), name = 'SO%k'+str(nK))    SOd = pd.Series(SOk.rolling(window=nD, center=False).mean(), name = 'SO%d'+str(nD))    SOk = SOk.rolling(window=nS, center=False).mean()    SOd = SOd.rolling(window=nS, center=False).mean()    df = df.join(SOk)    df = df.join(SOd)    return df
#Trixdef TRIX(df, n):    EX1 = pd.ewma(df['Close'], span = n, min_periods = n - 1)    EX2 = pd.ewma(EX1, span = n, min_periods = n - 1)    EX3 = pd.ewma(EX2, span = n, min_periods = n - 1)    i = 0    ROC_l = [0]    while i + 1 <= df.index[-1]:        ROC = (EX3[i + 1] - EX3[i]) / EX3[i]        ROC_l.append(ROC)        i = i + 1    Trix = pd.Series(ROC_l, name = 'Trix_' + str(n))    df = df.join(Trix)    return df
#Average Directional Movement Indexdef ADX(df, n, n_ADX):    i = 0    UpI = []    DoI = []    while i + 1 <= df.index[-1]:          UpMove = df.get_value(i + 1, 'High') - df.get_value(i, 'High')          DoMove = df.get_value(i, 'Low') - df.get_value(i + 1, 'Low')          if UpMove > DoMove and UpMove > 0:              UpD = UpMove          else: UpD = 0          UpI.append(UpD)          if DoMove > UpMove and DoMove > 0:              DoD = DoMove          else: DoD = 0          DoI.append(DoD)          i = i + 1      i = 0      TR_l = [0]      while i < df.index[-1]:          TR = max(df.get_value(i + 1, 'High'), df.get_value(i, 'Close')) - min(df.get_value(i + 1, 'Low'), df.get_value(i, 'Close'))          TR_l.append(TR)          i = i + 1      TR_s = pd.Series(TR_l)      ATR = pd.Series(pd.ewma(TR_s, span = n, min_periods = n))      UpI = pd.Series(UpI)      DoI = pd.Series(DoI)      PosDI = pd.Series(pd.ewma(UpI, span = n, min_periods = n - 1) / ATR)      NegDI = pd.Series(pd.ewma(DoI, span = n, min_periods = n - 1) / ATR)      ADX = pd.Series(pd.ewma(abs(PosDI - NegDI) / (PosDI + NegDI), span = n_ADX, min_periods = n_ADX - 1), name = 'ADX_' + str(n) + '_' + str(n_ADX))      df = df.join(ADX)      return df
#MACD, MACD Signal and MACD difference  def MACD(df, n_fast, n_slow):      EMAfast = pd.Series(pd.ewma(df['Close'], span = n_fast, min_periods = n_slow - 1))      EMAslow = pd.Series(pd.ewma(df['Close'], span = n_slow, min_periods = n_slow - 1))      MACD = pd.Series(EMAfast - EMAslow, name = 'MACD_' + str(n_fast) + '_' + str(n_slow))      MACDsign = pd.Series(pd.ewma(MACD, span = 9, min_periods = 8), name = 'MACDsign_' + str(n_fast) + '_' + str(n_slow))      MACDdiff = pd.Series(MACD - MACDsign, name = 'MACDdiff_' + str(n_fast) + '_' + str(n_slow))      df = df.join(MACD)      df = df.join(MACDsign)      df = df.join(MACDdiff)      return df
#Mass Index  def MassI(df):      Range = df['High'] - df['Low']      EX1 = pd.ewma(Range, span = 9, min_periods = 8)      EX2 = pd.ewma(EX1, span = 9, min_periods = 8)      Mass = EX1 / EX2      MassI = pd.Series(pd.rolling_sum(Mass, 25), name = 'Mass Index')      df = df.join(MassI)      return df
#Vortex Indicator: http://www.vortexindicator.com/VFX_VORTEX.PDF  def Vortex(df, n):      i = 0  
    
    TR = [0]      while i < df.index[-1]:          Range = max(df.get_value(i + 1, 'High'), df.get_value(i, 'Close')) - min(df.get_value(i + 1, 'Low'), df.get_value(i, 'Close'))          TR.append(Range)          i = i + 1      i = 0      VM = [0]      while i < df.index[-1]:          Range = abs(df.get_value(i + 1, 'High') - df.get_value(i, 'Low')) - abs(df.get_value(i + 1, 'Low') - df.get_value(i, 'High'))          VM.append(Range)          i = i + 1      VI = pd.Series(pd.rolling_sum(pd.Series(VM), n) / pd.rolling_sum(pd.Series(TR), n), name = 'Vortex_' + str(n))      df = df.join(VI)      return df
#KST Oscillatordef KST(df, r1, r2, r3, r4, n1, n2, n3, n4):      M = df['Close'].diff(r1 - 1)      N = df['Close'].shift(r1 - 1)      ROC1 = M / N      M = df['Close'].diff(r2 - 1)      N = df['Close'].shift(r2 - 1)      ROC2 = M / N      M = df['Close'].diff(r3 - 1)      N = df['Close'].shift(r3 - 1)      ROC3 = M / N      M = df['Close'].diff(r4 - 1)      N = df['Close'].shift(r4 - 1)      ROC4 = M / N      KST = pd.Series(pd.rolling_sum(ROC1, n1) + pd.rolling_sum(ROC2, n2) * 2 + pd.rolling_sum(ROC3, n3) * 3 + pd.rolling_sum(ROC4, n4) * 4, name = 'KST_' + str(r1) + '_' + str(r2) + '_' + str(r3) + '_' + str(r4) + '_' + str(n1) + '_' + str(n2) + '_' + str(n3) + '_' + str(n4))      df = df.join(KST)    return df
#Relative Strength Index  def RSI(df, n):      i = 0      UpI = [0]      DoI = [0]      while i + 1 <= df.index[-1]:          UpMove = df.get_value(i + 1, 'High') - df.get_value(i, 'High')          DoMove = df.get_value(i, 'Low') - df.get_value(i + 1, 'Low')          if UpMove > DoMove and UpMove > 0:              UpD = UpMove          else: UpD = 0          UpI.append(UpD)          if DoMove > UpMove and DoMove > 0:              DoD = DoMove          else: DoD = 0          DoI.append(DoD)          i = i + 1      UpI = pd.Series(UpI)      DoI = pd.Series(DoI)      PosDI = pd.Series(pd.ewma(UpI, span = n, min_periods = n - 1))      NegDI = pd.Series(pd.ewma(DoI, span = n, min_periods = n - 1))      RSI = pd.Series(PosDI / (PosDI + NegDI), name = 'RSI_' + str(n))      df = df.join(RSI)      return df
#True Strength Index  def TSI(df, r, s):      M = pd.Series(df['Close'].diff(1))      aM = abs(M)      EMA1 = pd.Series(pd.ewma(M, span = r, min_periods = r - 1))      aEMA1 = pd.Series(pd.ewma(aM, span = r, min_periods = r - 1))      EMA2 = pd.Series(pd.ewma(EMA1, span = s, min_periods = s - 1))      aEMA2 = pd.Series(pd.ewma(aEMA1, span = s, min_periods = s - 1))      TSI = pd.Series(EMA2 / aEMA2, name = 'TSI_' + str(r) + '_' + str(s))      df = df.join(TSI)      return df
#Accumulation/Distribution  def ACCDIST(df, n):      ad = (2 * df['Close'] - df['High'] - df['Low']) / (df['High'] - df['Low']) * df['Volume']      M = ad.diff(n - 1)      N = ad.shift(n - 1)      ROC = M / N      AD = pd.Series(ROC, name = 'Acc/Dist_ROC_' + str(n))      df = df.join(AD)      return df
#Chaikin Oscillator  def Chaikin(df):      ad = (2 * df['Close'] - df['High'] - df['Low']) / (df['High'] - df['Low']) * df['Volume']      Chaikin = pd.Series(pd.ewma(ad, span = 3, min_periods = 2) - pd.ewma(ad, span = 10, min_periods = 9), name = 'Chaikin')      df = df.join(Chaikin)      return df
#Money Flow Index and Ratio  def MFI(df, n):      PP = (df['High'] + df['Low'] + df['Close']) / 3      i = 0      PosMF = [0]      while i < df.index[-1]:          if PP[i + 1] > PP[i]:              PosMF.append(PP[i + 1] * df.get_value(i + 1, 'Volume'))          else:              PosMF.append(0)          i = i + 1      PosMF = pd.Series(PosMF)      TotMF = PP * df['Volume']      MFR = pd.Series(PosMF / TotMF)      MFI = pd.Series(pd.rolling_mean(MFR, n), name = 'MFI_' + str(n))      df = df.join(MFI)      return df
#On-balance Volume  def OBV(df, n):      i = 0      OBV = [0]      while i < df.index[-1]:          if df.get_value(i + 1, 'Close') - df.get_value(i, 'Close') > 0:              OBV.append(df.get_value(i + 1, 'Volume'))  
    
        if df.get_value(i + 1, 'Close') - df.get_value(i, 'Close') == 0:              OBV.append(0)          if df.get_value(i + 1, 'Close') - df.get_value(i, 'Close') < 0:              OBV.append(-df.get_value(i + 1, 'Volume'))          i = i + 1      OBV = pd.Series(OBV)      OBV_ma = pd.Series(pd.rolling_mean(OBV, n), name = 'OBV_' + str(n))      df = df.join(OBV_ma)      return df
#Force Index  def FORCE(df, n):      F = pd.Series(df['Close'].diff(n) * df['Volume'].diff(n), name = 'Force_' + str(n))      df = df.join(F)      return df
#Ease of Movement  def EOM(df, n):      EoM = (df['High'].diff(1) + df['Low'].diff(1)) * (df['High'] - df['Low']) / (2 * df['Volume'])      Eom_ma = pd.Series(pd.rolling_mean(EoM, n), name = 'EoM_' + str(n))      df = df.join(Eom_ma)      return df
#Commodity Channel Index  def CCI(df, n):      PP = (df['High'] + df['Low'] + df['Close']) / 3      CCI = pd.Series((PP - pd.rolling_mean(PP, n)) / pd.rolling_std(PP, n), name = 'CCI_' + str(n))      df = df.join(CCI)      return df
#Coppock Curve  def COPP(df, n):      M = df['Close'].diff(int(n * 11 / 10) - 1)      N = df['Close'].shift(int(n * 11 / 10) - 1)      ROC1 = M / N      M = df['Close'].diff(int(n * 14 / 10) - 1)      N = df['Close'].shift(int(n * 14 / 10) - 1)      ROC2 = M / N      Copp = pd.Series(pd.ewma(ROC1 + ROC2, span = n, min_periods = n), name = 'Copp_' + str(n))      df = df.join(Copp)      return df
#Keltner Channel  def KELCH(df, n):      KelChM = pd.Series(pd.rolling_mean((df['High'] + df['Low'] + df['Close']) / 3, n), name = 'KelChM_' + str(n))      KelChU = pd.Series(pd.rolling_mean((4 * df['High'] - 2 * df['Low'] + df['Close']) / 3, n), name = 'KelChU_' + str(n))      KelChD = pd.Series(pd.rolling_mean((-2 * df['High'] + 4 * df['Low'] + df['Close']) / 3, n), name = 'KelChD_' + str(n))      df = df.join(KelChM)      df = df.join(KelChU)      df = df.join(KelChD)      return df
#Ultimate Oscillator  def ULTOSC(df):      i = 0      TR_l = [0]      BP_l = [0]      while i < df.index[-1]:          TR = max(df.get_value(i + 1, 'High'), df.get_value(i, 'Close')) - min(df.get_value(i + 1, 'Low'), df.get_value(i, 'Close'))          TR_l.append(TR)          BP = df.get_value(i + 1, 'Close') - min(df.get_value(i + 1, 'Low'), df.get_value(i, 'Close'))          BP_l.append(BP)          i = i + 1      UltO = pd.Series((4 * pd.rolling_sum(pd.Series(BP_l), 7) / pd.rolling_sum(pd.Series(TR_l), 7)) + (2 * pd.rolling_sum(pd.Series(BP_l), 14) / pd.rolling_sum(pd.Series(TR_l), 14)) + (pd.rolling_sum(pd.Series(BP_l), 28) / pd.rolling_sum(pd.Series(TR_l), 28)), name = 'Ultimate_Osc')      df = df.join(UltO)      return df
#Donchian Channel  def DONCH(df, n):      i = 0      DC_l = []      while i < n - 1:          DC_l.append(0)          i = i + 1      i = 0      while i + n - 1 < df.index[-1]:          DC = max(df['High'].ix[i:i + n - 1]) - min(df['Low'].ix[i:i + n - 1])          DC_l.append(DC)          i = i + 1      DonCh = pd.Series(DC_l, name = 'Donchian_' + str(n))      DonCh = DonCh.shift(n - 1)      df = df.join(DonCh)      return df
#Standard Deviationdef STDDEV(df, n):    df = df.join(pd.Series(pd.rolling_std(df['Close'], n), name = 'STD_' + str(n)))    return df