从零开始搭建一个量化交易系统

论坛 期权论坛 期权     
期权匿名问答   2022-1-9 18:03   16037   18
本期将从零开始,手动搭建一个基于规则的简单量化交易系统,旨在提高对整个过程的理解能力。
Step 1: 数据加载

其中股票数据采用已存好的CSV数据,数据的获取方式可参见
另外采用 TA-lib 的 RSI 技术指标构建买入卖出信号,详情可参见
# Step 1: load dataset and generate features
def prepare_data(codes=['000300.SH', '399006.SZ'], start_time="20100101", end_time="20211231"):
    df = load_data(codes, start_time, end_time)

    df["rsi"] = ta.RSI(df.close, timeperiod=14)
    df["to_buy"] = ""
    df.loc[df["rsi"] <= 30, 'to_buy'] = True
    df['to_buy'] = df['to_buy'].astype('bool')

    df["to_sell"] = ""
    df.loc[df["rsi"] >= 70, 'to_sell'] = True
    df['to_sell'] = df['to_sell'].astype('bool')
    return dfStep 2: 指定策略

策略规则非常简单:

  • 账户初始值100000,每只股票占相同比重
  • 根据数据的买入信号买入,卖出信号卖出
# Step 2: prepare strategy
class SelectBySignal(object):
    def __init__(self, signal_buy='to_buy', signal_sell='to_sell'):
        super(SelectBySignal, self).__init__()
        self.signal_buy = signal_buy
        self.signal_sell = signal_sell

    def __call__(self, context):
        bar = context['bar'].copy()

        acc = context['acc']
        holding = acc.get_holding_instruments()

        to_buy = list(bar[bar[self.signal_buy]].index)
        to_sell = list(bar[bar[self.signal_sell]].index)

        instruments = to_buy + holding
        to_selected = []
        for s in instruments:
            if s not in to_sell:
                to_selected.append(s)
        context['selected'] = to_selected

        n = len(to_selected)
        if n > 0:
            context['weights'] = {code:1/n for code in to_selected}
        else:
            context['weights'] = {}
        return False

class Strategy:
    def __init__(self, algo=None):
        self.algo = algo
        self.acc = Account()

    def algo_processor(self, context):
        if self.algo(context) is True: #如果algo返回True,直接不运行,本次不调仓
            return None
        return context['weights']

    def onbar(self, index, date, df_bar):
        self.acc.update_bar(date, df_bar)
        weights = self.algo_processor({'index': index, 'bar':df_bar, 'date':date, 'acc':self.acc})
        if weights is not None:
            self.acc.adjust_weights(date, weights)Step 3: 回测

根据上述数据和策略进行回测
# Step 3: backtest
class Backtest:
    def __init__(self, df):
        self.df = df
        self.dates = self.df.index.unique()
        self.observers = []

    def onbar(self, index, date):
        df_bar = self.df.loc[date]
        if type(df_bar) is pd.Series:
            df_bar = df_bar.to_frame().T

        df_bar.index = df_bar['code']
        self.strategy.onbar(index, date, df_bar)

    def run(self, s):
        self.strategy = s
        for index, date in enumerate(self.dates):
            self.onbar(index, date)
        return self.get_results()

    def get_results(self):
        s = self.strategy
        df = s.acc.get_results_df()
        return dfStep 4: 分析

分析该策略的表现,并与沪深300指数进行对比
# Step 4: analysis
def analysis(start, end, benchmarks=[]):
    equities = []
    for benchmark in benchmarks:
        bench_df = load_from_file(benchmark)[start:end]
        se = (bench_df['rate'] + 1).cumprod()
        se.name = benchmark
        equities.append(se)

    path = os.path.dirname(__file__)
    filename = os.path.dirname(path)+'/results/first_test.csv'
    if os.path.exists(filename):
        df = pd.read_csv(filename)
        df['date'] = df['date'].apply(lambda x: str(x))
        df.index = df['date']
        se = (df['rate'] + 1).cumprod()
        se.name = 'strategy'
        equities.append(se)

    df_equities = pd.concat(equities, axis=1)
    df_equities.dropna(inplace=True)
    print(df_equities)

    from performance import PerformanceUtils

    df_ratios, df_corr, df_years = PerformanceUtils().calc_equity(df_equity=df_equities)
    return df_equities, df_ratios, df_corr, df_years运行主函数

if __name__ == '__main__':
    date_start = "20100101"
    date_end = "20211231"
    df = prepare_data(codes=['000300.SH', '399006.SZ'], start_time=date_start, end_time=date_end)

    algo = SelectBySignal(signal_buy='to_buy', signal_sell='to_sell')
    s = Strategy(algo=algo)

    b = Backtest(df=df)
    df = b.run(s)

    path = os.path.dirname(__file__)
    df.to_csv(os.path.dirname(path) + '/results/first_test.csv')

    df_equities, df_ratios, df_corr, df_years = analysis(start=date_start, end=date_end, benchmarks=['000300.SH'])
    display(df_ratios)

    fig = plt.figure(figsize=(8, 6))
    ax1 = fig.add_subplot(2, 1, 1)
    ax2 = fig.add_subplot(2, 1, 2)
    df_equities.plot(ax=ax1)
    if df_years is not None:
        print(df_years)
        df_years.T.plot(kind='bar', ax=ax2, use_index=True)
    plt.show()得到结果如下:
分别为累积的各项数据对比,以及各年的年化对比

  • 累计收益



:策略最终股票和现金的总价值

:策略开始股票和现金的总价值

  • 年化收益



:策略收益

:策略执行天数

  • 波动率(Algorithm Volatility)
用来测量策略的风险性,波动越大代表策略风险越高



:策略每日收益率

:策略每日收益率的平均值

  • 夏普比率
表示每承受一单位总风险,会产生多少的超额报酬,可以同时对策略的收益与风险进行综合考虑。



:策略年化收益率

:无风险利率(默认0.04)

:策略收益波动率
            累计收益   年化收益    波动率    夏普比   最大回撤  年化收益与最大回撤比
000300.SH  0.413      0.030     0.228     0.132   -0.467       0.064
strategy   1.241      0.073     0.248     0.294   -0.576       0.127

            2010   2011   2012   2013   2014  ...   2017   2018   2019   2020   2021
000300.SH -0.117 -0.265  0.098 -0.077  0.522  ...  0.206 -0.263  0.380  0.255 -0.044
strategy  -0.051 -0.316  0.057  0.319  0.305  ...  0.040 -0.276  0.424  0.430  0.044做成图如下所示:


完整代码参见
另外也可参考:
分享到 :
0 人收藏

18 个回复

倒序浏览
2#
期权匿名回答  16级独孤 | 2022-1-9 18:03:19 发帖IP地址来自 中国
我是个废物[捂脸]
3#
期权匿名回答  16级独孤 | 2022-1-9 18:03:52 发帖IP地址来自 北京
老哥在哪家?刚入职方正量化[调皮]加个微信互相学习啊
4#
期权匿名回答  16级独孤 | 2022-1-9 18:04:23 发帖IP地址来自 北京
可以呀,欢迎互相讨论学习
5#
期权匿名回答  16级独孤 | 2022-1-9 18:04:28 发帖IP地址来自 北京
6#
期权匿名回答  16级独孤 | 2022-1-9 18:04:50 发帖IP地址来自 广东广州
完全看不懂
7#
期权匿名回答  16级独孤 | 2022-1-9 18:05:16 发帖IP地址来自 北京
你用rsi过高(大于0.7)和过低(小于0.3)作为交易信号。你考虑到rsi在当下随时在变了吗?换句话说,假设一根k线从开始到走完,对应的rsi的波动区间是0.25到0.35,这根k线收盘的时候rsi是0.35,那么你的回测代码会认为这个k线根本不会触发做多信号。但在实盘的话,你会捕捉到rsi小于0.3,也就是说实盘会做多的。[doge]
8#
期权匿名回答  16级独孤 | 2022-1-9 18:05:50 发帖IP地址来自 中国
因为我现在用天级数据进行测试,实际交易数据刻度更细,则可以计算更细粒度的RSI。
9#
期权匿名回答  16级独孤 | 2022-1-9 18:06:01 发帖IP地址来自 北京
那你的code恐怕要大改了
10#
期权匿名回答  16级独孤 | 2022-1-9 18:06:31 发帖IP地址来自 河北石家庄
请问如何用tushare获取某一天所有股票的收盘价呢
11#
期权匿名回答  16级独孤 | 2022-1-9 18:06:45 发帖IP地址来自 北京
参见该专栏的第一期哈
12#
期权匿名回答  16级独孤 | 2022-1-9 18:07:04 发帖IP地址来自 北京
现在只是在展示一个过程,而非实际上线的代码,改代码是容易的
13#
期权匿名回答  16级独孤 | 2022-1-9 18:07:27 发帖IP地址来自 北京林业大学
推给我干嘛,这是我看得懂的嘛
14#
期权匿名回答  16级独孤 | 2022-1-9 18:07:36 发帖IP地址来自 北京
然后亏完了
15#
期权匿名回答  16级独孤 | 2022-1-9 18:08:08 发帖IP地址来自 中国
不明觉厉...[有、厉害]
16#
期权匿名回答  16级独孤 | 2022-1-9 18:08:29 发帖IP地址来自 北京
现在这样也能成为量化系统了么……
17#
期权匿名回答  16级独孤 | 2022-1-9 18:09:15 发帖IP地址来自 中国
说个鸡巴锤子
18#
期权匿名回答  16级独孤 | 2022-1-9 18:10:00 发帖IP地址来自 中国
[赞同]等我赚钱了回来给您点赞!
19#
期权匿名回答  16级独孤 | 2022-1-9 18:10:05 发帖IP地址来自 北京
收藏比咱多系列
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:400157
帖子:80032
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP