量化回测系统之benchmark管理

benchmark

投资结果如果也有考核的话,当然就是收益率的。但是收益率是相对的,比如说市场(大盘指数在一个周期跌了30%,那如果一个基金经理同期的业绩的-10%,那还算跑盈了大盘)。私募更多会看绝对收益率。更严格或者说对用户更合理的,应该是必须跑赢同期存款利率外加通胀(否则存银行就好了)。

所以做为一个量化系统,肯定需要一个benchmark做参照系。一般会取上证综指或沪深300指数。

在系统里,我们允许用户自己配置。

    code        name    change   preclose      close       high        low  \
0   000001    上证指数  -1.13   4527.396   4476.215   4572.391   4432.904
1   000002    A股指数  -1.13   4744.093   4690.628   4791.534   4645.190
2   000003    B股指数  -2.15    403.694    395.018    405.795    392.173
3   000008    综合指数   0.79   3724.496   3753.906   3848.575   3695.817
4   000009   上证380  -2.79   7689.128   7474.305   7695.329   7398.911
5   000010   上证180  -1.13  10741.180  10619.610  10863.080  10529.900
6   000011    基金指数  -1.02   7033.291   6961.659   7058.856   6918.273
7   000012    国债指数   0.01    148.626    148.641    148.656    148.510
8   000016    上证50  -0.79   3308.454   3282.330   3370.025   3255.769
9   000017     新综指  -1.13   3826.013   3782.936   3864.307   3746.284
10  000300   沪深300  -1.37   4807.592   4741.861   4839.078   4703.567
11  399001    深证成指  -0.69  14809.424  14707.245  14979.810  14580.422
12  399002    深成指R  -0.69  17193.832  17075.202  17391.652  16927.959
13  399003    成份B指  -1.93   9027.079   8853.081   9013.194   8826.048
14  399004  深证100R  -1.79   5994.881   5887.414   6036.322   5832.431
15  399005    中小板指  -3.34   8935.338   8637.195   8953.813   8551.202
16  399006    创业板指  -2.17   2747.497   2687.974   2779.200   2650.425
17  399100   新 指 数  -2.77  10091.194   9811.256  10111.664   9718.085
18  399101    中小板综  -3.31  12792.057  12368.868  12800.453  12253.744
19  399106    深证综指  -2.76   2271.275   2208.561   2275.344   2187.897
20  399107    深证A指  -2.77   2375.176   2309.466   2379.507   2287.784
21  399108    深证B指  -1.77   1398.244   1373.512   1397.996   1367.343
22  399333    中小板R  -3.34   9640.766   9319.085   9660.699   9226.304
23  399606    创业板R  -2.16   2828.251   2767.127   2861.040   2728.472

系统支持大部分的A股指数如上表。

在aiquant/engine/benchmark.py里新建类Benchmark

#初始化传入benchmark_code,比如000001为上证综指
#起止日期
class Benchmark(object):
    def __init__(self,benchmark_code,start_date,end_date):
        self.benchmark_code = benchmark_code
        self.start_date = start_date
        self.end_date = end_date
        #从服务器把benchmark的数据一次性加载到内存里
        self.load_benchmark_data()

    def load_benchmark_data(self):
        self.df_index_data = data_utils.get_prices_by_code(self.benchmark_code,self.start_date,self.end_date)

更新下取数据的函数,支持从指数表里读:

    #根据code和日期范围从表里取这个时间段的数据
def get_prices_by_code(code,start_date,end_date,index=False):
    if index:
        items = query_docs('index_prices', {'code': code, 'date': {'$gt': start_date,
                                                                       '$lt': end_date}})
    else:
        items = query_docs('stock_prices', {'code': code, 'date': {'$gt': start_date,
                                                                       '$lt': end_date}})
    return list(items)

这里有个小tip,股票股票存在停牌等事项,可能在某几天没有交易记录,所以交易日,从指数里取。

    #benchmark.py
    def get_trading_days(self):
        return list(self.df_index_data['date'])

    class MongodbHandler(DataHandler):
        ...
        def __init__(self,events,start_date,end_date,benchmark='000300'):
        
        self.benchmark = Benchmark(benchmark,start_date,end_date)
        self.trading_days = self.benchmark.get_trading_days()

自动化测试

对于一个计算型的系统,自动化单元测试是非常有必要的,因为很难通过观察去发现里边细小的错误。

通过系统加载的沪深300指数数据与网易财经的数据作对比,除了小数点上的区别,是对的。

import unittest
import queue
import math

from aiquant.engine.benchmark import Benchmark
from datetime import datetime

class TestBenchmark(unittest.TestCase):
    def setUp(self):
        events = queue.Queue()
        self.benchmark = Benchmark('000300',datetime(2016,7,1),datetime(2017,7,1))

    def testOnbar(self):
        data = self.benchmark.df_index_data

        #dataframe用iloc下标取行,各到是一个小的dataframe
        items = data.iloc[[0,1]].to_dict(orient='records')
        self.assertAlmostEqual(items[0]['close'],3154.20)
        self.assertLess(math.fabs(items[1]['open']-3136.39),0.01 )