乐于分享
好东西不私藏

动量选股策略源码及回测数据

动量选股策略源码及回测数据

最近又想试试量化,虽然之前中道崩殂很多次,但我认为这次会不一样,因为人到了一定年纪,很多以为有希望的事情后幻灭了,最后还是要找一两件能长久、能自控的事情来做,能够赚钱自然是最好的。废话不多说,开始正题:
动量选股策略很早就有了,估计也有很多人在用了。我在网上差了很多资料,拿这个举例的很多,但都没有给出源码(我感觉主要是为了卖铲子),给出的回测结果又都很亮眼,所以只能自己动手丰衣足食。
根据网上翻阅的资料,借助ds实现了代码,然后选了几个不相关的场内etf作为标的作为备选对象。从结果来看,还是不错的。
回测结果
看起来相当不错,年化达到了22%(哈哈哈)
为了便于归因,还计算了每个股票自己的涨幅,以及买卖操作对最终收益的贡献度
说明一下,这里的价格都是都是前复权价格。
涨幅最好的是纳指,但是贡献度最高却是创业板etf、黄金etf和2000etf,这点没搞懂,可能动量逻辑总是选不中纳指吧。
顺便看一下每个月的情况
2015年居然逃顶了(汗汗汗),不可思议,肯定哪里出了问题。
以下是关键代码,动量计算方法和选股逻辑
def calculate_momentum(df):    # 计算动量、RSRS斜率、R2    mon_window = 20    rsrs_window_N = 18    rsrs_window_M = 600    df[f"mon{mon_window}"] = df[COL_CLOSE].pct_change(mon_window)    rsrs_slopes = []    rsrs_r2 = []    for i in range(len(df)):        if i < rsrs_window_N:            rsrs_slopes.append(np.nan)            rsrs_r2.append(np.nan)            continue        # 获取最近N天的最高价和最低价        high_prices = df[COL_HIGH].iloc[i-rsrs_window_N+1:i+1].values        low_prices = df[COL_LOW].iloc[i-rsrs_window_N+1:i+1].values        # 线性回归计算斜率        if len(high_prices) == rsrs_window_N and len(low_prices) == rsrs_window_N:            X = low_prices.reshape(-11)            y = high_prices            try:                model = LinearRegression()                model.fit(X, y)                beta = model.coef_[0]                r2 = model.score(X, y)                rsrs_slopes.append(beta)                rsrs_r2.append(r2)            except:                rsrs_slopes.append(np.nan)                rsrs_r2.append(np.nan)                logger.info(f"{df['ts_code'].iloc[i]}{df['trade_date'].iloc[i]}: rsrs计算报错。")        else:            rsrs_slopes.append(np.nan)    df['rsrs_slope'] = rsrs_slopes    df['rsrs_r2'] = rsrs_r2    # 计算标准分    df['rsrs_mean'] = df['rsrs_slope'].rolling(window=rsrs_window_M, min_periods=60).mean()    # 计算RSRS斜率的M日滚动标准差,衡量RSRS斜率的波动性/离散程度    df['rsrs_std'] = df['rsrs_slope'].rolling(window=rsrs_window_M, min_periods=60).std()    # Z=(当前值-均值)/标准差,将原始RSRS斜率转换为标准正态分布分数    df['rsrs_std_score'] = (df['rsrs_slope'] - df['rsrs_mean']) / df['rsrs_std']    return df
    def _select_stocks(self, prev_date, current_date):        # prev_date : datetime 前一日日期        # current_date : datetime 当日日期(用于检查可交易性)        candidate_stocks = []        for stock_code, df in self.data.items():            prev_row = df.loc[prev_date]            current_row = df.loc[current_date]            # 获取指标            rsrs_score = prev_row.get('rsrs_std_score')            r2 = prev_row.get('rsrs_r2')            momentum = prev_row.get('mon20')            current_open = current_row.get('open')            # 检查条件:指标有效、RSRS标准分大于买入阈值、R²大于0.2            if (pd.notna(rsrs_score) and pd.notna(r2) and pd.notna(momentum) and pd.notna(current_open)):                if (rsrs_score > self.buy_threshold and r2 > 0.2):                    # 计算综合得分:动量 × R²                    composite_score = momentum * r2                    candidate_stocks.append((stock_code, composite_score))        # 按综合得分排序(降序)        candidate_stocks.sort(key=lambda x: x[1], reverse=True)        # 返回前self.stock_num个        selected_stocks = [stock_code for stock_code, score in candidate_stocks[:self.stock_num]]        return selected_stocks
抛砖引玉,供参考。
从回测结果的详细数据看,很多细节还需要核验和确认,因为每一步都可能藏着魔鬼,所以离实盘还远得很。
祝自己好运!!!摸着石头过河的你好运!!!
本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 动量选股策略源码及回测数据

评论 抢沙发

4 + 3 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮