量化交易之回测篇 - 单策略回测数据(净值曲线、最大回撤、夏普、收益风险比)

import re
import math

from public_module.tqz_extern.tools.position_operator.position_operator import TQZJsonOperator
from public_module.tqz_extern.tools.pandas_operator.pandas_operator import pandas
from public_module.object import BarData

from back_tester_branch.back_tester_source_data import TQZBackTesterFutureSourceData
from back_tester_source_data import TQZBackTesterFutureConfigPath, TQZBackTesterFutureResultPath

import matplotlib.pyplot as pyplot

class TQZBackTesterFuture:

    __future_contracts_setting = TQZJsonOperator.tqz_load_jsonfile(
        jsonfile=TQZBackTesterFutureConfigPath.future_contracts_setting_path()
    )

    @classmethod
    def start(cls):
        _strategies = TQZBackTesterFutureSourceData.load_future_strategies()

        back_tester_result = {}
        for strategy_name, data in _strategies.items():
            strategy, bars = data['strategy'], data['bars']
            per_result_df, daily_per_result_df = pandas.DataFrame(columns={'date', 'close_price', 'pos'}), pandas.DataFrame(columns={'date', 'balance'})

            # back tester single strategy
            strategy.on_init()
            strategy.on_start()

            strategy_fund = cls.__get_contract_fund(tq_sym=bars[0].vt_symbol)
            for bar in bars:
                strategy.on_bar(bar)
                per_result_df = cls.__update_per_result_df(per_result_df=per_result_df, bar=bar, strategy=strategy)

                search_result = re.search('(\d+)-(\d+)-(\d+) 14:(\d+):(\d+)', bar.datetime)
                if search_result is not None:
                    daily_per_result_df_current_row = len(daily_per_result_df)
                    daily_per_result_df.loc[daily_per_result_df_current_row, 'date'] = re.match('(\d+)-(\d+)-(\d+)', bar.datetime).group()
                    daily_per_result_df.loc[daily_per_result_df_current_row, 'balance'] = per_result_df[-1:]['balance'].tolist()[0]
                    daily_per_result_df.loc[daily_per_result_df_current_row, 'net_value'] = daily_per_result_df.loc[daily_per_result_df_current_row, 'balance'] / strategy_fund

            daily_per_result_df['fund'] = strategy_fund
            strategy.on_stop()

            back_tester_result[strategy_name] = daily_per_result_df

        cls.__settle_back_tester_result(back_tester_result=back_tester_result)


    # --- private part ---
    @classmethod
    def __settle_back_tester_result(cls, back_tester_result: {str, pandas.DataFrame}):

        back_tester_result_dictionary = {}
        for strategy_name, per_back_tester_result in back_tester_result.items():
            cls.__create_per_netValue_png(strategy_name=strategy_name, per_back_tester_result=per_back_tester_result)

            max_net_value_drop_down = cls.__max_drop_down(values=per_back_tester_result['net_value'].tolist())
            back_tester_result_dictionary[strategy_name] = {
                "max_net_value_drop_down": max_net_value_drop_down,
                "sharpe_ratio": cls.__sharp_ratio(per_back_tester_result=per_back_tester_result),
                "profit_risk_ratio": round(per_back_tester_result['net_value'].tolist()[-1] / abs(max_net_value_drop_down), 4)
            }

        TQZJsonOperator.tqz_write_jsonfile(
            content=back_tester_result_dictionary,
            target_jsonfile=TQZBackTesterFutureResultPath.future_fold() + f'/per_back_tester_result.json'
        )


    @classmethod
    def __create_per_netValue_png(cls, strategy_name, per_back_tester_result):
        strategy_name_format = strategy_name.replace(".", "_")
        bt_result_excel = TQZBackTesterFutureResultPath.future_fold() + f'/{strategy_name_format}.png'

        pyplot.figure(figsize=(15, 10))  # size

        begin_date = per_back_tester_result['date'].tolist()[0]
        end_date = per_back_tester_result['date'].tolist()[-1]

        pyplot.title(f'{strategy_name_format}   {begin_date}~{end_date}')
        pyplot.gca().get_xaxis().set_visible(False)  # clear x title
        pyplot.plot(per_back_tester_result['date'], per_back_tester_result['net_value'], alpha=0.9)  # data
        pyplot.savefig(bt_result_excel)

    @classmethod
    def __max_drop_down(cls, values: list) -> float:
        net_values_counts = len(values)

        max_drop_downs = []
        for i in range(net_values_counts):
            i_begin = values[i]

            max_drop_down = 0
            new_loop = True
            for j in range(i + 1, net_values_counts):
                if new_loop is True or max_drop_down < i_begin - values[j]:
                    max_drop_down = i_begin - values[j]
                    new_loop = False
                else:
                    break

            max_drop_downs.append(max_drop_down)

        return round(max(max_drop_downs) * -1, 4)

    @classmethod
    def __sharp_ratio(cls, per_back_tester_result: pandas.DataFrame):
        per_back_tester_result['net_value_flow_per_day'] = per_back_tester_result['net_value'] - per_back_tester_result['net_value'].shift(1)
        yield_rate_annualized = round(((per_back_tester_result['net_value'].values.tolist()[-1] - 1) / len(per_back_tester_result)) * 250, 5)

        if per_back_tester_result['net_value_flow_per_day'].std(ddof=0) is 0:
            sharpe_ratio = 0
        else:
            sharpe_ratio = round(yield_rate_annualized / per_back_tester_result['net_value_flow_per_day'].std(ddof=0) / math.sqrt(250), 4)
        del per_back_tester_result['net_value_flow_per_day']

        return sharpe_ratio

    @classmethod
    def __update_per_result_df(cls, per_result_df: pandas.DataFrame, bar: BarData, strategy) -> pandas.DataFrame:
        current_row = len(per_result_df)
        per_result_df.loc[current_row, 'date'] = bar.datetime
        per_result_df.loc[current_row, 'close_price'] = bar.close_price
        per_result_df.loc[current_row, 'pos'] = strategy.pos

        if current_row is 0:
            per_result_df.loc[current_row, 'cc_diff'] = 0
        else:
            per_result_df.loc[current_row, 'cc_diff'] = bar.close_price - per_result_df.loc[current_row - 1, 'close_price']

        per_result_df.loc[current_row, 'cc_pnl'] = per_result_df.loc[current_row, 'cc_diff'] * per_result_df.loc[current_row, 'pos']

        if current_row is 0:
            per_result_df.loc[current_row, 'pnl'] = 0
        elif current_row is not 0 and per_result_df.loc[current_row, 'pos'] is not 0 and per_result_df.loc[current_row - 1, 'pos'] is 0:
            per_result_df.loc[current_row, 'pnl'] = per_result_df.loc[current_row - 1, 'pnl']
        else:
            per_result_df.loc[current_row, 'pnl'] = per_result_df.loc[current_row, 'cc_pnl'] + per_result_df.loc[current_row - 1, 'pnl']

        per_result_df.loc[current_row, 'real_pnl'] = per_result_df.loc[current_row, 'pnl'] * cls.__future_contracts_setting[strategy.vt_symbol]['contract_multiple']
        per_result_df.loc[current_row, 'balance'] = per_result_df.loc[current_row, 'real_pnl'] + cls.__get_contract_fund(tq_sym=strategy.vt_symbol)

        return per_result_df

    @classmethod
    def __get_contract_fund(cls, tq_sym: str) -> float:
        future_contracts_fund = {
            "CFFEX.T": 100000,
            "CFFEX.IF": 1000000,
            "CFFEX.IC": 1000000,
            "CFFEX.IH": 1000000
        }

        if tq_sym in future_contracts_fund.keys():
            return future_contracts_fund[tq_sym]
        else:
            return 50000


if __name__ == '__main__':
    TQZBackTesterFuture.start()

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
股票回测量化交易中非常重要的一环,它可以通过历史数据对交易策略进行模拟和评估,从而评估策略的可行性和优劣性。在Python中,有很多开源的量化交易框架可以用来进行股票回测,如zipline、backtrader等。 下面是一个使用zipline框架进行简交易策略回测的例子: 1. 安装zipline ```python pip install zipline ``` 2. 编写交易策略代码 ```python from zipline.api import order_target_percent, record, symbol def initialize(context): context.asset = symbol('AAPL') def handle_data(context, data): # 获取过去10天的收盘价 prices = data.history(context.asset, 'price', 10, '1d') # 计算平均价 mean_price = prices.mean() # 如果当前价格低于平均价,则买入 if data.current(context.asset, 'price') < mean_price: # 调整持仓比例至100% order_target_percent(context.asset, 1.0) # 否则卖出 else: # 调整持仓比例至0% order_target_percent(context.asset, 0.0) # 记录当前持仓比例 record(position=context.portfolio.positions[context.asset].amount) ``` 3. 运行回测 ```python from zipline import run_algorithm from zipline.api import symbol from datetime import datetime start = datetime(2016, 1, 1) end = datetime(2017, 1, 1) result = run_algorithm( start=start, end=end, initialize=initialize, capital_base=10000, handle_data=handle_data, bundle='quandl' ) ``` 在上述代码中,我们定义了一个简的交易策略,即如果当前价格低于过去10天的平均价,则买入,否则卖出。然后我们使用zipline框架进行回测,设定回测开始和结束时间、初始资本、数据来源等参数,最终得到回测结果。 需要注意的是,这只是一个简的例子,实际的交易策略可能会更加复杂,需要考虑更多的因素。另外,在进行股票回测时,也需要注意避免过度拟合或过度优化,以免出现回测虚高的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值