pyecharts绘制K线的记录

发布时间 2023-09-20 22:50:16作者: C羽言
import tushare as ts
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pyecharts.charts import *
from pyecharts import options as opts
from pyecharts.globals import ThemeType

pd.set_option('expand_frame_repr', False)
pd.set_option('display.max_rows',None)

token = 'd6e7a61d3655208dd2cf86180344ae804df9661be085a7c3233f957c'
pro = ts.pro_api(token)
df_price = pro.daily(ts_code='600519.SH', start_date='20110101', end_date='20211008',
                     fields='ts_code,trade_date,close,open,high,low')
df_price.index = pd.to_datetime(df_price.trade_date)
df_price['year'] = df_price.index.year
df_price = df_price.sort_index()

# print(df_price.iloc[0:5,:])
# print(df_price)
# exit()

# 列名flag 索引为10
# 列名position 索引为9
# 列名close 索引为5
def Strategy(data_price, window_short=5, window_long=10, loss_ratio=0.20):
    # df_price:价格数据;
    # window_short:短均线周期,默认为5;
    # window_long:长均线周期,默认为10;
    # lossratio:止损率,默认为1%,即开仓后下跌超过1%止损。

    ##2.1绘制K线和均线
    data_price = data_price.copy()
    data_price.index = data_price.index.strftime('%Y%m%d')

    data_price['sma'] = data_price.close.rolling(window_short).mean()
    data_price['lma'] = data_price.close.rolling(window_long).mean()
    data_price['position'] = 0  # 记录仓位
    data_price['flag'] = 0  # 记录买卖


    kline = Kline(init_opts=opts.InitOpts(width='1200px', height='600px', theme=ThemeType.DARK))
    kline.add_xaxis(data_price.index.tolist())
    y = list(data_price.loc[:, ['open', 'close', 'low', 'high']].round(2).values)  # 现在里面的单个元素是数组
    y = [i.tolist() for i in y]  # 里面的单个数组也必须转换成list
    kline.add_yaxis('K线', y)
    # kline.extend_axis(yaxis=opts.AxisOpts( axislabel_opts=opts.LabelOpts(formatter="{value}") ))
    kline.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  # 是否显示数据标签
    kline.set_global_opts(
        xaxis_opts=opts.AxisOpts(is_scale=True, axislabel_opts=opts.LabelOpts(rotate=60)),
        yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value}")),
        datazoom_opts=[opts.DataZoomOpts(type_='inside')],  # 内部滑动
        title_opts=opts.TitleOpts(title="贵州茅台(600519.SH)K线及均线", pos_left='45%'),  # 题目位置
        legend_opts=opts.LegendOpts(pos_right="35%", pos_top="5%"),  # 图例位置
        tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross")  # 添加趋势线
    )

    line = Line()
    line.add_xaxis(data_price.index.tolist())
    line.add_yaxis('MA5', data_price.sma.round(2).tolist(), is_smooth=True)
    line.add_yaxis('MA10', data_price.lma.round(2).tolist(), is_smooth=True)
    line.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  # 是否显示数据标签
    line.set_global_opts(
        datazoom_opts=[opts.DataZoomOpts(type_='inside')],  # 内部滑动
        legend_opts=opts.LegendOpts(pos_right="20%", pos_top="5%"),  # 图例位置
        tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross")  # 添加趋势线
    )

    kline.overlap(line)
    kline.render("16.1 贵州茅台(600519.SH)K线及均线.html")

    ##2.2均线策略的交易记录
    Buy = []  # 保存买入记录
    Sell = []  # 保存卖出记录
    price_in = 1  # 初始买入价设置为1

    for i in range(max(1, window_long), data_price.shape[0] - 1):
        # 情形一:当前无仓位且短均线上穿长均线(金叉),则买入股票
        if (data_price['position'][i] == 0) and (data_price['sma'][i - 1] < data_price['lma'][i - 1]) and (
                data_price['sma'][i] > data_price['lma'][i]):
            data_price.iloc[i, 10] = 1
            data_price.iloc[i + 1, 9] = 1

            date_in = data_price.index[i]
            price_in = data_price.iloc[i, 5]
            Buy.append([date_in, price_in, '金叉买入'])

        # 情形二:当前持仓且下跌超过止损率,则平仓止损
        elif (data_price['position'][i] == 1) & (1 - data_price['close'][i] / price_in > loss_ratio):
            data_price.iloc[i, 10] = -1
            data_price.iloc[i + 1, 9] = 0

            date_out = data_price.index[i]
            price_out = data_price.iloc[i, 5]
            Sell.append([date_out, price_out, '止损平仓'])

        # 情形三:当前持仓且短均线下穿长均线(死叉),则卖出股票
        elif (data_price['position'][i] == 1) & (data_price['sma'][i - 1] > data_price['lma'][i - 1]) & (
                data_price['sma'][i] < data_price['lma'][i]):
            data_price.iloc[i, 10] = -1
            data_price.iloc[i + 1, 9] = 0

            date_out = data_price.index[i]
            price_out = data_price.iloc[i, 5]
            Sell.append([date_out, price_out, '死叉卖出'])

        # 其他情形:保持之前的仓位不变
        else:
            data_price.iloc[i + 1, 9] = data_price.iloc[i, 9]

    p1 = pd.DataFrame(Buy, columns=['买入日期', '买入价格', '备注'])
    p2 = pd.DataFrame(Sell, columns=['卖出日期', '卖出价格', '备注'])
    transactions = pd.concat([p1, p2], axis=1)  # 交易记录

    data_price = data_price.iloc[window_long:, :]
    data_price['ret'] = data_price.close.pct_change(1).fillna(0)
    data_price['nav'] = (1 + data_price.ret * data_price.position).cumprod()
    data_price['benchmark'] = data_price.close / data_price.close[0]

    ##2.3返回交易记录和全过程数据
    return transactions, data_price


trans, data = Strategy(df_price, window_short=25, window_long=50, loss_ratio=0.10)
print('交易记录:\n', trans)
print('结果展示:\n', data)


def performance(transactions, strategy):
    ##3.1策略评价指标
    # 年化收益率
    N = 250
    rety = strategy.nav[strategy.shape[0] - 1] ** (N / strategy.shape[0]) - 1

    # 夏普比
    Sharp = (strategy.ret * strategy.position).mean() / (strategy.ret * strategy.position).std() * np.sqrt(N)

    # 胜率
    VictoryRatio = ((transactions['卖出价格'] - transactions['买入价格']) > 0).mean()

    # 最大回撤率
    DD = 1 - strategy.nav / strategy.nav.cummax()
    MDD = max(DD)

    # 单次最大亏损
    maxloss = min(transactions['卖出价格'] / transactions['买入价格'] - 1)

    # 月均交易次数
    trade_count = strategy.flag.abs().sum() / strategy.shape[0] * 20

    print('------------------------------')
    print('夏普比率为:', round(Sharp, 2))
    print('年化收益率为:{}%'.format(round(rety * 100, 2)))
    print('胜率为:{}%'.format(round(VictoryRatio * 100, 2)))
    print('最大回撤率为:{}%'.format(round(MDD * 100, 2)))
    print('单次最大亏损为:{}%'.format(round(-maxloss * 100, 2)))
    print('月均交易次数为:{}(买卖合计)'.format(round(trade_count, 2)))
    print('------------------------------')

    result = {'Sharp': Sharp,
              'RetYearly': rety,
              'WinRate': VictoryRatio,
              'MDD': MDD,
              'maxlossOnce': -maxloss,
              'num': round(strategy.flag.abs().sum() / strategy.shape[0], 1)}

    result = pd.DataFrame.from_dict(result, orient='index').T
    print(result)

    ##3.2策略逐年表现
    nav_peryear = strategy.nav.groupby(strategy.year).last() / strategy.nav.groupby(strategy.year).first() - 1
    benchmark_peryear = strategy.benchmark.groupby(strategy.year).last() / strategy.benchmark.groupby(
        strategy.year).first() - 1

    excess_ret = nav_peryear - benchmark_peryear
    result_peryear = pd.concat([nav_peryear, benchmark_peryear, excess_ret], axis=1)
    result_peryear.columns = ['strategy_ret', 'bench_ret', 'excess_ret']
    result_peryear = result_peryear.T
    print('------------------------------')
    print(result_peryear)
    print('------------------------------')

    ##3.3策略净值可视化
    line1 = Line(init_opts=opts.InitOpts(width='1200px', height='600px', theme=ThemeType.DARK))
    line1.add_xaxis(strategy.index.tolist())
    line1.add_yaxis('策略净值', strategy.nav.round(2).to_list(), yaxis_index=0, is_smooth=True)
    line1.add_yaxis('基准净值', strategy.benchmark.round(2).to_list(), yaxis_index=0, is_smooth=True)
    line1.extend_axis(yaxis=opts.AxisOpts(min_=0.8, axislabel_opts=opts.LabelOpts(formatter="{value}")))
    line1.set_series_opts(label_opts=opts.LabelOpts(is_show=True))  # 是否显示数据标签
    line1.set_global_opts(
        xaxis_opts=opts.AxisOpts(is_scale=True, axislabel_opts=opts.LabelOpts(rotate=60)),
        yaxis_opts=opts.AxisOpts(min_=0.75, axislabel_opts=opts.LabelOpts(formatter="{value}")),
        datazoom_opts=[opts.DataZoomOpts(type_='inside')],  # 内部滑动
        title_opts=opts.TitleOpts(title="双均线择时策略回测", pos_left='45%'),  # 题目位置
        legend_opts=opts.LegendOpts(pos_right="35%", pos_top="5%"),  # 图例位置
        tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross")  # 添加趋势线
    )

    line2 = Line()
    line2.add_xaxis(strategy.index.tolist())
    line2.add_yaxis('净值之比', (strategy.nav / strategy.benchmark).round(2).tolist(), yaxis_index=1, is_smooth=True)
    line2.set_global_opts(
        datazoom_opts=[opts.DataZoomOpts(type_='inside')],  # 内部滑动
        legend_opts=opts.LegendOpts(pos_right="20%", pos_top="5%"),  # 图例位置
        tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross")  # 添加趋势线
    )

    line1.overlap(line2)
    line1.render("16.2 双均线择时策略回测.html")

    return result, result_peryear


performance(trans, data)