ptrade量化策略之ST股弱转强,回测年化115%
本策略的核心是精选ST股票,国九条过滤,防止直接暴雷退市,只做ST股票的弱转强
·
本文是策略的ptrade版本,可以直接在ptrade上回测和实盘交易
本策略的核心是精选ST股票,国九条过滤,防止直接暴雷退市,只做ST股票的弱转强,弱转强的认定标准是前日涨停但昨日不涨停,昨日收盘价大于前一日最低价,昨日收盘价大于10日线,昨日成交量大于前一日的成交量但小于前一日成交量的10倍,股价大于1;这类股票强势,有资金介入,能获得超额收益。
回测(2025.1.1-2025.6.26)数据如下:
*回测数据只作测试用,不代表未来实际收益
1、策略初始化配置
主要定义了持股数量、当日股票池、运行周期等等
g.check_out_lists = []
g.today_list = []
g.stock_num = 5
# 9点35买入
if context.current_dt.hour == 9 and context.current_dt.minute == 35:
buy(context)
# 13:00卖出
if context.current_dt.hour == 13 and context.current_dt.minute == 0:
sell(context)
# 14:00卖出
if context.current_dt.hour == 14 and context.current_dt.minute == 0:
sell(context)
2、选股逻辑
(1)选取全市场所有的ST股票
##获取所有ST股##
def get_st(context):
pre_date = context.previous_date.strftime('%Y%m%d')
stocks = get_Ashares(pre_date)
st_stocks = []
st_status = get_stock_status(stocks, query_type='ST', query_date=pre_date)
for i in stocks:
if st_status[i] is True:
st_stocks.append(i)
return st_stocks
(2)1、4、12月份进行国九条过滤
1、4、12月份进行国九条过滤,避免直接暴雷和退市风险
def today_is_between(context):
today = context.current_dt.strftime('%m-%d')
if ('01-15' <= today) and (today <= '01-31'):
return True
elif ('04-15' <= today) and (today <= '04-31'):
return True
elif ('12-15' <= today) and (today <= '12-31'):
return True
else:
return False
(3)国九条过滤条件
市值在10到500亿,归属于母公司所有者的净利润大于0,净利润大于0,营业收入大于1亿,roe大于0,roa大于0
def GJT_filter_stocks(context, stocks):
# 国九更新:过滤近一年净利润为负且营业收入小于1亿的
# 国九更新:过滤近一年期末净资产为负的 (经查询没有为负数的,所以直接pass这条)
df1 = get_fundamentals(stocks,"income_statement", fields=["np_parent_company_owners", "net_profit", "operating_revenue"],date=context.previous_date)
df2 = get_fundamentals(stocks,"profit_ability", fields=["roa", "roe"],date=context.previous_date)
df4 = pd.concat([df1, df2], axis=1)
df4 = df4[(df4["np_parent_company_owners"] > 0) & (df4["net_profit"] > 0) & (df4["operating_revenue"] > 1e8) & (df4["roa"] > 0) & (df4["roe"] > 0)]
final_list = df4.index.tolist()
final_list = filter_halt_status(final_list)
return final_list
(4)技术指标筛选
昨日收盘价大于前一日最低价,昨日收盘价大于10日线,昨日成交量大于前一日成交量但小于前一日成交量的10倍,股价大于1
def filter_stocks(context, stocks):
pre_date = context.previous_date.strftime('%Y%m%d')
df = get_price(stocks, end_date=pre_date, frequency='1d',
fields=['close', 'low', 'volume'], count=11)
# 按股票分组处理
df['time'] = df.index.tolist()
df = df.reset_index()
grouped = df.groupby('code')
# 计算技术指标
ma10 = grouped['close'].transform(lambda x: x.rolling(10).mean()) # 10日均线
prev_low = grouped['low'].shift(1) # 前一日最低价
prev_volume = grouped['volume'].shift(1) # 前一日成交量
# 构建筛选条件
conditions = (
(df['close'] > prev_low) & # 多头排列
(df['close'] > ma10) & # 10日线上方
(df['volume'] > prev_volume) & # 放量
(df['volume'] < 10 * prev_volume) & # 成交量未暴增
(df['close'] > 1) # 股价>1
)
# 获取最新交易日数据
latest_data = df[df['time'] == pre_date]
valid_stocks = latest_data[conditions]['code'].unique().tolist()
print("valid_stocks-------", valid_stocks)
return valid_stocks
(5)弱转强认定
前日涨停但昨日不涨停的股票
def rzq_list(context,initial_list):
trading_days = get_trade_days(count=3)
date = trading_days[1] #昨日
date_1 = trading_days[0] #前日
# 昨日不涨停
h1_list = get_ever_hl_stock(initial_list, transform_date(date))
# 前日涨停过滤
elements_to_remove = get_hl_stock(initial_list, transform_date(date_1))
zb_list = [stock for stock in h1_list if stock in elements_to_remove]
return zb_list
##筛选不涨停##
def get_ever_hl_stock(initial_list, date):
hl_list = []
for i in initial_list:
stock_flag = check_limit(i, query_date=date)[i]
if stock_flag == 0:
hl_list.append(i)
return hl_list
##筛选出涨停的股票##
def get_hl_stock(initial_list, date):
hl_list = []
for i in initial_list:
stock_flag = check_limit(i, query_date=date)[i]
if stock_flag == 1:
hl_list.append(i)
return hl_list
(6)开盘认定
选择开盘低开3个点左右,换手率高的股票
pre_date = context.previous_date.strftime('%Y%m%d')
df = get_price(filter_list, end_date=pre_date, frequency='1d',
fields=['close'], count=1)
df['open_now'] = [get_trend_data(stocks=s)[s]['hq_px'] for s in filter_list]
df = df[(df['open_now']/df['close'])< 1.01] #低开越多风险越大,选择3个多点即可
df = df[(df['open_now']/df['close'])> 0.95]
3、调仓逻辑
(1)买入
可用余额平均分配金额买入
def buy(context):
target=g.today_list
hold_list = list(context.portfolio.positions.keys())
num=g.stock_num-len(hold_list)
if num==0:
return
target=[x for x in target if x not in hold_list][:num]
if len(target) > 0:
# 分配资金(等权重买入)
value=context.portfolio.cash
cash_per_stock = value / num
for stock in target:
# 排除停牌和涨跌停无法交易的股票
stock_flag = check_limit(stock)[stock]
if stock_flag != 0:
continue
order_value(stock, cash_per_stock) # 按金额买入[6](@ref)
log.info (f"买入 {stock}")
(2)卖出
第二天未涨停并且亏损超过3%或者盈利大于0或者昨日涨停并且当前不是跌停,满足上述条件就卖出
def sell(context):
hold_list = updatePositions(context)
sell_list = []
if len(hold_list) > 0:
for stock in hold_list:
pre_date = context.previous_date.strftime('%Y%m%d')
pos = get_position(stock)
last_price = pos.last_sale_price
avg_cost = pos.cost_basis
ret_matrix = (last_price / avg_cost - 1) * 100
# 条件2.1:亏损超过3%
cond2_1 = ret_matrix < -3
# 条件2.2:盈利超过0%
cond2_2 = ret_matrix > 0
stock_flag = check_limit(stock)[stock]
# 条件2.3:昨日涨停
cond2_3 = check_limit(stock, query_date=pre_date)[stock]
if (stock_flag == 0) and (cond2_1 or cond2_2 or cond2_3 == 1):
sell_list.append(stock)
# 批量下单
for s in sell_list:
pos = get_position(s)
if pos.enable_amount > 0:
order_target_value(s, 0)
print("卖出:", s)
这篇文章主要分享ST股弱转强策略,主要的逻辑在选股条件以及卖出条件上,适合小仓位的风险投资者。
如果有不懂的,欢迎找我一起交流,加入量化交易大家庭
更多推荐
所有评论(0)