问题描述
解决方案
在 PTrade 中识别“红三兵”(Three White Soldiers)等 K 线形态,主要通过获取历史行情数据(Open, High, Low, Close),然后根据形态的定义编写逻辑判断条件来实现。
对于更复杂的形态(如乌云盖顶、启明星等),PTrade 环境中通常内置了 talib 库,可以直接调用相关函数进行识别,无需手动编写每一个形态的逻辑。
以下为您提供两种实现方式:
- 基础版:通过手动编写逻辑识别“红三兵”(适合理解原理)。
- 进阶版:使用
talib库识别多种形态(适合实战,包含红三兵、三只乌鸦等)。
方法一:手动编写逻辑识别“红三兵”
红三兵的基本定义是:连续三天收阳线(收盘价 > 开盘价),且每天的收盘价都高于前一天的收盘价。
def initialize(context):
# 设置股票池,这里以几只股票为例
g.security = ['600570.SS', '000001.SZ', '600000.SS']
set_universe(g.security)
def handle_data(context, data):
# 获取过去3天的日线数据,包含开盘价和收盘价
# 注意:get_history 返回的数据不包含当前正在进行的 bar(如果是盘中运行),
# 如果是盘后分析或回测,取最近3根完整的K线
count = 3
hist = get_history(count, '1d', ['open', 'close'], security_list=g.security, fq='pre')
for stock in g.security:
# 获取该股票的开盘价和收盘价序列(数组)
# 数据顺序:索引 0 为前天,1 为昨天,2 为今天(或最近一天)
opens = hist['open'][stock].values
closes = hist['close'][stock].values
# 确保数据长度足够
if len(closes) < 3:
continue
# --- 红三兵逻辑判断 ---
# 条件1:连续三天都是阳线 (收盘价 > 开盘价)
is_red_1 = closes[0] > opens[0]
is_red_2 = closes[1] > opens[1]
is_red_3 = closes[2] > opens[2]
# 条件2:收盘价重心上移 (后一天收盘价 > 前一天收盘价)
is_rising = (closes[2] > closes[1]) and (closes[1] > closes[0])
# 综合判断
if is_red_1 and is_red_2 and is_red_3 and is_rising:
log.info("股票 %s 触发【红三兵】形态" % stock)
# 策略逻辑:如果目前没有持仓,则买入
if get_position(stock).amount == 0:
order_value(stock, 10000)
log.info("买入 %s" % stock)
方法二:使用 TA-Lib 库识别多种形态(推荐)
PTrade 支持 talib 库,这是量化领域标准的金融技术指标库,内置了数十种 K 线形态识别函数。
常用形态函数示例:
CDL3WHITESOLDIERS: 红三兵CDL3BLACKCROWS: 三只乌鸦CDLDOJI: 十字星CDLHAMMER: 锤头线
import talib
import numpy as np
def initialize(context):
g.security = ['600570.SS', '000001.SZ']
set_universe(g.security)
def handle_data(context, data):
# TA-Lib 计算通常需要较长的数据以保证准确性,这里取 20 天
# 必须获取 open, high, low, close 四个字段
count = 20
hist = get_history(count, '1d', ['open', 'high', 'low', 'close'], security_list=g.security, fq='pre')
for stock in g.security:
# 提取数据并转换为 numpy 数组 (TA-Lib 需要 float 类型的 numpy 数组)
# 注意:PTrade 的 get_history 返回的 DataFrame 可能包含 NaN,需处理
try:
opens = np.array(hist['open'][stock].values, dtype=float)
highs = np.array(hist['high'][stock].values, dtype=float)
lows = np.array(hist['low'][stock].values, dtype=float)
closes = np.array(hist['close'][stock].values, dtype=float)
except Exception as e:
# 数据不足或获取失败时跳过
continue
# --- 1. 识别红三兵 (Three White Soldiers) ---
# 返回值:100 表示出现该形态(看涨),-100 表示反向形态(看跌),0 表示未识别
res_3_soldiers = talib.CDL3WHITESOLDIERS(opens, highs, lows, closes)
# 获取最近一天的结果
if res_3_soldiers[-1] == 100:
log.info("股票 %s 出现【红三兵】形态" % stock)
# 执行买入逻辑
order(stock, 100)
# --- 2. 识别三只乌鸦 (Three Black Crows) ---
res_3_crows = talib.CDL3BLACKCROWS(opens, highs, lows, closes)
if res_3_crows[-1] == -100:
log.info("股票 %s 出现【三只乌鸦】形态" % stock)
# 执行卖出逻辑
if get_position(stock).amount > 0:
order_target(stock, 0)
# --- 3. 识别锤头线 (Hammer) ---
res_hammer = talib.CDLHAMMER(opens, highs, lows, closes)
if res_hammer[-1] == 100:
log.info("股票 %s 出现【锤头线】形态" % stock)
代码关键点解析
-
数据获取 (
get_history):- K 线形态识别必须依赖历史数据。
count参数:对于手动逻辑,取形态所需的最少天数(如红三兵取3天);对于talib,建议取稍多一些(如15-20天),虽然形态只看最近几天,但部分指标内部计算需要一定的历史平滑。fq='pre':建议使用前复权数据,消除分红配股造成的 K 线缺口干扰。
-
TA-Lib 的返回值:
- TA-Lib 的形态识别函数返回的是一个整数数组。
0: 无形态。100: 识别出看涨形态(Bullish)。-100: 识别出看跌形态(Bearish)。- 我们通常检查数组的最后一个值
result[-1]来判断当前最新 K 线是否触发形态。
-
数据类型转换:
talib严格要求输入数据为numpy.array且类型为float。PTrade 的get_history返回的数据通常可以直接使用.values转换,但为了稳健性,显式指定dtype=float是个好习惯。
常见 K 线形态函数速查表
如果您想识别其他形态,只需替换代码中的 talib 函数名即可:
| 形态名称 | TA-Lib 函数名 | 说明 |
|---|---|---|
| 红三兵 | CDL3WHITESOLDIERS |
看涨,三根连续阳线 |
| 三只乌鸦 | CDL3BLACKCROWS |
看跌,三根连续阴线 |
| 早晨之星 | CDLMORNINGSTAR |
看涨,底部反转 |
| 黄昏之星 | CDLEVENINGSTAR |
看跌,顶部反转 |
| 吞没形态 | CDLENGULFING |
阳包阴(100) 或 阴包阳(-100) |
| 十字星 | CDLDOJI |
变盘信号 |
| 锤头线 | CDLHAMMER |
底部看涨 |
| 射击之星 | CDLSHOOTINGSTAR |
顶部看跌 |
Q&A
Q: 为什么我的代码在盘中运行时,红三兵信号一直在变?
A: get_history 在盘中获取的是包含当天实时数据的。当天的 K 线尚未收盘,价格还在波动。可能上午价格上涨构成了红三兵,下午价格回落就不是了。建议在收盘前(如 14:50)或收盘后运行此类形态策略,或者在逻辑中确认前两天是确定的历史数据,仅最后一天做实时判断。
Q: 如何提高形态识别的成功率?
A: 单纯依靠 K 线形态(如红三兵)的胜率通常不高。建议结合成交量(如红三兵伴随放量)、均线趋势(如在均线多头排列时出现红三兵)或技术指标(如 MACD 金叉)进行多因子共振验证。
Q: PTrade 回测时 get_history 包含当天数据吗?
A: 在 handle_data 中,如果 include=False(默认),get_history 获取的是截止到前一分钟或前一日的数据。如果需要包含当前正在形成的 K 线进行实时判断,需要设置 include=True。但在日线级别回测中,通常使用前一日收盘后的数据或在每日收盘前进行判断。