欢迎访问369期货网 369会员登录 注册

当前位置: 主页 > 期货量化

量化交易实战22 - 因子与支持向量机(二)动态因子选择策略,根据思路写代码,完整代码直接复制可回测

时间:2025-10-14 15:19|来源:369期货网|作者:369期货网|点击:

今天开始上强度了,觉得有难度的朋友可能就要多思考一下了。


说完了算法,再来说因子。在之前说到的因子与线性模型,因子与决策树中(文末指路),我们使用机器学习算法和多因子做的策略虽然年化收益都不高,但如果朋友认真观察的话,就会发现“拖累”整体收益的,大致是在2018年1月到10月这段时间,也正好是最大回撤的时间范围。这说明在这段时间当中,我们选择的因子可能是失效了

基于这种情况,有的朋友就会思考:在我们的策略中,有没有可能设计一种机制,即能够在每次运行时,自动判断因子的重要性再选出重要的因子来训练模型呢?换句话说,就是不要人为地选定因子,而是让机器动态地选择因子。下面我们就来尝试一下这个可能性。

在前面,我们已经尝试了如何使用决策树算法计算特征。这次我们就直接在回测环境中修改代码,尝试让策略每次启动都自动判断哪些特征最重要。先设置好相关的函数,在回测环境中输入以下代码。

我把步骤全部说一下,然后直接贴代码吧,反正最后都要附上完整的代码:

第一步: 导入需要的库

第二步:盘前准备

定义一下每日开盘前做的事情。先要判断当日是否是调仓日,如果是调合日,则要运行计管滑占及手续费的函数,挑选可交易股票的函数。

第三步:交易逻辑

第四步:机器学习的部分

用决策树计算出特征重要性,动态进行因子选择

第五步:进行买卖定义

买入条件,卖出条件,然后调仓,细节滑点都要注意。

整体思路就是这样的,大家写策略的时候多多注意细节,多运行几次,修改一下细节处,代码在最后。
我在写的时候,也是常常出错,就一个限价的问题,都会导致最终收益很大,代码不是很完善,需要再改进,不可直接用于实盘交易。
我贴出来给大家看一下:同一个策略,就是买入限价的问题,就几个修改了一下几个代码,收益可以相差55个点。
细节非常重要!!!
图片
图片
图片

注意:以下代码需要在回测环境中使用

    # ==========================================# 0. 引用库# ==========================================from jqlib.technical_analysis import *import pandas as pdimport numpy as npfrom sklearn.tree import DecisionTreeClassifierfrom sklearn.svm import SVRimport jqdataimport datetime# ==========================================# 1. 框架入口# ==========================================def initialize(context):    set_params()    set_backtest()    set_variables()def set_params():    g.tc       = 10          # 调仓周期    g.stocknum = 5           # 持仓数    g.ret      = -0.05       # 沪深300阈值def set_backtest():    set_benchmark('000300.XSHG')    set_option('use_real_price', True)    log.set_level('order', 'error')def set_variables():    g.days = 0    g.if_trade = False    g.stocks = []    g.feasible_stocks = []# ==========================================# 2. 盘前# ==========================================def before_trading_start(context):    if g.days % g.tc == 0:        g.if_trade = True        g.stocks = get_index_stocks('000300.XSHG')        g.feasible_stocks = set_feasible_stocks(g.stocks, context)    g.days += 1def set_feasible_stocks(initial_stocks, context):    current_data = get_current_data()    return [s for s in initial_stocks if not current_data[s].paused]# ==========================================# 3. 主交易逻辑(每天)# ==========================================def handle_data(context, data):    if not g.if_trade:        return    list_to_buy = stocks_to_buy(context)    list_to_sell = stocks_to_sell(context, list_to_buy)    def sell_operation(context, list_to_sell):        for stock in list_to_sell:             order_target_value_safe(context, stock, 0)    buy_operation(context, list_to_buy)    g.if_trade = False# ==========================================# 4. 机器学习因子构造# ==========================================def get_svr(context, stock_list):    q = query(        valuation.code,        valuation.market_cap,        balance.total_current_assets,        balance.total_current_liability,        (balance.total_liability - balance.total_assets).label('liab_minus_asset'),        (balance.total_liability / balance.equities_parent_company_owners).label('debt_to_eq'),        ((balance.total_assets - balance.total_current_assets) / balance.total_assets).label('fix_asset_ratio'),        (balance.equities_parent_company_owners / balance.total_assets).label('equity_ratio'),        indicator.inc_total_revenue_year_on_year,        valuation.turnover_ratio,        valuation.pe_ratio,        valuation.pb_ratio,        valuation.ps_ratio,        indicator.roa    ).filter(valuation.code.in_(stock_list))    df = get_fundamentals(q, date=None)    df.index = df['code'].values    del df['code']    start  = context.current_dt.date()    today  = start - datetime.timedelta(days=1)    preday = start - datetime.timedelta(days=50)    df['close1'] = get_price(stock_list, end_date=today, count=1, fq='pre', panel=False)['close'].iloc[0]    df['close2'] = get_price(stock_list, end_date=preday, count=1, fq='pre', panel=False)['close'].iloc[0]    df['return'] = df['close1'] / df['close2'] - 1    df['signal'] = np.where(df['return'] < df['return'].mean(), 0, 1)    x = df.drop(['close1', 'close2', 'return', 'signal'], axis=1).fillna(0)    y = df['signal']    tree = DecisionTreeClassifier(random_state=0)    tree.fit(x, y)    top5 = pd.Series(tree.feature_importances_, index=x.columns).nlargest(5).index    X = df[top5].fillna(0)    Y = df['market_cap'].fillna(0)    svr = SVR(kernel='rbf').fit(X, Y)    df['pred_cap'] = svr.predict(X)    factor = (df['pred_cap'] - df['market_cap']).to_frame('undervalue').sort_values(by='undervalue', ascending=False)    return factor# ==========================================# 5. 生成买卖列表# ==========================================def stocks_to_buy(context):    day1 = context.current_dt.date()    day2 = day1 - datetime.timedelta(days=5)    hs300_close = get_price('000300.XSHG', day2, day1, fq='pre', panel=False)['close']    hs300_ret = hs300_close[-1] / hs300_close[0] - 1    if hs300_ret > g.ret:        factor = get_svr(context, g.feasible_stocks)        return list(factor.index[:g.stocknum])    return []def stocks_to_sell(context, list_to_buy):    return [s for s in context.portfolio.positions if s not in list_to_buy]# ==========================================# 6. 下单执行(已修复:限价 + 100股)# ==========================================def buy_operation(context, list_to_buy):    if not list_to_buy:        return    cash_per_stock = context.portfolio.available_cash / len(list_to_buy)    for stock in list_to_buy:        order_target_value_safe(context, stock, cash_per_stock)def sell_operation(list_to_sell):    for stock in list_to_sell:        order_target_value_safe(context, stock, 0)from decimal import Decimal, ROUND_FLOOR, ROUND_CEILINGdef order_target_value_safe(context, stock, value):    current_data = get_current_data()    last_price = current_data[stock].last_price    if last_price <= 0:        return    # 1. 向下截断到分的涨停价    limit_price = float((Decimal(str(last_price)) * Decimal('1.1')).quantize(Decimal('0.01'), rounding=ROUND_FLOOR))    # 2. 卖出场景:跌停价    if value <= 0:        amt = context.portfolio.positions[stock].total_amount        sell_amt = (amt // 100) * 100        if sell_amt > 0:            limit_down_price = float((Decimal(str(last_price)) * Decimal('0.9')).quantize(Decimal('0.01'), rounding=ROUND_CEILING))            order(stock, -sell_amt, style=LimitOrderStyle(limit_down_price))        return    # 3. 买入场景    shares = int(value / limit_price / 100) * 100    if shares < 100:        return    order(stock, shares, style=LimitOrderStyle(limit_price))# ==========================================# 7. 滑点/手续费# ==========================================def set_slip_fee(context):    set_slippage(FixedSlippage(0))    set_commission(PerTrade(buy_cost=0.0003, sell_cost=0.0004, min_cost=5))


    编辑推荐

    Copyright © 2024-2025 成都宁时科技有限公司 版权所有

    蜀ICP备2022023994号