🚀 AI 一键生成 ptrade 策略代码

PTrade 价值投资实战:如何构建低市净率 (Low PB) 选股策略?

ptrade | 发布时间: 2025/12/22
以下内容由 EasyQuant 生成。

问题描述

怎么写一个低市净率选股策略?

解决方案

这是一个基于 低市净率(Low P/B Ratio) 的选股策略。

策略逻辑

  1. 选股范围:沪深300指数成分股(确保流动性和基本面相对稳定)。
  2. 核心因子:市净率(PB)。市净率越低,通常意味着股票估值越低,安全边际可能越高。
  3. 过滤条件:剔除ST股、停牌股、退市股。
  4. 调仓频率:按月调仓(每月第一个交易日)。
  5. 持仓方式:选取PB最低的10只股票,等权重买入。

策略代码实现

import pandas as pd
import numpy as np

def initialize(context):
    """
    初始化函数,设置策略参数
    """
    # 1. 设置基准为沪深300
    set_benchmark('000300.SS')
    
    # 2. 设置股票池为沪深300
    g.index_security = '000300.SS'
    
    # 3. 设置持仓数量
    g.stock_num = 10
    
    # 4. 设置交易费率(可选,根据实际情况调整)
    set_commission(commission_ratio=0.0003, min_commission=5.0, type="STOCK")
    
    # 5. 每日检查是否需要调仓
    run_daily(context, rebalance_check, time='09:30')

def rebalance_check(context):
    """
    每日运行,判断是否为月初,如果是则执行调仓逻辑
    """
    # 获取当前日期
    current_date = context.blotter.current_dt.date()
    
    # 获取上一个交易日
    prev_day = get_trading_day(-1)
    
    # 如果当前月份与上一个交易日月份不同,说明是月初,执行调仓
    if current_date.month != prev_day.month:
        log.info("今日为月初,开始执行低市净率选股调仓...")
        trade_logic(context)

def trade_logic(context):
    """
    核心交易逻辑:选股与买卖
    """
    # 1. 获取沪深300成分股
    security_list = get_index_stocks(g.index_security)
    
    # 2. 过滤ST、停牌、退市股票
    # filter_stock_by_status 是PTrade内置便捷函数
    # 默认过滤 ["ST", "HALT", "DELISTING"]
    valid_stocks = filter_stock_by_status(security_list)
    
    if not valid_stocks:
        log.warning("没有符合条件的股票")
        return

    # 3. 获取基本面数据:市净率 (pb)
    # 注意:get_fundamentals 在回测中默认取当前回测日期的数据
    # 为了避免未来函数,通常数据是截止到前一天的,PTrade底层已做处理
    df = get_fundamentals(
        security=valid_stocks,
        table='valuation',
        fields=['pb'],
        date=None # 默认为当前回测日期
    )
    
    # 数据清洗:去除空值
    df = df.dropna()
    
    # 4. 排序与筛选
    # 按市净率从小到大排序
    df = df.sort_values(by='pb', ascending=True)
    
    # 取前 N 只股票
    target_list = df.index[:g.stock_num].tolist()
    log.info("本月目标持仓股票: %s" % target_list)
    
    # 5. 执行交易
    # 获取当前持仓
    current_positions = list(context.portfolio.positions.keys())
    
    # 卖出不在目标列表中的股票
    for stock in current_positions:
        if stock not in target_list:
            # 检查是否停牌,停牌无法卖出(虽然下单会失败,但最好判断一下)
            # 这里简单直接下单,依靠系统拒单机制,或者使用 get_stock_status 判断
            order_target_value(stock, 0)
    
    # 买入目标列表中的股票
    # 采用等权重分配资金
    if len(target_list) > 0:
        # 计算每只股票的目标市值 = 总资产 / 股票数量
        target_value = context.portfolio.portfolio_value / len(target_list)
        
        for stock in target_list:
            order_target_value(stock, target_value)

def handle_data(context, data):
    """
    盘中运行函数,本策略主要逻辑在 run_daily 中,此处留空
    """
    pass

代码关键点解析

  1. get_fundamentals: 这是获取财务数据的核心函数。我们查询 valuation 表中的 pb(市净率)字段。
  2. filter_stock_by_status: 这是一个非常实用的辅助函数,用于一键过滤掉ST股、停牌股和退市股,防止买入风险过大或无法交易的标的。
  3. order_target_value: 使用目标市值下单。这个函数会自动计算需要买入或卖出的股数,使该股票的持仓市值达到指定金额。这对于等权重策略非常方便。
  4. 调仓时间控制: 通过比较 current_date.monthprev_day.month 来判断是否是该月的第一个交易日。

策略风险提示

  • 低估值陷阱:市净率极低的公司可能面临经营困难、资产减值或破产风险(即所谓的“烟蒂股”)。
  • 行业偏差:低PB策略容易集中在银行、钢铁、煤炭等传统行业,缺乏成长性,可能导致策略在成长股行情中跑输大盘。
  • 流动性风险:虽然选择了沪深300,但在极端市场下,部分股票仍可能缺乏流动性。

常见问题 (Q&A)

Q: 如何修改选股范围为中证500?
A: 将 initialize 函数中的 g.index_security = '000300.SS' 修改为 g.index_security = '000905.XBHS' (注意PTrade中指数后缀可能不同,通常中证500是 000905.SS000905.XBHS,具体请参考PTrade文档附录)。

Q: 如何增加过滤条件,例如剔除亏损股?
A: 在 get_fundamentals 中增加查询 income_statement 表的 net_profit 字段,然后在获取数据后,筛选 net_profit > 0 的股票。

Q: 为什么回测时要用 order_target_value 而不是 order
A: order_target_value 是调仓神器。比如你持有股票A市值5万,目标是10万,它会自动买入5万;如果目标是0,它会自动卖出所有。这比手动计算买卖数量要简单且准确得多。