Python股票量化学习(3)——简单的策略回测

        搞一个简单的交易策略回测(难点的我也不会啊)。此策略基于布林通道,当股价低于布林通道下轨线且持仓为0时,以下轨线价格全仓买入(不关心风险,反正也不要本钱)。当股价突破布林线上轨且有持仓时,以上轨线价格清仓卖出。(期间也没有考虑交易的费用)

        首先,从MySQL数据库读取已经下载好的股票历史数据,我用的库名是stock_databases。股票就随便选一个。

from sqlalchemy import create_engine
import pandas as pd

def calculate_boll(code):

    # 用于数据读取的表字段
    columns = 'date, open, high, low, close, volume'

    # 连接数据库引擎对象
    engine = create_engine("mysql+pymysql://root:123456@localhost:3306/stock_databases?charset=utf8")

    # 读取数据
    df = pd.read_sql('SELECT {} FROM {}_{}'.format(columns, code[:2], code[3:]),
                     con=engine,index_col='date')

    print(df.info())


calculate_boll('sh.600478')

 读取的数据如下图Python股票量化学习(3)——简单的策略回测_第1张图片

数据类型在我保存到数据库之前就做了相应的处理,接着计算布林通道:

from sqlalchemy import create_engine
import pandas as pd

def calculate_boll(code):

    # 用于数据读取的表字段
    columns = 'date, open, high, low, close, volume'

    # 连接数据库引擎对象
    engine = create_engine("mysql+pymysql://root:123456@localhost:3306/stock_databases?charset=utf8")

    # 读取数据
    df = pd.read_sql('SELECT {} FROM {}_{}'.format(columns, code[:2], code[3:]),
                     con=engine,index_col='date')

    # 计算标准差
    standard_deviations = df['close'].rolling(20).std(ddof=1)

    # 添加布林通道的上轨、下轨和中轨
    df['md'] = round(df['close'].rolling(20).mean(), 2)
    df['upper'] = round(df['md'] + 2 * standard_deviations, 2)
    df['lower'] = round(df['md'] - 2 * standard_deviations, 2)

    return df

        有了布林通道的数据后,就可以通过比较最高价、最低价与上下轨的大小来确定交易信号。

import numpy as np
import pandas as pd
from sqlalchemy import create_engine


def trade_signal(code):
    """
    计算交易信号,当股价低于布林下轨时买入,高于上轨时卖出
    :param code: 股票代码
    :return: 包含交易信号的DataFrame
    """
    data = calculate_boll(code)
    # 新建一个DataFrame,以data的index为index
    df = pd.DataFrame(index=data.index)
    df[['price', 'upper', 'lower']] = data[['close', 'upper', 'lower']]

    # 比较最高价、最低价与BOLL上下轨的大小来确定突破信号
    df['buy_signal'] = np.where(data['low'] < data['lower'], True, False)
    df['sell_signal'] = np.where(data['high'] > data['upper'], True, False)

    # 初始化订单状态为0
    df['orders'] = 0

    # 初始仓位为0
    position = 0

    # 遍历数据表
    for i in range(len(df)):
        # 当买入信号为True且仓位为0时买入
        if df.buy_signal[i] and position == 0:
            # 买入指令为1
            df.orders.values[i] = 1
            # 仓位加1
            position = 1

        # 当卖出信号为True且仓位为1时卖出
        elif df.sell_signal[i] and position == 1:
            # 卖出指令为-1
            df.orders.values[i] = -1
            # 仓位清0
            position = 0

    return df

        有了交易指令,就可以买进卖出了。当指令为1且持仓为0时,以布林下轨价格全仓买入。当指令为-1且有持仓时,以布林上轨价格清仓卖出。

def calculate_income(code):
    """
    计算收益
    :param code: 股票代码
    :return: 计算后的DataFrame
    """

    data = trade_signal(code)
    # 初始最大可持仓量,可用资金为100000元
    max_position = 0
    money = 100000.0

    # 初始持仓股票手数、可用资金
    data['stock'] = 0
    data['money'] = 100000.0
    for i in range(len(data)):
        # 当买卖指令为1
        if data.orders.values[i] == 1:
            # 以下轨价全仓买入
            max_position = money // (data.lower.values[i] * 100)
            # 可用资金减去买入股票所花费的资金
            money = money - max_position * data.lower.values[i] * 100
        # 当买卖指令为-1
        elif data.orders.values[i] == -1:
            # 可用资金加上卖出股票所得到的资金
            money = money + max_position * data.upper.values[i] * 100
            # 持仓清零
            max_position = 0

        # 持仓手数、可用资金
        data['stock'].values[i] = max_position
        data['money'].values[i] = money

    # 账户资产总额
    data['total'] = data['stock'] * data['price'] * 100 + data['money']

    return data

查看一下对不对Python股票量化学习(3)——简单的策略回测_第2张图片

         如上图所示,2022年8月31日,交易指令为1,全仓买入,狠狠的干了一票。再看'total'列,账户总资产从最初的100000元变成了现在的560000+元(当然我没有考虑交易费用等等之类因素)。乍一看好像还不错,实际上行不行的通?用matplotlib可视化来看一看。

def plt_show(code):
    """
    数据可视化
    :param code:股票代码
    :return: None
    """
    # 用来正常显示中文字体
    plt.rcParams['font.sans-serif'] = ['KaiTi']
    # 解决负号显示为方块的问题
    plt.rcParams['axes.unicode_minus'] = False

    data = calculate_income(code)

    # 设置画布大小
    plt.figure(figsize=(10, 6))

    # 设置标签
    plt.title('{}布林策略收益图'.format(code))
    plt.xlabel('日期')
    plt.ylabel('股价')

    # 绘制股价、布林上轨和下轨的折线图
    plt.plot(data.index, data.price, color='black', label='股价')
    plt.plot(data.index, data.upper, color='blue', label='上轨')
    plt.plot(data.index, data.lower, color='pink', label='下轨')

    # 用红色正三角标记买入点
    plt.scatter(data.loc[data.orders == 1].index, data['lower'][data.orders == 1],
                marker='^', c='r', s=80, label='买入')
    # 用绿色倒三角标记卖出点
    plt.scatter(data.loc[data.orders == -1].index, data['upper'][data.orders == -1],
                marker='v', c='g', s=80,label='卖出')

    # 图例放在左上角
    plt.legend(loc='upper left')

    # 绘制双坐标
    plt.twinx()

    # 绘制收益折线图
    plt.plot(data.index, data.total, color='red', label='总资产')

    # 图例放在右上角
    plt.legend(loc='upper right')

    plt.show()

请看图Python股票量化学习(3)——简单的策略回测_第3张图片

这个。。。。看起来好乱啊!从图中可以看出这是近20年的数据。注意图中红线,从18年开始就脱离了股价噌噌往上,显得有些不真实,是不是我哪里计算错了?

再来放大看看Python股票量化学习(3)——简单的策略回测_第4张图片 这个回撤的幅度容易把心脏挤爆 

再换一个股票看看Python股票量化学习(3)——简单的策略回测_第5张图片

Python股票量化学习(3)——简单的策略回测_第6张图片这个看起来好很多,看起来也真实。 

 

完整代码:

from sqlalchemy import create_engine
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt

def calculate_boll(code):
    """
    计算BOLL通道
    :param code:股票代码
    :return:添加了BOLL数值的DataFrame
    """

    # 用于数据读取的表字段
    columns = 'date, open, high, low, close, volume'

    # 连接数据库引擎对象
    engine = create_engine("mysql+pymysql://root:123456@localhost:3306/stock_databases?charset=utf8")

    # 读取数据
    df = pd.read_sql('SELECT {} FROM {}_{}'.format(columns, code[:2], code[3:]),
                     con=engine, index_col='date')

    # 计算标准差
    standard_deviations = df['close'].rolling(20).std(ddof=1)

    # 添加布林通道的上轨、下轨和中轨.这里注意:下面的保留2位小数,因为python的缘故,并不能做到真正的四舍五入
    df['md'] = round(df['close'].rolling(20).mean(), 2)
    df['upper'] = round(df['md'] + 2 * standard_deviations, 2)
    df['lower'] = round(df['md'] - 2 * standard_deviations, 2)

    return df


def trade_signal(code):
    """
    计算交易信号,当股价低于布林下轨时买入,高于上轨时卖出
    :param code: 股票代码
    :return: 包含交易信号的DataFrame
    """
    data = calculate_boll(code)
    # 新建一个DataFrame,以data的index为index
    df = pd.DataFrame(index=data.index)
    df[['price', 'upper', 'lower']] = data[['close', 'upper', 'lower']]

    # 比较最高价、最低价与BOLL上下轨的大小来确定突破信号
    df['buy_signal'] = np.where(data['low'] < data['lower'], True, False)
    df['sell_signal'] = np.where(data['high'] > data['upper'], True, False)

    # 初始化订单状态为0
    df['orders'] = 0

    # 初始仓位为0
    position = 0

    # 遍历数据表
    for i in range(len(df)):
        # 当买入信号为True且仓位为0时买入
        if df.buy_signal[i] and position == 0:
            # 买入指令为1
            df.orders.values[i] = 1
            # 仓位加1
            position = 1

        # 当卖出信号为True且仓位为1时卖出
        elif df.sell_signal[i] and position == 1:
            # 卖出指令为-1
            df.orders.values[i] = -1
            # 仓位清0
            position = 0

    return df


def calculate_income(code):
    """
    计算收益
    :param code: 股票代码
    :return: 计算后的DataFrame
    """

    data = trade_signal(code)
    # 初始最大可持仓量,可用资金为100000元
    max_position = 0
    money = 100000.0

    # 初始持仓股票手数
    data['stock'] = 0
    data['money'] = 100000.0
    for i in range(len(data)):
        # 当买卖指令为1
        if data.orders.values[i] == 1:
            # 以下轨价全仓买入
            max_position = money // (data.lower.values[i] * 100)
            # 可用资金减去买入股票所花费的资金
            money = money - max_position * data.lower.values[i] * 100
        # 当买卖指令为-1
        elif data.orders.values[i] == -1:
            # 可用资金加上卖出股票所得到的资金
            money = money + max_position * data.upper.values[i] * 100
            # 持仓清零
            max_position = 0

        # 持仓手数、可用资金
        data['stock'].values[i] = max_position
        data['money'].values[i] = money

    # 账户资产总额
    data['total'] = data['stock'] * data['price'] * 100 + data['money']

    return data


def plt_show(code):
    """
    数据可视化
    :param code:股票代码
    :return: None
    """
    # 用来正常显示中文字体
    plt.rcParams['font.sans-serif'] = ['KaiTi']
    # 解决负号显示为方块的问题
    plt.rcParams['axes.unicode_minus'] = False

    data = calculate_income(code)

    # 设置画布大小
    plt.figure(figsize=(10, 6))

    # 设置标签
    plt.title('{}布林策略收益图'.format(code))
    plt.xlabel('日期')
    plt.ylabel('股价')

    # 绘制股价、布林上轨和下轨的折线图
    plt.plot(data.index, data.price, color='black', label='股价')
    plt.plot(data.index, data.upper, color='blue', label='上轨')
    plt.plot(data.index, data.lower, color='pink', label='下轨')

    # 用红色正三角标记买入点
    plt.scatter(data.loc[data.orders == 1].index, data['lower'][data.orders == 1],
                marker='^', c='r', s=80, label='买入')
    # 用绿色倒三角标记卖出点
    plt.scatter(data.loc[data.orders == -1].index, data['upper'][data.orders == -1],
                marker='v', c='g', s=80,label='卖出')

    # 图例放在左上角
    plt.legend(loc='upper left')

    # 绘制双坐标
    plt.twinx()

    # 绘制收益折线图
    plt.plot(data.index, data.total, color='red', label='总资产')

    # 图例放在右上角
    plt.legend(loc='upper right')

    plt.show()


plt_show('sz.002056')

 股市有风险,入市须谨慎!

这个粗糙的策略是极不靠谱的。

你可能感兴趣的:(股票量化,学习,python)