均线、MACD、KDJ、RSI,那这4个指标,在什么情况下是最值得购买的股票?

在技术分析中,如果 均线、MACD、KDJ、RSI 这四个指标都同时发出买入信号,那么该股票可能处于一个较好的买点。我们可以从多个角度来分析何时是最佳买点


四大指标共振的最佳买点

要让这四个指标同时发出买入信号,通常市场会出现以下几种情况:

1. 突破均线支撑,形成多头排列

  • 5日均线(MA5)上穿 10日均线(MA10),金叉向上
  • 股价站上 20 日均线,均线系统由空头排列转为多头排列
  • 股价在 60 日均线附近获得支撑,不破位

📌 信号解读:说明市场买盘强劲,趋势可能反转向上。


2. MACD 形成金叉,红柱放量

  • DIF 线上穿 DEA 线,形成 MACD 金叉
  • MACD 绿柱逐渐缩短,红柱开始放量
  • MACD 处于 0 轴下方刚刚翻红,后续上涨空间大

📌 信号解读:MACD 反映的是趋势强度,金叉说明上涨趋势正在形成。


3. KDJ 低位金叉,K 值向上穿 D 值

  • K 线(短期趋势)上穿 D 线(长期趋势),形成金叉
  • K 值小于 30,说明股价严重超卖
  • J 线急速上升,表明短期资金流入较强

📌 信号解读:说明短期市场可能出现强势反弹,尤其是在底部区域。


4. RSI 超卖反弹,突破 30

  • RSI < 30 时,开始向上回升
  • RSI 由下往上突破 50,表明市场买盘增强
  • 股价创新低,但 RSI 没有创新低(底背离),预示着反弹可能到来

📌 信号解读:说明市场已经超卖,买方力量开始增强。


综合结论:最值得买入的时机

均线:股价突破 5 日、10 日均线,且均线形成多头排列。
MACD:0 轴下方形成金叉,红柱放量。
KDJ:K 线和 D 线在低位金叉,J 线向上突破。
RSI:从超卖区(30 以下)反弹,突破 50 进一步确认。

最佳买点
股价经过长期下跌,在底部震荡整理后,四个指标同时形成买入信号,这时买入胜率较高。


注意事项

  1. 结合成交量:如果四个指标共振,同时放量上涨,信号更加强烈。
  2. 避免单边趋势行情:在强烈下跌趋势中,RSI 和 KDJ 的买入信号可能会失效。
  3. 结合市场环境:如果大盘指数整体走弱,即使个股技术指标看起来很好,也要谨慎。

附:通过python选股 

import akshare as ak
import pandas as pd
import numpy as np
from tqdm import tqdm



# 计算均线
def calculate_ma(df, windows=[5, 10, 20]):
    for w in windows:
        df[f'MA{w}'] = df['close'].rolling(window=w).mean()
    return df


# 计算 MACD 指标
def calculate_macd(df, short=12, long=26, signal=9):
    df['EMA12'] = df['close'].ewm(span=short, adjust=False).mean()
    df['EMA26'] = df['close'].ewm(span=long, adjust=False).mean()
    df['DIF'] = df['EMA12'] - df['EMA26']
    df['DEA'] = df['DIF'].ewm(span=signal, adjust=False).mean()
    df['MACD'] = 2 * (df['DIF'] - df['DEA'])  # 计算 MACD
    return df


# 计算 KDJ 指标
def calculate_kdj(df, n=9, k_smooth=3, d_smooth=3):
    low_min = df['low'].rolling(window=n).min()
    high_max = df['high'].rolling(window=n).max()
    df['RSV'] = (df['close'] - low_min) / (high_max - low_min) * 100
    df['K'] = df['RSV'].ewm(alpha=1 / k_smooth, adjust=False).mean()
    df['D'] = df['K'].ewm(alpha=1 / d_smooth, adjust=False).mean()
    df['J'] = 3 * df['K'] - 2 * df['D']
    return df


# 计算 RSI 指标
def calculate_rsi(df, period=14):
    delta = df['close'].diff()
    gain = np.where(delta > 0, delta, 0)
    loss = np.where(delta < 0, -delta, 0)
    avg_gain = pd.Series(gain).rolling(window=period).mean()
    avg_loss = pd.Series(loss).rolling(window=period).mean()
    rs = avg_gain / avg_loss
    df['RSI'] = 100 - (100 / (1 + rs))
    return df


# 获取 A 股市场所有股票
def get_shanghai_stock_list():
    stock_list = ak.stock_info_sh_name_code()
   # print(stock_list)
    return stock_list[['证券代码', '证券简称']]

# 获取 sz市场所有股票
def get_sz_stock_list():
    stock_list = ak.stock_info_sz_name_code()
   # print(stock_list)
    return stock_list[['A股代码', 'A股简称']]


# 获取单只股票的行情数据
def get_stock_data(stock_code, days=100):
    try:

        df = ak.stock_zh_a_hist(symbol=stock_code, start_date="20250101", period="daily", adjust="qfq")
        #print(df)
        df = df[['日期', '开盘', '最高', '最低', '收盘', '成交量']]
        df.columns = ['date', 'open', 'high', 'low', 'close', 'volume']
        df['date'] = pd.to_datetime(df['date'])
        df = df.sort_values(by='date', ascending=True).reset_index(drop=True)
        return df.tail(days)
    except:
        return None

# 筛选符合四大指标的股票
def stock_screening(stock_list, code_column, name_column, lookback_days=1):
    selected_stocks = []
    seeked_stocks = []
    print(stock_list)

    for index, row in tqdm(stock_list.iterrows(), total=len(stock_list)):
        stock_code = row[code_column]
        stock_name = row[name_column]
        df = get_stock_data(stock_code)

        if df is None or len(df) < 30:
            continue

        # 计算技术指标
        df = calculate_ma(df)
        df = calculate_macd(df)
        df = calculate_kdj(df)
        df = calculate_rsi(df)

        # 遍历过去 lookback_days 天的数据
        for i in range(1, lookback_days + 1):
            last_row = df.iloc[-i]
            prev_row = df.iloc[-(i + 1)]

            # 打印最后几天的指标数据(仅当 lookback_days > 1 时)
            # if lookback_days > 1:
            #     print(df.tail(lookback_days)[
            #               ['date', 'close', 'MA5', 'MA10', 'MA20', 'DIF', 'DEA', 'MACD', 'K', 'D', 'J', 'RSI']])

            seeked_stocks.append((stock_code, stock_name))

            # 筛选条件
            if (
                    # 1. 均线:MA5 上穿 MA10,且股价站上 MA20
                    #prev_row['MA5'] < prev_row['MA10'] and last_row['MA5'] > last_row['MA10'] and last_row['close'] > last_row['MA20']
                    #2. MACD:DIF 上穿 DEA,且 MACD 处于 0 轴下方
                    #prev_row['DIF'] < prev_row['DEA'] and last_row['DIF'] > last_row['DEA'] and last_row['MACD'] < 0
                    # 3. KDJ:K 值和 D 值在 20 以下,形成金叉
                    prev_row['K'] < prev_row['D'] and last_row['K'] > last_row['D'] and last_row['K'] < 20
                    # # 4. RSI:RSI 从 30 以下反弹,并突破 50
                    #and prev_row['RSI'] < 30 and last_row['RSI'] > 30 and last_row['RSI'] > 50
            ):
            # if (
            #         # 1. 均线:MA5 上穿 MA10,且股价站上 MA20
            #         # prev_row['MA5'] < prev_row['MA10'] and last_row['MA5'] > last_row['MA10'] and last_row['close'] > last_row['MA20']
            #         # 2. MACD:DIF 上穿 DEA(不要求 MACD 低于 0)
            #         prev_row['DIF'] < prev_row['DEA'] and last_row['DIF'] > last_row['DEA']
            #         # 3. KDJ:K 值和 D 值形成金叉(不要求 K < 30)
            #         and prev_row['K'] < prev_row['D'] and last_row['K'] > last_row['D']
            #         # 4. RSI:RSI 低位反弹(不要求 RSI 必须突破 50)
            #         and prev_row['RSI'] < 30 and last_row['RSI'] > prev_row['RSI']
            # ):
                if lookback_days > 1:
                    print(f"{stock_code} 在 {last_row['date']} 符合条件")
                else:
                    selected_stocks.append((stock_code, stock_name))

    return selected_stocks, seeked_stocks


# 使用示例
def stock_sh_screening():
    stock_list = get_shanghai_stock_list()
    return stock_screening(stock_list, '证券代码', '证券简称')


def stock_sz_screening():
    stock_list = get_sz_stock_list()
    return stock_screening(stock_list, 'A股代码', 'A股简称')


def stock_sh_last30_screening():
    stock_list = get_shanghai_stock_list()
    return stock_screening(stock_list, '证券代码', '证券简称', lookback_days=30)

def stock_sz_last30_screening():
    stock_list = get_sz_stock_list()
    return stock_screening(stock_list, 'A股代码', 'A股简称', lookback_days=30)

# 运行筛选
if __name__ == "__main__":
    #stocks_sh, seeks_sh = stock_sz_last30_screening()

    stocks_sh, seeks_sh = stock_sh_screening()
    stocks_sz, seeks_sz = stock_sz_screening()

    print("检索过的股票:")
    print(seeks_sz)
    print(seeks_sh)

    print("符合条件的股票:")
    print(stocks_sz)
    print(stocks_sh)

 结果输出示例:

检索过的股票:
[('000048', '京基智农'), ('000078', '海王生物'), ('000158', '常山北明'), ('000410', '沈阳机床'), ('000505', '京粮控股'), ('000506', '*ST中润'), ('000518', '四环生物'), ('000559', '万向钱潮'), ('000581', '威孚高科'), ('000603', '盛达资源'), ('000615', 'ST美谷'), ('000622', '*ST恒立'), ('000665', '湖北广电'), ('000668', '荣丰控股'), ('000678', '襄阳轴承'), ('000710', '贝瑞基因'), ('000711', '*ST京蓝'), ('000723', '美锦能源'), ('000735', '罗 牛 山'), ('000766', '通化金马'), ('000837', '秦川机床'), ('000890', '法尔胜'), ('000905', '厦门港务'), ('000910', '大亚圣象'), ('000929', '兰州黄河'), ('001212', '中旗新材'), ('001267', '汇绿生态'), ('001270', '铖昌科技'), ('001288', '运机集团'), ('001306', '夏厦精密'), ('002020', '京新药业'), ('002026', '山东威达'), ('002029', '七 匹 狼'), ('002033', '丽江股份'), ('002046', '国机精工'), ('002052', '*ST同洲'), ('002093', '国脉科技'), ('002099', '海翔药业'), ('002101', '广东鸿图'), ('002104', '恒宝股份'), ('002105', '信隆健康'), ('002109', '兴化股份'), ('002114', '罗平锌电'), ('002158', '汉钟精机'), ('002162', '悦心健康'), ('002167', '东方锆业'), ('002194', '武汉凡谷'), ('002196', '方正电机'), ('002197', 'ST证通'), ('002205', '国统股份'), ('002207', '准油股份'), ('002215', '诺 普 信'), ('002246', '北化股份'), ('002271', '东方雨虹'), ('002277', '友阿股份'), ('002306', '中科云网'), ('002338', '奥普光电'), ('002347', '泰尔股份'), ('002388', 'ST新亚'), ('002420', '毅昌科技'), ('002428', '云南锗业'), ('002472', '双环传动'), ('002484', '江海股份'), ('002523', '天桥起重'), ('002524', '光正眼科'), ('002529', '海源复材'), ('002553', '南方精工'), ('002580', '圣阳股份'), ('002589', '瑞康医药'), ('002593', '日上集团'), ('002614', '奥佳华'), ('002622', '皓宸医疗'), ('002628', '成都路桥'), ('002631', '德尔未来'), ('002633', '申科股份'), ('002634', '棒杰股份'), ('002672', '东江环保'), ('002690', '美亚光电'), ('002713', '东易日盛'), ('002741', '光华科技'), ('002760', '凤形股份'), ('002773', '康弘药业'), ('002802', '洪汇新材'), ('002816', '和科达'), ('002822', 'ST中装'), ('002840', '华统股份'), ('002846', '英联股份'), ('002851', '麦格米特'), ('002921', '联诚精密'), ('002947', '恒铭达'), ('002992', '宝明科技'), ('003018', '金富科技'), ('003019', '宸展光电'), ('300008', '天海防务'), ('300022', '吉峰科技'), ('300024', '机器人'), ('300048', '合康新能'), ('300078', '思创医惠'), ('300125', 'ST聆达'), ('300147', '香雪制药'), ('300167', '*ST迪威'), ('300168', '万达信息'), ('300179', '四方达'), ('300180', '华峰超纤'), ('300181', '佐力药业'), ('300199', '翰宇药业'), ('300208', '*ST中程'), ('300210', '森远股份'), ('300233', '金城医药'), ('300253', '卫宁健康'), ('300268', 'ST佳沃'), ('300320', '海达股份'), ('300404', '博济医药'), ('300412', '迦南科技'), ('300432', '富临精工'), ('300436', '广生堂'), ('300451', '创业慧康'), ('300457', '赢合科技'), ('300459', '汤姆猫'), ('300476', '胜宏科技'), ('300491', '通合科技'), ('300499', '高澜股份'), ('300506', '*ST名家'), ('300545', '联得装备'), ('300547', '川环科技'), ('300548', '博创科技'), ('300565', '科信技术'), ('300570', '太辰光'), ('300572', '安车检测'), ('300585', '奥联电子'), ('300593', '新雷能'), ('300605', '恒锋信息'), ('300630', '*ST普利'), ('300667', '必创科技'), ('300675', '建科院'), ('300693', '盛弘股份'), ('300726', '宏达电子'), ('300731', '科创新源'), ('300737', '科顺股份'), ('300752', '隆利科技'), ('300777', '中简科技'), ('300778', '新城市'), ('300810', '中科海讯'), ('300813', '泰林生物'), ('300819', '聚杰微纤'), ('300841', '康华生物'), ('300842', '帝科股份'), ('300853', '申昊科技'), ('300855', '图南股份'), ('300863', '卡倍亿'), ('300870', '欧陆通'), ('300873', '海晨股份'), ('300878', '维康药业'), ('300885', '海昌新材'), ('300901', '中胤时尚'), ('300922', '天秦装备'), ('300938', '信测标准'), ('300946', '恒而达'), ('300960', '通业科技'), ('300961', '深水海纳'), ('300986', '志特新材'), ('301005', '超捷股份'), ('301012', '扬电科技'), ('301017', '漱玉平民'), ('301021', '英诺激光'), ('301031', '中熔电气'), ('301032', '新柴股份'), ('301071', '力量钻石'), ('301079', '邵阳液压'), ('301093', '华兰股份'), ('301099', '雅创电子'), ('301120', '新特电气'), ('301161', '唯万密封'), ('301170', '锡南科技'), ('301177', '迪阿股份'), ('301190', '善水科技'), ('301216', '万凯新材'), ('301221', '光庭信息'), ('301225', '恒勃股份'), ('301251', '威尔高'), ('301255', '通力科技'), ('301256', '华融化学'), ('301257', '普蕊斯'), ('301261', '恒工精密'), ('301263', '泰恩康'), ('301268', '铭利达'), ('301279', '金道科技'), ('301320', '豪江智能'), ('301379', '天山电子'), ('301382', '蜂助手'), ('301389', '隆扬电子'), ('301397', '溯联股份'), ('301408', '华人健康'), ('301419', '阿莱德'), ('301421', '波长光电'), ('301459', '丰茂股份'), ('301486', '致尚科技'), ('301525', '儒竞科技'), ('301603', '乔锋智能'), ('301611', '珂玛科技')]
[('600107', '美尔雅'), ('600121', '郑州煤电'), ('600126', '杭钢股份'), ('600165', '*ST宁科'), ('600172', '黄河旋风'), ('600179', '安通控股'), ('600184', '光电股份'), ('600187', '国中水务'), ('600190', 'ST锦港'), ('600228', '返利科技'), ('600234', '*ST科新'), ('600237', '铜峰电子'), ('600272', '开开实业'), ('600288', '大恒科技'), ('600289', '*ST信通'), ('600315', '上海家化'), ('600345', '长江通信'), ('600359', '新农开发'), ('600360', 'ST华微'), ('600361', '创新新材'), ('600375', '*ST汉马'), ('600398', '海澜之家'), ('600403', '大有能源'), ('600416', '湘电股份'), ('600421', '华嵘控股'), ('600423', '柳化股份'), ('600478', '科力远'), ('600482', '中国动力'), ('600538', '国发股份'), ('600539', '狮头股份'), ('600560', '金自天正'), ('600563', '法拉电子'), ('600589', '大位科技'), ('600608', 'ST沪科'), ('600610', '中毅达'), ('600619', '海立股份'), ('600641', '万业企业'), ('600693', '东百集团'), ('600711', 'ST盛屯'), ('600712', '南宁百货'), ('600802', '福建水泥'), ('600815', '厦工股份'), ('600825', '新华传媒'), ('600829', '人民同泰'), ('600833', '第一医药'), ('600835', '上海机电'), ('600857', '宁波中百'), ('600860', '京城股份'), ('600895', '张江高科'), ('600897', '厦门空港'), ('600960', '渤海汽车'), ('600980', '北矿科技'), ('600984', '建设机械'), ('601010', '文峰股份'), ('601020', '华钰矿业'), ('601900', '南方传媒'), ('603002', '宏昌电子'), ('603003', '*ST龙宇'), ('603007', 'ST花王'), ('603023', '*ST威帝'), ('603036', '如通股份'), ('603057', '紫燕食品'), ('603062', '麦加芯彩'), ('603063', '禾望电气'), ('603065', '宿迁联盛'), ('603067', '振华股份'), ('603070', '万控智造'), ('603108', '润达医疗'), ('603112', '华翔股份'), ('603151', '邦基科技'), ('603161', '科华控股'), ('603166', '福达股份'), ('603200', '上海洗霸'), ('603228', '景旺电子'), ('603233', '大参林'), ('603267', '鸿远电子'), ('603291', '联合水务'), ('603303', '得邦照明'), ('603326', '我乐家居'), ('603330', '天洋新材'), ('603368', '柳药集团'), ('603377', 'ST东时'), ('603378', '亚士创能'), ('603388', 'ST元成'), ('603393', '新天然气'), ('603399', '永杉锂业'), ('603489', '八方股份'), ('603506', '南都物业'), ('603565', '中谷物流'), ('603588', '高能环境'), ('603607', '京华激光'), ('603618', '杭电股份'), ('603678', '火炬电子'), ('603688', '石英股份'), ('603716', '塞力医疗'), ('603717', '天域生物'), ('603721', '中广天择'), ('603737', '三棵树'), ('603788', '宁波高发'), ('603799', '华友钴业'), ('603813', '原尚股份'), ('603833', '欧派家居'), ('603878', '武进不锈'), ('603883', '老百姓'), ('603887', '城地香江'), ('603898', '好莱客'), ('603901', '永创智能'), ('603931', '格林达'), ('603939', '益丰药房'), ('603956', '威派格'), ('603960', '克来机电'), ('603983', '丸美生物'), ('603993', '洛阳钼业'), ('605008', '长鸿高科'), ('605018', '长华集团'), ('605068', '明新旭腾'), ('605081', '太和水'), ('605100', '华丰股份'), ('605128', '上海沿浦'), ('605133', '嵘泰股份'), ('605155', '西大门'), ('605178', '时空科技'), ('605198', '安德利'), ('605199', '葫芦娃'), ('605208', '永茂泰'), ('605228', '神通科技'), ('605266', '健之佳'), ('605303', '园林股份'), ('605318', '法狮龙'), ('605389', '长龄液压'), ('605566', '福莱蒽特'), ('605589', '圣泉集团')]
符合条件的股票:
[('000518', '四环生物'), ('300147', '香雪制药'), ('300548', '博创科技')]
[('600421', '华嵘控股'), ('603721', '中广天择'), ('605199', '葫芦娃')]

升级版python代码:

测试发现akshare库在linux系统下获取的数据才是全的,window下不全,应该是多线程并发造成后端有部分数据没回

import akshare as ak
import pandas as pd
import numpy as np
from tqdm import tqdm
import sqlite3
from datetime import datetime


# Database setup (SQLite example)
def init_db():
    conn = None
    try:
        conn = sqlite3.connect('stock_data.db')
        cursor = conn.cursor()

        cursor.execute('''
            CREATE TABLE IF NOT EXISTS stock_history (
                stock_code TEXT,
                date TEXT,
                code TEXT,
                open REAL,
                close REAL,
                high REAL,
                low REAL,
                volume REAL,
                amount REAL,
                amplitude REAL,
                change_pct REAL,
                change_amt REAL,
                turnover REAL,
                PRIMARY KEY (stock_code, date)
            )
        ''')

        # 为常用查询字段创建索引
        cursor.execute('CREATE INDEX IF NOT EXISTS idx_stock_code ON stock_history(stock_code)')
        cursor.execute('CREATE INDEX IF NOT EXISTS idx_date ON stock_history(date)')

        conn.commit()
        print("数据库初始化/检查完成")
    except sqlite3.Error as e:
        print(f"数据库错误: {e}")
    finally:
        if conn:
            conn.close()

# Initialize database on import
init_db()


def save_to_db(stock_code, df):
    conn = None
    try:
        conn = sqlite3.connect('stock_data.db')

        # 添加stock_code列(用户输入的代码)
        df = df.copy()
        df['stock_code'] = stock_code

        # 确保列顺序与数据库一致
        db_columns = [
            'stock_code', 'date', 'code', 'open', 'close', 'high', 'low',
            'volume', 'amount', 'amplitude', 'change_pct', 'change_amt', 'turnover'
        ]

        # 只保留需要的列
        df = df[db_columns]

        # 使用事务批量插入
        with conn:
            df.to_sql('stock_history', conn, if_exists='append', index=False)

        #print(f"成功保存 {stock_code} 的 {len(df)} 条数据")
    except Exception as e:
        print(f"保存到数据库出错: {str(e)}")
    finally:
        if conn:
            conn.close()


def get_stock_data(stock_code, days=100, online=False):
    if online:
        try:
            # 获取原始数据
            df = ak.stock_zh_a_hist(symbol=stock_code, period="daily", adjust="qfq")
            if df is None or len(df) < 30:
                return None
            #print(df)
            # 确保包含所有列(防止某些列缺失的情况)
            expected_columns = ['日期', '股票代码', '开盘', '收盘', '最高', '最低',
                                '成交量', '成交额', '振幅', '涨跌幅', '涨跌额', '换手率']

            # 检查是否有缺失的列
            missing_cols = [col for col in expected_columns if col not in df.columns]
            if missing_cols:
                print(f"警告:缺失以下列: {missing_cols}")
                df = df.reindex(columns=expected_columns)  # 确保列顺序一致

            # 重命名所有列(中英文对照)
            column_mapping = {
                '日期': 'date',
                '股票代码': 'code',
                '开盘': 'open',
                '收盘': 'close',
                '最高': 'high',
                '最低': 'low',
                '成交量': 'volume',
                '成交额': 'amount',
                '振幅': 'amplitude',
                '涨跌幅': 'change_pct',
                '涨跌额': 'change_amt',
                '换手率': 'turnover'
            }
            df = df.rename(columns=column_mapping)

            # 转换日期格式
            # df['date'] = pd.to_datetime(df['date'])

            # 按日期排序
            df = df.sort_values(by='date', ascending=True)
            #print(df)

            # 保存到数据库(如果需要)
            if online:  # 只有在线获取时才保存
                save_to_db(stock_code, df.tail(days))

            return df.tail(days)

        except Exception as e:
            #print(f"获取 {stock_code} 数据出错: {str(e)}")
            # 尝试从数据库加载
            return None

    else:  # 离线模式
        return load_from_db(stock_code, days)



# 数据库读取函数也需要对应修改
def load_from_db(stock_code, days=100):
    try:
        conn = sqlite3.connect('stock_data.db')
        query = f"""
        SELECT * FROM stock_history 
        WHERE stock_code = '{stock_code}'
        ORDER BY date DESC
        LIMIT {days}
        """
        df = pd.read_sql(query, conn)

        if not df.empty:
            # 转换日期格式
            df['date'] = pd.to_datetime(df['date'])
            # 按日期升序排列
            df = df.sort_values(by='date', ascending=True)
            return df
        return None
    except Exception as e:
        print(f"从数据库加载出错: {str(e)}")
        return None
    finally:
        if conn:
            conn.close()




# 计算均线
def calculate_ma(df, windows=[5, 10, 20]):
    for w in windows:
        df[f'MA{w}'] = df['close'].rolling(window=w).mean()
    return df


# 计算 MACD 指标
def calculate_macd(df, short=12, long=26, signal=9):
    df['EMA12'] = df['close'].ewm(span=short, adjust=False).mean()
    df['EMA26'] = df['close'].ewm(span=long, adjust=False).mean()
    df['DIF'] = df['EMA12'] - df['EMA26']
    df['DEA'] = df['DIF'].ewm(span=signal, adjust=False).mean()
    df['MACD'] = 2 * (df['DIF'] - df['DEA'])  # 计算 MACD
    return df


# 计算 KDJ 指标
def calculate_kdj(df, n=9, k_smooth=3, d_smooth=3):
    low_min = df['low'].rolling(window=n).min()
    high_max = df['high'].rolling(window=n).max()
    df['RSV'] = (df['close'] - low_min) / (high_max - low_min) * 100
    df['K'] = df['RSV'].ewm(alpha=1 / k_smooth, adjust=False).mean()
    df['D'] = df['K'].ewm(alpha=1 / d_smooth, adjust=False).mean()
    df['J'] = 3 * df['K'] - 2 * df['D']
    return df


# 计算 RSI 指标
def calculate_rsi(df, period=14):
    delta = df['close'].diff()
    gain = np.where(delta > 0, delta, 0)
    loss = np.where(delta < 0, -delta, 0)
    avg_gain = pd.Series(gain).rolling(window=period).mean()
    avg_loss = pd.Series(loss).rolling(window=period).mean()
    rs = avg_gain / avg_loss
    df['RSI'] = 100 - (100 / (1 + rs))
    return df


# 获取 A 股市场所有股票
def get_shanghai_stock_list():
    stock_list = ak.stock_info_sh_name_code()
   # stock_list.to_csv("stock_list.csv", index=False, encoding="utf-8-sig")
   # print(stock_list)
    return stock_list[['证券代码', '证券简称']]

# 获取 sz市场所有股票
def get_sz_stock_list():
    stock_list = ak.stock_info_sz_name_code()
   # print(stock_list)
    return stock_list[['A股代码', 'A股简称']]




# 筛选符合四大指标的股票
def stock_screening(stock_list, code_column, name_column, lookback_days=1):
    selected_stocks = []
    seeked_stocks = []
    print(stock_list)

    for index, row in tqdm(stock_list.iterrows(), total=len(stock_list)):
        stock_code = row[code_column]
        stock_name = row[name_column]
        df = get_stock_data(stock_code)
        # print("#####################")
        # print(df)
        # print("#####################")

        if df is None or len(df) < 30:
            continue

        # 计算技术指标
        df = calculate_ma(df)
        df = calculate_macd(df)
        df = calculate_kdj(df)
        df = calculate_rsi(df)

        # 遍历过去 lookback_days 天的数据
        for i in range(1, lookback_days + 1):
            last_row = df.iloc[-i]
            prev_row = df.iloc[-(i + 1)]

            # 打印最后几天的指标数据(仅当 lookback_days > 1 时)
            # if lookback_days > 1:
            #     print(df.tail(lookback_days)[
            #               ['date', 'close', 'MA5', 'MA10', 'MA20', 'DIF', 'DEA', 'MACD', 'K', 'D', 'J', 'RSI']])

            seeked_stocks.append((stock_code, stock_name))

            # 筛选条件
            if (
                    # 1. 均线:MA5 上穿 MA10,且股价站上 MA20
                    # prev_row['MA5'] < prev_row['MA10'] and last_row['MA5'] > last_row['MA10'] and last_row['close'] > last_row['MA20']
                    #2. MACD:DIF 上穿 DEA,且 MACD 处于 0 轴下方
                     #prev_row['DIF'] < prev_row['DEA'] and last_row['DIF'] > last_row['DEA'] and last_row['MACD'] < 0
                    # 3. KDJ:K 值和 D 值在 20 以下,形成金叉
                    #prev_row['K'] > 80 and last_row['D'] > 80
                    prev_row['K'] < prev_row['D'] and last_row['K'] > last_row['D'] and last_row['K'] < 20
                    # # 4. RSI:RSI 从 30 以下反弹,并突破 50
                    #and prev_row['RSI'] < 30 and last_row['RSI'] > 30 and last_row['RSI'] > 50
            ):
            # if (
            #         # 1. 均线:MA5 上穿 MA10,且股价站上 MA20
            #         # prev_row['MA5'] < prev_row['MA10'] and last_row['MA5'] > last_row['MA10'] and last_row['close'] > last_row['MA20']
            #         # 2. MACD:DIF 上穿 DEA(不要求 MACD 低于 0)
            #         prev_row['DIF'] < prev_row['DEA'] and last_row['DIF'] > last_row['DEA']
            #         # 3. KDJ:K 值和 D 值形成金叉(不要求 K < 30)
            #         and prev_row['K'] < prev_row['D'] and last_row['K'] > last_row['D']
            #         # 4. RSI:RSI 低位反弹(不要求 RSI 必须突破 50)
            #         and prev_row['RSI'] < 30 and last_row['RSI'] > prev_row['RSI']
            # ):
                if lookback_days > 1:
                    print(f"{stock_code} 在 {last_row['date']} 符合条件")
                else:
                    selected_stocks.append((stock_code, stock_name))

    return selected_stocks, seeked_stocks


# 使用示例
def stock_sh_screening():
    stock_list = get_shanghai_stock_list()
    return stock_screening(stock_list, '证券代码', '证券简称')


def stock_sz_screening():
    stock_list = get_sz_stock_list()
    return stock_screening(stock_list, 'A股代码', 'A股简称')


def stock_sh_last30_screening():
    stock_list = get_shanghai_stock_list()
    return stock_screening(stock_list, '证券代码', '证券简称', lookback_days=30)

def stock_sz_last30_screening():
    stock_list = get_sz_stock_list()
    return stock_screening(stock_list, 'A股代码', 'A股简称', lookback_days=30)

# 运行筛选
if __name__ == "__main__":
    #stocks_sh, seeks_sh = stock_sz_last30_screening()

    stocks_sh, seeks_sh = stock_sh_screening()
    stocks_sz, seeks_sz = stock_sz_screening()

    print("检索过的股票:")
    print(seeks_sz)
    print(seeks_sh)
    print(len(seeks_sh)+len(seeks_sz))

    print("符合条件的股票:")
    print(stocks_sz)
    print(stocks_sh)
    print(len(stocks_sz)+len(stocks_sh))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值