择时策略1:一个金叉死叉(python)

学习目标:择时策略1:一个金叉买入死叉卖出策略

学习内容:

1:导入必要的库

import pandas as pd
pd.set_option('expand_frame_repr', False)  # 当列太多时不换行
import matplotlib.pyplot as plt
import mplfinance as mpf #替换 import matplotlib.finance as mpf
import matplotlib#显示汉字
matplotlib.rc("font",family='YouYuan')#显示汉字

2:导入数据

# 导入数据
def import_stock_data(stock_code):
    """
    只导入如下字段:'交易日期', '股票代码', '开盘价', '最高价', '最低价', '收盘价', '涨跌幅'
    最终输出结果按照日期排序
    :param stock_code:
    :return:
    """
    df = pd.read_csv(stock_code + '.csv', encoding='gbk')
    #print(df)
    
    df = df[['交易日期','股票代码', '开盘价', '最高价', '最低价', '收盘价', '涨跌幅']]
    df.sort_values(by=['交易日期'], inplace=True)
    df['交易日期'] = pd.to_datetime(df['交易日期'])
    df.reset_index(inplace=True, drop=True)

    return df

3:计算复权价

# 计算复权价
def cal_fuquan_price(input_stock_data, fuquan_type='后复权'):
    """
    计算复权价
    :param input_stock_data:
    :param fuquan_type:复权类型,可以是'后复权'或者'前复权'
    :return:
    """
    # 创建空的df
    df = pd.DataFrame()

    # 计算复权收盘价
    num = {'后复权': 0, '前复权': -1}
    price1 = input_stock_data['收盘价'].iloc[num[fuquan_type]]
    df['复权因子'] = (1.0 + input_stock_data['涨跌幅']).cumprod()
    price2 = df['复权因子'].iloc[num[fuquan_type]]
    df['收盘价_' + fuquan_type] = df['复权因子'] * (price1 / price2)

    # 计算复权的开盘价、最高价、最低价
    df['开盘价_' + fuquan_type] = input_stock_data['开盘价'] / input_stock_data['收盘价'] * df['收盘价_' + fuquan_type]
    df['最高价_' + fuquan_type] = input_stock_data['最高价'] / input_stock_data['收盘价'] * df['收盘价_' + fuquan_type]
    df['最低价_' + fuquan_type] = input_stock_data['最低价'] / input_stock_data['收盘价'] * df['收盘价_' + fuquan_type]

    return df[[i + '_' + fuquan_type for i in ['开盘价', '最高价', '最低价', '收盘价']]]

4:均线策略主体

# 普通均线策略
def signal_ma(df, ma_short=5, ma_long=15):
    """
    均线策略:
    当短期均线由下向上穿过长期均线的时候,第二天以开盘价全仓买入并在之后一直持有股票。
    当短期均线由上向下穿过长期均线的时候,第二天以开盘价卖出全部股票并在之后一直空仓,直到下一次买入。

    :param df:
    :param ma_short: 短期均线
    :param ma_long: 长期均线
    :return:
    """

    # ===计算均线
    df['ma_short'] = df['收盘价_后复权'].rolling(ma_short, min_periods=1).mean()
    df['ma_long'] = df['收盘价_后复权'].rolling(ma_long, min_periods=1).mean()

    # ===找出买入信号
    # 当天的短期均线大于等于长期均线
    condition1 = (df['ma_short'] >= df['ma_long'])
    # 上个交易日的短期均线小于长期均线
    condition2 = (df['ma_short'].shift(1) < df['ma_long'].shift(1))
    # 将买入信号当天的signal设置为1
    df.loc[condition1 & condition2, 'signal'] = 1

    # ===找出卖出信号
    # 当天的短期均线小于等于长期均线
    condition1 = (df['ma_short'] <= df['ma_long'])
    # 上个交易日的短期均线大于长期均线
    condition2 = (df['ma_short'].shift(1) > df['ma_long'].shift(1))
    # 将买入信号当天的signal设置为0
    df.loc[condition1 & condition2, 'signal'] = 0

    # 将无关的变量删除
    df.drop(['ma_short', 'ma_long'], axis=1, inplace=True)

    return df

# 加强均线斜率策略
def signal_ma_d(df, ma_short=5, ma_long=15):
    """
    均线策略:
    当短期均线由下向上穿过长期均线的时候,第二天以开盘价全仓买入并在之后一直持有股票。
    当短期均线由上向下穿过长期均线的时候,第二天以开盘价卖出全部股票并在之后一直空仓,直到下一次买入。

    :param df:
    :param ma_short: 短期均线
    :param ma_long: 长期均线
    :return:
    """

    # ===计算均线
    df['ma_short'] = df['收盘价_后复权'].rolling(ma_short, min_periods=1).mean()
    df['ma_long'] = df['收盘价_后复权'].rolling(ma_long, min_periods=1).mean()
    
    # ===计算均线变化率
    df['ma_short_d']=df['ma_short']-df['ma_short'].shift(1)
    df['ma_long_d']=df['ma_long']-df['ma_long'].shift(1)
    
    # ===找出买入信号
    # 当天的短期均线大于等于长期均线
    condition1 = (abs(df['ma_short_d']) >= df['ma_long'])
    

    # ===找出买入信号
    # 当天的短期均线大于等于长期均线
    condition1 = (df['ma_short'] >= df['ma_long'])
    # 上个交易日的短期均线小于长期均线
    condition2 = (df['ma_short'].shift(1) < df['ma_long'].shift(1))
    # 将买入信号当天的signal设置为1
    df.loc[condition1 & condition2, 'signal'] = 1

    # ===找出卖出信号
    # 当天的短期均线小于等于长期均线
    condition1 = (df['ma_short'] <= df['ma_long'])
    # 上个交易日的短期均线大于长期均线
    condition2 = (df['ma_short'].shift(1) > df['ma_long'].shift(1))
    # 将买入信号当天的signal设置为0
    df.loc[condition1 & condition2, 'signal'] = 0

    # 将无关的变量删除
    df.drop(['ma_short', 'ma_long'], axis=1, inplace=True)

    return df

5:根据交易信号,计算每天的仓位

def position(df):
    """
    根据交易信号,计算每天的仓位
    :param df:
    :return:
    """
    # 由signal计算出实际的每天持有股票仓位
    df['pos'] = df['signal'].shift()
    df['pos'].fillna(method='ffill', inplace=True)

    # 将涨跌停时不得买卖股票考虑进来
    # 找出开盘涨停的日期
    cond_cannot_buy = df['开盘价'] > df['收盘价'].shift(1) * 1.097  # 今天的开盘价相对于昨天的收盘价上涨了9.7%
    # 将开盘涨停日、并且当天position为1时的'pos'设置为空值
    df.loc[cond_cannot_buy & (df['pos'] == 1), 'pos'] = None

    # 找出开盘跌停的日期
    cond_cannot_sell = df['开盘价'] < df['收盘价'].shift(1) * 0.903  # 今天的开盘价相对于昨天的收盘价下得了9.7%
    # 将开盘跌停日、并且当天position为0时的'pos'设置为空值
    df.loc[cond_cannot_sell & (df['pos'] == 0), 'pos'] = None

    # position为空的日期,不能买卖。position只能和前一个交易日保持一致。
    df['pos'].fillna(method='ffill', inplace=True)

    # 在position为空值的日期,将position补全为0
    df['pos'].fillna(value=0, inplace=True)

    return df

6:计算资金曲线,简单版本

# 计算资金曲线,简单版本
def equity_curve_simple(df):
    """
    最简单的计算资金曲线的方式,与实际不符合
    :param df:
    :return:
    """

    # ===计算实际资金曲线
    # 当当天空仓时,pos为0,资产涨幅为0
    # 当当天满仓时,pos为1,资产涨幅为股票本身的涨跌幅
    df['equity_change'] = df['涨跌幅'] * df['pos']
    # 根据每天的涨幅计算资金曲线
    df['equity_curve'] = (df['equity_change'] + 1).cumprod()

    return df

7:计算资金曲线,实用版本

# 计算资金曲线,实用版本
def equity_curve(df, initial_money=1000000, slippage=0.01, c_rate=5.0/10000, t_rate=1.0/1000):
    """
    :param df:
    :param initial_money: 初始资金,默认为1000000元
    :param slippage: 滑点,默认为0.01元
    :param c_rate: 手续费,commission fees,默认为万分之5
    :param t_rate: 印花税,tax,默认为千分之1
    :return:
    """

    # ===第一天的情况
    df.at[0, 'hold_num'] = 0  # 持有股票数量
    df.at[0, 'stock_value'] = 0  # 持仓股票市值
    df.at[0, 'actual_pos'] = 0  # 每日的实际仓位
    df.at[0, 'cash'] = initial_money  # 持有现金现金
    df.at[0, 'equity'] = initial_money  # 总资产 = 持仓股票市值 + 现金

    # ===第一天之后每天的情况
    for i in range(1, df.shape[0]):

        # 前一天持有的股票的数量
        hold_num = df.at[i - 1, 'hold_num']

        # 若发生除权,需要调整hold_num
        if abs((df.at[i, '收盘价'] / df.at[i - 1, '收盘价'] - 1) - df.at[i, '涨跌幅']) > 0.001:
            stock_value = df.at[i - 1, 'stock_value']
            last_price = df.at[i, '收盘价'] / (df.at[i, '涨跌幅'] + 1)
            hold_num = stock_value / last_price
            hold_num = int(hold_num)

        # 判断是否需要调整仓位
        # 需要调整仓位
        if df.at[i, 'pos'] != df.at[i - 1, 'pos']:

            # 昨天的总资产 * 今天的仓位 / 今天的收盘价,得到需要持有的股票数
            theory_num = df.at[i - 1, 'equity'] * df.at[i, 'pos'] / df.at[i, '开盘价']
            # 对需要持有的股票数取整
            theory_num = int(theory_num)  # 向下取整数

            # 判断加仓还是减仓
            # 加仓
            if theory_num >= hold_num:
                # 计算实际需要买入的股票数量
                buy_num = theory_num - hold_num
                # 买入股票只能整百,对buy_num进行向下取整百
                buy_num = int(buy_num / 100) * 100

                # 计算买入股票花去的现金
                buy_cash = buy_num * (df.at[i, '开盘价'] + slippage)
                # 计算买入股票花去的手续费,并保留2位小数
                commission = round(buy_cash * c_rate, 2)
                # 不足5元按5元收
                if commission < 5 and commission != 0:
                    commission = 5
                df.at[i, '手续费'] = commission

                # 计算当天收盘时持有股票的数量和现金
                df.at[i, 'hold_num'] = hold_num + buy_num  # 持有股票
                df.at[i, 'cash'] = df.at[i - 1, 'cash'] - buy_cash - commission  # 剩余现金

            # 减仓
            else:
                # 计算卖出股票数量,卖出股票可以不是整数,不需要取整百。
                sell_num = hold_num - theory_num

                # 计算卖出股票得到的现金
                sell_cash = sell_num * (df.at[i, '开盘价'] - slippage)
                # 计算手续费,不足5元按5元收并保留2位小数
                commission = round(max(sell_cash * c_rate, 5), 2)
                df.at[i, '手续费'] = commission
                # 计算印花税,保留2位小数。历史上有段时间,买入也会收取印花税
                tax = round(sell_cash * t_rate, 2)
                df.at[i, '印花税'] = tax

                # 计算当天收盘时持有股票的数量和现金
                df.at[i, 'hold_num'] = hold_num - sell_num  # 持有股票
                df.at[i, 'cash'] = df.at[i - 1, 'cash'] + sell_cash - commission - tax  # 剩余现金

        # 不需要调仓
        else:
            # 计算当天收盘时持有股票的数量和现金
            df.at[i, 'hold_num'] = hold_num  # 持有股票
            df.at[i, 'cash'] = df.at[i - 1, 'cash']  # 剩余现金

        # 计算当天的各种数据
        df.at[i, 'stock_value'] = df.at[i, 'hold_num'] * df.at[i, '收盘价']  # 剩余现金
        df.at[i, 'equity'] = df.at[i, 'cash'] + df.at[i, 'stock_value']  # 总资产
        df.at[i, 'actual_pos'] = df.at[i, 'stock_value'] / df.at[i, 'equity']  # 实际仓位

    return df

8:主体第一个模块:数据准备

# =====第一个模块:数据准备
# ===读入数据
code = 'sz300001'
#print(code)
df = import_stock_data(code)

# 判断股票上市是否满一定时间,若不满足,则不运行策略
if df.shape[0] < 250:
    print ('股票上市未满一年,不运行策略')
    exit()
    
# ===计算复权价
fuquan_type = '后复权'
df[[i + '_' + fuquan_type for i in ['开盘价', '最高价', '最低价', '收盘价']]] = cal_fuquan_price(df, fuquan_type)

9:主体2

# =====第二个模块:产生交易信号
# ===根据均线策略产生交易信号
df = signal_ma(df, ma_short=5, ma_long=15)
# =====第三个模块:根据交易信号计算每天的仓位
# ===计算仓位
df = position(df)
# ==截取上市一年之后的交易日
#df = df.iloc[250-1:]
# 将第一天的仓位设置为0
#df.iloc[0, -1] = 0
# =====第四个模块:根据仓位计算资金曲线
# ===简单方式
df_easy=equity_curve_simple(df)
# ===实际方式
df = df[['交易日期', '股票代码', '开盘价', '最高价', '最低价', '收盘价', '涨跌幅', 'pos']]
df.reset_index(inplace=True, drop=True)
df_difficult = equity_curve(df, initial_money=1000000, slippage=0.01, c_rate=5.0/10000, t_rate=1.0/1000)

10:主体3-绘图

# 对象式绘图
# pyplot模块中的figure()函数创建名为fig的Figure对象
fig = plt.figure(figsize=(12, 8))

# 在Figure对象中创建一个Axes对象,每个Axes对象即为一个绘图区域
ax = fig.add_subplot(111)
ax.plot(df_difficult['交易日期'],df_difficult['equity'], '--r', lw=1)
ax.set_ylabel('Y 轴-资产',fontsize=15)
ax.set_xlabel('X 轴-时间',fontsize=15)
ax.axhline(y=1000000, c='blue', ls=':', lw=2)#均值水平线
# 设置标题
ax.set_title(u"均线金叉死叉交易策略",fontsize=25)
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值