北上资金策略研究

ID:383995

本文基于华泰金工2020年8月研报《华泰证券金工量化资产配置7月月报:北向资金走向预示市场短期或震荡》,构建基于北向资金的股市择时策略,并进行历史回测。结果表明,北向资金对于判断沪深 300指数涨跌具有较好的预示作用。

一、调用相关库

tushare为数据来源,numpy、pandas为数据处理库,matplotlib、seaborn用于数据可视化,empyrical用于策略评价

import tushare as ts
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from secret import *
import empyrical
# secret.py中存放tstoken和图片保存路径save_path
sns.set()
pro = ts.pro_api(tstoken)

二、获取相关数据

# 获取交易日历,返回格式:array
def get_cal_date(start, end):
    cal_date = pro.trade_cal(start_date=start, end_date=end)
    cal_date = cal_date[cal_date.is_open == 1]
    dates = cal_date.cal_date.values
    return dates


def get_north_money(start, end):
    dates = get_cal_date(start, end)
    df = pd.DataFrame()
    # 因为每次数据库只能返回300条数据,分段调取数据
    for i in range(0, len(dates), 300):
        if len(dates) - (i + 1) > 300:
            d = pro.moneyflow_hsgt(start_date=dates[i], end_date=dates[i + 299])
            d.sort_index(ascending=False, inplace=True)
            d.reset_index(inplace=True, drop=True)
        else:  # 对于最后一段日期
            d = pro.moneyflow_hsgt(start_date=dates[i], end_date=dates[-1])
            d.sort_index(ascending=False, inplace=True)
            d.reset_index(inplace=True, drop=True)
        df = df.append(d, ignore_index=True)
    df.trade_date = pd.to_datetime(df.trade_date)
    return df


def get_index_data(code, start, end):
    index_df = pro.index_daily(ts_code=code, start_date=start, end_date=end)
    index_df.sort_index(ascending=False, inplace=True)
    index_df.reset_index(inplace=True, drop=True)
    index_df.trade_date = pd.to_datetime(index_df.trade_date)
    return index_df


# code:指数代码
# start1:获取数据起始时间
# end1:获取数据终止时间
def get_total_data(code, start1, end1):
    north_df = get_north_money(start1, end1)
    index_df = get_index_data(code, start1, end1)
    total_data = pd.merge(index_df, north_df, on='trade_date', how='left')
    total_data.fillna(0, inplace=True)  # 用0来替代
    return total_data[['trade_date', 'open', 'high', 'close', 'low', 'north_money']]

三、 策略原理:采取布林带方法

当日北上资金流入规模突破上轨时,以第二天开盘价进行全仓买入
当日北上资金流入规模突破下轨时,以第二天开盘价进行全仓卖出
手续费:双边千分之三
# 输入参数:
# data:包含北向资金和指数价格数据
# window:移动窗口
# stdev:几倍标准差
# cost:手续费


def North_Strategy(df, window, stdev_n, cost):
    # 布林带参数
    # 中轨和标准差
    df = df.copy()
    df['mid'] = df['north_money'].rolling(window).mean()
    stdev = df['north_money'].rolling(window).std()
    # 上下轨
    df['upper'] = df['mid'] + stdev_n * stdev
    df['lower'] = df['mid'] - stdev_n * stdev

    # 当天收益率
    df['ret'] = df.close / df.close.shift(1) - 1
    df.dropna(inplace=True)

    # 设计买卖信号,1表示全仓,0表示空仓
    df['signal'] = np.NaN
    # 当日北向资金突破上轨线发出买入信号
    df.loc[df['north_money'] > df['upper'], 'signal'] = 1
    # 当日北向资金跌破下轨线发出卖出信号
    df.loc[df['north_money'] < df['lower'], 'signal'] = 0

    # 某日的position为前一日的signal
    df['position'] = df['signal'].shift(1)
    # 其余时间的position以上一个position进行填充
    df.fillna(method='ffill', inplace=True)
    # 由于布林带的rolling计算开始会有一部分空值,记为空仓
    df['position'].fillna(0, inplace=True)

    # 根据交易信号和仓位计算策略的每日收益率
    df['capital_ret'] = np.NaN
    # 初始收益率记为0
    df.loc[df.index[0], 'capital_ret'] = 0
    # 买卖按照成交金额扣除手续费
    # 第二天开盘价买入
    df.loc[df['position'] > df['position'].shift(1), 'capital_ret'] = \
        (df.close - df.open - df.open * cost) / df.open
    # 第二天开盘价卖出
    df.loc[df['position'] < df['position'].shift(1), 'capital_ret'] = \
        (df.open - df.close.shift(1) - df.open * cost) / df.close.shift(1)
    # 当仓位不变时,当天的capital是当天的涨幅乘上持有头寸
    df.loc[df['position'] == df['position'].shift(1), 'capital_ret'] = \
        df['ret'] * df['position']
    df.set_index('trade_date', inplace=True)
    return df

四、策略回测

这里调用了empyrical库,年化收益率采取IRR复利年化

# df:经过处理后的策略数据
# start2:回测时间起点
# end2:回测时间终点
# 返回回测的数据
def strategy_evaluation(df, start2, end2):
    # 选取所关注的时间范围
    df2 = df.loc[(df.index > pd.to_datetime(start2)) &
                 (df.index < pd.to_datetime(end2)), :]
    df2 = df2.copy()
    # 计算策略、指数的净值
    df2['strategy_net_value'] = (df2.capital_ret + 1.0).cumprod()
    df2['index_net_value'] = (df2.ret + 1.0).cumprod()
    # 导出数组
    arr = df2['capital_ret'].values
    arr_index = df2['ret'].values

    index_r = empyrical.cum_returns_final(arr_index)
    cum_r = empyrical.cum_returns_final(arr)
    alpha, beta = empyrical.alpha_beta(arr, arr_index)
    annualized_r = empyrical.annual_return(arr)
    annualized_v = empyrical.annual_volatility(arr)
    drawback = empyrical.max_drawdown(arr)
    # 无风险收益,默认0
    rf = 0
    sharpe_r = empyrical.sharpe_ratio(arr, rf)
    print('指数累计收益率为:%.2f%%' % (index_r * 100))

    print('策略alpha=%.2f,beta=%.2f' % (alpha, beta))
    print('策略累计收益率为:%.2f%%' % (cum_r * 100))
    print('年化收益率为:%.2f%%' % (annualized_r * 100))
    print('年化波动率为:%.2f%%' % (annualized_v * 100))
    print('最大回撤为:%.2f%%' % (drawback * 100))
    print('夏普比率为:%.2f' % sharpe_r)
    print('收益回撤比为:%.2f' % (annualized_r / drawback))
    return df2

 

五、策略可视化 

def plot_strategy(d,sfpath):
    plt.figure(figsize=(10, 6))
    plt.plot(d['strategy_net_value'], 'b', label='Strategy')
    plt.plot(d['index_net_value'], 'y', label='Index')
    # 买卖点标记
    plt.plot(d[d['position'] > d['position'].shift(1)].index_net_value, 'r.')
    plt.plot(d[d['position'] < d['position'].shift(1)].index_net_value, 'k.')
    plt.legend()
    plt.title('North Money Strategy')
    plt.savefig(sfpath)
    plt.show()

六、主函数运行

if __name__ == '__main__':
    '''
    index_code:指数代码
    start_time1:选取数据时间起点
    end_time1:选取数据时间终点
    start_time2:回测时间起点
    end_time2:回测时间终点
    save_fig_path:图片保存路径的文件夹
    '''
    index_code = '000300.SH'
    start_time1 = '20150101'
    end_time1 = '20210413'
    start_time2 = '20200101'
    end_time2 = '20210413'
    # save_fig_path = save_path+'North_money_strategy.jpg'
    data = get_total_data(index_code, start_time1, end_time1)
    strategy_data = North_Strategy(data, 252, 1.5, 0.003)
    print(strategy_data.tail())
    print('回测结果为:')
    strategy_data2 = strategy_evaluation(strategy_data, start_time2, end_time2)
    print('\n\n红点为买,黑点为卖,策略净值可视化如图:')
    print('回测时间范围:%s-%s' % (start_time2, end_time2))
    plot_strategy(strategy_data2, save_fig_path)

 七、回测结果

可以看出,相对沪深300指数有较好的择时效果

 

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值