这篇带大家按照官方入门指南来体验下Quantopian的魅力,文章中会涉及到一些平台中常用的API,大家可以先有个初步印象,用的时候再去查文档即可。文章最后会实现一个简单的均值回归的策略,好了,宽客动手不动口,开始我们的旅程!
交易算法框架
在Quantopian中,交易算法是一段Python脚本,其中包含了几个关键的函数
- initialize()
程序启动时调用的函数,用于处理启动时需要的一次性逻辑,需要context(上下文对象)作为输入。
context是一个增强的Python字典,用于存储回测或交易中的一些状态与有效数据,它作为算法中的共享区域,在各个函数中均可访问其内容(使用context.键值的方式)。 - handle_data()
按照自定义的周期,定时调用,一般在其中处理当前周期中需要处理的订单,context与data是函数的两个参数。
data是用于存放一些API方法的对象。 - before_trading_start()
每交易日开盘前调用的函数,通常用于选定当天待交易的股票,入参与handle_data一样,也为context与data两个对象。
一个最基础的算法框架如下:
def initialize(context):
# 启动后需要处理的一次性逻辑
def handle_data(context, data):
# 定时执行,处理当前周期中待处理订单
def before_trading_start(context, data):
# 开盘前执行,选定当天待交易股票
整体框架基本可以模拟了我们平日里的交易操作,很容易理解。对于框架有初步了解后,我们再继续前进一小步,如何获购买一只股票呢?首先我们要获取到这只股票
获取证券信息
平台提供了sid()函数,这个函数会根据输入返回一只股票在平台中的唯一ID,且是始终不变的。
比如你想获取到苹果公司的股票,只需要在代码区域调用sid函数,并在入参里输入股票简称AAPL,编辑器就会智能地给出相应提示。
获取之后一般都会存放到context对象中,供之后的逻辑使用
def initialize(context):
context.aapl = sid(24)
def handle_data(context, data):
print context.aapl
下单
下单平台提供了几种不同的函数,这个点后续再展开详述,这篇文章我们以order_target_percent()这个为例进行说明,order_target_percent函数需要两个参数,sid(证券ID)与target_percent(投资比例,分母为可用头寸)
**注:
可用头寸=现金账户+敞口头寸(open position)
敞口头寸应该为当日买卖差价
**
如果用可用头寸的50%去做多苹果,代码如下
order_target_percent(sid(24), 0.50)
而如果用可用头寸的50%去做空,代码如下
order_target_percent(sid(24), -0.50)
获取证券信息
在下单前,策略可能需要根据历史及当前该证券的信息进行分析,data对象提供了几个快捷的函数供使用。
- data.current() 获取当前信息
需要两个参数,第一个参数为证券sid或一个证券sid列表,第二个参数为你需要的信息字段,目前支持的有价格(price)、开盘价(open)、收盘价(close)、最高价(high)、最低价(low)与交易量(volume)。
例如:
data.current(sid(24), 'price') #获取苹果的当前价格
data.current([sid(24), sid(46631)], 'price') 获取苹果与谷歌的当前股价
data.current([sid(24), sid(46631)], ['low', 'high'])获取苹果与谷歌截止当前的最高最低价
- data.can_trade() 该证券能否交易
需要传入证券sid或证券sid列表 - data.history() 获取历史信息
在current参数的基础上增加了回看窗口大小(lookback window length)及回看频率(lookback frequency)的参数
hist = data.history(sid(24), 'price', 10, '1d')#获取苹果过去十天的价格,频率为每天
mean_price = hist.mean()
#注:获取历史价格的时候,会先返回当前的价格,及前9天的日中收盘价,所以如果你希望获取前十天的均价,先取11天,然后丢弃掉最新的一条。
data.history(sid(8554), 'price', 11, '1d')[:-1].mean()
注: data.current()与data.history()返回的均为pandas的DataFrame结构,pandas这个python科学库后续会专门一系列说明
自定义调度
可以根据自己需要的周期,灵活地定义自己需要的定时处理逻辑函数,参数中包含自定义函数名、日期规则与实践规则,
#在每周第一天开盘后一小时,已目前可用的10%买入苹果股票。
def initialize(context):
context.aapl = sid(24)
schedule_function(func=open_positions,
data_rules=date_rules.week_start(),
time_rules=time_rules.market_open(hours=1)
def open_positions(context, data):
order_target_percent(context.aapl, 0.10)
查询当前持仓
Quantopian在context中内置了portfolio对象,持仓存储在context.portfolio.positions中,positions可以看做sid为键,Postion对象为值的一个字典。我们可以使用for...in语法进行循环处理:
#清盘所有持仓
for security in context.portfolio.positions:
order_target_percent(security, 0)
绘制变量
平台提供了record()方法帮助你将自己关注的变量绘制成时间序列图,可以方便的监控关心的变量。
def initialize(context):
context.aapl = sid(24)
schedule_function(record_vars, date_rules.every_day(), time_rules.market_close())
def record_vars(context, data):
long_count = 0
short_count = 0
for position in context.portfolio.positions.itervalues():
if position.amount > 0:
long_count += 1
if position.amount < 0:
short_count += 1
# 绘制图表
record(num_long=long_count, num_short=short_count)
重新编译算法后,可直观地展现每日收盘空头/多头的时间序列图。
滑点与佣金
滑点与佣金都属于交易成本的范畴,对于算法的实际表现影响很大
- 滑点
滑点表示由于下单(尤其是大单)对于股价潜在的影响,会导致实际成交比预期差的情况,平台中集成了多种滑点的策略,下面为默认的策略:
# 假设过去几分钟内交易量为1000时,限制每次只能下1000*0.025=25的单,系统会根据这个值进行拆单
set_slippage(slippage.VolumeShareSlippage(volume_limit=0.025, price_impact=0.1))
- 佣金
可以根据真实经纪商的情况设置佣金费率及最小费用。
set_commission(commission.PerShare(cost=0.0075, min_trade_cost=1))
订单管理
因为订单基本都不是即刻成交的,而且当前未成交订单对于后续的策略执行也有很大的指导意义,所以订单管理的功能也是至关重要的。
get_open_orders()函数帮助我们获取当前已下但尚未成交的订单
open_orders = get_open_orders()
if context.xtl not in open_orders and data.can_trade(context.xtl):
order_target_percent(context.xtl, 1.0)
动手均值回归策略
好了,把之前所有的内容整合起来,我们很容易就能实现一个简单的均值回归策略。均值回归策略可以简单的理解为短期的均值会有一定波动,但随时间一定会逐步与长期均值趋同。
我们就来实现这样一个策略:
如果一个股票10天移动平均高于了其30天移动平均值,我们则认为股价一定会下跌,反之亦然。
def initialize(context):
#初始化证券池
context.security_list = [sid(5061), sid(7792), sid(1941), sid(24556), sid(1746)]
# 自定义调度:每周第一交易日开盘时重新计算并执行组合重新配置
schedule_function(rebalance,
date_rules.week_start(days_offset=0),
time_rules.market_open())
def compute_weights(context, data):
#计算10日与30日均值
hist = data.history(context.security_list, 'price', 30, '1d')
prices_10 = hist[-10:]
prices_30 = hist
sma_10 = prices_10.mean()
sma_30 = prices_30.mean()
# 加权计算
raw_weights = (sma_30 - sma_10) / sma_30
normalized_weights = raw_weights / raw_weights.abs().sum()
return normalized_weights
def rebalance(context, data):
# 重置权重
weights = compute_weights(context, data)
# 组合重新分配
for security in context.security_list:
if data.can_trade(security):
order_target_percent(security, weights[security])
这一篇到这里就结束了,是不是觉得很容易上手呢?建议大家去官方教程里克隆策略,自己上手跑一跑,对于量化交易会更有体会。