代码只能保证运行在ricequant上
海龟策略这里我就不着重介绍,感兴趣可以去买书自己看。
海龟法则的加仓原则是定义好一个小单位(Unit),使得该仓位的预期价值波动与总净资产的1%对应。
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个Unit
如果开的底仓是多仓且资产的价格在上一次建仓(或者加仓)的基础上又上涨了0.5N,就再加一个Unit的多仓;
如果开的底仓是空仓且资产的价格在上一次建仓(或者加仓)的基础上又下跌了0.5N,就再加一个Unit的空仓。
我们看到,海龟策略其实是一个追涨杀跌的策略的。
如果开的底仓是多仓且资产的价格在上一次建仓(或者加仓)的基础上又下跌了2N,就卖出全部头寸止损;
如果开的底仓是空仓且资产的价格在上一次建仓(或者加仓)的基础上又上涨了2N,就buy cover全部的头寸止损。
当然,用户可以自定义动态止损方案,比如下跌了0.5N就开始部分平仓,而不用等到下跌了2N后才匆忙一次性清仓,毕竟冲击成本摆在那里。
海龟法则里面,止盈信号是这样产生的:
如果开的底仓是多仓且当前资产价格跌破了10日唐奇安通道的下轨,就清空所有头寸结束策略;
如果开的底仓是空仓且当前资产价格升破了10日唐奇安通道的上轨,就清空所有头寸结束策略。
当然,用户可以自定义动态止盈方案,比如总净资产/初始净资产>1.5, 就止盈离场。
下面是我实现的代码,较常规的海龟策略而言,我增加了每次开盘前有一个筛选股票的策略,我只会在市值最大的几只股票进行操作。
10年回撤效果。。。可喜可贺,跑过了沪深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 pricereturn -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