微盘反转策略 v1

2 min read

import pandas as pd

==================== 可调参数 ====================

g_pool_size = 400 # 微盘股池大小 g_buy_count = 20 # 买入股票数量 g_look_back = 5 # 反转回看天数

def init(context): context.pool_size = g_pool_size context.buy_count = g_buy_count context.look_back = g_look_back context.micro_stocks = []

# 每周第1个交易日调仓
scheduler.run_weekly(rebalance, tradingday=1)

logger.info("策略初始化完成")

def before_trading(context): """盘前获取微盘股池"""

# 获取全市场股票
all_stocks = all_instruments('CS').order_book_id.tolist()

# 获取市值数据
df = get_factor(all_stocks, 'a_share_market_val_3')

if df is None or df.empty:
    logger.warn("获取市值数据失败")
    context.micro_stocks = []
    return

# 处理索引
if isinstance(df.index, pd.MultiIndex):
    df = df.reset_index(level=1, drop=True)

# 删除空值
df = df.dropna()

if df.empty:
    context.micro_stocks = []
    return

# 按市值升序排序(小市值在前)
df = df.sort_values(ascending=True)

# 取市值最小的N只
df = df.head(context.pool_size)

context.micro_stocks = df.index.tolist()
logger.info("微盘股池数量: {}".format(len(context.micro_stocks)))

def rebalance(context, bar_dict): """周度调仓"""

if len(context.micro_stocks) == 0:
    logger.warn("微盘股池为空")
    return

# 计算反转因子
stock_returns = {}
for stock in context.micro_stocks:
    try:
        hist = history_bars(stock, context.look_back + 1, '1d', 'close')
        if hist is not None and len(hist) >= 2:
            ret = float(hist[-1]) / float(hist[0]) - 1.0
            stock_returns[stock] = ret
    except:
        pass

if len(stock_returns) == 0:
    logger.warn("无法计算收益率")
    return

# 按收益率升序排序(跌幅最大在前)
sorted_returns = sorted(stock_returns.items(), key=lambda x: x[1])
candidates = [s[0] for s in sorted_returns[:context.buy_count * 2]]

# 过滤可交易股票
target_list = []
for stock in candidates:
    if can_trade(stock, bar_dict):
        target_list.append(stock)
    if len(target_list) >= context.buy_count:
        break

if len(target_list) == 0:
    logger.warn("无可交易股票")
    return

logger.info("本周选中: {} 只".format(len(target_list)))

# 先卖
for stock in list(context.portfolio.positions.keys()):
    if stock not in target_list:
        if stock in bar_dict and bar_dict[stock].is_trading:
            order_target_percent(stock, 0)

# 后买
weight = 0.95 / len(target_list)
for stock in target_list:
    order_target_percent(stock, weight)

def can_trade(stock, bar_dict): """检查是否可交易""" try: # 检查ST if is_st_stock(stock): return False

    # 检查是否在bar_dict中
    if stock not in bar_dict:
        return False
    
    # 检查是否停牌
    if not bar_dict[stock].is_trading:
        return False
    
    # 检查涨跌停
    hist = history_bars(stock, 2, '1d', 'close')
    if hist is None or len(hist) < 1:
        return True
    
    yesterday_close = hist[-1]
    zt = round(1.10 * yesterday_close, 2)
    dt = round(0.90 * yesterday_close, 2)
    current = bar_dict[stock].close
    
    if current >= zt or current <= dt:
        return False
    
    return True
except:
    return False

def handle_bar(context, bar_dict): pass