[策略编写系列三] 动量策略( Python 字典实战)

2017-07-26 08:45:46 +08:00
 a499492580
概要:
在 [策略编写系列一] 、 [策略编写系列二] 中,详细的讲述了如何用 Python 语言编写单因子策略和多因子策略。本章内容讲解如何用 Python 语言编写一个简单的动量策略,希望给有需要的同学提供一些帮助。内容主要分为:动量效应的介绍、构建简单动量策略、运用 Python 编写出策略(本节有 Python 的字典用法)、策略回测结果分析。

正文:

一、动量效应的介绍

动量效应:由 Jegadeesh 和 Titman ( 1993 )提出,他们认为:股票的收益率有延续原来的运动方向的趋势,即过去一段时间收益率较高的股票,在未来依旧会取得高于平均的收益率。整个解释中最核心的词汇是“延续”,“延续”的左边是过去的历史行情,右边是未来的未知行情,由此分析得出:动量效应是研究过去的历史行情,并预测过去的行情能延续。
动量效应是否具有可行性?可能很多投资者都认为动量效应是一种非常激进、盲目的投资策略,说白了就是追涨杀跌,其风险程度相当高,一不小心就可能买在顶点,接着就是无尽的站岗模式开启~~~~~但不可否认的是,目前中国 A 股市场上确实存在着不少长期走牛的个股,比如:索菲亚、贵州茅台等,除此之外,还存在不少短时间内翻倍,甚至三倍的个股。那么在控制好风险的前提下,捕捉这类动量效应明显的个股,貌似也是一个不错的动量策略。其潜在的优势就是盈利空间巨大,劣势是盈利机会较少。

二、构建简单动量策略

延续上一节的思路,构建一个简单的动量策略。首先需要一个动量效应的衡量指标,以一个月内个股涨幅为标的,涨幅越大,则动量效应越明显,筛选前 50 只股票作为股票池。其次确定调仓周期,本策略是按月调仓。最后确定风险控制措施,本策略止损为:当个股亏损超过 10%时,止损离场;止盈即为时间止盈,周期一个月。

三、运用 Python 编写出策略
第一步:导入编写过程中需要用到的代码包。
from datetime import timedelta, date
#1.导入时间数据包
import pandas as pd
#2.导入 pandas 数据包,快捷使用为 pd
第二步:设置初始条件
def initialize(account):
account.n = 50 # 最大持股数
#调仓月份设置,为 1-12 月
account.trade_date = range(1,13,1)
## 按月调仓,调仓日为月末最后一个交易日
run_monthly(trade,date_rule=2)
#运用 I 问财进行股票筛选
get_iwencai('未停牌,上市时间超过 2 年')
其中 get_iwencai()是 MindGo 平台特有的自然语言选股器,输入文字即可。选出的股票池放到 account.iwencai_securities,可以直接调用。



第三步:创建选股函数,以一个月的涨幅排序,选前 50 只股票入股票池。
def stocks_zf(account,data):
# 创建字典用于存储涨跌幅
df = {'security': [], '30zf': []}
stocks=account.iwencai_securities
for symbol in stocks:
df['security'].append(symbol)
for i in range(len(df['security'])):
# 获取前 30 日的涨跌幅
quote = data.attribute_history(
df['security'][i], ['quote_rate'], 30, '1d', True, fq='pre')
AMP30 = quote.values[:].sum()
df['30zf'].append(AMP30)
# 由大到小排序
df = pd.DataFrame(df).sort_values(by ='30zf', ascending=False)
account.sample = df['security'][:50]
return account.sample



1.def stocks_zf(account,data):
这行代码用来创建自定义函数,取名 stock_zf,后缀是两个参数。
2.df = {'security': [], '30zf': []}
这行代码用来创建字典,定义为 df,且必须是大括号{},'security'是字典项,:[]用来存放该项的值,一个字典可以存多个,中间用逗号分开,本策略的字典一共两个项,分别是'security'和'30zf',后面的[]用来存放。
3.stocks=account.iwencai_securities
account.iwencai_securities 是 MINDGO 平台的中 i 问财自然语言选股后的结果这行代码将结果植入 stocks.
4.for symbol in stocks:
for in 循环函数,将 stocks 中的个股逐一取出,执行操作。
5.df['security'].append(symbol)
将每个股票存放到字典中的'security'的项中,x.append ( y )是在 x 中加入 y。
6.for i in range(len(df['security'])):
for in 循环函数,range ()用来创建数列,len()用来获取对象的数量,这行代码是以字典中 security 的数量为准,形成一组等长数列,i 就是该数列中的每个数。
7.quote = data.attribute_history(
df['security'][i], ['quote_rate'], 30, '1d', True, fq='pre')
这行代码用来获取字典中第 i 个项对应的股票的数据,数据内容为个股最近 30 天的涨跌幅。
8.AMP30 = quote.values[:].sum()
这行代码用来计算 30 天涨跌幅的和

9.if AMP30>0 :
if 判断函数,判断个股 30 天的涨跌幅的和是否大于 0.
10.df['30zf'].append(AMP30)
如果大于 0.则将这个值添加到字典的项中。
11.else:
如果不大于 0,则执行该行代码。
12.df['30zf'].append(0)
将 0 这个值添加到字典的项中。
13.for i in range(len(df['security'])):
同 6
14.num = len(df['security']) - i
计算 num 值
15.for j in range(1,num):
同 6
16.if(df['30zf'][i] < df['30zf'][-j]):
if 判断函数,将字典中 30zf 的项中的第 i 和第-j 项比较。
17.t = df['security'][-j]
如果-j 项大于 i 项,则将-j 项赋值给 t,18-22 都是同理。
18.df['security'][-j] = df['security'][i]
19.df['security'][i] = t
20.t = df['30zf'][-j]
21.df['30zf'][-j] = df['30zf'][i]
22.df['30zf'][i] = t
13-22 行代码用来排序,基本逻辑就是,字典中每个项与每个项做比较,一旦出现更大的项,则把两者位置调换,security 和 30zf 两者都需要换。
23.account.sample = df['security'][:30]
将字典中的前 30 项取出。
24.return account.sample
输出函数结果。
第四步:设置交易函数:
def trade(account, data):
date = get_datetime()
months = get_datetime().month
if months in account.trade_date:
##获得 50 只股票列表
zf_list = stocks_zf(account,data)
## 获得满足每种条件的股票池
stock_list = list(set(zf_list))
## 卖出
if len(account.positions) > 0:
for stock in list(account.positions):
if stock not in stock_list:
order_target(stock, 0)
## 买入
if len(stock_list) > 0:
for stock in stock_list:
if stock not in list(account.positions):
if len(account.positions) < account.n :
number = account.n - len(account.positions)
order_value(stock,account.cash/number)
else:
order_value(stock,account.cash)

else:
pass

1.def trade(account, data):
这行代码用来自定义交易函数,与选股函数同理。

2.date = get_datetime()
这行代码用来获取当前时间

3.months = get_datetime().month
这行代码用来获取当前时间的月份

4.if months in account.trade_date:
这行代码用来判断当前月份是否符合调仓月份要求,后半段 account.trade_date 是初始设置条件之一。如果不满足直接跳到 19 行。

5.pb_list = stocks_zf(account,data)
这行代码用来获取股票列表,将选股函数的结果输出到列表上

6.stock_list = list(set(zf_list))
这行代码用来将列表的股票转移到新的列表,用来交易,其中 list()是列表形式,set()用来创建集合。
7.if len(account.positions) > 0:
这行代码用来判断目前持仓股票数量,如果有数量,则进行下一步。

8. for stock in list(account.positions):
for in 是一个循环函数,将持仓股票逐一选出,并逐一进行下一步
9.if stock not in stock_list:
if 判断函数,如果选出的股票不在股票列表,则表明,个股经过一个月后不在是市盈率最低的 15 只了。需要进行下一步卖出。

10.order_target(stock, 0)
order_target 是下单函数,用于买卖股票,参数 stock 是交易对象,参数 0 代表将股票清仓。具体下单函数可以阅读 MINDGO 的 API 文档,进行学习。http://quant.10jqka.com.cn/platform/html/help-api.html#7/145

11.if len(stock_list) > 0:
if 判断函数,用来判断股票列表中个股数量是否大于 0,符合则进行下一步。

12.for stock in stock_list:
for in 循环函数,将股票列表中的个股逐一选股,并逐一进行下一步。
13.if stock not in list(account.positions):
if 函数,逐一拿出来的股票是否在当前持仓中,如果不在当前持仓则进到下一步。

14. if len(account.positions) < account.n :
if 函数,判断当前持仓数量是否小于最大持股数,如果小于则下一步。如果不满足,则跳到 17 行。

15.number = account.n - len(account.positions)
计算出最大持股数与当前持仓数量的差值。

16.order_value(stock,account.cash/number)
order_valuse 是下单函数,参数 stock 是交易对象,也就是逐一选的股票,参数 account.cash 是当前可用资金,number 是 15 行计算的结果,整合就是买入的资金,即当前可用资金平均分配到每个个股。

17.else:
用来接收 14 行中,if 函数不满足的个股

18.order_value(stock,account.cash)
order_valuse 是下单函数,参数 stock 是交易对象,参数 accunt.cash 是买入金额,即当然可用资金。

19.else:
用于接收第 4 行代码中不符合的情况发生,进行下一步。

20pass
当出现第 4 行代码不符合的情况,则 pass,跳过。

第五步:设置风控条件
def handle_data(account,data):
## 个股止损
if len(account.positions) > 0:
# 止损:个股跌幅超过 10%,卖出
securities = list(account.positions)
for stock in securities:
price = data.attribute_history(stock, ['close'], 1, '1d', skip_paused=False, fq='pre')
if account.positions[stock].cost_basis /price['close'][0]-1 < -0.1:
order_target(stock, 0)
1.def handle_data(account,data):
这行代码是函数创建。
2.if len(account.positions) > 0:
if 判断函数,用来判断目前是否有持仓。
3.securities = list(account.positions)
如果有持仓,则将持仓股票植入 securities
4.for stock in securities:
for in 循环函数,从持仓股票池中逐一取出股票,进行操作。
5.price = data.attribute_history(stock, ['close'], 1, '1d', skip_paused=False, fq='pre')
data.attribute_history ()获取数据的函数,参数 stock 是对象,['close']获取的数据为收盘价。详细可以参考 MindGo 的 API 文档: http://quant.10jqka.com.cn/platform/html/help-api.html?t=data#3/0
6.if account.positions[stock].cost_basis /price['close'][0]-1 < -0.1:
if 判断函数,account.positions[stock].cost_basis 是持仓个股的成本价,price['close'][0]是收盘价,这行代码是用来止损,当个股亏损超过 10%时,执行下一步。

7.order_target(stock, 0)
order_target 是下单函数,参数 stock 是操作对象,0 代表清仓。
至此我们已经编写完整个策略,进行历史行情回测。

四、策略回测结果分析
从历史回测结果看,动量策略在 13-15 年具有非常不错的收益,其没有严重的回测,但是 16-17 年,该策略几乎一直在亏钱,这就说明目前中国市场的动量效应极低,非常不利于追涨杀跌。当然该策略还有很多改进之处,比如对大盘的动量效应进行检验,如果具有动量效应则进行操作,不然就空仓。小伙伴们还不动手试试,get>>>http://quant.10jqka.com.cn/platform/html/study.html#/
3296 次点击
所在节点    Python
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/377976

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX