量化交易之海龟策略

量化交易之海龟策略

代码只能保证运行在ricequant上

海龟策略这里我就不着重介绍,感兴趣可以去买书自己看。

下面是我搬运的,重点我加粗了。

一、海龟交易法

海龟交易法是著名的公开交易系统,1983年著名的商品投机家理查德. 丹尼斯在一个交易员培训班上推广而闻名于世,它涵盖了交易系统的各个方面。其法则覆盖了交易的各个方面,并且不给交易员留下一点主观想象决策的余地。它具备一个完整的交易系统的所有成分。

著名的商品投机家理查德·丹尼斯想弄清楚伟大的交易员是天生造就的还是后天培养的。为此,在1983年他招募了13个人,教授给他们期货交易的基本概念,以及他自己的交易方法和原则。 “学员们被称为‘海龟’(丹尼斯先生说这项计划开始时他刚刚从亚洲回来,他解释了自己向别人说过的话,‘我们正在成长为交易员,就象在新加坡他们正在成长为海龟一样’)。”—-斯坦利.W.安格瑞斯特,《华尔街期刊》,1989年9月5日。
海龟成为交易史上最著名的实验,因为在随后的四年中海龟们取得了年均复利80%的收益。 丹尼斯证明用一套简单的系统和法则,可以使仅有很少或根本没有交易经验的人成为优秀的交易员。 当时,海龟们认为应对理查德·丹尼斯负责,商定甚至在他们议定的10年保密协定于1993年终止后也不泄露这些法则。但是,有个别海龟在网站上出售海龟交易法则而谋取钱财。两个原版海龟科蒂斯·费思和阿瑟·马多克,为了阻止个别海龟对知识产权的偷窃和出售海龟交易法则而赚钱的行为,决定在网站上将海龟交易法则免费公之于众。

海龟交易策略是一套非常完整的趋势跟随型的自动化交易策略。这个复杂的策略在入场条件、仓位控制、资金管理、止损止盈等各个环节,都进行了详细的设计,这基本上可以作为复杂交易策略设计和开发的模板。

二、趋势信号的捕捉

在趋势信号的扑捉上,海龟交易法则使用了一个非常重要的技术指标—唐奇安通道(Donchian channel)。这个通道很类似布林通道(Bollinger Bands),只是在具体计算方式上有些不一样。

唐奇安通道指标是Richard Donchian发明的,由3条不同颜色的曲线组成,该指标用周期(一般都是20,有的平台系统设置时可以改变的,有的则设置的不可以)内的最高价和最低价来显示市场价格的波动性,当其通道窄时表示市场波动较小,反之通道宽则表示市场波动比较大。

当价格冲破该通道的上轨道时,就是可能的买信号;反之,冲破下轨时就是可能的卖信号。唐奇安通道的各项指标的计算方法为:

上轨=Max(最高低,n), n日最高价的最大值
下轨=Min(最低价,n), n日最低价的最小值
中轨=(上轨+下轨)/2

在金融领域的多因子分析框架内,这个策略对突破后的价格走势预测就是基于动量因子(momentum)的有效性假设。当然,这个因子的有效性也确实被严格地验证过,并作为Fama-French三因素模型的补充,被广泛地应用于金融市场。

当然,我们可以进行优化,使用更合理的趋势突破型指标。

三、交易原理

那么,既然动量因子是已经公开并普遍使用的因子,那么海龟交易法则是凭什么能够脱颖而出呢?答案很简单,海龟交易法则定义了一整套非常严谨的仓位控制、止盈止损的规则。对于币市的大涨大跌行情,海龟交易法则正是应付这种极端行情的利器。下面我们来一一剖析。

  1. 仓位的基本单位N

海龟法则的加仓原则是定义好一个小单位(Unit),使得该仓位的预期价值波动与总净资产的1%对应。也就是说,如果买入了这1个小单位的资产,那当天该仓位的市值变动幅度不会超过总净资产的1%。

那么,如何定义这个小单位?又如何预估这个小单位能带来的价值波动呢?首先,在预估这个小单位带来的价值波动(该价值波动被称为N)上,海龟策略使用了对历史的价格波动进行统计的方法。具体计算公式如下:

**TrueRange=Max(High−Low, High−PreClose, PreClose−Low)
N =(前19日的N值之和+当时的TrueRange)/20**

其中,High表示当日最高价,Low表示当日最低价,PreClose表示前一日收盘价。

我们可以从定义上看出,N值确实能很恰当地表达该资产在价格上的最近波动幅度。

这样,一个Unit就应该是这样计算出来的:
Unit = (1%*Total_net)/N, total_net就是总资产净值
可以看出,一个Unit的资产的价格波动幅度 = 总净资产的1%

  1. 什么时候建仓

建仓的动作来自于趋势突破信号的产生。如果当前价格冲破上轨,就产生了一个买的建仓信号,如果当前价格跌破下轨,就产生了一个卖空的建仓信号(如果该市场支持卖空,某些数字资产市场是支持借币卖空的!)
初始建仓的大小 = 1个Unit

  1. 什么时候加仓

如果开的底仓是多仓且资产的价格在上一次建仓(或者加仓)的基础上又上涨了0.5N,就再加一个Unit的多仓;
如果开的底仓是空仓且资产的价格在上一次建仓(或者加仓)的基础上又下跌了0.5N,就再加一个Unit的空仓。
我们看到,海龟策略其实是一个追涨杀跌的策略的。

  1. 怎么做动态止损

如果开的底仓是多仓且资产的价格在上一次建仓(或者加仓)的基础上又下跌了2N,就卖出全部头寸止损;
如果开的底仓是空仓且资产的价格在上一次建仓(或者加仓)的基础上又上涨了2N,就buy cover全部的头寸止损。
当然,用户可以自定义动态止损方案,比如下跌了0.5N就开始部分平仓,而不用等到下跌了2N后才匆忙一次性清仓,毕竟冲击成本摆在那里。

  1. 怎么做止盈,可以自定义动态止盈吗

海龟法则里面,止盈信号是这样产生的:
如果开的底仓是多仓且当前资产价格跌破了10日唐奇安通道的下轨,就清空所有头寸结束策略;
如果开的底仓是空仓且当前资产价格升破了10日唐奇安通道的上轨,就清空所有头寸结束策略。
当然,用户可以自定义动态止盈方案,比如总净资产/初始净资产>1.5, 就止盈离场。

下面是我实现的代码,较常规的海龟策略而言,我增加了每次开盘前有一个筛选股票的策略,我只会在市值最大的几只股票进行操作。

效果还行,跑过了沪深300指数:)

# 可以自己import我们平台支持的第三方python模块,比如pandas、numpy等。
import numpy as np
import pandas as pd
import talib
import math

# 在这个方法中编写任何的初始化逻辑。context对象将会在你的算法策略的任何方法之间做传递。
def init(context):
    # 在context中保存全局变量
    context.s = []
    context.vis = {}
    context.last_buy_price = {} 
    #上一次买入价格
    context.hold_flag = {}
    #False 
    #是否持有头寸标志,即是否买入
    context.limit_unit = 4 
    #最多买入单元数
    context.unit = {}#0 
    #现在买入一单元股数
    context.add_time = {}#0 
    #买入次数 最多四次
    context.long_time = 55
    context.short_time = 20
    context.ten_time = 10

def CalcATR(high,low,close):
    TR_List = []
    for i in range(1,min(21,len(high),len(close))):
        TR = max(high[i]-low[i],abs(high[i]-close[i-1]),abs(close[i-1]-low[i]))
        TR_List.append(TR)
    ATR = np.array(TR_List).mean()
    return ATR

def CalcUnit(perValue,ATR):#计算unit

    return int((perValue/ATR)/100)*30
    # 乘了个系数,莽

def IN_OR_OUT(high,low,price): #判断入场还是离场
    up = max(high)
    down = min(low) 
    if price>up:
        return 1
    elif price<down:
        return -1
    else:
        return 0 

def Add_OR_Stop(price,lastprice,ATR):
    if price >= lastprice + 0.5*ATR:
        return 1
    elif price <= lastprice - 0.5*ATR:
        return -1
    else:
        return 0    

# before_trading此函数会在每天策略交易开始前被调用,当天只会被调用一次
# 选股策略,选三个满足特征的几个,然后来进行排序
def before_trading(context): # 获得stock list
    num_stocks = 5
    fundamental_df = get_fundamentals(
        query(
            fundamentals.eod_derivative_indicator.pb_ratio,
            fundamentals.eod_derivative_indicator.pe_ratio,
            fundamentals.eod_derivative_indicator.peg_ratio,
        )
        .filter(
            fundamentals.eod_derivative_indicator.pe_ratio<60
        )
        .filter(
            fundamentals.eod_derivative_indicator.pb_ratio<5
        )
        .filter(
            fundamentals.eod_derivative_indicator.peg_ratio<0.9
        )
        .order_by(
            fundamentals.eod_derivative_indicator.market_cap.desc()
        ).limit(
            num_stocks
        )
    )
    '''
    fundamental_df = get_fundamentals(
        query(
            fundamentals.eod_derivative_indicator.market_cap
        ).filter(
            fundamentals.eod_derivative_indicator.market_cap<5e10
        ).order_by(
            fundamentals.eod_derivative_indicator.market_cap.asc()   
        ).limit(
            num_stocks
        )
    )
    '''
    drop_list = []
    for stock in context.s:
        if context.add_time.get(stock,0) == 0:
            drop_list.append(stock)
    for stock in drop_list:
        init_stock(context,stock)
    if len(context.s) < 20:
        for stock in fundamental_df:
            if stock not in context.vis:
                context.s.append(stock)
                context.vis[stock] = 1
    logger.info(str(context.s))


# 你选择的证券的数据更新将会触发此段逻辑,例如日或分钟历史数据切片或者是实时数据切片更新
def handle_bar(context, bar_dict):
    # 开始编写你的主要的算法逻辑
    stock_list = []
    for stock in context.s:
        stock_list.append(stock)
    for stock in stock_list:
        do_trade(context,stock)

def init_stock(context,stock):
    context.s.remove(stock)
    if stock in context.last_buy_price:
        del context.last_buy_price[stock]
    if stock in context.hold_flag:
        del context.hold_flag[stock]
    if stock in context.unit:
        del context.unit[stock] 
    if stock in context.add_time:
        del context.add_time[stock]
    if stock in context.vis:
        del context.vis[stock]

def do_trade(context,stock):
    high_price = history_bars(stock,context.short_time+1, '1d', 'high')
    low_price = history_bars(stock,context.short_time+1, '1d', 'low')
    close_price = history_bars(stock,context.short_time+2, '1d', 'close')
    close_price_atr = close_price[:-1]

    high_price_55 = history_bars(stock,context.long_time+1, '1d', 'high')
    low_price_55 = history_bars(stock,context.long_time+1, '1d', 'low')
    high_price_10 = history_bars(stock,context.ten_time+1, '1d', 'high')
    low_price_10 = history_bars(stock,context.ten_time+1, '1d', 'low')
    price_minute = history_bars(stock,context.short_time,'1m','close')
    price = price_minute[-1] #股票最新价格
    # 计算ATR
    ATR = CalcATR(high_price,low_price,close_price_atr)
    out = IN_OR_OUT(high_price_55,low_price_55,price) #判断入场还是离场

    if out==1 and context.hold_flag.get(stock,False)==False: #入场
        value = context.portfolio.cash*0.01  # 我的资金的1%
        context.unit[stock] = CalcUnit(value,ATR)
        order_shares(stock, context.unit[stock])
        context.add_time[stock]=1
        context.hold_flag[stock]=True
        context.last_buy_price[stock]=price
    elif out==-1 and context.hold_flag.get(stock,False) ==True: #离场
        order_percent(stock,-1)
        init_stock(context,stock) #重新初始化

    #判断加仓还是止损
    if context.hold_flag.get(stock,False)==True :
        temp = Add_OR_Stop(price,context.last_buy_price.get(stock,0),ATR)
        if temp==1 and context.add_time.get(stock,4) < context.limit_unit:#加仓
            order_shares(stock,context.unit[stock])
            context.last_buy_price[stock]=price
            context.add_time[stock]+=1
        elif temp==-1:
            if context.add_time.get(stock,0) == 1:
                order_percent(stock,-1)
                init_stock(context,stock)
            else:
                order_shares(stock,context.unit.get(stock,0))
                context.add_time[stock]-=1


# after_trading函数会在每天交易结束后被调用,当天只会被调用一次
def after_trading(context):
    pass

猜你喜欢

转载自blog.csdn.net/qscqesze/article/details/79965820