欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

金融量化-基于K线形态锤子线的趋势跟踪策略

程序员文章站 2022-03-21 13:04:24
...

1.基本原理

1.1 K线部位定义:

实体:某一根K线开盘价和收盘价之间部分;
上影线:某一根K线最高价到实体上端的部分;
下影线:某一根K线最低价到实体下端的部分;

1.2 锤子线定义

实体处于整个价格区间上端,实体颜色本身不影响;
下影线长度至少达到实体高度的2倍;
上影线很短;

1.3 策略原理

下跌过程中,当某一日出现锤子线,意味着当天行情先继续下跌后出现大幅反弹,行情可能由此反转;
由此以观察期均线识别趋势下跌,以下跌趋势中出现锤子线作为开仓信号; 采用移动止损方式进行止损构建此策略;

1.4 止损条件

当天最低价 < max(均价-观察期内一定倍数的标准差,开仓价-开仓时标准差);

1.5 形态要点:

在出现锤头线(锤子线)之前,股价需经过一段时间的下跌后,处于下跌趋势中,此时出现此形态才具有参考意义;
锤头实体越小,下影线越长,止跌作用就越明显,参考价值越大;

2.策略实现

2.1 收集并计算所需数据

import pandas as pd
import numpy as np
import tushare as ts
code = '002398'         # 股票代码
body_size = 0.03        # 表示锤子实体大小上限,基准为当日开盘价,实体不能太大,波动范围限制在3%;
head_size = 0.5         # 表示锤子上影线长度上限,基准为下影线长度,上影线要短一点,不能超过下影线的的一半;
tail_size = 2           # 表示下影线与实体大小比值,下影线要大于实体两倍;
length = 10             # 表示观察期时间长短;
stoplose_trigger = 1    # 表示当价格偏离均线满足几倍标准差时止损
data.sort_index(ascending=True, inplace=True)
data.head()
open high close low volume amount
date
2012-01-04 6.64 6.80 6.40 6.39 283430.0 4564127.0
2012-01-05 6.38 6.40 5.76 5.76 820954.0 12005136.0
2012-01-06 5.71 5.83 5.70 5.43 972637.0 13330505.0
2012-01-09 5.69 5.97 5.94 5.57 536522.0 7710121.0
2012-01-10 5.94 6.27 6.21 5.94 1121594.0 17023694.0
data.reset_index(inplace=True)        #把索引设置成为默认;为了后面交易策略逻辑循环更方便一些;
data.head()
date open high close low volume amount
0 2012-01-04 6.64 6.80 6.40 6.39 283430.0 4564127.0
1 2012-01-05 6.38 6.40 5.76 5.76 820954.0 12005136.0
2 2012-01-06 5.71 5.83 5.70 5.43 972637.0 13330505.0
3 2012-01-09 5.69 5.97 5.94 5.57 536522.0 7710121.0
4 2012-01-10 5.94 6.27 6.21 5.94 1121594.0 17023694.0
data['pct_change'] = data['close'].pct_change()
data['ma'] = data['close'].rolling(length).mean()
data['std'] = data['close'].rolling(length).std()
del data['volume']
del data['amount']
data.tail()
date open high close low pct_change ma std
1188 2016-12-26 12.10 12.57 12.42 11.98 0.023908 12.141 0.161620
1189 2016-12-27 12.34 12.47 12.34 12.32 -0.006441 12.179 0.158986
1190 2016-12-28 12.34 12.45 12.38 12.31 0.003241 12.233 0.117289
1191 2016-12-29 12.41 12.59 12.49 12.27 0.008885 12.273 0.130559
1192 2016-12-30 12.41 12.55 12.50 12.27 0.000801 12.305 0.143778

由于实盘中当天的日线级别参考指标未实现,因此使用昨日参考指标指导当日交易,避免未来函数;

data['yes_ma'] = data['ma'].shift(1)          #昨天的mean和昨天的std;
data['yes_std'] = data['std'].shift(1)

2.2 识别锤子形态

计算实体,上影线,下影线

data['body'] = abs(data['open'] - data['close'])                   #计算K线实体;
data['head'] = data['high'] - data[['open', 'close']].max(axis = 1 )       #计算上影线,按行计算
data['tail'] = data[['open', 'close']].min(axis=1) - data['low']        #计算下影线
data.head()
date open high close low pct_change ma std yes_ma yes_std body head tail
0 2012-01-04 6.64 6.80 6.40 6.39 NaN NaN NaN NaN NaN 0.24 0.16 0.01
1 2012-01-05 6.38 6.40 5.76 5.76 -0.100000 NaN NaN NaN NaN 0.62 0.02 0.00
2 2012-01-06 5.71 5.83 5.70 5.43 -0.010417 NaN NaN NaN NaN 0.01 0.12 0.27
3 2012-01-09 5.69 5.97 5.94 5.57 0.042105 NaN NaN NaN NaN 0.25 0.03 0.12
4 2012-01-10 5.94 6.27 6.21 5.94 0.045455 NaN NaN NaN NaN 0.27 0.06 0.00

判断K线各部分是否符合锤子线要求

data['body_cond'] = np.where(data['body']/data['open'] < body_size, 1, 0)     #实体的大小比开盘价要小于3%,K线实体不能太大;
data['head_cond'] = np.where(data['tail']==0, False, data['head'] / data['tail'] < head_size)   #上影线不能比下影线的一半长;
# 当尾部长度为0,为防止判断除法报错,两步判断;
# data['head_cond'] = np.where(data['head']/data['tail'] < head_size, 1, 0)   有可能tail = 0 
data['tail_cond'] = np.where(data['body']==0, True, (data['tail']/data['body']) > tail_size)    #下影线要比实体的两倍更长才满足条件;
data.head()
date open high close low pct_change ma std yes_ma yes_std body head tail body_cond head_cond tail_cond
0 2012-01-04 6.64 6.80 6.40 6.39 NaN NaN NaN NaN NaN 0.24 0.16 0.01 0 False False
1 2012-01-05 6.38 6.40 5.76 5.76 -0.100000 NaN NaN NaN NaN 0.62 0.02 0.00 0 False False
2 2012-01-06 5.71 5.83 5.70 5.43 -0.010417 NaN NaN NaN NaN 0.01 0.12 0.27 1 True True
3 2012-01-09 5.69 5.97 5.94 5.57 0.042105 NaN NaN NaN NaN 0.25 0.03 0.12 0 True False
4 2012-01-10 5.94 6.27 6.21 5.94 0.045455 NaN NaN NaN NaN 0.27 0.06 0.00 0 False False

判断K线形态是否符合锤子线

data['hammer'] = data[['head_cond', 'body_cond', 'tail_cond']].all(axis=1)      #同时满足以上三个条件才是锤子K线;
data['hammer'].tail()
1188    False
1189    False
1190    False
1191    False
1192    False
Name: hammer, dtype: bool
data[data['hammer']].tail(10)
date open high close low pct_change ma std yes_ma yes_std body head tail body_cond head_cond tail_cond hammer
1107 2016-08-24 12.08 12.09 12.06 11.99 0.002494 12.039 0.168091 12.031 0.168882 0.02 0.01 0.07 1 True True True
1111 2016-08-30 11.99 12.01 11.99 11.93 0.004188 12.038 0.109118 12.051 0.110499 0.00 0.02 0.06 1 True True True
1116 2016-09-06 12.29 12.35 12.34 12.10 0.007347 12.038 0.147558 12.007 0.102854 0.05 0.01 0.19 1 True True True
1129 2016-09-27 11.88 11.96 11.89 11.70 -0.000840 12.175 0.166483 12.242 0.173705 0.01 0.07 0.18 1 True True True
1136 2016-10-13 12.40 12.40 12.39 12.27 -0.000806 12.106 0.209772 12.098 0.198986 0.01 0.00 0.12 1 True True True
1140 2016-10-19 12.55 12.62 12.50 12.36 -0.001597 12.294 0.204407 12.234 0.224311 0.05 0.07 0.14 1 True True True
1144 2016-10-25 12.59 12.64 12.61 12.49 0.000000 12.482 0.098748 12.449 0.106087 0.02 0.03 0.10 1 True True True
1145 2016-10-26 12.60 12.63 12.62 12.46 0.000793 12.504 0.102870 12.482 0.098748 0.02 0.01 0.14 1 True True True
1167 2016-11-25 13.28 13.36 13.16 12.91 -0.009036 13.346 0.226137 13.333 0.241249 0.12 0.08 0.25 1 True True True
1179 2016-12-13 11.89 12.02 11.96 11.74 0.007582 12.556 0.348718 12.657 0.299742 0.07 0.06 0.15 1 True True True

由于实盘中当天的日线级别参考指标未实现,因此应根据昨日是否满足锤子形态要求作为开仓信号

data['yes_hammer'] = data['hammer'].shift(1)

2.3 编写交易逻辑——循环法

flag = 0    # 持仓记录,1代码有仓位,0代表空仓;
for i in range(2*length, len(data)):     #从20天开始计算,因为前期数据无效;
    # 如果已持仓,判断是否止损
    if flag == 1:
        stoplose_price = max(data.loc[i, 'yes_ma'] - stoplose_trigger*data.loc[i, 'yes_std'],
                             long_open_price-long_open_delta) 
        # 当天价格低于止损价,则进行止损,一个是移动止损,一个是开仓时候的开仓和开仓价-1倍标准差;
        if data.loc[i, 'low'] < stoplose_price:  #接下来要做的都是止损的操作;
            flag = 0
#             data.loc[i, 'return'] = min(data.loc[i, 'open'], stoplose_price)/data.loc[i-1, 'close'] - 1 
            #计算清盘当天的收益;取min是因为,如果当天开盘价就小于了止损价,那么我们就要以开盘价就止损;
            #不然会导致策略收益高估;
            #收益计算时要除以前一天的收盘价;
            data.loc[i, 'return'] = stoplose_price/data.loc[i-1, 'close'] - 1 
            
           
        
        # 如果不满足止损条件,则继续持仓
        else:
            data.loc[i, 'return'] = data.loc[i, 'close']/data.loc[i-1, 'close'] - 1
            data.loc[i, 'trade_mark'] = 1    # 表示当天持仓



    # 如果未持仓,判断是否进行开仓
    else:
        # 判断是否为下降趋势,平均重心是下降的;锤子线开仓要满足形态和下降趋势;
        if data.loc[i-length, 'yes_ma'] > data.loc[i, 'yes_ma']:
            # 判断是否符合锤子形态
            if data.loc[i, 'yes_hammer']:
                # 更改持仓标记
                flag = 1
                # 记录开仓时开仓价格及标准差:是为了做固定止损;
                long_open_price = data.loc[i, 'open']
                long_open_delta = data.loc[i, 'yes_std']
                # 计算当天收益率
                data.loc[i, 'return'] = data.loc[i, 'close']/data.loc[i, 'open'] - 1  #以产生信号之后的第二天开盘价开仓;
               

2.4 计算策略收益率

data['return'].fillna(0, inplace=True)      #对大循环中未处理的:既没有持仓,也不满足开仓条件的日期进行处理,则让这些天的return都等于0;
data['strategy_return'] = (data['return'] + 1).cumprod()
data['stock_return'] = (data['pct_change'] + 1).cumprod()

2.5 绘图

import matplotlib
import matplotlib.pyplot as plt
matplotlib.style.use('ggplot')
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(1,1,1)
ax.plot(data.stock_return)
ax.plot(data.strategy_return)
plt.title(code)
plt.legend()
plt.show()

金融量化-基于K线形态锤子线的趋势跟踪策略

3. 策略改进和优化思考

  1. 考虑成交量的配合:在锤子线后面的一根K线如果放量的话,交易信号更可信;
  2. 考虑跟其他形态的结合,例如锤子线后面紧跟着一根大阳线,交易信号更可信;
  3. 考虑和其他技术指标的结合,配合技术指标一起进行条件选股;
相关标签: 金融量化