本策略的核心是精选ST股票,国九条过滤,防止直接暴雷退市,只做ST股票的弱转强,弱转强的认定标准是前日涨停但昨日不涨停,昨日收盘价大于前一日最低价,昨日收盘价大于10日线,昨日成交量大于前一日的成交量但小于前一日成交量的10倍,股价大于1;这类股票强势,有资金介入,能获得超额收益。

回测数据如下:

*回测数据只作测试用,不代表未来实际收益

1、策略初始化配置

主要定义了持股数量、当日股票池、运行周期等等

g.stock_num=5
g.today_list=[]#当日股票池

run_daily(perpare,time="9:25")
run_daily(buy,time="9:30")
run_daily(sell,time='13:00')
run_daily(sell,time='14:00')
2、选股逻辑
(1)选取全市场所有的ST股票
##获取所有ST股##               
def get_st(context):
    yesterday=context.previous_date
    stockList=get_all_securities(types='stock',date=yesterday).index
    st_data=get_extras('is_st',stockList, count = 1,end_date=yesterday)
    st_data = st_data.T
    st_data.columns = ['is_st']
    st_data=st_data[st_data['is_st']==True]
    df = st_data.index.tolist()
    return df 
(2)1、4、12月份进行国九条过滤

1、4、12月份进行国九条过滤,避免直接暴雷和退市风险

#1 4 12月 国九
singal=today_is_between(context)
if singal==True:
    print(f'筛选前:{len(stk_list)}')
    stk_list=GJT_filter_stocks(stk_list)
    print(f'筛选后:{len(stk_list)}')
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(stocks):
    # 国九更新:过滤近一年净利润为负且营业收入小于1亿的
    # 国九更新:过滤近一年期末净资产为负的 (经查询没有为负数的,所以直接pass这条)
    q = query(
        valuation.code,
        valuation.market_cap,  # 总市值 circulating_market_cap/market_cap
        income.np_parent_company_owners,  # 归属于母公司所有者的净利润
        income.net_profit,  # 净利润
        income.operating_revenue  # 营业收入
        #security_indicator.net_assets
    ).filter(
        valuation.code.in_(stocks),
        valuation.market_cap.between(10, 500),
        income.np_parent_company_owners > 0,
        income.net_profit > 0,
        income.operating_revenue > 1e8,
        indicator.roe>0,
        indicator.roa>0,
    )
    df = get_fundamentals(q)

    final_list=list(df.code)
            
    return final_list
(4)技术指标筛选

昨日收盘价大于前一日最低价,昨日收盘价大于10日线,昨日成交量大于前一日成交量但小于前一日成交量的10倍,股价大于1

def filter_stocks(context, stocks):
    yesterday = context.previous_date
    df = get_price(
        stocks,
        count=11,
        frequency='1d',
        fields=['close', 'low', 'volume'],
        end_date=yesterday,
        panel=False
        ).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'] == yesterday]
    valid_stocks = latest_data[conditions]['code'].unique().tolist()
    
    return valid_stocks
(5)弱转强认定

前日涨停但昨日不涨停的股票

##筛选昨日不涨停的股票##
def rzq_list(context,initial_list): 
    # 文本日期
    date = context.previous_date #昨日
    date = transform_date(date, 'str')
    date_1=get_shifted_date(date, -1, 'T')#前日
    # 昨日不涨停
    h1_list = get_ever_hl_stock(initial_list, date)
    # 前日涨停过滤
    elements_to_remove = get_hl_stock(initial_list, date_1)

    
    zb_list = [stock for stock in h1_list if stock  in elements_to_remove]


    return zb_list
(6)开盘认定

选择开盘低开3个点左右,换手率高的股票

    df['open_now'] = [current_data[s].day_open for s in stk_list]
    df = df[(df['open_now']/df['close'])< 1.01] #低开越多风险越大,选择3个多点即可
    df = df[(df['open_now']/df['close'])> 0.95]
    stk_list = list(df.index)
    if len(stk_list)==0:
        return
    df=get_valuation(stk_list, start_date=context.previous_date, 
                                    end_date=context.previous_date, 
                                    fields=['turnover_ratio', 'market_cap','circulating_market_cap']
                                    )
                       
    df = df.sort_values(by='turnover_ratio', ascending=False)
3、调仓逻辑
(1)买入

可用余额平均分配金额买入

def buy(context):
    target=g.today_list
    hold_list = list(context.portfolio.positions)
    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.available_cash
        cash_per_stock = value / num
        current_data = get_current_data()  # 实时数据对象
        for stock in target:
            # 排除停牌和涨跌停无法交易的股票
            if current_data[stock].paused or \
            current_data[stock].last_price==current_data[stock].low_limit or \
            current_data[stock].last_price==current_data[stock].high_limit:
                continue
            order_value(stock, cash_per_stock)  # 按金额买入[6](@ref)
            log.info (f"买入 {stock}")
(2)卖出

第二天未涨停并且亏损超过3%或者盈利大于0或者昨日涨停并且当前不是跌停,满足上述条件就卖出

def sell(context):
    hold_list = list(context.portfolio.positions)
    if hold_list:
        current_data = get_current_data()
        yesterday=context.previous_date
    
        # 批量获取昨日涨停数据
        df_history = get_price(hold_list,end_date=yesterday,frequency='daily',fields=['close', 'high_limit'],count=1,panel=False)
        df_history['avg_cost']=[context.portfolio.positions[s].avg_cost for s in hold_list]
        df_history['price']= [context.portfolio.positions[s].price for s in hold_list]
        df_history['high_limit']= [current_data[s].high_limit for s in hold_list]
        df_history['low_limit']= [current_data[s].low_limit for s in hold_list]
        df_history['last_price']= [current_data[s].last_price for s in hold_list]
        # 条件1:未涨停
        cond1 = (df_history['last_price'] != df_history['high_limit'])
        # 条件2.1:亏损超过3%
        ret_matrix = (df_history['price'] / df_history['avg_cost'] - 1) * 100
        cond2_1 = ret_matrix < -3
        # 条件2.2:盈利超过0%
        cond2_2 = ret_matrix > 0
        # 条件2.3:昨日涨停
        cond2_3 = (df_history['close'] == df_history['high_limit'])
        
        # 组合卖出条件(逻辑或运算)
        sell_condition = cond1 & (cond2_1 | cond2_2 | cond2_3)
        # 生成卖出列表(过滤无效订单)
        sell_list = df_history[
            sell_condition & 
            (df_history['last_price'] > df_history['low_limit'])
        ].code.tolist()
        
        # 批量下单
        for s in sell_list:
            order_target_value(s, 0)
            print(f'卖出 {s} | 成本价:{context.portfolio.positions[s].avg_cost:.2f} 现价:{context.portfolio.positions[s].price:.2f}')
            print('-'*50)

这篇文章主要分享ST股弱转强策略,主要的逻辑在选股条件以及卖出条件上,适合小仓位的风险投资者。

如果有不懂的,欢迎找我一起交流,加入量化交易大家庭

Logo

加入社区!打开量化的大门,首批课程上线啦!

更多推荐