這篇帶大家按照官方入門指南來(lái)體驗(yàn)下Quantopian的魅力,文章中會(huì)涉及到一些平臺(tái)中常用的API,大家可以先有個(gè)初步印象,用的時(shí)候再去查文檔即可。文章最后會(huì)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的均值回歸的策略,好了,寬客動(dòng)手不動(dòng)口,開始我們的旅程!

交易算法框架
在Quantopian中,交易算法是一段Python腳本,其中包含了幾個(gè)關(guān)鍵的函數(shù)
-
initialize()
程序啟動(dòng)時(shí)調(diào)用的函數(shù),用于處理啟動(dòng)時(shí)需要的一次性邏輯,需要context(上下文對(duì)象)作為輸入。
context是一個(gè)增強(qiáng)的Python字典,用于存儲(chǔ)回測(cè)或交易中的一些狀態(tài)與有效數(shù)據(jù),它作為算法中的共享區(qū)域,在各個(gè)函數(shù)中均可訪問(wèn)其內(nèi)容(使用context.鍵值的方式)。 -
handle_data()
按照自定義的周期,定時(shí)調(diào)用,一般在其中處理當(dāng)前周期中需要處理的訂單,context與data是函數(shù)的兩個(gè)參數(shù)。
data是用于存放一些API方法的對(duì)象。 -
before_trading_start()
每交易日開盤前調(diào)用的函數(shù),通常用于選定當(dāng)天待交易的股票,入?yún)⑴chandle_data一樣,也為context與data兩個(gè)對(duì)象。
一個(gè)最基礎(chǔ)的算法框架如下:
def initialize(context):
# 啟動(dòng)后需要處理的一次性邏輯
def handle_data(context, data):
# 定時(shí)執(zhí)行,處理當(dāng)前周期中待處理訂單
def before_trading_start(context, data):
# 開盤前執(zhí)行,選定當(dāng)天待交易股票
整體框架基本可以模擬了我們平日里的交易操作,很容易理解。對(duì)于框架有初步了解后,我們?cè)倮^續(xù)前進(jìn)一小步,如何獲購(gòu)買一只股票呢?首先我們要獲取到這只股票
獲取證券信息
平臺(tái)提供了sid()函數(shù),這個(gè)函數(shù)會(huì)根據(jù)輸入返回一只股票在平臺(tái)中的唯一ID,且是始終不變的。
比如你想獲取到蘋果公司的股票,只需要在代碼區(qū)域調(diào)用sid函數(shù),并在入?yún)⒗镙斎牍善焙?jiǎn)稱AAPL,編輯器就會(huì)智能地給出相應(yīng)提示。

獲取之后一般都會(huì)存放到context對(duì)象中,供之后的邏輯使用
def initialize(context):
context.aapl = sid(24)
def handle_data(context, data):
print context.aapl
下單
下單平臺(tái)提供了幾種不同的函數(shù),這個(gè)點(diǎn)后續(xù)再展開詳述,這篇文章我們以order_target_percent()這個(gè)為例進(jìn)行說(shuō)明,order_target_percent函數(shù)需要兩個(gè)參數(shù),sid(證券ID)與target_percent(投資比例,分母為可用頭寸)
**注:
可用頭寸=現(xiàn)金賬戶+敞口頭寸(open position)
敞口頭寸應(yīng)該為當(dāng)日買賣差價(jià)
**
如果用可用頭寸的50%去做多蘋果,代碼如下
order_target_percent(sid(24), 0.50)
而如果用可用頭寸的50%去做空,代碼如下
order_target_percent(sid(24), -0.50)
獲取證券信息
在下單前,策略可能需要根據(jù)歷史及當(dāng)前該證券的信息進(jìn)行分析,data對(duì)象提供了幾個(gè)快捷的函數(shù)供使用。
- data.current() 獲取當(dāng)前信息
需要兩個(gè)參數(shù),第一個(gè)參數(shù)為證券sid或一個(gè)證券sid列表,第二個(gè)參數(shù)為你需要的信息字段,目前支持的有價(jià)格(price)、開盤價(jià)(open)、收盤價(jià)(close)、最高價(jià)(high)、最低價(jià)(low)與交易量(volume)。
例如:
data.current(sid(24), 'price') #獲取蘋果的當(dāng)前價(jià)格
data.current([sid(24), sid(46631)], 'price') 獲取蘋果與谷歌的當(dāng)前股價(jià)
data.current([sid(24), sid(46631)], ['low', 'high'])獲取蘋果與谷歌截止當(dāng)前的最高最低價(jià)
- data.can_trade() 該證券能否交易
需要傳入證券sid或證券sid列表 - data.history() 獲取歷史信息
在current參數(shù)的基礎(chǔ)上增加了回看窗口大小(lookback window length)及回看頻率(lookback frequency)的參數(shù)
hist = data.history(sid(24), 'price', 10, '1d')#獲取蘋果過(guò)去十天的價(jià)格,頻率為每天
mean_price = hist.mean()
#注:獲取歷史價(jià)格的時(shí)候,會(huì)先返回當(dāng)前的價(jià)格,及前9天的日中收盤價(jià),所以如果你希望獲取前十天的均價(jià),先取11天,然后丟棄掉最新的一條。
data.history(sid(8554), 'price', 11, '1d')[:-1].mean()
注: data.current()與data.history()返回的均為pandas的DataFrame結(jié)構(gòu),pandas這個(gè)python科學(xué)庫(kù)后續(xù)會(huì)專門一系列說(shuō)明
自定義調(diào)度
可以根據(jù)自己需要的周期,靈活地定義自己需要的定時(shí)處理邏輯函數(shù),參數(shù)中包含自定義函數(shù)名、日期規(guī)則與實(shí)踐規(guī)則,
#在每周第一天開盤后一小時(shí),已目前可用的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)
查詢當(dāng)前持倉(cāng)
Quantopian在context中內(nèi)置了portfolio對(duì)象,持倉(cāng)存儲(chǔ)在context.portfolio.positions中,positions可以看做sid為鍵,Postion對(duì)象為值的一個(gè)字典。我們可以使用for...in語(yǔ)法進(jìn)行循環(huán)處理:
#清盤所有持倉(cāng)
for security in context.portfolio.positions:
order_target_percent(security, 0)
繪制變量
平臺(tái)提供了record()方法幫助你將自己關(guān)注的變量繪制成時(shí)間序列圖,可以方便的監(jiān)控關(guān)心的變量。
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)
重新編譯算法后,可直觀地展現(xiàn)每日收盤空頭/多頭的時(shí)間序列圖。

滑點(diǎn)與傭金
滑點(diǎn)與傭金都屬于交易成本的范疇,對(duì)于算法的實(shí)際表現(xiàn)影響很大
- 滑點(diǎn)
滑點(diǎn)表示由于下單(尤其是大單)對(duì)于股價(jià)潛在的影響,會(huì)導(dǎo)致實(shí)際成交比預(yù)期差的情況,平臺(tái)中集成了多種滑點(diǎn)的策略,下面為默認(rèn)的策略:
# 假設(shè)過(guò)去幾分鐘內(nèi)交易量為1000時(shí),限制每次只能下1000*0.025=25的單,系統(tǒng)會(huì)根據(jù)這個(gè)值進(jìn)行拆單
set_slippage(slippage.VolumeShareSlippage(volume_limit=0.025, price_impact=0.1))
- 傭金
可以根據(jù)真實(shí)經(jīng)紀(jì)商的情況設(shè)置傭金費(fèi)率及最小費(fèi)用。
set_commission(commission.PerShare(cost=0.0075, min_trade_cost=1))
訂單管理
因?yàn)橛唵位径疾皇羌纯坛山坏?,而且?dāng)前未成交訂單對(duì)于后續(xù)的策略執(zhí)行也有很大的指導(dǎo)意義,所以訂單管理的功能也是至關(guān)重要的。
get_open_orders()函數(shù)幫助我們獲取當(dāng)前已下但尚未成交的訂單
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)
動(dòng)手均值回歸策略
好了,把之前所有的內(nèi)容整合起來(lái),我們很容易就能實(shí)現(xiàn)一個(gè)簡(jiǎn)單的均值回歸策略。均值回歸策略可以簡(jiǎn)單的理解為短期的均值會(huì)有一定波動(dòng),但隨時(shí)間一定會(huì)逐步與長(zhǎng)期均值趨同。
我們就來(lái)實(shí)現(xiàn)這樣一個(gè)策略:
如果一個(gè)股票10天移動(dòng)平均高于了其30天移動(dòng)平均值,我們則認(rèn)為股價(jià)一定會(huì)下跌,反之亦然。
def initialize(context):
#初始化證券池
context.security_list = [sid(5061), sid(7792), sid(1941), sid(24556), sid(1746)]
# 自定義調(diào)度:每周第一交易日開盤時(shí)重新計(jì)算并執(zhí)行組合重新配置
schedule_function(rebalance,
date_rules.week_start(days_offset=0),
time_rules.market_open())
def compute_weights(context, data):
#計(jì)算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()
# 加權(quán)計(jì)算
raw_weights = (sma_30 - sma_10) / sma_30
normalized_weights = raw_weights / raw_weights.abs().sum()
return normalized_weights
def rebalance(context, data):
# 重置權(quán)重
weights = compute_weights(context, data)
# 組合重新分配
for security in context.security_list:
if data.can_trade(security):
order_target_percent(security, weights[security])
這一篇到這里就結(jié)束了,是不是覺(jué)得很容易上手呢?建議大家去官方教程里克隆策略,自己上手跑一跑,對(duì)于量化交易會(huì)更有體會(huì)。