Skip to content

用 Python 自动检测交易图形态的实用指南请查收

作者:老余捞鱼

原创不易,转载请标明出处及原作者。

写在前面的话:本文详细介绍了如何利用 Python 和 EODHD API 来自动化检测股票交易市场中的蜡烛图形态。我会解释作为交易策略重要组成部分蜡烛图的基本概念,并说明这些数据如何在图表上展现形态,最后还会举例进行展示说明。

为了探讨今天这个主题,我们需要先从基础讲起。多数人都熟悉交易图表,它通常由绿红柱形图组成线形图,虽简单却蕴含大量数据信息。一根蜡烛代表一个数据区间,如一小时图表,包含开盘价、最高价、最低价和收盘价四个关键信息,简称 OHLC 数据。若收盘价高于开盘价,蜡烛图为绿色;反之则为红色(美股中的绿涨红跌和我们A股是反的,需要大家注意这个细节)。下图更清晰地说明了这一概念。

举例说明:

您可能会注意到蜡烛序列中出现的特定形态,即蜡烛图形态。交易策略通常就是根据这些形态制定的。

一、使用 EODHD API 中的标准普尔 500 指数进行演示

第一步是获取分析所需的数据集,为此我们将使用官方的 EODHD API Python 库。下面提供的代码片段可获取 720 小时(30 天)的数据。

官方库地址:https://eodhd.com/financial-apis/python-financial-libraries-and-code-samples/

import config as cfg
from eodhd import APIClient

api = APIClient(cfg.API_KEY)


def get_ohlc_data():
    df = api.get_historical_data("AAPL.US", "1h", results=(24*30))
    return df


if __name__ == "__main__":
    df = get_ohlc_data()
    print(df)

我们先来看一个简单明了的蜡烛图形态,即锤子或锤子形态。这种形态是一种看涨反转指标,通常出现在下跌趋势的末端。它的特点是开盘价和收盘价在顶部几乎完全相同,加上较长的低位灯芯,其长度至少是空头主体的两倍。

为说明这种形态,现将蜡烛图的修改版介绍如下:

总之,锤子形态是看涨转折的信号,被视为温和的反转形态,表明市场方向可能从下行转向上行。

我们开发了一些代码,以便在数据集中找出这些蜡烛。

import pandas as pd
import config as cfg
from eodhd import APIClient

api = APIClient(cfg.API_KEY)


def candle_hammer(df: pd.DataFrame = None) -> pd.Series:
    """* Candlestick Detected: Hammer ("Weak - Reversal - Bullish Signal - Up"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        ((df["high"] - df["low"]) > 3 * (df["open"] - df["close"]))
        & (((df["close"] - df["low"]) / (0.001 + df["high"] - df["low"])) > 0.6)
        & (((df["open"] - df["low"]) / (0.001 + df["high"] - df["low"])) > 0.6)
    )


def get_ohlc_data():
    df = api.get_historical_data("AAPL.US", "1h", results=(24*30))
    return df


if __name__ == "__main__":
    df = get_ohlc_data()
    df["hammer"] = candle_hammer(df)
    print(df)
    print(df[df["hammer"] == True])

为了便于演示,我们将数据集显示两次。初始数据集显示锤形蜡烛的识别。随后的数据集只显示检测到锤子形态的情况。在过去的 720 个小时中,锤子形态出现了 67 次。

在此基础上,自然会出现一种相关的蜡烛图形态–倒锤子形态。作为锤子的对应形态,它在图形上类似于一个倒转的锤子。它仍然是一个看涨信号,通常出现在下跌趋势的末端。

def candle_inverted_hammer(df: pd.DataFrame = None) -> pd.Series:
    """* Candlestick Detected: Inverted Hammer ("Weak - Continuation - Bullish Pattern - Up")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        ((df["high"] - df["low"]) > 3 * (df["open"] - df["close"]))
        & ((df["high"] - df["close"]) / (0.001 + df["high"] - df["low"]) > 0.6)
        & ((df["high"] - df["open"]) / (0.001 + df["high"] - df["low"]) > 0.6)
    )


二、烛台形态

蜡烛图形态基本上可分为两类:

  1. 看涨或看跌
  2. 犹豫不决/中性、弱、可靠或强

下面简要介绍一下流行的蜡烛图形态:

2.1 犹豫不决/中性(Indecision / Neutral)

  • Doji

2.2 弱(Weak)

  • 锤子(看涨)
  • 倒锤(看涨)
  • Shooting Star (看跌)

2.3 可靠(Reliable)

  • Hanging Man (看跌)
  • Three Line Strike (看涨)
  • Two Black Gapping (看跌)
  • Abandoned Baby (看涨)
  • Morning Doji Star (看涨)
  • Evening Doji Star (看跌)

2.4 (Strong)

  • Three White Soldiers (三个白兵)(看涨)
  • Three Black Crows(三只黑鸦) (看跌)
  • Morning Star (看涨)
  • Evening Star (看跌)

我们将这些烛台形态编码成 Python 函数,并使用 Numpy 来处理一些更复杂的形态。

import numpy as np

def candle_hammer(df: pd.DataFrame = None) -> pd.Series:
    """* Candlestick Detected: Hammer ("Weak - Reversal - Bullish Signal - Up"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        ((df["high"] - df["low"]) > 3 * (df["open"] - df["close"]))
        & (((df["close"] - df["low"]) / (0.001 + df["high"] - df["low"])) > 0.6)
        & (((df["open"] - df["low"]) / (0.001 + df["high"] - df["low"])) > 0.6)
    )


def candle_inverted_hammer(df: pd.DataFrame = None) -> pd.Series:
    """* Candlestick Detected: Inverted Hammer ("Weak - Reversal - Bullish Pattern - Up")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        ((df["high"] - df["low"]) > 3 * (df["open"] - df["close"]))
        & ((df["high"] - df["close"]) / (0.001 + df["high"] - df["low"]) > 0.6)
        & ((df["high"] - df["open"]) / (0.001 + df["high"] - df["low"]) > 0.6)
    )


def candle_shooting_star(df: pd.DataFrame = None) -> pd.Series:
    """* Candlestick Detected: Shooting Star ("Weak - Reversal - Bearish Pattern - Down")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        ((df["open"].shift(1) < df["close"].shift(1)) & (df["close"].shift(1) < df["open"]))
        & (df["high"] - np.maximum(df["open"], df["close"]) >= (abs(df["open"] - df["close"]) * 3))
        & ((np.minimum(df["close"], df["open"]) - df["low"]) <= abs(df["open"] - df["close"]))
    )


def candle_hanging_man(df: pd.DataFrame = None) -> pd.Series:
    """* Candlestick Detected: Hanging Man ("Weak - Reliable - Bearish Pattern - Down")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        ((df["high"] - df["low"]) > (4 * (df["open"] - df["close"])))
        & (((df["close"] - df["low"]) / (0.001 + df["high"] - df["low"])) >= 0.75)
        & (((df["open"] - df["low"]) / (0.001 + df["high"] - df["low"])) >= 0.75)
        & (df["high"].shift(1) < df["open"])
        & (df["high"].shift(2) < df["open"])
    )


def candle_three_white_soldiers(df: pd.DataFrame = None) -> pd.Series:
    """*** Candlestick Detected: Three White Soldiers ("Strong - Reversal - Bullish Pattern - Up")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        ((df["open"] > df["open"].shift(1)) & (df["open"] < df["close"].shift(1)))
        & (df["close"] > df["high"].shift(1))
        & (df["high"] - np.maximum(df["open"], df["close"]) < (abs(df["open"] - df["close"])))
        & ((df["open"].shift(1) > df["open"].shift(2)) & (df["open"].shift(1) < df["close"].shift(2)))
        & (df["close"].shift(1) > df["high"].shift(2))
        & (
            df["high"].shift(1) - np.maximum(df["open"].shift(1), df["close"].shift(1))
            < (abs(df["open"].shift(1) - df["close"].shift(1)))
        )
    )


def candle_three_black_crows(df: pd.DataFrame = None) -> pd.Series:
    """* Candlestick Detected: Three Black Crows ("Strong - Reversal - Bearish Pattern - Down")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        ((df["open"] < df["open"].shift(1)) & (df["open"] > df["close"].shift(1)))
        & (df["close"] < df["low"].shift(1))
        & (df["low"] - np.maximum(df["open"], df["close"]) < (abs(df["open"] - df["close"])))
        & ((df["open"].shift(1) < df["open"].shift(2)) & (df["open"].shift(1) > df["close"].shift(2)))
        & (df["close"].shift(1) < df["low"].shift(2))
        & (
            df["low"].shift(1) - np.maximum(df["open"].shift(1), df["close"].shift(1))
            < (abs(df["open"].shift(1) - df["close"].shift(1)))
        )
    )


def candle_doji(df: pd.DataFrame = None) -> pd.Series:
    """! Candlestick Detected: Doji ("Indecision / Neutral")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        ((abs(df["close"] - df["open"]) / (df["high"] - df["low"])) < 0.1)
        & ((df["high"] - np.maximum(df["close"], df["open"])) > (3 * abs(df["close"] - df["open"])))
        & ((np.minimum(df["close"], df["open"]) - df["low"]) > (3 * abs(df["close"] - df["open"])))
    )


def candle_three_line_strike(df: pd.DataFrame = None) -> pd.Series:
    """** Candlestick Detected: Three Line Strike ("Reliable - Reversal - Bullish Pattern - Up")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        ((df["open"].shift(1) < df["open"].shift(2)) & (df["open"].shift(1) > df["close"].shift(2)))
        & (df["close"].shift(1) < df["low"].shift(2))
        & (
            df["low"].shift(1) - np.maximum(df["open"].shift(1), df["close"].shift(1))
            < (abs(df["open"].shift(1) - df["close"].shift(1)))
        )
        & ((df["open"].shift(2) < df["open"].shift(3)) & (df["open"].shift(2) > df["close"].shift(3)))
        & (df["close"].shift(2) < df["low"].shift(3))
        & (
            df["low"].shift(2) - np.maximum(df["open"].shift(2), df["close"].shift(2))
            < (abs(df["open"].shift(2) - df["close"].shift(2)))
        )
        & ((df["open"] < df["low"].shift(1)) & (df["close"] > df["high"].shift(3)))
    )


def candle_two_black_gapping(df: pd.DataFrame = None) -> pd.Series:
    """*** Candlestick Detected: Two Black Gapping ("Reliable - Reversal - Bearish Pattern - Down")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        ((df["open"] < df["open"].shift(1)) & (df["open"] > df["close"].shift(1)))
        & (df["close"] < df["low"].shift(1))
        & (df["low"] - np.maximum(df["open"], df["close"]) < (abs(df["open"] - df["close"])))
        & (df["high"].shift(1) < df["low"].shift(2))
    )


def candle_morning_star(df: pd.DataFrame = None) -> pd.Series:
    """*** Candlestick Detected: Morning Star ("Strong - Reversal - Bullish Pattern - Up")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        (np.maximum(df["open"].shift(1), df["close"].shift(1)) < df["close"].shift(2)) & (df["close"].shift(2) < df["open"].shift(2))
    ) & ((df["close"] > df["open"]) & (df["open"] > np.maximum(df["open"].shift(1), df["close"].shift(1))))


def candle_evening_star(df: pd.DataFrame = None) -> np.ndarray:
    """*** Candlestick Detected: Evening Star ("Strong - Reversal - Bearish Pattern - Down")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        (np.minimum(df["open"].shift(1), df["close"].shift(1)) > df["close"].shift(2)) & (df["close"].shift(2) > df["open"].shift(2))
    ) & ((df["close"] < df["open"]) & (df["open"] < np.minimum(df["open"].shift(1), df["close"].shift(1))))


def candle_abandoned_baby(df: pd.DataFrame = None) -> pd.Series:
    """** Candlestick Detected: Abandoned Baby ("Reliable - Reversal - Bullish Pattern - Up")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (
        (df["open"] < df["close"])
        & (df["high"].shift(1) < df["low"])
        & (df["open"].shift(2) > df["close"].shift(2))
        & (df["high"].shift(1) < df["low"].shift(2))
    )


def candle_morning_doji_star(df: pd.DataFrame = None) -> pd.Series:
    """** Candlestick Detected: Morning Doji Star ("Reliable - Reversal - Bullish Pattern - Up")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (df["close"].shift(2) < df["open"].shift(2)) & (
        abs(df["close"].shift(2) - df["open"].shift(2)) / (df["high"].shift(2) - df["low"].shift(2)) >= 0.7
    ) & (abs(df["close"].shift(1) - df["open"].shift(1)) / (df["high"].shift(1) - df["low"].shift(1)) < 0.1) & (
        df["close"] > df["open"]
    ) & (
        abs(df["close"] - df["open"]) / (df["high"] - df["low"]) >= 0.7
    ) & (
        df["close"].shift(2) > df["close"].shift(1)
    ) & (
        df["close"].shift(2) > df["open"].shift(1)
    ) & (
        df["close"].shift(1) < df["open"]
    ) & (
        df["open"].shift(1) < df["open"]
    ) & (
        df["close"] > df["close"].shift(2)
    ) & (
        (df["high"].shift(1) - np.maximum(df["close"].shift(1), df["open"].shift(1)))
        > (3 * abs(df["close"].shift(1) - df["open"].shift(1)))
    ) & (
        np.minimum(df["close"].shift(1), df["open"].shift(1)) - df["low"].shift(1)
    ) > (
        3 * abs(df["close"].shift(1) - df["open"].shift(1))
    )


def candle_evening_doji_star(df: pd.DataFrame = None) -> pd.Series:
    """** Candlestick Detected: Evening Doji Star ("Reliable - Reversal - Bearish Pattern - Down")"""

    # Fill NaN values with 0
    df = df.fillna(0)

    return (df["close"].shift(2) > df["open"].shift(2)) & (
        abs(df["close"].shift(2) - df["open"].shift(2)) / (df["high"].shift(2) - df["low"].shift(2)) >= 0.7
    ) & (abs(df["close"].shift(1) - df["open"].shift(1)) / (df["high"].shift(1) - df["low"].shift(1)) < 0.1) & (
        df["close"] < df["open"]
    ) & (
        abs(df["close"] - df["open"]) / (df["high"] - df["low"]) >= 0.7
    ) & (
        df["close"].shift(2) < df["close"].shift(1)
    ) & (
        df["close"].shift(2) < df["open"].shift(1)
    ) & (
        df["close"].shift(1) > df["open"]
    ) & (
        df["open"].shift(1) > df["open"]
    ) & (
        df["close"] < df["close"].shift(2)
    ) & (
        (df["high"].shift(1) - np.maximum(df["close"].shift(1), df["open"].shift(1)))
        > (3 * abs(df["close"].shift(1) - df["open"].shift(1)))
    ) & (
        np.minimum(df["close"].shift(1), df["open"].shift(1)) - df["low"].shift(1)
    ) > (
        3 * abs(df["close"].shift(1) - df["open"].shift(1))
    )

探索我们的数据集,看看能否识别出任何强势蜡烛图形态…

if __name__ == "__main__":
    df = get_ohlc_data()

    df["three_white_soldiers"] = candle_three_white_soldiers(df)
    df["three_black_crows"] = candle_three_black_crows(df)
    df["morning_star"] = candle_morning_star(df)
    df["evening_star"] = candle_evening_star(df)

    print(df[(df["three_white_soldiers"] == True) | (df["three_black_crows"] == True) | (df["morning_star"] == True) | (df["evening_star"] == True)])

事实上,在过去的 30 天里,强势形态已经出现了很多次。从分享的数据中,我们可以看出蜡烛图形态出现的时间和类型。一个有趣的练习是查看标准普尔 500 指数小时图中的这些特定时间段,并尝试找出这些形态。

观点回顾

请大家记住,没有任何一种策略能保证交易成功。与其他交易方法一样,基于蜡烛图形态的策略的可行性受市场动态、流动性和波动性的影响。因此,建议交易者将这些策略纳入更全面、更多样化的交易方法中,并通过其他分析和工具对其进行强化,以获得更全面的视角。

  • 蜡烛图形态是交易分析的关键工具:文章强调了蜡烛图形态在交易策略中的作用,并通过实际的 Python 代码示例展示了如何识别这些形态。
  • 自动化分析提高了交易效率:通过使用 Python 和 EODHD API,交易者可以自动化地分析大量历史数据,快速识别潜在的交易机会。
  • 蜡烛图形态的多样性和复杂性:文章列举了多种蜡烛图形态,并根据它们的可靠性和强度对它们进行了分类,从而为交易者提供了一个全面的参考框架。
  • 综合交易策略的重要性:作者指出,虽然蜡烛图形态提供了有价值的市场信号,但交易者应该将它们与其他技术分析和基本面分析相结合,以形成一个更为健全的交易策略。
  • 持续学习和适应市场变化:文章鼓励交易者持续学习新的分析技巧和工具,以适应不断变化的市场环境。

感谢您阅读到最后。如果对文中的内容有任何疑问,请给我留言,必复。


文内容仅仅是技术探讨和学习,并不构成任何投资建议。

转发请注明原作者和出处。

Published inAI&Invest专栏

Be First to Comment

    发表回复