为了将投资灵感高效地转化成计算机可执行的量化策略,必须基于一种模式来写,框架就是指这种模式。而此框架包含两个部分即初始化与周期循环:
其实人本身日常做交易就是符合“初始化+周期循环”框架的,初始化就是已存在人脑的交易思想与知识,周期循环就是每天或每分钟地查看行情、判断、下单等行为。
def initialize(context):#初始化函数
run_daily(period,time='every_bar')#运行函数
g.security = '000001.XSHE'#g转换为全局变量
def period(context):#周期函数
order(g.security, 100)#下单函数
run_daily(func, time=‘9:30’, reference_security)函数:每天内何时运行,func是一个自定义的函数,此函数必须接受context参数,例如自定义函数period(context);time是具体执行时间,time="every_bar"按天会在交易日的开盘时调用一次,按分钟会在交易时间每分钟运行;reference_security是时间的参照标的代码,字符串类型,默认为‘000001.XSHG’。期货策略一定要修改参考标的,建议修改为对应的主力合约。
API:即应用程序编程接口,量化平台的函数,在API文档中查阅。下单函数主要有:
#按股数下单函数
order("000001.XSHE",100)
#参数有5个,分别是security,amount,style,side,pindex。
#深交所(0开头)股票代码后缀为.XSHE,上交所股票代码(6开头)后缀为.XSHG
#amout有正负,最小为1股100手
#style默认市价,side默认多头,pindex选资金仓位
order_target(security,amount)
#含义是通过买卖,将股票仓位稳定在一定数量(单位:股)
order_value(security,value)
#含义是买卖一定价值量(单位:元)股票。比如P=9,V=1万,大概买入1100股
order_target_value(security,value)
#通过买卖,将股票仓位稳定在一定价值量(单位:元)
context是一个回测系统建立的Context类型的对象,其中存储了如当前策略运行的时间点、所持有的股票、数量、持仓成本等数据。
# context.portfolio.positions的含义是仓位信息,所以为了让它有数据,需要在取之前买入并持有股票。
def initialize(context):
run_daily(period,time='every_bar')
g.security = '000001.XSHE'
def period(context):
order(g.security, 100)
# 打印所有键
print(context.portfolio.positions.keys())
# 打印所有值
print(context.portfolio.positions.values())
# 打印g.security的开仓均价
print(context.portfolio.positions[g.security].avg_cost)
# 执行后日志内容如下
# ['000001.XSHE']
# [UserPosition({'avg_cost': 8.539999999999997, 'security': '000001.XSHE', 'closeable_amount': 0, 'price': 8.53, 'total_amount': 100})]
# 8.54
通过context的数据可以得到持有股票的成本和现价,从而可以算出该股票的盈亏情况,运用条件判断语句根据盈亏情况从而决定是否卖出股票,从而实现止损操作,代码如下:
def initialize(context):
run_daily(period,time='every_bar')
g.security = '000001.XSHE'
def period(context):
# 买入股票
order(g.security, 100)
# 获得股票持仓成本
cost=context.portfolio.positions['000001.XSHE'].avg_cost
# 获得股票现价
price=context.portfolio.positions['000001.XSHE'].price
# 计算收益率
ret=price/cost-1
# 打印日志
print('成本价:%s' % cost)
print('现价:%s' % price)
print('收益率:%s' % ret)
# 如果收益率小于-0.01,即亏损达到1%则卖出股票,幅度可以自己调,一般10%
if ret<-0.01:
order_target('000001.XSHE',0)#调整仓位价值为0
print('触发止损')
止盈的思路也一样,将判断条件改一下即可。比如在23号触发了止损,至23号持有了1700股,由于该日买入了100股,实际上平仓的只有1600股(T+1),平仓以后还剩今天买的100股,若24号未触及平仓则总持仓量为200股。
def initialize(context):
run_daily(period,time='every_bar')
# 把两个股票代码作为list存入g.security中
g.security = ['000001.XSHE','000002.XSHE']
def period(context):
for stk in g.security:
order(stk,100)
# 获得股票持仓成本
cost=context.portfolio.positions[stk].avg_cost
# 获得股票现价
price=context.portfolio.positions[stk].price
# 计算收益率
ret=price/cost-1
# 如果收益率小于-0.01,即亏损达到1%则卖出股票,幅度可以自己调,一般10%
if ret<-0.01:
order_target(stk,0)
print('触发止损')
在聚宽的python版API中,用ctrl+f进行网页搜索可以快速搜索需要的数据。
# 获取20180301时,上证50指数(000016.XSHG)成分股
t=get_index_stocks("000016.XSHG","2018-03-01")
print(t[0])
print(t)
# 打印日志如下。股票代码在list中被打印出来前面会带有的u代表是对字符串进行unicode编码,只是显示效果,单独打印t[0]时就没有u。
# 600000.XSHG
#[u'600000.XSHG', u'600016.XSHG', u'600019.XSHG', u'600028.XSHG', u'600029.XSHG', u'600030.XSHG', u'600036.XSHG', u'600048.XSHG', u'600050.XSHG', u'600104.XSHG', u'600111.XSHG', u'600309.XSHG', u'600340.XSHG', u'600518.XSHG', u'600519.XSHG', u'600547.XSHG', u'600606.XSHG', u'600837.XSHG', u'600887.XSHG', u'600919.XSHG', u'600958.XSHG', u'600999.XSHG', u'601006.XSHG', u'601088.XSHG', u'601166.XSHG', u'601169.XSHG', u'601186.XSHG', u'601211.XSHG', u'601229.XSHG', u'601288.XSHG', u'601318.XSHG', u'601328.XSHG', u'601336.XSHG', u'601390.XSHG', u'601398.XSHG', u'601601.XSHG', u'601628.XSHG', u'601668.XSHG', u'601669.XSHG', u'601688.XSHG', u'601766.XSHG', u'601800.XSHG', u'601818.XSHG', u'601857.XSHG', u'601878.XSHG', u'601881.XSHG', u'601985.XSHG', u'601988.XSHG', u'601989.XSHG', u'603993.XSHG']
history可以同时获得多个股票的数据,但只能获得相同的一个数据字段。如获得平安银行,建设银行,农业银行这3只股票,前3天的交易额。history(count, unit=‘1d’, field=‘avg’, security_list=None, df=True, skip_paused=False, fq=‘pre’)
w=history(count=3,unit='5d',field='close', security_list=['000001.XSHE','000002.XSHE'])
print(w)
''' 000001.XSHE 000002.XSHE
2017-03-09 9.01 18.35
2017-03-16 9.15 19.03
2017-03-23 8.84 19.19'''
上述代码中,选择回测时间为2017-3-24,那么就挑出该日之前的3天数据。
attribute_history只能获取单独一个股票的多个字段的数据。如获得平安银行这一只股票,前3天的交易额,交易量,最高价,最低价等。
w=attribute_history(security='000001.XSHE',count=3, fields=['money','high','close'])
print(w)
# 按位置获取某几列.iloc[:,[位置,...]],位置的含义是第几个,从0开始。下文同。
print(w.iloc[:,[0,2]])
''' money high close
2017-03-21 5.119143e+08 8.90 8.88
2017-03-22 5.193310e+08 8.86 8.80
2017-03-23 3.989866e+08 8.88 8.84
money close
2017-03-21 5.119143e+08 8.88
2017-03-22 5.193310e+08 8.80
2017-03-23 3.989866e+08 8.84'''
get_fundamentals(query_object, date=None, statDate=None),表和字段可以在财务数据文档查看。
query_object参数是要求传入一个Query对象用于描述所需的数据,这里提供一个快速上手的模板用来产生query_object参数,多数情况下往里套就可以了;
date: 查询日期,一个字符串(格式类似’2015-10-15’),传入date时,查询指定日期date收盘后所能看到的最近(对市值表来说, 最近一天, 对其他表来说, 最近一个季度)的数据, 我们会查找上市公司在这个日期之前(包括此日期)发布的数据, 不会有未来函数;
statDate: 查询 statDate 指定的季度或者年份的财务数据,财报统计的季度或者年份,有两种格式:季度格式例如’2015q1’,‘2013q4’;年份格式例如: ‘2015’,‘2016’;
date和statDate参数只能同时传入其中一个。当 date 和 statDate 都不传入时,相当于使用 date 参数,date 的默认值为回测日期的前一天。
# 快速上手模板
query(表.字段).filter(筛选条件).order_by(排序方法).limit(数量上限)
#比较长的话可以分行写
# 例子
# 获取 市值表.股票代码,资产负债表.未分配利润
q=query(valuation.code,balance.retained_profit
# 筛选 市值大于100 并且 市盈率小于10
).filter(valuation.market_cap>100,valuation.pe_ratio < 10
# 排序 按市值从大到小排
).order_by(valuation.market_cap.desc()
# 数量 上限10条数据
).limit(10)
w=get_fundamentals(q)
print(w)
'''2017-03-24 00:00:00 - INFO - code retained_profit
0 601398.XSHG 9.210480e+11
1 601939.XSHG 7.730580e+11
2 601988.XSHG 5.591120e+11
3 601288.XSHG 4.871250e+11
4 600036.XSHG 1.980190e+11
5 601328.XSHG 9.299600e+10
6 600000.XSHG 8.579200e+10
7 601998.XSHG 1.366660e+11
8 600016.XSHG 1.324380e+11
9 601166.XSHG 1.728410e+11'''
查找上证指数成分股中流通市值最大的5个股票的股票代码:
t=get_index_stocks("000001.XSHG")
q=query(valuation.code).filter(valuation.code.in_(t)
).order_by(valuation.circulating_market_cap.desc()).limit(5)
print(get_fundamentals(q).values)
'''[[601857.XSHG]
[601398.XSHG]
[601288.XSHG]
[601988.XSHG]
[600028.XSHG]]'''
https://www.joinquant.com/view/community/detail/8ec7aaaa899cf928550f89a104637f22?type=1