下跌行情你不妨试试这个策略,准确率还行,附量化源码

188 阅读7分钟

下跌行情你不妨试试这个策略,准确率还行,附量化源码

大家好,我是花姐。最近不是行情又一波回落嘛,说实话,我本人对这种“横着等死、竖着止损”的行情体感……挺差的 😂 不过也不能全躺平,有意思的东西还是得多琢磨。上周一个朋友,专做短线技术面的那种,突然发我个图形思路——日线涨停→缩量调整→20日线企稳买入

image.png

image.png 我一开始听还挺敷衍:“又是这种套路?👀” 但他说了句很关键的话:“你去叠下题材试试,搭配热点,成功率能拉到80%。”

啊这……那我肯定不信啊。

不过咱搞量化的,不信嘴信回测。于是我开干了。


图形信号的逻辑拆解

咱们先把这个“技术图形”讲清楚:

  1. 涨停板:说明这票短期有点妖,或者资金关注度比较高。哪怕后面调整,主力可能没完全走。
  2. 缩量调整:典型洗盘动作,筹码干净,不怕砸。
  3. 20日线企稳:常用做中期支撑,很多游资/量化都会在这儿盯盘。

简单说,这逻辑就像——有辆车突然提速(涨停),然后靠边慢慢滑行(缩量整理),接着到了个减震带(20日线)发现,嗯?没掉下去?那可能要继续起飞 🚀

所以,聪明的你一定会发现:这其实是一种博短反的形态策略,关键是低风险、小肉稳、偶尔吃大肉


思路明确了,怎么用Python搞定?

说干就干。我用的还是老搭档 AKshare + pandas

我分3步走:

  • 下载25年的全部A股日线行情
  • 找出符合这种图形条件的股票
  • 再看买入以后的表现,验证传说中“80%成功率”靠不靠谱

第一步:下载数据

这里我们用了AKShare库来实现

先下载所有股票的列表,返回一个Dataframe的格式

import akshare as ak
stock_list = ak.stock_info_a_code_name()
print(stock_list)

for i,r in stock_list.iterrows():
    code = r.code
    df = ak.stock_zh_a_hist(
                        symbol=code,
                        start_date='20250101',
                        end_date='20250523',
                        adjust='qfq'
                    )
    df.to_csv(str(code)+".csv",index=False)

有条件的可以写个多线程,下载速度会很快 或者像花姐这样写个小工具

image.png

第二步:识别图形(重点在图形)

别直接上来就写策略,踩坑了你就知道多痛。

我第一版就吃了个大亏:简单以为“涨停→连跌→到20日线”可以用收盘价判定。但实际上,涨停也分真假,连跌也有洗盘or崩盘之分,20日线也不能当神明膜拜

所以,我做了些小修正👇

核心判断条件:
  • 最近N天(我这里用的是30天)内有一天涨停
  • 涨停后到回踩20日均线期间呈缩量状态
  • 当前股价贴近或略破20日均线(容忍度2%)
  • 成交量不能突然放大(防止反抽砸盘)

数据验证: 发现信号以后,次日按照开盘价买入,然后统计未来10天内最高收益。

以下是策略源码,感兴趣的可以在这个基础上做优化。

import pandas as pd
import numpy as np
import os
from scipy.stats import linregress

def is_zhangting(row, exchange='sh'):
    pct = row['涨跌幅']
    if exchange == 'sh':
        return pct >= 9.8
    else:
        return pct >= 19.5

def is_shrink_trend(volumes):
    # 用线性回归判断是否为下降趋势
    x = np.arange(len(volumes))
    slope, _, _, _, _ = linregress(x, volumes)
    return slope < 0  # 斜率小于0说明在缩量

def check_signal(df, code):
    df = df.copy()
    df = df.sort_values('日期')
    df.reset_index(drop=True, inplace=True)
    df['20日均线'] = df['收盘'].rolling(20).mean()

    if len(df) < 45:
        return []

    if code.startswith('6'):
        exchange = 'sh'
    else:
        exchange = 'sz'

    signals = []
    
    # 提取最近30根K线
    recent_df = df.iloc[-30:].copy()
    recent_df.reset_index(drop=True, inplace=True)
    
    i = 0 
    while i < len(recent_df)-3:
        row = recent_df.iloc[i]
        if not is_zhangting(row, exchange):
            i += 1
            continue

        # 涨停后找回踩
        for j in range(i + 1, len(recent_df)):
            price = recent_df.iloc[j]['收盘']
            ma20 = recent_df.iloc[j]['20日均线']
            if pd.isna(ma20):
                continue
            if abs(price - ma20) / ma20 <= 0.02:
                回踩段 = recent_df.iloc[i + 1 : j + 1]
                if len(回踩段) < 3:
                    break

                diff = 回踩段['收盘'] - 回踩段['开盘']
                red_k = (diff > 0).sum()
                green_k = (diff <= 0).sum()
                if red_k >= green_k:
                    break
                if not is_shrink_trend(回踩段['成交量'].values):
                    break

                buy_index = j + 1
                if buy_index >= len(recent_df):
                    break

                buy_row = recent_df.iloc[buy_index]
                buy_price = buy_row['开盘']
                buy_date = buy_row['日期']

                future = recent_df.iloc[buy_index+1 : buy_index + 11]
                max_high = future['最高'].max()
                max_gain = (max_high - buy_price) / buy_price * 100

                signals.append({
                    '股票代码': code,
                    '涨停日期': row['日期'],
                    '买入日期': buy_date,
                    '买入价': round(buy_price, 2),
                    '未来最高涨幅%': round(max_gain, 2),
                    '红K数': int(red_k),
                    '绿K数': int(green_k),
                    '是否缩量': True,
                    '观察K线数': len(future)
                })

                i = j + 10  # 跳过一段,防止重复命中同一波段
                break

        i += 1

    return signals



# 遍历CSV文件夹
path = r'行情目录'  # 当前目录
files = [f for f in os.listdir(path) if f.endswith('.csv')]

all_signals = []

for file in files:
    
    file = os.path.join(path,file)
    try:
        df = pd.read_csv(file)
        if '股票代码' not in df.columns:
            print(f"文件 {file} 缺少必要字段")
            continue
        code = str(df['股票代码'].iloc[0])
        signals = check_signal(df, code)
        if signals:
            print(code,'发现信号')
            all_signals.extend(signals)
    except Exception as e:
        print(f"错误处理文件 {file}: {e}")
        

# 保存结果
result_df = pd.DataFrame(all_signals)
result_df.to_csv('信号筛选结果.csv', index=False)
print("筛选完成,结果保存至 信号筛选结果.csv")

回测验证:小肉多?大肉看命?

📊结果感人:

我把未来够10个交易日的数据做了个统计,涨幅大于1%的准确率大于91.53%,高的可怕。 这是最高收益散点图,收益为负的基本上没几个。

image.png

当日这只是统计最高收益,操作不好可能还拿不到,所以我那个朋友说他有80%的成功率还是相当靠谱的。


题材叠加才是精华部分

单纯靠图形就能有9成胜率,那如果你再懂点“题材炒作”——比如CPO、新质生产力、AI大模型啥的。

你在信号出现那几天叠个热点进去,胜率会明显更稳,爆发力也会更猛。


最后啰嗦几句

这类图形交易的风险其实不在图形本身,而在你无法判断什么时候不该上。比如大盘破位、板块退潮、情绪低迷,那种时候,即便信号再漂亮也别动。

还有很多人问我:“花姐,这图形策略能不能全自动运行?” 当然能。但如果你没做好风险控制和过滤逻辑,那不是策略,是刀片机。切谁都快,就看你心脏硬不硬了。

策略世界不缺模型,缺的是你这种肯愿意多敲几行代码的灵魂 😉

我们下期见。

—— 花姐 🐍💰

当然可以,像这种涉及技术图形和量化策略的文章,风险提示一定要写得专业但不过于吓人,既要起到免责作用,又不能打击读者热情。

下面是我为你量身定制的一段风险提示,语气是偏“实在型”,既保持花姐一贯的真诚,也能给读者留个好印象👇


风险提示

本文内容仅为本人在实盘研究过程中的经验分享与技术交流,不构成任何投资建议。文中提及的选股逻辑、量化策略均基于历史数据回测,有一定的统计参考意义,但并不代表未来一定有效。

股票市场受多种不可控因素影响,即使技术形态符合,也可能因市场情绪、题材退潮、突发事件等原因出现偏离。策略实测中提及的“胜率”是统计结果,不保证每次操作都能获利。

请各位读者在使用策略前务必结合自身风险偏好进行独立判断,若据此操作,风险自负。感谢理解与支持。