如何使用Python实现欧奈尔杯柄形态选股

企业微信

欧奈尔杯柄形态到底长什么样

杯柄形态是威廉·欧奈尔在CANSLIM体系中提出的经典看涨中继形态。它像一只带把手的茶杯:股价先下跌20%到35%,形成圆弧底(杯部),随后反弹到前期高点附近,再小幅回落5%到15%形成柄部,柄部成交量萎缩,最终放量突破杯柄高点。

杯部深度通常不超过35%,柄部回调幅度不超过杯部深度的一半。柄部持续时间1到2周,长柄容易失败。突破点需要成交量比日均量放大40%以上。

如何使用Python实现欧奈尔杯柄形态选股

用Python把形态翻译成数字

人眼识别杯柄容易,让计算机理解需要把形态拆解成可计算的指标。核心维度包括:

  • 杯部深度:区间最高价与最低价之差除以最高价,落在0.2到0.35之间

  • 杯部时长:最低点前后各20到60个交易日,整体杯部3到6个月

  • 柄部回撤:柄部最低价与杯部最高价之差除以杯部深度,小于0.5

  • 柄部缩量:柄部日均成交量小于杯部日均成交量的70%

  • 突破确认:收盘价创杯部新高,且成交量大于20日均量的1.4倍

数据获取与清洗

使用tushare获取A股日线数据。免费版有频率限制,建议先下载全市场数据到本地数据库。


import tushare as ts

import pandas as pd

import numpy as np

pro = ts.pro_api('你的token')

df = pro.daily(ts_code='000001.SZ', start_date='20200101', end_date='20231231')

df = df.sort_values('trade_date').reset_index(drop=True)

df['trade_date'] = pd.to_datetime(df['trade_date'])

df.set_index('trade_date', inplace=True)

注意前复权处理。未复权数据在除权日会出现巨大跳空,破坏形态识别。tushare的daily接口返回不复权数据,需要配合adj_factor计算复权价。

形态识别函数实现

采用滑动窗口扫描。对每个交易日,向前回溯120个交易日作为潜在杯部区间。


def find_cup_handle(df, window=120, min_depth=0.2, max_depth=0.35):

    results = []

    for i in range(window, len(df)):

        cup = df.iloc[i-window:i]

        high_idx = cup['high'].idxmax()

        low_idx = cup['low'].idxmin()



        if low_idx <= cup.index[10] or low_idx >= cup.index[-10]:

            continue



        cup_high = cup.loc[high_idx, 'high']

        cup_low = cup.loc[low_idx, 'low']

        depth = (cup_high - cup_low) / cup_high



        if not (min_depth <= depth <= max_depth):

            continue



        handle = cup.loc[high_idx:]

        if len(handle) < 5 or len(handle) > 20:

            continue



        handle_low = handle['low'].min()

        handle_retrace = (cup_high - handle_low) / (cup_high - cup_low)



        if handle_retrace > 0.5:

            continue



        cup_vol = cup.loc[low_idx:high_idx, 'vol'].mean()

        handle_vol = handle['vol'].mean()



        if handle_vol > cup_vol * 0.7:

            continue



        today = df.iloc[i]

        vol_ma20 = df['vol'].iloc[i-20:i].mean()



        if today['close'] > cup_high and today['vol'] > vol_ma20 * 1.4:

            results.append({

                'date': today.name,

                'cup_high': cup_high,

                'cup_low': cup_low,

                'depth': round(depth, 3),

                'handle_retrace': round(handle_retrace, 3),

                'breakout_vol_ratio': round(today['vol'] / vol_ma20, 2)

            })

    return pd.DataFrame(results)

回测验证与常见坑

识别出形态后,用未来20个交易日的收益率验证。欧奈尔原版要求突破后买入,跌破柄部低点止损。


def backtest(df, signals, hold_days=20):

    trades = []

    for _, row in signals.iterrows():

        entry_idx = df.index.get_loc(row['date'])

        if entry_idx + hold_days >= len(df):

            continue

        entry_price = df.iloc[entry_idx]['close']

        exit_price = df.iloc[entry_idx + hold_days]['close']

        ret = (exit_price - entry_price) / entry_price

        trades.append(ret)

    return np.mean(trades), np.std(trades), len(trades)

常见坑:

  1. 除权缺口未处理,导致假突破。必须使用前复权数据。

  2. 参数过拟合。杯部深度20%到35%是欧奈尔经验值,不同市场需要微调。A股波动大,可放宽到40%。

  3. 忽略大盘环境。CANSLIM强调只在上升趋势中买入。加入沪深300均线过滤,指数在200日均线下方时暂停开仓。

  4. 柄部时间太短。少于5个交易日的柄部往往是假柄,容易直接失败。

优化方向

用向量化替代循环。pandas的rolling配合argmax能大幅提速。计算杯部最高点位置时,用cup['high'].rolling(window).apply(np.argmax)。

引入机器学习过滤。把形态特征输入随机森林,用历史突破后收益作为标签,自动学习哪些杯柄更可能成功。特征包括杯部深度、柄部回撤、成交量萎缩比例、突破日相对大盘强度。

多周期共振。日线杯柄叠加周线MACD金叉,胜率提升明显。周线过滤掉日线级别的噪音。

基本面叠加。欧奈尔要求当季EPS增长25%以上。用tushare的fina_indicator接口获取净利润同比增速,只保留高增长股票。

转载请注明出处:https://www.lianghuajiaoyi.top/wenzhang/ou-naier-bei-bing-xingtai-Python-xuangu-897.html