Python量化交易学习——Part8:带有技术因子指标的多因子策略
技术面分析又称技术分析(Technical Analysis ),是股票投资分析的专业术语。技术分析研究以往价格和交易量数据,进而预测未来的价格走向。此类型分析侧重于图表与公式的构成,以捕获主要和次要的趋势,并通过估测市场周期长短,识别买入 / 卖出机会。根据您选择的时间跨度,您可以使用日内(每 5 分钟、每 15 分钟、每小时)技术分析,也可使用每周或每月技术分析。技术因子主要是作为基本面选股的
技术面分析又称技术分析(Technical Analysis ),是股票投资分析的专业术语。技术分析研究以往价格和交易量数据,进而预测未来的价格走向。此类型分析侧重于图表与公式的构成,以捕获主要和次要的趋势,并通过估测市场周期长短,识别买入 / 卖出机会。根据您选择的时间跨度,您可以使用日内(每 5 分钟、每 15 分钟、每小时)技术分析,也可使用每周或每月技术分析。
技术因子主要是作为基本面选股的一个重要补充,解决基本面选股策略中对买入时机的选择有所欠缺的问题。
技术因子的来源可以有多个方面,比如我们可以根据自身的数据处理知识自行编写技术因子,也直接调用第三方函数库的函数作为技术因子,甚至还可以将前两种方式融合,对函数进行重构,以实现自己特有目的的技术因子
技术因子构建策略1:自行编写技术因子
在这里我随便写一个作为例子:
假设技术因子为:对股票过去5天的成交量和最高价进行排名,再取相关系数
# coding=utf-8
import gm.api as gm
import talib
import numpy as np
import pandas as pd
from scipy.stats import rankdata
import datetime
gm.set_token("自己的token码")
"""
1、set_token 设置用户token, 如果token不正确, 函数调用会抛出异常;
2、调用数据查询函数, 直接进行数据查询。
"""
def eg(symbol,now):
last_day = gm.get_previous_trading_date("SHSE",now)
data = gm.history_n(symbol=symbol,frequency="1d",count=5,end_time=last_day,fields="volume,high",adjust=gm.ADJUST_PREV,df=True)
volume = data["volume"].values
high = data["high"].values
high_rank = rankdata(high)#根据最高价大小进行排名
print(high_rank)
volume_rank = rankdata(volume) #根据成交量大小进行排名
print(volume_rank)
corr =np.corrcoef(high_rank,volume_rank)
return corr[0][1]
day_time, hour_and_mins = str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')).split(" ")
corr = eg("SZSE.002415",day_time)
print(corr)
由此,我们得到了一个技术因子,我们可以用过这个技术因子对股票进行买卖回测,看该技术因子的实战效果如何。
技术因子构建策略2:调用talib库中现成的技术因子
这个我们在前面章节已经讲过,我这里不再详述,以获取EMA均线因子为例,代码如下:
from gm.api import *
import talib
import numpy as np
#MA_Type: 0=SMA, 1=EMA, 2=WMA, 3=DEMA, 4=TEMA, 5=TRIMA, 6=KAMA, 7=MAMA, 8=T3 (Default=SMA)
# 与数据库进行通信,将账号密码加密后发送给服务器
set_token("自己的token码")
# 获取历史交易数据,海康威视近100工作日交易数据的收盘价)
data = history_n(symbol="SZSE.002415",frequency="3600s",count=100,end_time="2024-05-31",fields="close",fill_missing="last",adjust=ADJUST_PREV,df=True)
# 从字典中提取收盘价到数组中
close = np.asarray(data["close"].values)
# 方法1,选择matype=1,得到3日EMA均线
SMA3 = talib.MA(close,timeperiod = 3,matype = 1)
SMA3 = np.nan_to_num(SMA3)
print(SMA3)
# 方法2,采用talib.EMA函数,得到3日EMA均线
EMA3= talib.EMA(close,timeperiod = 3)
EMA3 = np.nan_to_num(EMA3)
print(EMA3)
技术因子构建策略3:基于Talib的技术因子重构
Talib库有很多现成的技术指标,但是某些技术指标和国内还是有一定差别的,例如MACD函数和RSI函数等,我们再这里对这些函数进行重构
重构RSI函数以及MACD函数
RSI最早被用于期货交易中,后来人们发现用该指标来指导股票市场投资效果也十分不错,并对该指标的特点不断进行归纳和总结。现在,RSI已经成为被投资者应用最广泛的技术指标之一。投资的一般原理认为,投资者的买卖行为是各种因素综合结果的反映,行情的变化最终取决于供求关系,而RSI指标正是根据供求平衡的原理,通过测量某一个期间内股价上涨总幅度占股价变化总幅度平均值的百分比,来评估多空力量的强弱程度,进而提示具体操作的。RSI的应用法则表面上比较复杂,包括了交叉、数值、形态和背离等多方面的判断原则。
相对强弱指标RSI是用以计测市场供需关系和买卖力道的方法及指标。
计算公式:
N日RSI =A/(A+B)×100
A=N日内收盘涨幅之和
B=N日内收盘跌幅之和(取正值)
由上面算式可知RSI指标的技术含义,即以向上的力量与向下的力量进行比较,若向上的力量较大,则计算出来的指标上升;若向下的力量较大,则指标下降,由此测算出市场走势的强弱。
MACD(Moving Average Convergence and Divergence) 是从双指数移动平均线发展而来,Geral Appel 于1979年提出。是利用收盘价的短期(常用为12日)指数移动平均线与长期(常用为26日)指数移动平均线之间的聚合与分离状况,对买进、卖出时机作出研判的技术指标。
“离差值”(DIF)即MACD“快速线”,是以快速(一般选12日)移动平均值与慢速(一般选26日)移动平均值作为测量两者(快速与慢速线)间的"差离值"依据,即12日EMA数值减去26日EMA数值。因此,在持续的涨势中,12日EMA在26日EMA之上。其间的正差离值(+DIF)会愈来愈大。反之在跌势中,差离值可能变负(-DIF),也愈来愈大。
DIF计算公式:EMA(12)—EMA(26)。
MACD“慢速线”(DEA):即DIF的加权平均值;
MACD”红绿柱”:用(DIF-DEA)×2即为MACD柱状图;
MACD”零轴”:
当EMA12下穿EMA26,形成均线死叉后DIF值下穿零轴;
当EMA12上穿EMA26形成均线金叉后DIF值上穿零轴。
在开始编程之前,我先介绍一个匿名函数,可以提高程序的简洁程度:Lambda函数,
Lambda是一种不需要名字(即标识符)、由一个单独表达式成的匿名内联函数,表达式会在调用时被求值。
在Python中,创建 lambda 函数的语法为:
lambda [parameters]: expression
也就是说lambda函数包含三个部分:
关键字lamdbda;
参数;
函数体
lambda函数可以包含任意多的参数,但是函数体部分只能包含一个表达式。
此外,lambda函数用一行代码写成,并且被立即调用。
eg:
lambda_add_ten = lambda x: x + 10
print(lambda_add_ten(5))
# 15
此外,lambda还可以和reduce/map等函数配合使用:
result = map(lambda x: x * x, [1, 2, 3, 4, 5])
print(list(result)) # 输出[1, 4, 9, 16, 25]
正式代码如下,我们先建立一个新的py文件,用于保存汉化版的TAlib数据库,我这里命名为talib_CN.py,内容如下:
import numpy as np
import pandas as pd
import talib
from gm.api import *
import datetime
from dateutil.relativedelta import relativedelta
from sklearn.preprocessing import MinMaxScaler
from functools import reduce
"""
RSI指标
相对强弱指标RSI是用以计测市场供需关系和买卖力道的方法及指标。
通过测量某一个期间内股价上涨总幅度占股价变化总幅度平均值的百分比,来评估多空力量的强弱程度,进而提示具体操作的。
计算公式:
N日RSI =A/(A+B)×100
A=N日内收盘涨幅之和
B=N日内收盘跌幅之和(取正值)
"""
def RSI_CN(close,timeperiod):
diff = list(map(lambda x,y:x - y,close[1:],close[:-1])) #求第N天与第N-1天收盘价的差值
diff_grow = list(map(lambda x: 0 if x < 0 else x, diff)) #提取所有正涨幅形成新的数组,涨幅为负的置零,以便于后期累加计算A
diff_abs = list(map(lambda x:abs(x),diff)) #取所有涨幅的绝对值以便于计算(A+B)
sum_diff_grow = []
sum_diff_abs = []
if len(close) < timeperiod:
print("收盘价小于时间间隔,无法计算")
else:
for i in range(0,(len(diff_grow)-timeperiod)):
sum_diff_grow.append(sum(diff_grow[i:(i+timeperiod)]))
sum_diff_abs.append(sum(diff_abs[i:(i+timeperiod)]))
RSI = list(map(lambda x,y:x/(x+y)*100,sum_diff_grow,sum_diff_abs))
return np.array(RSI)
"""
MACD指标
MACD称为异同移动平均线,是从双指数移动平均线发展而来的;
由快的指数移动平均线(EMA12)减去慢的指数移动平均线(EMA26)得到快线DIF,再用2×(快线DIF-DIF的9日加权移动均线DEA)得到MACD柱。
MACD的意义和双移动平均线基本相同,即由快、慢均线的离散、聚合表征当前的多空状态和股价可能的发展变化趋势,但阅读起来更方便。
MACD的变化代表着市场趋势的变化,不同K线级别的MACD代表当前级别周期中的买卖趋势。
"""
def MACD_CN(close, fastperiod = 12, slowperiod = 26, signalperiod = 9):
macdDIFF,macdDEA,macd = talib.MACDEXT(close,fastperiod = fastperiod,fastmatype=1,slowperiod = slowperiod,slowmatype =1,signalperiod =signalperiod,signalmatype =1) #平滑异同移动平均线(可控制移动平均算法)
macd = macd * 2
return macdDIFF,macdDEA,macd
在这个函数中,我对RSI指标以及MACD指标进行了重构,之后我们在主函数中引用此函数库,对新构建的MACD函数进行回测。
我们选择海康威视(SZSE.002415)2022年-2024年之间的股票数据进行回测,策略为:当MACD柱从下而上穿过零轴的时候,说明多头更加强势,我们全仓买入,当MACD柱从上而下穿过零轴的时候,说明空头更加强势,我们平仓卖出。
代码如下:
# coding=utf-8
from __future__ import print_function, absolute_import
from gm.api import *
import numpy as np
import talib
import talib_CN as tlcn
def init(context):
# 每天14:50 定时执行algo任务,
# algo执行定时任务函数,只能传context参数
# date_rule执行频率,目前暂时支持1d、1w、1m,其中1w、1m仅用于回测,实时模式1d以上的频率,需要在algo判断日期
# time_rule执行时间, 注意多个定时任务设置同一个时间点,前面的定时任务会被后面的覆盖
context.symbol = "SZSE.002415" # 海康威视
context.frequency = "1d"
context.fields = "open,high,low,close"
context.volume = 400
schedule(schedule_func=algo, date_rule='1d', time_rule='09:35:00')
def algo(context):
now = context.now
last_day = get_previous_trading_date("SZSE",now)
data = history_n(symbol = context.symbol,frequency= context.frequency,count=60,end_time=last_day,fields=context.fields,fill_missing="last",adjust=ADJUST_PREV,df=True)
open = np.asarray((data["open"].values))
high = np.asarray(data["high"].values)
low = np.asarray(data["low"].values)
close = np.asarray(data["close"].values)
macdDIFF,macdDFA,macd = tlcn.MACD_CN(close)
if macd[-1] > 0 and macd[-2] < 0: #MACD柱从下到上穿过零轴,买入信号
order_target_percent(symbol=context.symbol, percent=1, position_side=PositionSide_Long, order_type=OrderType_Market)
account = context.account(account_id=None).positions()
print("BUY",last_day,account)
elif macd[-1] < 0 and macd[-2] > 0:#MACD柱从上到下穿过零轴,卖出信号
order_target_percent(symbol=context.symbol, percent=0, position_side=PositionSide_Long, order_type=OrderType_Market) #这里需要注意position_side的方向也是多仓方向,调整多仓的持仓比例为0,而不是调整空仓去平仓
account = context.account(account_id=None).positions()
print("SELL",last_day,account)
# 查看最终的回测结果
def on_backtest_finished(context, indicator):
print(indicator)
if __name__ == '__main__':
'''
strategy_id策略ID, 由系统生成
filename文件名, 请与本文件名保持一致
mode运行模式, 实时模式:MODE_LIVE回测模式:MODE_BACKTEST
token绑定计算机的ID, 可在系统设置-密钥管理中生成
backtest_start_time回测开始时间
backtest_end_time回测结束时间
backtest_adjust股票复权方式, 不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
backtest_initial_cash回测初始资金
backtest_commission_ratio回测佣金比例
backtest_slippage_ratio回测滑点比例
backtest_match_mode市价撮合模式,以下一tick/bar开盘价撮合:0,以当前tick/bar收盘价撮合:1
'''
run(strategy_id='自己的策略ID',
filename='自己的文件名',
mode=MODE_BACKTEST,
token='自己的token码',
backtest_start_time='2022-01-01 09:00:00',
backtest_end_time='2024-5-31 15:00:00',
backtest_adjust=ADJUST_PREV,
backtest_initial_cash=20000,
backtest_commission_ratio=0.000,
backtest_slippage_ratio=0.000,
backtest_match_mode=1)
回测结果如下:
从回测结果看,持仓收益率为-30%,这个策略并不理想,引起我们后期可以通过调整策略以及调整MACD所用的时间线来优化策略,以达到超额收益的目的。
更多推荐


所有评论(0)