今天开始上强度了,觉得有难度的朋友可能就要多思考一下了。
说完了算法,再来说因子。在之前说到的因子与线性模型,因子与决策树中(文末指路),我们使用机器学习算法和多因子做的策略虽然年化收益都不高,但如果朋友认真观察的话,就会发现“拖累”整体收益的,大致是在2018年1月到10月这段时间,也正好是最大回撤的时间范围。这说明在这段时间当中,我们选择的因子可能是失效了。
基于这种情况,有的朋友就会思考:在我们的策略中,有没有可能设计一种机制,即能够在每次运行时,自动判断因子的重要性再选出重要的因子来训练模型呢?换句话说,就是不要人为地选定因子,而是让机器动态地选择因子。下面我们就来尝试一下这个可能性。
在前面,我们已经尝试了如何使用决策树算法计算特征。这次我们就直接在回测环境中修改代码,尝试让策略每次启动都自动判断哪些特征最重要。先设置好相关的函数,在回测环境中输入以下代码。
我把步骤全部说一下,然后直接贴代码吧,反正最后都要附上完整的代码:
第一步: 导入需要的库
第二步:盘前准备
定义一下每日开盘前做的事情。先要判断当日是否是调仓日,如果是调合日,则要运行计管滑占及手续费的函数,挑选可交易股票的函数。
第三步:交易逻辑
第四步:机器学习的部分
用决策树计算出特征重要性,动态进行因子选择
第五步:进行买卖定义
买入条件,卖出条件,然后调仓,细节滑点都要注意。
注意:以下代码需要在回测环境中使用
# ==========================================# 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 成都宁时科技有限公司 版权所有