作者:老余捞鱼
原创不易,转载请标明出处及原作者。

写在前面的话:本文介绍了如何在 Python 中使用 Jupyter 笔记本和 EODHD API 来可视化交易信号,并通过实例演示了如何绘制简单移动平均线 (SMA)、MACD 和 RSI 指标,以及如何结合这些指标来识别买卖交易信号。
本文的开始,我们需要假设您熟悉交易技术分析的一些基础知识。如果不熟悉,我们建议您阅读我的文章《2025 已近,你想进入量化金融行业吗?看这一篇就够!》,任何时候开始学习都不晚。
如果您想了解如何安装 EODHD APIs Python Financial Official Library 并激活您的 API 密钥,请从浏览我的这篇文档开始《手把手教会你用 AI 和 Python 进行股票交易预测(完整代码干货)》。
一、建立用例
我们的目标是确定买入或卖出股票或加密货币的最佳时机。我们要如何做到这一点呢?有一种方法是通过移动平均线来辨别市场方向。
简单移动平均线 (SMA) 是过去 ‘n’ 期历史交易数据收盘价的平均值。例如,在日线图中,每个蜡烛图或数据点代表一天。SMA50 是过去 50 天收盘价的平均值,有助于消除市场 “噪音”。不过,当我们将其与另一条移动平均线(如 SMA200)进行比较时,它就会变得更加有用。
当 SMA50 在 SMA200 上方交叉时,预示着市场趋势向上。相反,当 SMA50 穿过 SMA200 下方时,则预示着市场趋势向下。虽然可以使用任何移动平均线,但这种特殊的组合是独一无二的。当 SMA50 在 SMA200 上方交叉时,它被称为 “黄金交叉”,而当它跌破 SMA200 时,它被称为 “死亡交叉”。这些事件往往发生在重大价格变动之前。
二、准备工作
我们使用的是 Google Colab 中的 Jupyter 笔记本,这是一个免费且方便的工具,如果您想跟进的话。出于演示目的,我们还使用了 EODHD API 的 end-of-day demo key密钥。
步骤 1:导入必要的库
首先,导入以下库:
import json
import requests
import pandas as pd
import matplotlib.pyplot as plt
第 2 步:处理数据
接下来,使用 API 获取苹果公司 (AAPL) 的每日数据,并将其存储到 Pandas DataFrame 中:
resp = requests.get("https://eodhistoricaldata.com/api/eod/AAPL?api_token=demo&fmt=json")
json_data = json.loads(resp.content)
df = pd.DataFrame(json_data)
df
如果一切正常,生成的 DataFrame 应包含苹果公司的每日股票数据。这些数据将成为分析和可视化交易信号的基础。

苹果公司有 10,534 天的历史数据可用,值得注意的是,虽然 “close “和 “adjusted_close “值非常相似,但本教程中我们将使用 “close”。
现在,让我们计算 SMA50 和 SMA200 列并将其添加到 DataFrame 中:
df["sma50"] = df.close.rolling(50, min_periods=50).mean()
df["sma200"] = df.close.rolling(200, min_periods=200).mean()
df

在更新后的 DataFrame 中,您会注意到标有 “sma50 “和 “sma200 “的两个新列。之所以会出现 “NaN “值,是因为每个 SMA 都需要最少的数据点数才能计算出第一个值。例如,SMA50 需要 50 个收盘价才能生成初始平均值。
我们有两种方法来处理这些 “NaN “值:用其他值(如收盘价)替换,或者直接删除数据缺失的行。由于我们有大量可用数据,删除前 200 行不会影响我们的分析。
删除包含 “NaN “的行:
df.dropna(inplace=True)
df
删除缺失值的行后,我们的 DataFrame 将只包含 SMA50 和 SMA200 值已填充完整的行。

三、创建可视化图
下一步,我们将使用 Matplotlib 库,它在数据科学中被广泛用于数据可视化(你可能还记得,我们在前面导入过它)。
下面介绍如何绘制交易信号:
plt.figure(figsize=(30,10))
plt.plot(df["close"], color="black", label="Price")
plt.plot(df["sma50"], color="blue", label="SMA50")
plt.plot(df["sma200"], color="green", label="SMA200")
plt.ylabel("Price")
plt.xticks(rotation=90)
plt.title("APPL Daily SMA50/SMA200")
plt.legend()
plt.show()

虽然图表显示成功,但却不容易解读,因为它可视化了超过 28 年的数据,超出了我们的需要。为了让图表更清晰,我们可以将数据集限制在过去 365 天内,只显示过去一年的数据:
df = df.tail(365)
这将以更聚焦的视角显示图表的外观。

您可能会注意到,X 轴目前显示的是指数数字,而不是实际日期。虽然这并不一定有问题,但在视觉上并不特别吸引人。为了改善这种情况,我们将用实际日期取代 x 轴上的刻度。
df.set_index(['date'], inplace=True)
df

现在,更新后的图表显示如下:

目前,X 轴显示所有 365 天,导致图表杂乱无章,难以阅读。不过不用担心,我们可以通过调整 x 轴标签来解决这个问题,即只显示第七天。
代码工作原理如下:
ax = plt.gca()
for index, label in enumerate(ax.xaxis.get_ticklabels()):
if index % 7 != 0:
label.set_visible(False)
在这段代码中,我们会检查一个指数是否能被 7 整除,如果不能,相应的 x 轴标签就会被隐藏,只有每隔 7 天才会显示出来。这使得图表更加清晰易懂。
下面是完整的代码:
plt.figure(figsize=(30,10))
plt.plot(df["close"], color="black", label="Price")
plt.plot(df["sma50"], color="blue", label="SMA50")
plt.plot(df["sma200"], color="green", label="SMA200")
plt.ylabel("Price")
plt.xticks(rotation=90)
plt.title("APPL Daily SMA50/SMA200")
ax = plt.gca()
for index, label in enumerate(ax.xaxis.get_ticklabels()):
if index % 7 != 0:
label.set_visible(False)
plt.legend()
plt.show()

在图表中,您会看到一条黑线代表每日收盘价,一条蓝线代表 SMA50,一条绿线代表 SMA200。
观察图表,在过去一年中,蓝色 SMA50 线首次越过绿色 SMA200 线时,市场就会进入上升趋势。这本来是一个极好的买入时机,也是一个有利可图的时机。随后,蓝线跌破绿线,价格随之大幅下跌。理想情况下,我们不希望在卖出前等待太长时间,我们可以通过一些方法来完善这一点。
四、确定均线交叉点
在 Pandas 中,我们可以根据特定条件创建包含布尔值(真或假)的新特征或列。
pd.options.mode.chained_assignment = None
df.loc[df["sma50"] > df["sma200"], "sma50gtsma200"] = True
df["sma50gtsma200"].fillna(False, inplace=True)
df.loc[df["sma50"] < df["sma200"], "sma50ltsma200"] = True
df["sma50ltsma200"].fillna(False, inplace=True)
df

在我们的 DataFrame 中,您会注意到两个新列:”SMA50GTSMA200 “和 “SMA50LTSMA200″。当 SMA50 大于 SMA200 时,第一列返回 “真”;当 SMA50 小于 SMA200 时,第二列返回 “真”。如果两个条件都不满足,则值为 “假”。我们要确定发生交叉的精确点。您可以通过以下方法来实现这一目标:
df["sma50gtsma200co"] = df.sma50gtsma200.ne(df.sma50gtsma200.shift())
df.loc[df["sma50gtsma200"] == False, "sma50gtsma200co"] = False
df["sma50ltsma200co"] = df.sma50ltsma200.ne(df.sma50ltsma200.shift())
df.loc[df["sma50ltsma200"] == False, "sma50ltsma200co"] = False
df

新的功能列 “SMA50GTSMA200CO “和 “SMA50LTSMA200CO “将显示何时出现交叉。让我们确认买入信号:
buysignals = df[df["sma50gtsma200co"] == True]
buysignals

别忘记还有卖出信号:
sellsignals = df[df["sma50ltsma200co"] == True]
sellsignals

五、绘制买卖交易信号图
下一步我们需要用 Python 在图表上绘制我们的买入和卖出交易信号。下面是实现这一目标的方法:
for idx in buysignals.index.tolist():
plt.plot(
idx,
df.loc[idx]["close"],
"g*",
markersize=25
)
for idx in sellsignals.index.tolist():
plt.plot(
idx,
df.loc[idx]["close"],
"r*",
markersize=25
)
这段代码将为 “buysignals “数据帧中的条目添加绿星,为 “sellsignals “数据帧中的条目添加红星。它将直观显示数据帧中的所有买入和卖出信号,以便结合信号得出更高级的结果。完整代码如下:
plt.figure(figsize=(30,10))
plt.plot(df["close"], color="black", label="Price")
plt.plot(df["sma50"], color="blue", label="SMA50")
plt.plot(df["sma200"], color="green", label="SMA200")
plt.ylabel("Price")
plt.xticks(rotation=90)
plt.title("APPL Daily SMA50/SMA200")
for idx in buysignals.index.tolist():
plt.plot(
idx,
df.loc[idx]["close"],
"g*",
markersize=25
)
for idx in sellsignals.index.tolist():
plt.plot(
idx,
df.loc[idx]["close"],
"r*",
markersize=25
)
ax = plt.gca()
for index, label in enumerate(ax.xaxis.get_ticklabels()):
if index % 7 != 0:
label.set_visible(False)
plt.legend()
plt.show()

用 Python 绘制买入和卖出交易信号的好处是,它能清晰直观地显示市场走势,从而更容易完善交易策略。例如,虽然最初的买入信号可能看起来很有希望,但由于移动平均线固有的滞后性,随后的卖出信号会被延迟。
那让我们结合移动平均收敛背离 (MACD) 指标,看看它能否改善我们的信号。
df["ema12"] = df["close"].ewm(span=12, adjust=False).mean()
df["ema26"] = df["close"].ewm(span=26, adjust=False).mean()
df["macd"] = df["ema12"] - df["ema26"]
df["signal"] = df["macd"].ewm(span=9, adjust=False).mean()
df.loc[df["macd"] > df["signal"], "macdgtsignal"] = True
df["macdgtsignal"].fillna(False, inplace=True)
df.loc[df["macd"] < df["signal"], "macdltsignal"] = True
df["macdltsignal"].fillna(False, inplace=True)
df

MACD 的计算方法是从 12 期 EMA 减去 26 期指数移动平均线 (EMA)。信号是另一个使用最近 9 个 MACD 值的指数移动平均线。当 MACD 在信号上方交叉时,即为买入信号,反之则为卖出信号。我们通过在数据帧中添加捕捉精确交叉点的特征列来识别这些信号。
将 SMA50/200 和 MACD/信号指标组合成综合买卖交易信号:
buysignals = df[(df["sma50gtsma200co"] == 1) & (df["macdgtsignal"] == 1)]
sellsignals = df[(df["sma50ltsma200co"] == 1) & (df["macdltsignal"] == 1)]
根据这一逻辑,当 SMA50 穿过 SMA200 且 MACD 大于信号时,就会触发买入信号。反之,当 SMA50 穿过 SMA200 下方且 MACD 小于信号时,卖出信号就会触发。

你会发现,组合策略仍能准确识别我们的买入信号,但两个卖出信号已被删除。在这种情况下,它可能会建议持有头寸而不是卖出。不过,在这种特定情况下,还不能确定这是否是正确的方法。可能需要进一步分析来完善信号。

如果我们修改卖出信号,以表明即使 SMA50 仍高于 SMA200,当 MACD 穿过信号下方时也应卖出,我们就可以这样调整我们的条件:
df["macdgtsignalco"] = df.macdgtsignal.ne(df.macdgtsignal.shift())
df.loc[df["macdgtsignal"] == False, "macdgtsignalco"] = False
df["macdltsignalco"] = df.macdltsignal.ne(df.macdltsignal.shift())
df.loc[df["macdltsignal"] == False, "macdltsignalco"] = False
buysignals = df[(df["sma50gtsma200co"] == 1) & (df["macdgtsignal"] == 1)]
sellsignals = df[(df["sma50gtsma200"] == 1) & (df["macdltsignalco"] == 1)]
根据这一调整条件,我们的 DataFrame 中现在有五个卖出信号,这有助于我们通过考虑移动平均线和 MACD 交叉来完善交易策略。


这个设置现在看起来好多了。买入信号似乎是正确的,我们有几个卖出信号可供选择。在买入后,利用杠杆在最初的卖出信号上卖出,很可能会获得可观的利润。
可能会有读者要问,为什么不尝试添加相对强弱指数 (RSI)?当 RSI14 低于 30,通常表示买入信号;如果 RSI14 高于 70,则表示卖出信号。
这篇文章的目的是为了帮助您入门!请记住 RSI 的计算要比移动平均线或 MACD 复杂一些。使用 `pandas_ta` 库可以简化这一过程。我就顺便给大家介绍下。
在 Google Colab 中,使用单独的代码单元安装 `pandas_ta` 库:
pip install pandas_ta
然后,导入程序库:
import pandas_ta as ta
最后,计算 RSI14:
df["rsi14"] = ta.rsi(df["close"], length=14, fillna=50)
请记住,RSI14 的前 14 个条目将包含 “NaN “值。通过使用 `fillna`,我们将默认值设置为 50。
六、观点回顾
通过这个例子,我们可以很好地了解各种可能性以及实现这些可能性所需的工具。尝试使用各种技术指标和蜡烛图形态,创造属于自己的 “神奇策略”。总结要点如下:
- 技术分析是交易决策的重要工具,SMA、MACD 和 RSI 是常用的技术指标。
- 通过编程可以自动化地识别和可视化交易信号,有助于交易策略的开发和测试。
- 结合多个技术指标可以提高交易信号的质量和可靠性。
- 在实际交易中应谨慎使用技术指标,并结合其他分析方法和专业意见。
- 持续学习和实践是提高交易技能和市场理解的关键。
感谢您阅读到最后。如果对文中的内容有任何疑问,请给我留言,必复。
本文内容仅仅是技术探讨和学习,并不构成任何投资建议。
转发请注明原作者和出处。
Be First to Comment