问题描述
解决方案
在PTrade中,筛选低产权比率(Debt-to-Equity Ratio)的股票,主要通过获取财务数据接口 get_fundamentals 来实现。
核心概念
- 产权比率:属于偿债能力指标,计算公式为
负债总额 / 股东权益。该指标越低,表明企业长期偿债能力越强,债权人权益的保障程度越高,承担的风险越小。 - 数据获取:
- 表名:
debt_paying_ability(偿债能力表) - 字段名:
debt_equity_ratio(产权比率)
- 表名:
策略实现思路
- 确定股票池:例如选取沪深300指数成分股。
- 获取数据:在
before_trading_start中调用get_fundamentals获取成分股的debt_equity_ratio。 - 排序筛选:对获取的数据进行升序排列(从小到大),选取排名前N只股票(即产权比率最低的股票)。
- 交易执行:在
handle_data中买入筛选出的股票,卖出不在列表中的持仓。
PTrade 策略代码示例
以下是一个完整的策略代码,该策略每天从沪深300中筛选出产权比率最低的10只股票进行轮动持有。
def initialize(context):
"""
初始化函数,设置基准和全局变量
"""
# 设置回测基准为沪深300
set_benchmark('000300.SS')
# 设置要操作的股票池范围(这里仅作为初始标记,实际在before_trading_start获取)
g.index_code = '000300.SS'
# 设置持仓数量
g.stock_count = 10
# 待买入股票列表
g.target_stocks = []
def before_trading_start(context, data):
"""
盘前处理:获取财务数据并筛选股票
"""
# 1. 获取股票池(沪深300成分股)
# 注意:get_index_stocks 建议在 before_trading_start 中调用
stocks = get_index_stocks(g.index_code)
if not stocks:
log.info("未获取到成分股数据")
return
# 2. 获取前一个交易日的日期,避免未来函数
# get_fundamentals 默认在回测中取回测日期,但在实盘或为了严谨,通常指定日期
last_date = get_trading_day(-1)
date_str = last_date.strftime('%Y%m%d')
# 3. 查询偿债能力表中的产权比率
# 表名: debt_paying_ability, 字段: debt_equity_ratio
df = get_fundamentals(
stocks,
'debt_paying_ability',
'debt_equity_ratio',
date=date_str
)
# 4. 数据处理与筛选
if df is not None and not df.empty:
# 剔除空值
df = df.dropna()
# 对产权比率进行升序排列(越低越好)
# 注意:PTrade返回的DataFrame索引是股票代码,列名是字段名
df_sorted = df.sort_values(by='debt_equity_ratio', ascending=True)
# 取前 N 只股票
g.target_stocks = df_sorted.index[:g.stock_count].tolist()
log.info("今日筛选出的低产权比率股票: %s" % g.target_stocks)
else:
g.target_stocks = []
log.info("今日未获取到财务数据")
def handle_data(context, data):
"""
盘中处理:执行交易
"""
if not g.target_stocks:
return
# 1. 卖出不在目标列表中的持仓
# context.portfolio.positions.keys() 获取当前持仓的股票代码
for security in list(context.portfolio.positions.keys()):
if security not in g.target_stocks:
# 卖出该股票,目标持仓调整为0
order_target_value(security, 0)
log.info("卖出不再符合条件的股票: %s" % security)
# 2. 买入目标列表中的股票
# 简单的等权重分配资金
position_count = len(g.target_stocks)
if position_count > 0:
# 获取当前总资产(可用现金 + 持仓市值)
total_value = context.portfolio.portfolio_value
# 计算每只股票的目标市值
target_value = total_value / position_count
for security in g.target_stocks:
# 只有当股票正常交易(非停牌)时才下单
if data[security].is_open:
order_target_value(security, target_value)
# log.info("买入/调整股票: %s, 目标市值: %s" % (security, target_value))
代码关键点解析
-
get_fundamentals函数:- 这是获取财务数据的核心函数。
'debt_paying_ability':指定查询偿债能力表。'debt_equity_ratio':指定查询产权比率字段。date参数:传入前一个交易日,确保使用的是当时已发布的数据,避免回测中的“未来函数”问题。
-
数据排序:
- 使用 Pandas 的
sort_values(by='debt_equity_ratio', ascending=True)进行升序排序。产权比率越低,代表企业自有资本占比较高,财务结构相对稳健。
- 使用 Pandas 的
-
Python 版本兼容性:
- 代码使用了标准的 Python 语法,兼容 Python 3.5 及以上版本(避免了 f-string 等新特性),确保在 PTrade 环境中稳定运行。
常见问题 Q&A
Q: 产权比率的数据单位是什么?
A: 在 PTrade 的 debt_paying_ability 表中,debt_equity_ratio 通常以百分比数值形式返回(例如返回 50.5 代表 50.5%),具体数值大小可直接用于排序比较。
Q: 为什么要用 get_trading_day(-1)?
A: 财务数据通常在收盘后更新。在回测或交易当天的开盘前(before_trading_start),我们通常基于截止到昨天已有的信息做决策。虽然 PTrade 回测引擎会自动处理数据可见性,但显式指定日期是更严谨的做法。
Q: 如何结合其他指标(如市盈率)一起筛选?
A: 可以在 get_fundamentals 中同时查询多个表的数据,或者多次调用获取不同的 DataFrame,然后使用 Pandas 的 concat 或 merge 方法合并数据,再进行多重条件筛选。