# 克隆自聚宽文章:https://www.joinquant.com/post/65807 # 标题:11年26倍,简单的多因子策略 # 作者:foolmouse # https://www.joinquant.com/view/community/detail/625cc5268324506f6746fc0c6c605c44 # 很简单的几个随手可得的因子: # ROA高。 # 归母净利润增长快。 # PB排名小且小于2。 # 就这些,没有其他花里胡哨诘屈聱牙的高频因子,没有复杂计算,只是简单的排序,我觉得比较满意的几点: # 没有现在满社区的小市值暴露(虽然我也有小市值仓位,但是不会是重仓)。 # 所有因子都是理论基础很扎实的几个基础因子,即使暂时会失效长期看应该有效的。 # 没有太多参数(只有一个PB小于2,这个存在后视镜,是根据结果优化的。去掉也行但是2015年股灾回撤比较大,如果实盘我会考虑指数估值什么的大盘择时,但是聚宽没有这个因子因此没用) # 在牛市后半程会跑输,但是大部分熊市后会赚回来的。 ''' 1.市净率小于2; 2.负债比例高于市场平均值; 3.企业的流动资产至少是流动负债的1.2倍; 4.每年四次调仓,即在1/4/7/10月调仓; 5.可加入止损(十天HS300跌幅达10%清仓); ''' ## 初始化函数,设定要操作的股票、基准等等 def initialize(context): # 设定指数 g.stockindex = '000300.XSHG' # 设定沪深300作为基准 set_benchmark('000300.XSHG') # True为开启动态复权模式,使用真实价格交易 set_option('use_real_price', True) # 设定成交量比例 set_option('order_volume_ratio', 1) # 股票类交易手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱 set_order_cost(OrderCost(open_tax=0, close_tax=0.001, \ open_commission=0.0003, close_commission=0.0003,\ close_today_commission=0, min_commission=5), type='stock') # 最大持仓数量 g.stocknum = 10 ## 自动设定调仓月份(如需使用自动,注销下段) """ f = 4 # 调仓频率 log.info(range(1,13,12/f)) g.Transfer_date = range(1,13,12/f) """ ## 手动设定调仓月份(如需使用手动,注释掉上段) # g.Transfer_date = (3,9) #根据大盘止损,如不想加入大盘止损,注释下句即可 # run_daily(dapan_stoploss, time='open') ## 按月调用程序 run_weekly(trade, weekday=1, time='open') ## 交易函数 def trade(context): ## 获得Buylist Buylist = check_stocks(context) value = context.portfolio.total_value hold_count = min(len(Buylist),g.stocknum) per_value = value/hold_count if hold_count>0 else 0 ## 卖出 if len(context.portfolio.positions) > 0: for stock in context.portfolio.positions.keys(): if stock not in Buylist: order_target_value(stock, 0) else: order_target_value(stock,per_value) ## 买入 if len(Buylist) > 0: for stock in Buylist: if stock not in context.portfolio.positions.keys(): order_target_value(stock,per_value) ## 选股函数 def check_stocks(context): # 获取所有股票 yesterday = context.previous_date security = get_all_securities("stock", yesterday).index.tolist() # 排除st,科创,新股 security = filter_st_stock(security) security = filter_kcbj_stock(security) security = filter_new_stock(context,security) # 获取因子值 Stocks = get_fundamentals(query( valuation.code, valuation.pb_ratio, indicator.roa, indicator.inc_net_profit_to_shareholders_year_on_year ).filter( valuation.code.in_(security), valuation.pb_ratio<2 )) stock_count = len(Stocks) # 计算roa靠前的 Stocks = Stocks.sort_values(by='roa',ascending=False).iloc[:int(stock_count/10)] # 计算增长率考前的 stock_count = len(Stocks) Stocks = Stocks.sort_values(by='inc_net_profit_to_shareholders_year_on_year',ascending=False).iloc[:int(stock_count/10)] # 计算PB最小的 Stocks = Stocks.sort_values(by='pb_ratio',ascending=True).iloc[:10] Codes = Stocks.code return list(Codes) # 过滤停牌股票 def filter_paused_stock(stock_list): current_data = get_current_data() return [stock for stock in stock_list if not current_data[stock].paused] # 过滤ST及其他具有退市标签的股票 def filter_st_stock(stock_list): current_data = get_current_data() return [stock for stock in stock_list if not current_data[stock].is_st and 'ST' not in current_data[stock].name and '*' not in current_data[stock].name and '退' not in current_data[stock].name] # 过滤科创北交股票 def filter_kcbj_stock(stock_list): for stock in stock_list[:]: if stock[0] == '4' or stock[0] == '8' or stock[:2] == '68': stock_list.remove(stock) return stock_list # 过滤涨停的股票 def filter_limitup_stock(context, stock_list): last_prices = history(1, unit='1m', field='close', security_list=stock_list) current_data = get_current_data() return [stock for stock in stock_list if stock in context.portfolio.positions.keys() or last_prices[stock][-1] < current_data[stock].high_limit] # 过滤跌停的股票 def filter_limitdown_stock(context, stock_list): last_prices = history(1, unit='1m', field='close', security_list=stock_list) current_data = get_current_data() return [stock for stock in stock_list if (stock in context.portfolio.positions.keys() or last_prices[stock][-1] > current_data[stock].low_limit)] # 过滤次新股 def filter_new_stock(context, stock_list): yesterday = context.previous_date return [stock for stock in stock_list if not yesterday - get_security_info(stock).start_date < datetime.timedelta(days=375)]