| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- # 克隆自聚宽文章: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)]
|