V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
a499492580
V2EX  ›  Python

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

  •  
  •   a499492580 · 2017-07-26 08:45:46 +08:00 · 3283 次点击
    这是一个创建于 2472 天前的主题,其中的信息可能已经有所发展或是发生改变。
    概要:
    在 [策略编写系列一] 、 [策略编写系列二] 中,详细的讲述了如何用 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#/
    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   838 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 19:39 · PVG 03:39 · LAX 12:39 · JFK 15:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.