小市值单因子策略
首先我们设定一些初始化条件如下
from jqdata import *
from jqlib.technical_analysis import *
import datetime
from jqfactor import *
import numpy as np
import pandas as pd
#初始化函数
def initialize(context):
# 设定基准
set_benchmark('000905.XSHG')
# 用真实价格交易
set_option('use_real_price', True)
# 打开防未来函数
set_option("avoid_future_data", True)
# 将滑点设置为0
set_slippage(FixedSlippage(0.02))
# 设置交易成本万分之三,不同滑点影响可在归因分析中查看
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')
# 过滤order中低于error级别的日志
log.set_level('order', 'error')
#初始化全局变量
g.no_trading_today_signal = False
g.stock_num = 115
g.hold_list = [] #当前持仓的全部股票
g.yesterday_HL_list = [] #记录持仓中昨日涨停的股票
g.target_stock = []
# 设置交易运行时间
# 每周第一个交易日运行
run_weekly(weekly_filter, 1, time='before_open')
run_weekly(get_stock_list, 1, time='9:10')
run_daily(prepare_stock_list, '9:05')
run_weekly(weekly_adjustment, 1, '9:31')
run_daily(close_account, '14:30')
run_daily(print_position_info, '15:10')
g.pools = set()
上述代码可以知道,weekly_filter函数是每周一开盘前运行,get_stock_list函数是每周一9点10分运行,prepare_stock_list函数是每天9点5分运行,weekly_adjustment函数是每周一9点31分运行,close_account函数和print_position_info是每天14点30分和15点10分运行。
def weekly_filter(context):
today = context.current_dt
yesterday = today - datetime.timedelta(days=1)
start_day = today - datetime.timedelta(days=375)
yesterday = yesterday.strftime('%Y-%m-%d')
codes = list(g.pools)
# 选出小市值的股票
q = query(
valuation.code,
valuation.circulating_market_cap
).filter(
valuation.circulating_market_cap
).order_by(
valuation.circulating_market_cap.asc())
codes = get_fundamentals(q).code.tolist()
# 筛选出主板、创业板股票
codes = [code for code in codes if code[:2] in ('60','00','30')]
log.info("Top 10 小市值:" + str(codes[:10]))
# 过滤ST股票
df = get_extras('is_st', codes, end_date=yesterday,count=1)
df = df.T
df.columns = ['is_st']
df=df[df['is_st']==0]
codes = df.index.tolist()
# 过滤次新股
q = query(finance.STK_LIST.code).filter(
finance.STK_LIST.start_date <= start_day,
finance.STK_LIST.code.in_(codes)
)
codes = list(finance.run_query(q).code)
g.pools = set(codes)
#1-1 准备股票池
def prepare_stock_list(context):
#获取已持有列表
g.hold_list= list(context.portfolio.positions.keys())
#获取昨日涨停列表
if g.hold_list != []:
df = get_price(g.hold_list, end_date=context.previous_date, frequency='daily', fields=['close','high_limit'], count=1, panel=False, fill_paused=False)
df = df[df['close'] == df['high_limit']]
g.yesterday_HL_list = list(df.code)
else:
g.yesterday_HL_list = []
上述函数主要是对股票池进行过滤,剔除ST股、次新股、科创板和北交所,并从深市和沪市中选取股票作为备选股票池。
def get_stock_list(context):
final_list = set()
initial_list = list(g.pools)
if len(initial_list) == 0:
return []
initial_list = filter_paused_stock(initial_list)
initial_list = filter_st_stock(initial_list)
today = context.current_dt
# 通过timedelta算出前一天的日期
delta = datetime.timedelta(days=1)
yesterday = today - delta
yesterday = yesterday.strftime('%Y-%m-%d')
q = query(valuation.code,
valuation.circulating_market_cap).filter(valuation.code.in_(initial_list)).order_by(
valuation.circulating_market_cap.asc()).limit(g.stock_num)
codes = get_fundamentals(q).code.tolist()
circulating_market_cap_list = get_fundamentals(q).circulating_market_cap.tolist()
log.info('数据库拉取的股票数量:{}'.format(len(codes)))
log.info('最小流通市值:{}, 最大流通市值:{}'.format(min(circulating_market_cap_list), max(circulating_market_cap_list)))
final_list = codes.copy()
print('数据库拉取的股票:{}'.format(final_list))
final_list = filter_paused_stock(final_list)
print('过滤停牌后的股票:{}'.format(final_list) )
final_list = filter_st_stock(final_list)
g.target_stock = final_list
log.info('每周一买入的股票:', final_list)
message = '需要购买的股票为:\n {}'.format('\n'.join(final_list))
# send_message(message, channel='weixin')
#1-3 整体调整持仓
def weekly_adjustment(context):
if g.no_trading_today_signal:
return
#获取应买入列表
target_list = g.target_stock
#调仓卖出
for stock in g.hold_list:
if (stock not in target_list) and (stock not in g.yesterday_HL_list):
log.info("卖出[%s]" % (stock))
order_target(stock, 0)
else:
log.info("已持有[%s]" % (stock))
#调仓买入
position_count = len(context.portfolio.positions)
target_num = len(target_list)
if target_num > position_count:
value = context.portfolio.cash / (target_num - position_count)
for stock in target_list:
if context.portfolio.positions[stock].total_amount == 0:
if open_position(stock, value):
if len(context.portfolio.positions) == target_num:
break
上述代码主要是从备选股票池中,选取流通市值最小的N只股票,在每周一早上开盘时进行轮换。
如果想要完整的代码分享,请关注小木屋并私信领取哦。