精通金融模式识别-一-

63 阅读47分钟

精通金融模式识别(一)

原文:annas-archive.org/md5/4328078f8bab6733c95749acb5952053

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

发现模式是智慧的本质。

丹尼斯·普雷格

随着技术进步和金融信息的去中心化,编码和自动化研究已经成为交易世界中不可或缺的部分。掌握交易和编码艺术的人在市场上拥有巨大的优势。

交易技术是众多的,它们可以基于许多工具和概念。例如,基本分析交易员依赖经济和政治分析来获取不同资产的长期视角,而技术交易员则依赖更多的量化指标和少数心理概念来预测市场下一个可能的走势。

因此,我们可以说在高层次上存在两种类型的分析,基本分析和技术分析。本书将详细介绍技术分析中的一个领域,称为蜡烛图案识别

为什么这本书?

我整个职业生涯都在研究交易策略、模式以及与金融世界有关的任何事物。我对模式,特别是蜡烛图案,有了特别的热情,因为它们被广泛采用,但也因其有趣的表现结果。此外,多年来,我发现了一些蜡烛图案,我相信至少可以与经典模式媲美。这也是我写这本书的目的:我打算呈现所有蜡烛图案的完整性,包括我个人的图案,以及如何编写一个系统来跨多种市场对它们进行回测。

由于其客观性,机器可以比人类更好地执行模式识别和检测。因此,我已经把书的前几章专门用来创建蜡烛图案识别算法的结构,然后再深入研究后续章节中的模式和策略。这意味着您将学会的第一个技能是如何在 Python 中自动化数据导入过程。

有许多经典的蜡烛图形式,每个人都有责任测试它们以确定它们是否真的具有预测性。毕竟,如果我们用这些模式来预测市场,我们应该有客观的结果来证明它们确实是增值的。我们将获得这样的结果并解释它们,就像我多年来发现的蜡烛图案一样。我们还将看到每种模式的优势和局限性。

当我们找到能够帮助预测任务的好模式时,您应该将它们插入到整体交易框架中,其中包括其他工具和风险管理系统。您将学习如何编写技术指标并将它们与蜡烛图案结合以生成交易信号。最后,您将对这些信号进行回测,并能够优化参数,从而获得一个良好的全面模式识别策略。

因此,本书的实用性在于向您展示如何通过让您创建的算法评估不同的蜡烛图案来自动化您的研究。最后,您将学习如何确定您的策略,该策略将使用这些模式并与其他技术指标结合。

目标受众

本书适合有志于学习的学生、学者、好奇的头脑以及对蜡烛图案识别及其在金融中应用感兴趣的金融从业者。如果你不仅对使用 Python 感兴趣,还对开发策略和技术指标感兴趣,那么你将从本书中受益。

本书假设您在 Python 编程和金融交易方面具有基础知识(专业 Python 用户将发现代码非常直观)。我采用了一种清晰简单的方法,重点放在关键概念上,以便您理解每个想法的目的。

本书使用的约定

以下是本书使用的排版约定:

Italic

指示新术语、网址、电子邮件地址、文件名和文件扩展名。

Constant width

用于程序列表,以及段落内引用程序元素(如变量或函数名、数据库、数据类型、环境变量、语句和关键字)。

Constant width bold

显示用户应按照字面意义键入的命令或其他文本。

Constant width italic

显示应由用户提供的值或由上下文确定的值替换的文本。

提示

此元素表示提示或建议。

注意

此元素表示一般注释。

警告

此元素表示警告或注意事项。

使用代码示例

可以从https://github.com/sofienkaabar/mastering-financial-pattern-recognition下载补充材料(示例代码、练习等)。

如果您有技术问题或使用代码示例遇到问题,请发送电子邮件至bookquestions@oreilly.com

本书的目的是帮助你完成工作。一般情况下,如果本书提供示例代码,你可以在你的程序和文档中使用它。除非你复制了大部分代码,否则无需联系我们获取许可。例如,编写一个使用本书多个代码片段的程序不需要许可。销售或分发 O'Reilly 书籍中的示例代码需要许可。引用本书并引用示例代码来回答问题无需许可。将本书的大量示例代码整合到产品文档中需要许可。

我们感谢但通常不要求归因。归因通常包括标题、作者、出版商和 ISBN。例如:“掌握金融模式识别,索菲恩·卡巴尔(奥莱利)。版权所有 2023 年索菲恩·卡巴尔,978-1-098-12047-4。”

如果您认为您对代码示例的使用超出了合理使用或以上许可的范围,请随时通过permissions@oreilly.com与我们联系。

奥莱利在线学习

注意事项

40 多年来,奥莱利传媒提供技术和商业培训、知识和洞察力,帮助公司取得成功。

我们独特的专家和创新者网络通过图书、文章和我们的在线学习平台分享他们的知识和专业知识。奥莱利的在线学习平台为您提供按需访问直播培训课程、深入学习路径、交互式编码环境以及来自奥莱利和 200 多家其他出版商的广泛文本和视频。有关更多信息,请访问https://oreilly.com

如何联系我们

请将有关本书的评论和问题发送至出版商:

  • 奥莱利传媒公司

  • 北格拉文斯坦公路 1005 号

  • CA 95472,塞巴斯托波尔

  • 800-998-9938(美国或加拿大)

  • 707-829-0515(国际或本地)

  • 707-829-0104(传真)

我们为本书设有网页,列出勘误、示例和任何其他信息。您可以通过https://oreil.ly/mstrg-finan-pttrn-recog访问此页面。

发送电子邮件至bookquestions@oreilly.com发表评论或提出有关本书的技术问题。

欲获取有关我们的图书和课程的新闻和信息,请访问https://oreilly.com

在 LinkedIn 上找到我们:https://linkedin.com/company/oreilly-media

在 Twitter 上关注我们:https://twitter.com/oreillymedia

在 YouTube 上关注我们:https://youtube.com/oreillymedia

致谢

没有我父母的支持,一切都将不同,这也是我必须承认他们对本书直接和间接影响的原因。我还要提到,没有我妻子夏琳的耐心,我无法完成深夜写作。我对她的感激之情溢于言表。

我还要感谢编辑米歇尔·史密斯和科宾·柯林斯以及制作编辑伊丽莎白·费姆的持续支持和出色工作,也感谢他们的耐心。同样,我要感谢所有参与奥莱利传媒的每个人。

另外,我要特别感谢出色的技术审阅人员,宁王、蒂莫西·基普和库珊·沃拉,他们对这本书的巨大贡献不可估量。他们对使这本书易读、有用和简单的影响深远。我无法找到比他们更合适的人来审阅我的书。

最后,我深深感谢您,读者,您抽出宝贵的时间阅读我的作品并信任我的研究。希望您觉得它有用。

第一章:在 Python 中导入和处理金融数据

本章专门讨论通过编码分析金融数据所需的基础工作。这需要一些准备工作,如下载合适的软件和创建一个自动获取历史数据的算法。

到本章结束时,你应该知道如何使用 Python 自动导入历史金融数据,这个技能应该能为你节省时间。那么,让我们开始吧。

安装环境

第一步是准备环境和算法成功所需的其他一切。为此,你需要两个程序:

  • 你用来编写和执行代码的 Python 解释器

  • 你用作数据库的图表和金融软件

让我们从 Python 解释器开始。我使用一个名为 SPYDER 的软件。有些人可能更熟悉其他软件,如 Jupyter 和 PyCharm,但过程是相同的。你可以从官方网站下载 SPYDER,或者更好地,作为一个名为Anaconda的大包的一部分下载它,这样安装更方便并提供更多工具。注意它是开源、免费的软件。

SPYDER 的界面分为三个窗口,如你在图 1-1 中所见。左侧的窗口用于编写稍后执行的代码(告诉算法运行并应用代码)。通常,你会在那个区域看到多行代码。

右上角的窗口是变量资源管理器。每次存储变量时,你都可以在那里看到它。右下角的窗口是控制台,显示代码的结果,无论是错误还是输出。

图 1-1. SPYDER 的界面

你可以在代码中定义和使用的数据类型分为几类:

整数

这些是整数,可以是正数也可以是负数。例如 −8 和 745。然而,它们限制在 −2147483648 到 2147483647 之间。任何超出此范围的数字被认为是一个不同的数据类型,称为长整型。数据类型之间的区别与内存有关。整数的宽度为 32 位,而长整型为 64 位。

浮点数

这些是带有小数点的实数,例如 18.54 和 311.52。

字符串

这些是存储在变量中的单词。更科学地说,它们是一组结构化字符(文本)。在 Python 中,你可以用单引号或双引号之间写字符串。

在图 1-1 的代码第 1 行中,我定义了一个名为age的变量,并将其设置为 13。当你运行此代码时,你应该在变量资源管理器中看到age的创建,类型为int(整数),值为 13。在第 3 行,我执行了定义height变量并设置为 184.50 的代码(因此是浮点数数据类型)。

注意,变量定义旁边我写了in centimeters这个短语,前面加了井号。这叫做注释。在 Python 中,注释对于解释代码非常重要。因此,任何以#开头的内容都不会被执行。在变量资源管理器中,您可以看到height变量的float类型。第 5 行定义了一个字符串,在控制台中显示为str类型(字符串)。您可以看到代码已成功执行,因为没有显示为红色的错误。

准备环境的下一步是安装图表软件,以便您可以将历史数据导入到 SPYDER 中。在整本书中,我使用 MetaTrader 5,这是许多全球交易员使用的基准图表程序。按照以下步骤进行操作:

  1. 下载 SPYDER 并熟悉其工作方式。

  2. 下载 MetaTrader 5 软件。

  3. 使用 SPYDER 从 MetaTrader 5 导入历史价格。

官方网站下载并安装 MetaTrader 5。您需要创建一个演示账户,这只是一个带有虚拟货币的虚拟账户。单词demo不是指使用的时间有限,而是指它不使用真实货币。

要开设账户,请选择文件 > 打开账户,选择 MetaQuotes Software Corp,然后点击下一步。接下来,选择第一个选项以开设演示账户;这将让您交易虚拟货币。最后,输入一些基本信息,如姓名、电子邮件和账户类型。您将不会收到验证请求或任何类型的确认,因为演示应直接启动,允许您查看图表。

图 1-2 显示了平台的界面。默认情况下,MetaTrader 5 不显示其涵盖的所有市场,因此如果需要,您需要使其可访问以进行导入和可视化。单击查看,单击市场观察,然后右键单击新选项卡中显示的任何符号,并选择显示所有。这样,您可以看到扩展列表,其中包含更多市场。

图 1-2. MetaTrader 5 的界面。

创建导入算法。

能够自动调用任何时间段的历史数据是一种非常节省时间的方法,因为它使您能够专注于研究和分析,而不是浪费宝贵的时间获取和清理数据。让我们创建一组函数,几乎可以即时导入所选资产的历史数据。

在进行编码之前,您需要安装 MetaTrader 5 Python 集成库,以便稍后在 SPYDER 中使用它。这很简单,只需一步。打开 Anaconda 提示符并输入pip install Metatrader5,如图 1-3 所示。

图 1-3. Anaconda 提示显示安装 MetaTrader 5 库的命令。

安装是允许您在解释器中使用为 MetaTrader 5 设计的 Python 库的桥梁。

以下代码块使用了内置的import语句,调用了内部(自行创建的)或外部(第三方创建的)库。库是函数的存储库,因此,你需要导入与你想要做的事情相关的库。为了演示目的,导入以下模块、包和库:

import datetime
import pytz
import pandas as pd
import MetaTrader5 as mt5
import numpy as np 

datetime模块提供了操作日期和时间的工具,pytz库提供了跨平台的时区计算,这些对导入是必需的,而pandasnumpy库用于数据处理和分析。

注意

你主要使用numpy进行大多数计算和数据处理。

MetaTrader 5 库导入与软件模块相关的函数,是允许你导入金融历史数据的关键库。

注意,代码的最后三行包含as语句。这用于在你经常使用它并希望节省写作空间时,给库命名自定义名称。换句话说,从现在起,Python 将认识 MetaTrader 5 库为 mt5。

注意

模块是包含函数和变量的文件。是模块的集合;它需要一个init.py文件。库只是包的集合。

执行import语句意味着 Python 现在识别其中的函数,并允许你在未来的代码中使用它们(如果你决定调用它们)。你必须在每次打开新会话时运行它们,这就是为什么import语句通常位于代码的开头。

下一步是创建你将能够导入的时间框架的宇宙。即使我将向你展示如何分析和回测每小时数据,你可以定义一个更广泛的宇宙,如下面的代码片段所示:

frame_M15 = mt5.TIMEFRAME_M15 `# 15-minute time`
frameframe_M30 = mt5.TIMEFRAME_M30 `# 30-minute time frame`
frame_H1 = mt5.TIMEFRAME_H1 `# Hourly time frame`
frame_H4 = mt5.TIMEFRAME_H4 `# 4-hour time frame`
frame_D1 = mt5.TIMEFRAME_D1 `# Daily time frame`
frame_W1 = mt5.TIMEFRAME_W1 `# Weekly time frame`
frame_M1 = mt5.TIMEFRAME_MN1 `# Monthly time frame`

时间框架是你记录价格频率的频率。使用小时数据,你将每小时记录最后打印的价格。这意味着一天内,你可以有最多 24 个小时价格。这允许你看到价格的日内演变。然而,收盘价格只是你想要导入的事物之一。在一个时间段内(无论是每小时还是每日),你将看到以下内容:

  • 时间段的第一个价格,称为开盘价格

  • 打印在时间段内的最高价格,称为高价格

  • 打印在时间段内的最低价格,称为低价格

  • 在开始新的时间段之前所看到的最后价格,被称为收盘价格

总体而言,这些被称为 OHLC¹数据,通常按照写入的顺序排列。

以下代码定义了当前时间,这用于算法在导入数据时有一个参考点。基本上,你正在创建一个变量,用于存储当前的时间和日期:

now = datetime.datetime.now()

现在我们继续定义您要进行回测的资产范围。我涉及四种资产类别:货币、加密货币、大宗商品和股票指数:

  • 货币(也称为外汇,是外汇市场的缩写)是按日交易量计算的最大金融市场。货币以货币对的形式报价,这意味着您不能仅仅用美元购买美元;您必须使用另一种货币购买。因此,EURUSD 货币对指的是 1 欧元相当于多少美元。回测范围包括 EURUSD、USDCHF、GBPUSD 和 USDCAD。
注意

USD 是美元的缩写,EUR 是欧元货币的缩写,CHF 是瑞士法郎的缩写,GBP 是英镑的缩写,CAD 是加拿大元的缩写。

  • 加密货币(也称为加密货币)是一种新型、具有颠覆性的资产类别,其特点是严重的波动性。最知名的加密货币是比特币,其次是以太坊。请注意,两者都以美元计价;这就是为什么它们被标记为 BTCUSD 和 ETHUSD 的原因。
注意

请注意,比特币(BTC)和以太坊(ETH)是相对于美元报价的。它们通常被认为是最流动的加密货币对。

  • 大宗商品是金、银、铜等实物资产。它们被划分为许多类别,如能源(原油、布伦特原油等)和工业金属(铜、锌等)。在资产范围内,我只关注黄金和白银。

  • 股票指数是对一个国家股票精选篮子的加权计算。它们用于分析一个国家整体股票市场的健康状况。在这本书中,我涵盖了标普 500 指数,作为美国股票的代理,以及富时 100 指数,作为英国股票的代理:

assets = ['EURUSD', 'USDCHF', 'GBPUSD', 'USDCAD', 'BTCUSD', 
          'ETHUSD', 'XAUUSD', 'XAGUSD', 'SP500m', 'UK100']

现在您已经准备好了时间和资产变量,您所需的只是创建导入算法的结构。get_quotes()函数就是这样做的:

def get_quotes(time_frame, year = 2005, month = 1, day = 1, 
               asset = "EURUSD"):

    if not mt5.initialize():

        print("initialize() failed, error code =", mt5.last_error())

        quit()

    timezone = pytz.timezone("Europe/Paris")

    time_from = datetime.datetime(year, month, day, tzinfo = timezone)

    time_to = datetime.datetime.now(timezone) + datetime.timedelta(days=1)

    rates = mt5.copy_rates_range(asset, time_frame, time_from, time_to)

    rates_frame = pd.DataFrame(rates)

    return rates_frame

注意,在get_quotes()函数中,您最终使用了pytzpandas库。该函数首先通过定义奥尔森时区来开始,您可以自行设置。以下是一个简要的、不详尽的列表,根据您的时区可以输入的内容:

America/New_York
Europe/London
Europe/Paris
Asia/Tokyo
Australia/Sydney

之后,我定义了两个变量,分别称为time_fromtime_to

  • 变量time_from包含了指向导入日期开始的日期时间(例如,2020 年 01 月 01 日)。

  • 变量time_to包含了指向导入日期结束的日期时间(例如,2020 年 12 月 31 日)。

下一步是创建一个变量,使用您指定的时间段导入财务数据。这通过rates变量使用mt5.copy_rates_range()函数来完成。最后,使用pandas将数据转换为数据框。

注意

在整本书中,您将处理数组而不是数据框;不过,get_quotes()函数首先将值导入为数据框以保持兼容性,然后将其转换为数组。无论如何,数据框和数组之间的主要区别在于可以保存的数据类型及轴的结构。

导入过程所需的最终函数是mass_import()函数。它允许您使用变量选择时间框架,然后使用get_quotes()函数导入数据并格式化为数组。以下代码段定义了mass_import()函数:

def mass_import(asset, time_frame):

    if time_frame == 'H1':
        data = get_quotes(frame_H1, 2013, 1, 1, asset = assets[asset])
        data = data.iloc[:, 1:5].values
        data = data.round(decimals = 5)        

    if time_frame == 'D1':
        data = get_quotes(frame_D1, 2000, 1, 1, asset = assets[asset])
        data = data.iloc[:, 1:5].values
        data = data.round(decimals = 5)        

    return data 

mass_import()函数会自动将数据框转换为数组,因此在使用自动导入时不必担心转换问题。

注意

算法导入了由 MetaTrader 5 限制的大量历史数据。虽然该数量很大,但是随着时间的推移,您可能需要调整年份参数以获取数据。例如,如果使用mass_import()函数返回一个空数组,请尝试在get_quotes()函数中放入一个更新的年份(如前面示例中的“2014”而不是“2013”)。

尽管 MetaTrader 5 有 MAC 版本,但 Python 库仅适用于 Windows。在 Mac 上需要模拟器。对于 Mac 用户,您也可以尝试本章后面介绍的手动导入方法。

把一切放在一起

现在让我们看一个完整的数据导入示例。请记住,完整的导入代码可以在本书的 GitHub 仓库中找到。通常,本书中只处理每小时数据,因为交易者大量使用它,这会产生有趣的信号;不过,让我们尝试在定义了本章中所见函数之后应用几个导入示例:

  • 要导入每日 ETHUSD 数据,请输入以下代码:
my_data = mass_import(5, 'D1')
  • 要导入每小时 GBPUSD 数据,请输入以下代码:
my_data = mass_import(2, 'H1')

有许多方法可以将数据导入 Python;一些是自动的,一些是手动的。您刚刚看到了使用代码与图表平台通信并下载数据的第一种方式。手动方式是使用从第三方下载的包含 OHLC 数据的 Excel 文件。在这种情况下,您可以使用pandas库将其导入并转换为数组。

假设 Excel 文件名为my_data,并且文件存储在您的桌面上。您必须确保 SPYDER 目录与文件存放在同一位置。用通俗的话说,SPYDER 必须在桌面上搜索 Excel 文件。要选择正确的目录,您必须单击箭头旁边的文件夹按钮,如图 1-4 所示。

图 1-4. 目录选项卡

应该打开一个单独的窗口,您可以在其中选择桌面位置,然后验证选择。完成这些步骤后,选项卡应该看起来像图 1-5。

图 1-5. 目录选项卡

你必须使用read_excel()函数来获取 Excel 文件中的值。按照以下语法操作:

`# Importing the excel file into the Python interpreter`
my_data = pd.read_excel('my_data.xlsx')

此时,你有一个名为my_data的数据框,其中有四列,表示开盘价、最高价、最低价和收盘价。通常,在使用属于某个库的函数之前,你必须输入该库的名称;这就是为什么read_excel()之前要加上pd的原因。

注意

请记住,pd是用来引用pandas的快捷方式。同时,np是用来引用numpy的快捷方式。

下面的语法显示了如何将数据框中的结构化元素转换为数组,以便于操作。我使用的数组库称为numpy,这是本书中主要使用的库。

注意

我建议 Windows 用户使用自动方式,而 macOS 用户使用手动方式,因为兼容性问题。

在你使用numpy函数之前,你认为应该做什么?如果你的答案是导入库,那么你是正确的。以下代码片段导入numpy并将my_data转换为数组,以便进行分析:

`# Importing the library`
import numpy as np

`# Converting from data frame to array`
my_data = np.array(my_data)

或者,你可以通过在pd.​read_excel('my_data.xlsx')后添加.values,从而变为pd.read_excel('my_data.xlsx').​values,得到一个数组而不是数据框,这样就可以一行搞定所有操作。

摘要

研究和交易框架由四种不同的算法组成,这些算法将在下一章节详细讨论。它们可以总结如下:

导入算法

这是本章展示的算法,处理导入和准备历史 OHLC 数据以供分析或回测。我相信在这个阶段,你可以轻松地自动和手动完成这一操作。

信号算法

这个算法,你将在第二章中看到,将负责生成买入和卖出订单。本质上,它是一组条件,给出了绿灯,表明出现了某种模式,可以进行交易。

绘图算法

这是最简单的算法。你将用它来绘制价格图上的信号。其目的是在图表上视觉分析买入和卖出信号。你将在第二章中也学习到这一点。

性能算法

这个算法用于计算和分析从信号算法获取的结果。基本上,它计算了在符合条件的模式生成的信号上的性能指标。你也将在第二章中学习到这一点。

你必须知道如何自动导入和准备以后要分析的金融数据。Python 提供了强大而快速的工具来做到这一点;因此,请确保掌握这项技术,以便扩展你的研究能力。

¹ 开盘最高最低收盘的缩写。

第二章:算法思维与函数

算法是计算机根据特定条件的实现应用的一组规则。通常使用算法来解决特定问题或简单地遵循重复任务的序列。您还可以使用算法通过扫描设置的条件来查找模式。

本书的主要目的是向您展示如何扫描、发现和评估蜡烛图形和策略。使用算法而不是手动执行任务的主要好处如下:

速度

算法与人类相比能够以极快的速度运行。一个简单的算法可以在几秒钟内扫描数十万条数据,而人类可能需要数周甚至数月来完成同样的任务。

纪律

算法遵循一组明确的规则,并且不具备使它们偶尔忽略规则的情感或情绪。此外,算法不会陷入主观解释的陷阱。这对评估过程至关重要,因为您需要客观和清晰的衡量标准来评判您的交易系统。

错误百分比

当代码中没有错误时,算法通常是无误差的。人类由于不注意和疲劳可能会犯很多错误。

注意

交易系统由多个算法组成,如交易和风险管理算法。强大的交易系统依赖于明确和稳定的基于规则的算法来提供可靠的衡量标准。

本章分为四节。首先涵盖了原始函数,这些函数处理了本书中始终使用的基本数据操作。然后展示了如何对模式和策略的信号进行编码。接着介绍了如何在价格图表上可视化信号,以便您得到美观且易于解释的表现。最后,您将学习关键绩效评估指标及其编码方式。确保掌握本章的概念,因为它们对本书的其余部分至关重要;它们不仅有助于检测模式,还有助于创建策略。

编码原始函数

原始函数是一小组自定义函数,帮助您更好地操作数据数组。原始函数的概念始于我早年练习编码的一项练习,但随着时间的推移,它们成为我在研究中经常使用的常规代码片段。

分析和回测是重复性任务,需要一些函数在任何地方起作用。让我们从最基本的原始函数开始(记住,您将在整本书中使用numpy)。

向数组添加列的函数

有时,您需要添加列以填充它们,其中可以是指标或信号。例如,假设您有一个由四列组成的 OHLC 数组。您可以使用函数添加额外的列,您可以使用这些列来容纳以下内容:

  • 基于收盘价计算的指标,例如简单移动平均¹

  • 使用预定的二进制值(例如买入信号为 1,卖出信号为−1)作为买卖信号的代理

注意

数组中的行表示时间步骤。在本书中我使用小时时间框架,这意味着每行包含每小时的开盘、最高、最低、收盘价以及任何指标值或买卖代理。

因此,第一个原始函数是add_column(),如下面的代码片段所示:

def add_column(data, times):

    for i in range(1, times + 1):

        new = np.zeros((len(data), 1), dtype = float)

        data = np.append(data, new, axis = 1)

    return data

函数循环由变量times选择的预定次数,并且每次循环都会创建一个新的数组,其中包含与原始数据相同长度的零值,如len(data)所示。这个新数组由np.zeros()预建的numpy函数创建。

最后一步是使用np.append()将这个新创建的数组直接粘贴到原始四列旁边,从而得到一个有五列的数组。

随着循环的进行,列数逐渐增加。参数axis指定了行和列之间的二元选择。当它等于 0 时,它指的是行,当它等于 1 时,它指的是列。记住,变量times是您希望添加的列数,这在调用函数时指定。让我们看一个示例以使事情更清楚。

您有一个名为my_data的数组,由四列组成,您希望添加五列新列以更新原始数组,使其具有九列。假设您已经定义了add_column()函数,您会写什么来完成这个任务?答案如下:

`# Adding five columns to an existing array`
my_data = add_column(my_data, 5)

从数组中删除列的函数

当尝试计算复杂的指标时,可能会出现额外的列,因为您可能需要通过中间计算来填充它们,这些计算在指标准备好之后是不需要的。例如,使用一列来计算权重,然后使用下一列来计算基于权重的指标。您会保留指标列,但不保留权重列。

最后,您希望保留指标的最终结果,并删除先前的列,这样您就可以得到一个由 OHLC 数据组成的干净数组,其中下一列是指标的读数。

快速简便的方法是使用delete_column()函数。下面的代码片段显示了如何定义它:

def delete_column(data, index, times):

    for i in range(1, times + 1):

        data = np.delete(data, index, axis = 1)

    return data

注意

记住,在使用函数之前,必须先定义它。这意味着在编写函数的语法后,必须执行它,以便 Python 将其存储在内存中。

函数循环遍历指定范围,即您要从选定索引开始删除的列数。例如,函数删除从第 4 列开始的三列,包括第 4 列。

函数说明,新转换的数组使用内置的numpy函数np.delete()从变量index开始删除列。最后,变量times是要删除的列数,在调用函数时指定。以下是两个示例,以便更清晰地理解:

  • 你有一个包含 10 列的数组,并且想要从索引为 4 的列开始删除 4 列。下面是如何实现的:

    my_data = delete_column(my_data, 4, 4)
    
    
注意

请记住,索引为 4 的列不是第四列,而是第五列。这是因为 Python 从零开始索引。

  • 你有一个包含八列的数组,并且想要从索引为 1 的列开始删除两列。下面是如何实现的:

    my_data = delete_column(my_data, 1, 2)
    

你主要会在技术指标中使用delete_column(),而不是模式,因为后者是直接的规则,而不是像一些指标那样的顺序复杂计算。

向数组添加行的函数

有时候你想在数组末尾添加行,由于滞后或手动添加编码值。你可以使用以下函数向数组末尾添加空行:

def add_row(data, times):

    for i in range(1, times + 1):       

        columns = np.shape(data)[1]

        new = np.zeros((1, columns), dtype = float)

        data = np.append(data, new, axis = 0)

    return data

该函数循环遍历数据,并在每次循环中使用内置函数np.shape()找到数组的形状,该函数输出矩阵的行数和列数。由于你只关心列数,你会添加[1]来告诉算法你只想要第二个值,也就是列数。将列数存储在变量columns中后,算法继续执行下一行,并创建一个全由零组成的新数组,该数组只有一行,列数等于变量columns。最后,算法将这个新创建的带有一行的数组附加到原始数组的末尾,从而输出一个与其他行大小相同的新行。

从数组中删除行的函数

在计算指标时,一些计算可能需要过去最少数量的数据;否则,你会看到无效值,在 Python 中表现为 NaN²。以下函数从开头删除行:

def delete_row(data, number):

    data = data[number:, ]

    return data

这个简单的函数说明,实际数据从number索引开始,一直到数据的末尾。基本上,它忽略了从开头选择的若干行。

舍入数字的函数

为什么在这里讨论一个看起来不足以成为独立函数的简单概念?答案是除非你将 OHLC 数据的数字四舍五入,否则某些模式非常罕见。

让我们以欧元美元货币对为例。近年来,出于精确性原因,零售市场经销商开始对大多数货币对进行五位小数报价,而不是四位。尽管交易员习惯于的概念,即第四位小数(即 1.0964 中的 4),但现在他们有一个点分,即第五位小数(即 1.09645 中的 5)。

这种变化可能会让习惯于传统四位小数系统的市场参与者感到困惑,但这是必要的,并且有利于交易商报出更紧密的价差。价差是买入(询价)和卖出(出价)价格之间的差异。这是市场交易商承担风险的补偿。对交易者来说,它被视为交易成本,因为购买时稍微更贵,而出售时稍微更便宜。

让我们利用这个机会讨论一下货币对的工作原理,然后逐步详细解释为什么最终需要对数字进行四舍五入。

货币对由两种货币组成;左边的是基础货币,右边的是价格货币。当你看到 EURUSD 交易报价为 1.0500 时,意味着购买 1 欧元需要花费 1.0500 美元。

现在,假设你用 10500 美元购买了 10000 欧元,几周后发现 EURUSD 的报价为 1.0750。这到底意味着什么?这意味着相对于美元,欧元的价值上升了,因为现在你需要 10750 美元来购买 10000 欧元;因此,从理论上讲,你赚了 250 美元(你也可以称之为 250 点的收益,因为每 1 欧元的价值提高了 0.0250)。只有当你将你的欧元兑换成美元时才会实现这一点,这将使你获得 10750 美元,而不是 10500 美元。随着市场的发展和五位小数系统的引入,你现在可能会看到 EURUSD 的小数点后有五个数字(例如,EURUSD 为 1.07516)。

存在某些蜡烛图模式,需要接近开盘价的收盘价。这就是为什么我在后续章节中的某些模式中包含四舍五入的原因。货币对的最佳四舍五入数必须是四位小数——不多不少。以下代码块显示了四舍五入函数:

def rounding(data, how_far):

    data = data.round(decimals = how_far)

    return data

函数使用内置函数round()来简化过程。确保理解在搜索特定模式时需要对 OHLC 数据进行四舍五入的必要性,因为在后续章节中你会用到它。

在继续之前,让我们简要练习一下你在本节学到的新函数。假设你有一个 OHLC 数组,你想要:

  • 在数组中添加两列:

    my_data = add_column(my_data, 2)
    
  • 从第二列开始删除三列:

    my_data = delete_column(my_data, 1, 3)
    
  • 在数组末尾添加 11 行:

    my_data = add_row(my_data, 11)
    
  • 删除数组中的前四行:

    my_data = delete_row(my_data, 4)
    
  • 将数组中所有的值四舍五入到四位小数:

    my_data = rounding(my_data, 4)
    
    
注意

记住,变量data通常用于函数中,并引用你想引用的任何数据,而变量my_data是我导入的所有 OHLC 数组的默认名称。

编码信号

这标志着信号算法的开始。记住,本节的目的是看如何创建一组条件以寻找有效信号。到目前为止,你已经导入了一个资产或货币对的 OHLC 数组。

我还没有讨论任何蜡烛图案的情况下,我会创建一个假设的模式,然后编写其条件并创建信号算法。我们称这个假设的配置为 Alpha 模式:

  • 下一个开盘时会产生一个长期(买入)信号,只要当前的最低价低于 5 个周期前和 13 个周期前的最低价,但高于 21 个周期前的最低价。同时,当前条的收盘价必须高于 3 个周期前的收盘价。

  • 当前的高价高于 5 个周期前和 13 个周期前的高价,但低于 21 个周期前的高价时,下一个开盘会产生一个短期(卖出)信号。同时,当前条的收盘价必须低于 3 个周期前的收盘价。

注意

空头卖出位置是一种复杂的行为,您从第三方借入资产然后卖给买方以获利。最后,您将其买回并归还给原始所有者。如果价格下跌,您将会赚取差价;否则,您将以比您卖出时更高的价格买回它。

前面的要点提供了生成买入和卖出信号所需的一切,因此,您可以开始编写signal()函数。

signal()函数视为扫描每一行并在满足您设置的所有条件时留下痕迹的函数。这些痕迹是买入和卖出代理订单的代理。

您需要告诉算法的第一件事是拥有足够数量的列,您希望用买入和卖出订单填充。如前所述,您可以使用add_column()来完成这个操作。接下来的步骤是告诉算法在整个数据范围内进行循环,这可以通过for语句完成,它在预定义的范围内执行有限循环,而在您的情况下,是 OHLC 数据数组的长度。

当您在验证模式时,可以在接下来的开盘价格上启动仓位,您可以使用try()except()函数,它们仅仅是绕过由于在数组的最后一行出现信号并在下一个不存在的行上触发错误导致的错误。为了说明这个错误,想象有 500 行并在第 500 行得到一个买入信号。由于您在下一个开盘时购买,信号将出现在下一个不存在的行上。这将导致索引错误。

现在,您已经准备好编写 Alpha 模式的条件了。通常我先编写看涨条件。因此,在告诉算法添加两列并在忽略IndexError()问题的同时循环数据时,我会简单地使用if语句添加条件。

这些条件很直观:你只需知道变量i是当前循环中的行索引,因此i-5是当前行之前五行(时间步长)的变量。i变量与for循环一起使用,以便条件可以应用于每一行。

然而,有时候你可能需要在 OHLC 数据之后计算指标或波动率度量,这将推动买入和卖出列到下一个索引。这在“指标分析”中有所涵盖。以下代码片段定义了 Alpha 模式的函数:

def signal(data):

    data = add_column(data, 5)    

    for i in range(len(data)):    

       try:

          `# Bullish Alpha`
           if data[i, 2] < data[i - 5, 2] and data[i, 2] < data[i - 13, 2]
              and data[i, 2] > data[i - 21, 2] and \               data[i, 3] > data[i - 1, 3] and data[i, 4] == 0:

                    data[i + 1, 4] = 1 

           `# Bearish Alpha`
           elif data[i, 1] > data[i - 5, 1] and data[i, 1] > data[i - 13,
                ​1] and data[i, 1] < data[i - 21, 1] and \                 data[i, 3] < data[i - 1, 3] and data[i, 5] == 0:

                    data[i + 1, 5] = -1 

       except IndexError:

            pass

    return data

语句data[i, 4] == 0是要有买入信号,必须在上一行没有买入信号的条件。³ 这是为了避免在模式具有重复性质时出现连续信号。

长信号的代理是在索引为 4 的列中输入数字 1(因此是第五列)。我在验证模式后写data[i + 1, 4] = 1的原因是为了强制算法在完成当前收盘价后,在下一个开盘价上进行买入。相反,短信号的代理是在索引为 5 的列中输入数字-1(因此是第六列)。

这样,你就编写了根据行开盘创建信号所需的条件。你如何编写以下条件呢?

  • 当前收盘价高于两个周期前的收盘价时,下一个开盘价上生成一个长信号。

  • 当当前收盘价低于两个周期前的收盘价时,下一个开盘价上生成一个短信号。

答案在以下代码片段中:

def signal(data):

    data = add_column(data, 2)   

    for i in range(len(data)):   

        try:

            `# Bullish signal`
            if data[i, 3] > data[i - 2, 3]:

                data[i + 1, 4] = 1

  `# Bearish signal`
            elif data[i, 3] < data[i - 2, 3]:

                data[i + 1, 5] = -1

        except IndexError:

            pass

    return data

就是这样!你编写信号的方式是使用函数,然后调用它们。调用函数等同于应用它并看到它实现。

注意

要调用信号函数,请使用以下语法:

my_data = signal(my_data)

return语句强制你重新定义数组,使其成为你想要的(由信号函数定义)。这一义务来自于你正在添加列,因此你必须重新创建数组。

现在,让我们继续进行第三个算法:信号的可视化表示。看起来这是一个可选步骤,而且确实是,但是查看信号样本对你了解可以期待什么是有帮助的。你还必须视觉检查模式的信号,以确保算法正在检测到正确的配置。

创建信号图表

当你创建和应用长信号和短信号时,你的策略核心准备就绪,因此你需要以两种不同的方式进行评估:

主观地

目视检查多个信号图表,以检测可能提供算法错误提示的异常情况(例如,算法检测到的错误模式)。这也可以帮助你更好地理解信号的频率和连续性。这就是你将在本节中看到的内容。

客观地

目标评估是更重要的方法。这涉及计算诸如命中率之类的性能指标。随着我在下一节中深入讨论这些指标,您将看到并编写所需的所有指标。

图 2-1 中的简单图表可以帮助您了解当前所处位置。在导入历史数据和编写信号之后,可视化是框架中的第三个算法。

图 2-1. 算法图

第一章介绍了如何从 MetaTrader 5 平台导入历史 OHLC 数据,您还看到了如何在 MetaTrader 5 平台上没有包含数据的情况下加载您自己的数据。本章已经讨论了如何编写并应用从一组称为 Alpha 模式的条件生成的信号。让我们继续使用相同的例子。

这里是导致有效 Alpha 模式的交易条件的提醒:

  • 当前低点低于 5 个周期前和 13 个周期前的低点但高于 21 个周期前的低点时,下一个开盘产生长期信号。同时,当前柱的收盘价必须高于 3 个周期前的收盘价。

  • 当前高价高于 5 个周期前和 13 个周期前的高价但低于 21 个周期前的高价时,下一个开盘产生短期信号。同时,当前柱的收盘价必须低于 3 个周期前的收盘价。

下一步是使用简单的柱状图可视化这些信号。然后我在产生长期信号的地方应用朝上箭头,并在产生短期信号的地方应用朝下箭头。在本书后面,我将使用蜡烛图创建这些信号图,但由于我尚未介绍它们,我将使用简单柱状图,这是连接每个柱的高点和低点的黑色垂直线。图 2-2 展示了一个简单的柱状图。

图 2-2. 一个简单的高低杆

作为提醒,我还没有讨论技术分析,所以如果您感到信息过载,请不要担心,因为在下一章中一切都应该更清楚。

看一看图 2-3。这是一个简单的柱状图示例,其中在每个小时的高点和低点之间绘制了垂直线。

让我们将 Alpha 模式的信号应用于这些柱上,以便您了解交易相对于价格走势的位置。这使您可以看到每次获得 Alpha 信号时价格发生了什么变化。

图 2-3. USDCHF 上的简单柱状图

使用下面的函数,我称之为signal_chart()。在我定义信号图表之前,这是如何使用以下函数定义的ohlc_plot_bars()创建简单的柱状图的:

def ohlc_plot_bars(data, window):

    sample = data[-window:, ]

    for i in range(len(sample)):

        plt.vlines(x = i, ymin = sample[i, 2], ymax = sample[i, 1], 
        color = 'black', linewidth = 1)  

        if sample[i, 3] > sample[i, 0]:

            plt.vlines(x = i, ymin = sample[i, 0], ymax = sample[i, 3], 
            color = 'black', linewidth = 1)  

        if sample[i, 3] < sample[i, 0]:

            plt.vlines(x = i, ymin = sample[i, 3], ymax = sample[i, 0], 
            color = 'black', linewidth = 1)  

        if sample[i, 3] == sample[i, 0]:

            plt.vlines(x = i, ymin = sample[i, 3], ymax = sample[i, 0] + 
            0.00003, color = 'black', linewidth = 1.00)  

    plt.grid()

该函数允许你绘制简单的条形图。它首先通过定义回顾期(即最近观察到的数量)来开始,用于可视化的观察数量。回顾期的变量是window,因此,一个窗口大小为 500 将显示最近的 500 个观察结果。然后,它通过循环遍历在window变量内的观察结果,并使用plt.vlines()绘制垂直线,正如前面的代码所示。

要绘制最近的 500 个条形图,请使用以下语法:

ohlc_plot_bars(my_data, 500)

现在,让我们将由箭头表示的信号添加到简单的条形图中。看一下以下定义signal_chart()函数的代码块,并注意它如何使用我之前展示的函数:

def signal_chart(data, position, buy_column, sell_column, window = 500):

    sample = data[-window:, ]

    fig, ax = plt.subplots(figsize = (10, 5))

    ohlc_plot_bars(data, window)    

    for i in range(len(sample)):

        if sample[i, buy_column] == 1:

            x = i
            y = sample[i, position]

            ax.annotate(' ', xy = (x, y), 
                        arrowprops = dict(width = 9, headlength = 11, 
                        headwidth = 11, facecolor = 'green', color = 
                        'green'))

        elif sample[i, sell_column] == -1:

            x = i
            y = sample[i, position]

            ax.annotate(' ', xy = (x, y), 
                        arrowprops = dict(width = 9, headlength = -11, 
                        headwidth = -11, facecolor = 'red', color = 
                        'red'))  

变量position应设置为零,因为信号是指开盘价,这样箭头位置就合适了。因此,函数被这样调用:

signal_chart(my_data, 0, 4, 5, window = 250)

如果你将此函数应用于 OHLC 数组,其中已经应用了来自 Alpha 模式的信号,你将得到图 2-4 中的信号图表。请注意,向上箭头表示长信号的确切生成位置,而向下箭头表示短信号的生成位置。图 2-4 显示了 USDCHF 小时值上的信号图表。

图 2-4. USDCHF 上的信号图表

让我们回顾一下你迄今为止做过的事情。你首先定义和编码了信号,以便有你的买入和卖出订单的代理。然后,你编写了一个图表函数,显示这些信号叠加在价格图表上,这给出了你在过去买入和卖出的位置的概念。

请注意,即使简单的条形图只显示高点和低点,信号是放在开盘价处的,因为它考虑了 OHLC 数据的总体情况。

下面的代码显示了在 USDCHF 小时值上按时间顺序执行此过程的过程,如前面的图表所示:

`# Choosing the asset`
pair = 1

`# Time frame`
horizon = 'H1'

`# Importing the asset as an array`
my_data = mass_import(pair, horizon)

`# Creating the signal function`
def signal(data):

    data = add_column(data, 2)   

    for i in range(len(data)):   

        try:

        `# Bullish Alpha`
            if data[i, 2] < data[i - 5, 2] and data[i, 2] < 
               data[i - 13, 2] and data[i, 2] > data[i - 21, 2] and 
               data[i, 3] > data[i - 1, 3] and data[i, 4] == 0:

                     data[i + 1, 4] = 1

          `# Bearish Alpha`
            elif data[i, 1] > data[i - 5, 1] and data[i, 1] > 
                 data[i - 13, 1] and data[i, 1] < data[i - 21, 1] and 
                 data[i, 3] < data[i - 1, 3] and data[i, 5] == 0:

                     data[i + 1, 5] = -1

        except IndexError:

            pass

    return data

`# Calling the signal function`
my_data = signal(my_data)

`# Charting the latest 150 signals`
signal_chart(my_data, 0, 4, 5, window = 150)

图 2-5 展示了应用在 EURUSD 小时值上的相同过程。请注意,该模式是假设的,没有科学依据。

图 2-5. EURUSD 上的信号图表

编写绩效评估函数

可视化信号只是第一步。你需要客观数据来告诉你是否有一个赢利或亏损的交易系统。

为此,你需要绩效评估,这只是计算不同指标和比率的过程,这些指标和比率可以给你关于过去绩效的线索。之后,你的工作是解释这些指标,并尝试调整模式的使用以改善这些指标。

请记住,一个交易策略很容易是导致模式的几个条件;因此,即使本书讨论蜡烛图案的细节,我也将简要回顾交易策略。第十章和第十一章讨论了几个自然涉及蜡烛图案的策略。

注意

绩效评估还涉及到过去绩效预示未来绩效的期望。这种持续性可能性不大,但回测和评估过去绩效是验证和实施你的策略的最佳方法。

命中率

我们人类更喜欢正确而不是错误。通常情况下,我们为自己的成就感到自豪,为失败感到羞愧,这就是为什么当我们发现大多数决策导致预期和期望结果时,我们会感到安慰。大多数时候正确提供了一种每个人都欢迎的自我提升感。毕竟,谁不想被视为成功呢?

在交易行话中,命中率 是过去盈利交易数量除以总已实现过去交易数量。这意味着命中率衡量了你在预测未来方向时正确的百分比。70%的命中率意味着平均而言,你在每 100 次交易中有 70 次盈利,这并不差,但你必须绝对小心,因为这是一把双刃剑,稍后你会明白为什么。现在,让我们看一下命中率的数学表示:

Hit ratio = Numberofprofitabletrades Numberoftotaltrades

显然,总交易次数包括盈利交易和亏损交易的数量。命中率是接收者心理影响最观察和分析的绩效指标之一。确保你计算的比率是在已实现交易总数上,不包括未决交易。

收益率

当你投资 100 美元,一年后它变成 105 美元,你可以如何描述你的收益率呢?不要仅仅说你赚了 5 美元,而是可以将你的利润表述为相对于初始投资的回报,计算方法如下:

Rate of return = ( Newbalance Initialbalance ) - 1

这意味着你的收益率为 5%。用百分比来表达盈利能更好地反映投资组合的盈亏幅度。例如,看看以下两个假设情况:

  • A 组投资组合赚取了$125,000。

  • B 组投资组合回报率为 10%。

A 组投资组合似乎更令人印象深刻,但如果我告诉你两个投资组合的初始余额都是2,000,000呢?这确实使得A组的表现不佳,因为它只赚了6.252,000,000 呢?这确实使得 A 组的表现不佳,因为它只赚了 6.25%,而 B 组则赚了 10%(200,000)。

你还可以计算几种类型的回报率,即回报率和回报率。当然,你最应该关注的是后者,因为它扣除了费用。让我们看看以下例子中两者的区别:

  • 2021 年 01 月 01 日的初始余额为$1,000,000

  • 2021 年 12 月 31 日的最终总余额为$1,175,000。

  • 2021 年期间未支付的佣金和费用总计为$35,000。

  • 2021 年未支付的研究提供商费用为$10,000。

在这个例子中,毛收益率是不考虑支付给经纪人和其他第三方供应商的费用的收益率:

Gross rate of return = ( 1,175,000 1,000,000 ) - 1 = 17 . 5 %

净回报率更接近你实际获得的收益,通过以下计算得出:

Net rate of return = ( 1,175,000-35,000-10,000 1,000,000 ) - 1 = 13 %

警告

投资组合仍然盈利,但在扣除成本后受到了打击。有些投资组合在扣除成本后从赢家变成了输家,这就是选择一个有合理费率结构的经纪商的重要性,以免长期侵蚀你的利润。即使是佣金的小差异也会对积极交易者产生巨大影响。

盈利因子

盈利因子是一个快速衡量指标,用来看你每损失 1 个货币单位时赢得多少。它的计算方法是总毛利润与总毛亏损之间的比率,也就是将所有盈利交易的盈利总和除以所有亏损交易的亏损总和。在数学上,它表示如下:

Profit factor = Grosstotalprofit |Grosstotalloss|

将其除以总毛亏损的绝对值非常重要。让我们以 2020 年赚取了127,398和亏损了127,398 和亏损了88,318 的投资组合为例。在这种情况下,盈利比率将是多少?

答案是 1.44,这被解释为平均每损失1.00赢得1.00 赢得1.44。每当盈利性为正时,盈利因子大于 1,每当盈利性为负时,盈利因子小于 1。一些交易者喜欢通过调整策略来优化它们,直到找到最高的盈利因子。

风险-收益比

一个好的交易系统是通过获得奖励来衡量每承担一定风险量。当你冒着1.00的风险赢得1.00 的风险赢得1.00 时,风险-收益比等于 1.00,你有 50%的机会赢得或输掉相同的金额,除非你有一个随时间推移使你比输赢更多的统计优势。

这个统计优势是命中率的概念。当你进入一笔交易并设置目标(盈利平仓的水平)和止损(为避免更糟结果而进行的亏损平仓水平)时,你实际上可以计算出你的理论(预期)风险-收益比。

这是一个简单的例子,基于购买加密货币 Cardano(ADA)相对于 USDT(代表 USD):

  • 以$1.00 购买 ADAUSDT。

  • 设置止损价为$0.95。

  • 设置目标为$1.10。

这笔交易的风险-收益比是多少,你如何解释它?简单来说,你冒着0.05的风险来赚取0.05 的风险来赚取0.10,这意味着你的奖励是风险的两倍;因此,风险-收益比为 2.00。

Risk reward ratio = |Entryprice-Targetprice| |Entryprice-Stopprice|

上述公式显示了如何计算理论或预期风险-收益比,这是在交易之前设定的。

提示

一个经验法则是通常尝试选择提供接近 2.00 的风险-收益比的策略,因为这为你提供了足够的可预测误差余地以保持盈利。假设交易数量相同,具有 2.00 风险-收益比,你只需 33.33%的命中率即可打平。

保本命中率是达到零利润和损失所需的最低命中率,不包括成本和费用。由于它仅仅是一个指示性的测量,保本命中率在绩效报告中很少呈现。然而,你可以通过风险-收益比轻松计算它,如下所示:

保本命中率 = 1 1+风险收益比

这导致命中率与风险-收益比之间存在负相关关系,因为你的风险越小、收益越大,实际上命中收益(目标)的可能性就越小,因为你距离风险(止损)更近。考虑以下例子:

  • 自 2021 年 1 月 1 日以来的命中率 = 43.67%

  • 自 2021 年 1 月 1 日以来实现的风险-收益比 = 2.11

在这种情况下,保本命中率会是多少?通过使用公式,你可以找到以下结果:

保本命中率 = 1 1+2.11 = 32 . 15 %

这意味着你拥有一个获胜的策略,因为你平均每 100 笔交易中有 43-44 笔盈利交易。此外,对于这些交易中的每一笔,你的平均盈利是你亏损的 2.11 倍,这就是区别所在。良好的风险管理是使交易系统盈利的关键。仅凭命中率的初步观察,会让人感觉不起眼,但通过看风险-收益比,会呈现出全新的画面。

实际上,有些交易可以在止损或目标价位之前关闭,这是由于各种原因,例如在同一方向上再次得到信号。因此,你有两个不同的风险-收益比:

理论上的风险-收益比

这通常是在交易之前设置的,是一个预测。

实现的风险-收益比

这是每笔交易的平均利润除以每笔交易的平均亏损,这给出了你与理论风险-收益比有多接近的一个想法。

让我们看另一个例子来解释这两个比率:

  • 在 2021 年 1 月 1 日设定的理论风险-收益比 = 2.00

  • 2021 年平均每笔交易的盈利 = $241,597

  • 2021 年平均每笔交易的亏损 = $127,222

因此,实现的风险-收益比为 1.90,略低于理论值。这是可以接受的,因为有时你在达到止损或目标水平之前会退出一些交易。本书中回测中呈现的比率是实现的比率。

交易次数

交易频率对于性能评估至关重要。需要记住的一个经验法则是至少要有 30 笔交易,以达到可靠性的最低阈值。当然,多年来,交易频率必须要高得多。有些模式非常罕见,不太可能提供多少信号,从而阻止你对其进行正确的评估。

不幸的是,有些模式可能非常罕见,但无论如何,这本书并不是关于美化和夸大它们的,因为其中一些实际上非常糟糕且不具预测性,但仍被许多零售交易者用来分析市场。这将我们带到书的第二个实用性:揭秘。现在是时候看看如何在 Python 中编写这些指标了。

创建一个性能评估函数。

你终于结束了关于性能指标的理论部分讨论。即使在现实中有更多深入细节的性能指标,你也不需要计算它们所有来快速了解什么有效什么无效。

例如,回报率在后面的回测中并不是很有用,因为它是仓位大小和交易成本的函数,所以它并不直接处理可预测性。这就是为什么我只使用其他四个指标:

  • 命中率给你一个关于模式或策略可预测性的初步概念。

  • 利润因子让你看到生成的利润是否大于生成的损失,而不论仓位大小。

  • 实现的风险回报比告诉你,相比于承担的风险,你能得到多少回报。

  • 信号的频率显示结果是否有意义,以及你是否期望频繁信号或罕见信号。

性能指标被捆绑到一个我称为performance()的 Python 函数中,如下面的代码块所示。我将在代码之后解释它是如何工作的及其输出:

def performance(data, 
                 open_price, 
                 buy_column, 
                 sell_column, 
                 long_result_col, 
                 short_result_col, 
                 total_result_col):

    `# Variable holding period`
    for i in range(len(data)):

        try:

            if data[i, buy_column] == 1:

                for a in range(i + 1, i + 1000):

                    if data[a, buy_column] == 1 or data[a, sell_column] \                                               == -1:

                        data[a, long_result_col] = data[a, open_price] - \                                                    data[i, open_price]

                        break

                    else:

                        continue                

            else:

                continue

        except IndexError:

            pass

    for i in range(len(data)):

        try:

            if data[i, sell_column] == -1:

                for a in range(i + 1, i + 1000):

                    if data[a, buy_column] == 1 or data[a, sell_column] \                                               == -1:

                        data[a, short_result_col] = data[i, open_price] -\                                                     data[a, open_price]

                        break   

                    else:

                        continue

            else:
                continue

        except IndexError:

            pass   

  `# Aggregating the long & short results into one column`
    data[:, total_result_col] = data[:, long_result_col] + \                                 data[:, short_result_col]  

    `# Profit factor   ` 
    total_net_profits = data[data[:, total_result_col] > 0, \                         total_result_col]
    total_net_losses  = data[data[:, total_result_col] < 0, \                         total_result_col] 
    total_net_losses  = abs(total_net_losses)
    profit_factor     = round(np.sum(total_net_profits) / \                         np.sum(total_net_losses), 2)

`# Hit ratio`   
    hit_ratio         = len(total_net_profits) / (len(total_net_losses) \                         + len(total_net_profits))
    hit_ratio         = hit_ratio * 100

    `# Risk-reward ratio`
    average_gain            = total_net_profits.mean()
    average_loss            = total_net_losses.mean()
    realized_risk_reward    = average_gain / average_loss

    `# Number of trades`
    trades = len(total_net_losses) + len(total_net_profits)

    print('Hit Ratio         = ', hit_ratio)
    print('Profit factor     = ', profit_factor) 
    print('Realized RR       = ', round(realized_risk_reward, 3))
    print('Number of trades  = ', trades)    

函数并不像看起来那么复杂。例如,它接受七个变量,这些变量指的是数组及其列索引。变量data表示包含历史数据和模式生成信号的 OHLC 数组。记住,信号以 1 的形式表示长触发,以−1 的形式表示短触发。变量open_price只是数组的第一列,表示每个时间步的开盘价格。在你的情况下,因为你使用的是每小时的时间框架。

不要忘记,在 Python 中,数组的第一列索引为零,因此变量open_price始终具有值 0。接下来的两个变量分别表示买入和卖出信号的位置。对于简单的模式,通常是 4 和 5,而对于更复杂的模式,则可能更多。long_result_colshort_result_col变量是从buy_columnsell_column中找到的结果的索引。

这意味着每当你退出一个多头头寸时,该头寸的结果就会存储在由变量long_result_col描述的列中。最后,total_result_col总是紧随short_result_col之后的下一列,因为它是两个结果列的总和。total_result_col列被创建以便于计算绩效指标。现在,要调用函数,请使用以下语法:

my_data = performance(my_data, 0, 4, 5, 6, 7, 8)

该语句调用了一个名为my_data的数组上的性能函数,你已经学会如何导入它,然后用必要的值替换变量。如果你不确定如何找到这些变量,请仔细重新阅读前面的段落。

一个假设的例子:评估绩效

在我结束本章之前,让我们看一个完整的例子并解释其结果。毕竟,回测是关于理解策略或模式提供了什么,以便改进它。以下是一个在 2017 年到 2021 年之间使用单一策略的投资组合的详细信息:

  • 总交易数 = 2,348

  • 盈利交易 = 1,236

  • 亏损交易 = 1,112

  • 理论风险-收益比 = 2.00

  • 交易总净收益 = $457,995

  • 交易总净损失 = $321,589

  • 每笔交易的平均盈利 = $370.54

  • 每笔交易的平均损失 = $289.19

2017 年到 2021 年期间的命中率是多少?

命中率简单地指的是盈利交易数与实现交易总数的比率。在这个例子中,它是 52.64%。

2017 年到 2021 年之间的净利润因子是多少?你会如何解释它?

利润因子是总利润除以总损失。在这个例子中,它是 1.42,远高于 1.00。该投资组合每损失 1.00 美元就能赚取 1.42 美元。

2017 年到 2021 年之间的实现的风险-收益比是多少?与理论风险-收益比相比如何?

实现的风险-收益比是每笔交易的平均收益与平均损失的比率。在这个例子中,它是 1.28。远低于理论风险-收益比 2.00,因此投资组合正在经历次优的风险管理,可能是由于过早关闭头寸。

我们应如何解释信号的频率?

在五年中进行了 2,348 笔交易,这个投资组合的交易活动相对较高,约每年 469 笔交易。对于如此多的交易量来说,交易成本是一个令人担忧的问题。从统计上讲,性能指标应适当描述投资组合的情况,因为有很多信号。

这是一个管理良好的投资组合吗?

尽管通过利润因子显示的盈利能力很高,管理者应考虑在保持命中率超过 50%的情况下改进实现的风险-回报比。这可以通过调整、优化甚至审查进出场技术来实现。管理者还应该研究对交易进行过滤,以减少经纪费用。然而,投资组合处于盈利状态,并且似乎有一种预测策略。

你现在已经完成了构建算法核心的工作,这些算法将用于分析和回测蜡烛图形态。在展示这些图形态之前,你还有一个必须理解的技术分析,这是蜡烛图形态识别所属的研究领域。

¹ 移动平均线 是在滚动窗口上计算的均值。通常用于理解市场的趋势。移动平均线在第 Chapter 3 中有更深入的讨论。

² NaN 的缩写。如果单元格有 NaN,则被视为无效,如果用于计算,将只产生其他 NaN。

³ 由于本书始终使用小时时间框架,上一行指的是前一个小时蜡烛图。

第三章:介绍技术分析

技术分析依赖于对价格行为历史的视觉解释,以确定市场可能的总体方向。它依赖于过去是未来最好的预测因素的想法。在技术分析这个广阔领域内有几种技术,特别是以下几种:

图表分析

这是你将主观的视觉解释技巧应用到图表上的地方。你通常会使用绘制支撑和阻力线以及回撤等方法来找到旨在确定下一步动向的拐点水平。

指标分析

这是你使用数学公式创建客观指标的地方,这些指标可以是趋势跟踪或逆向的。已知的指标包括移动平均线和相对强弱指数(RSI),这两者在本章中将更详细地讨论。

模式识别

这是你监视某些反复出现的配置并采取行动的地方。模式通常是偶尔出现并呈现特定理论或经验结果的事件。在金融领域,情况更加复杂,但已知的某些模式随时间积累价值,这可能部分是由于一种被称为自我实现预言的现象(一种初始预期导致其确认的过程)所致。已知的模式包括蜡烛图模式,这些模式是本书的主角。

让我们快速了解技术分析的历史,这样你就能更清楚地知道可以期待什么。技术分析依赖于三个原则:

历史会重演。

在趋势和范围内,你可能会看到簇集。此外,某些配置和模式大多数时候可能会产生相似的结果。¹

市场会消化一切。

假设一切(所有基本、技术和定量信息)都包含在当前价格中。

市场波动成浪。

由于不同的时间框架和需求,交易者以不同的频率买卖,因此形成趋势和波动,而不是一条直线。

不幸的是,技术分析被零售交易社区过分吹捧和滥用,这使得它在专业行业中的声誉略显不佳。每种分析方法都有其优点和缺点,有成功的基本分析、技术分析和定量分析投资者,但也有三个领域的失败投资者。

基本分析依赖于经济和金融数据,以长期投资视角对特定证券或货币进行判断,而量化分析更为灵活,更常应用于短期数据。它使用数学和统计概念进行预测。

在其他假设中,技术分析表明市场不高效,但这意味着什么呢?市场效率指的是信息已经嵌入到当前价格中,并且价格和价值是相同的。当你购买资产时,你希望它是被低估的(在基本分析术语中)或者被过度卖出(在技术分析术语中),这就是你相信价格应该上涨以达到价值的原因。因此,你假设价值大于价格。市场效率否认任何价格不等于价值的主张,因此建议任何α交易不应导致高于平均回报(α交易是指从事超过基准表现的投机操作,通常是指一个指数或加权指数)。

市场效率假说是技术分析师的最大敌人之一,因为其原则之一是在弱式市场效率中,你无法通过技术分析获得超额回报。因此,技术分析在一开始就被打倒,然后基本分析也受到了打击。

可以合理地假设,未来某个时刻,由于参与者的数量和获取信息的便利性,市场将不得不变得高效。然而,正如政治和异常事件所示,市场往往远非高效。

注意

俄罗斯入侵乌克兰是一个政治事件的例子,引发了市场的恐慌和非理性行为。类似地,中央银行意外加息是异常经济事件的例子。

Charting Analysis

在理解图表分析之前,你需要知道在打开图表时会看到什么,更具体地说是蜡烛图。

假设市场开盘价为100。某些交易活动发生。让我们记录一下这一小时内的最高价(100。某些交易活动发生。让我们记录一下这一小时内的最高价(102)和最低价(98)。同时,记录这一小时的收盘价(98)。同时,记录这一小时的收盘价(101)。请记住,这四个数据分别被称为开盘价最高价最低价收盘价(OHLC)。它们代表了创建蜡烛图所必需的四个基本价格。

蜡烛图非常简单和直观。它们是沿时间线的盒状元素,包含 OHLC 数据。图 3-1 展示了蜡烛图的工作原理。

图 3-1。左侧是牛市蜡烛图;右侧是熊市蜡烛图

牛市蜡烛图的收盘价高于开盘价,而熊市蜡烛图的收盘价低于开盘价。

蜡烛图是分析金融时间序列的最著名方法之一。它们比简单的折线图包含更多信息,并且比条形图具有更强的视觉可解释性。Python 中许多库提供图表函数,但在我看来,自己动手做总是更好。让我们从折线图开始。

折线图 是通过按时间顺序连接收盘价创建的。这是绘制资产最简单的方法。它是三种图表类型中信息最少的,因为它只显示收盘价。

在 Python 中绘制基本线图非常简单,只需一行代码。您必须确保已导入一个名为matplotlib的库,该库会为您处理绘图。以下代码片段显示了如何绘制一个包含 EURUSD 小时时间框架中的收盘价的一维数组的折线图:

`# Importing the necessary charting library`
import matplotlib.pyplot as plt

`# The syntax to plot a line chart`
plt.plot(my_data, color = 'black', label = 'EURUSD')

`# The syntax to add the label created above`
plt.legend()

`# The syntax to add a grid`
plt.grid()

图 3-2 展示了 EURUSD 的折线图。该图仅提供收盘价和总体趋势方向的信息。

图 3-2. EURUSD 的折线图

现在让我们用蜡烛图将其提升到一个新的水平。

最简单的方法是考虑在每个时间步骤上画垂直线(就像第 2 章 中展示的简单条形图)。按照以下步骤操作:

  1. 为每行绘制表示最高和最低点的垂直线。例如,在 OHLC 数据上,您可以使用名为matplotlib的函数vlines(),它使用最小(低价)和最大(高价)值在图表上绘制垂直线。因此,此函数绘制了在每个高低之间延伸的条形图。图 3-3 显示了一个简单的条形图(类似于第 2 章 中的条形图),其中每根条形是 EURUSD 的高低范围。

  2. 重复第一步,只是这次在开盘和收盘价格上应用新的垂直线。这给你了一个垂直线里面的垂直线,但这是看不见的。如何解决?通过施加颜色条件和表示蜡烛图主体的更大宽度。

图 3-3. EURUSD 的简单条形图

图 3-4 显示了 EURUSD 的完整蜡烛图。您可以看到通过颜色编码和 OHLC 数据的可见性,您能够获取更多信息,如波动性和收盘价相对开盘价的总体趋势。这类信息在折线图中是不可用的。

图 3-4. EURUSD 的蜡烛图

实现这一点的完整代码如下:

def ohlc_plot_candles(data, window):

    sample = data[-window:, ]

    for i in range(len(sample)):

        plt.vlines(x = i, ymin = sample[i, 2], ymax = sample[i, 1], 
                   color = 'black', linewidth = 1)  

        if sample[i, 3] > sample[i, 0]:

            plt.vlines(x = i, ymin = sample[i, 0], ymax = sample[i, 3], 
                       color = 'green', linewidth = 3)  

        if sample[i, 3] < sample[i, 0]:

            plt.vlines(x = i, ymin = sample[i, 3], ymax = sample[i, 0], 
                       color = 'red', linewidth = 3)  

        if sample[i, 3] == sample[i, 0]:

            plt.vlines(x = i, ymin = sample[i, 3], ymax = sample[i, 0] + 
                       0.00003, color = 'black', linewidth = 1.00)  

    plt.grid()

要调用函数并显示最近的 100 根蜡烛图,请使用以下语法:

ohlc_plot_candles(my_data, window = 100)

有些人更喜欢用其他颜色绘制蜡烛图。为此,您必须调整color参数。例如,以下代码绘制了一个灰色(看涨)和黑色(看跌)的蜡烛图表:

def ohlc_plot_candles(data, window):

    sample = data[-window:, ]

    for i in range(len(sample)):

        plt.vlines(x = i, ymin = sample[i, 2], ymax = sample[i, 1],
                   color = 'black', linewidth = 1)  

        if sample[i, 3] > sample[i, 0]:

            plt.vlines(x = i, ymin = sample[i, 0], ymax = sample[i, 3],
                       color = 'grey', linewidth = 3)  

        if sample[i, 3] < sample[i, 0]:

            plt.vlines(x = i, ymin = sample[i, 3], ymax = sample[i, 0],
                       color = 'black', linewidth = 3)  

        if sample[i, 3] == sample[i, 0]:

            plt.vlines(x = i, ymin = sample[i, 3], ymax = sample[i, 0] +
                       0.00003, color = 'black', linewidth = 1.00)  

    plt.grid()

图表分析是通过主观绘制来寻找支撑和阻力线的任务。水平或对角线线条是找到预测市场反应水平的精髓:

  • 支持水平是市场应该反弹的水平,因为暗示周围的需求应该高于供应。

  • 阻力水平是市场应该退回的水平,因为暗示周围的供应应该高于需求。

资产在时间轴上的方向可以分为三种:上升趋势,价格创造更高高点,下降趋势,价格创造更低低点,以及横向(或盘整),价格在较长时间内围绕同一水平波动。

图 3-5 显示了在 EURUSD 接近 0.9850 附近的支持水平。通常情况下,当价格接近支持时,交易者开始考虑买入。这是因为预期应该会有向上的反应,因为力量平衡应该更向需求(积极)方向倾斜,交易者愿意支付更高的价格,因为他们预期将来会有更高的价格(记住之前讨论的价格对价值的论点)。这里的含义是大多数交易者看到的价格低于价值。

图 3-5. EURUSD 上显示 0.9850 支持的蜡烛图

图 3-6 显示在 EURUSD 接近 0.9960 附近的一个阻力水平。通常情况下,当市场接近阻力时,交易者开始考虑做空市场。这是因为预期应该会出现向下的反应,因为力量平衡应该更向供应方倾斜。这里的含义是大多数交易者看到的价格高于价值。

图 3-6. EURUSD 上显示 0.9960 阻力的蜡烛图

盘整(横向)市场使水平支撑和阻力线更有可能奏效。这是因为已经暗示了供需之间的一般平衡。因此,如果供应过剩,市场会迅速调整,因为需求应该足以稳定价格。

图 3-7 显示了一个被困在两个水平水平之间的盘整市场;这是 EURUSD 的情况。每当市场接近盘整市场中的阻力线时,您应该更有信心会发生下跌,而不像在上涨市场中那样,每当接近支持线时,您应该更有信心会发生反弹,而不像在下跌市场中那样。

此外,图表分析也适用于趋势市场。这体现在上升和下降通道形式上。它们与水平线具有相同的倾向,但有一个偏向(稍后讨论)。

图 3-7. EURUSD 上显示 0.9850 支持和 0.9960 阻力的蜡烛图

图 3-8 显示了一个上升通道,支撑和阻力点随时间上升,反映出因稳定上升的需求力量而产生的看涨压力。

图 3-8. AUDUSD 的蜡烛图显示了一个上升通道

见到这种情况的交易者会预期每当市场接近上升通道的下部时会有看涨反应,并且会预期每当市场接近通道的上部时会有看跌反应。

这并没有科学依据支持,因为没有什么规定市场必须平行运动,但自我实现的预言可能是为什么这样的通道被认为具有预测性质的原因。

图 3-9 展示了一个下降通道,支撑和阻力点随时间下降,反映出因稳定上升的供应力量而产生的看跌压力。一般来说,看跌通道往往更为激进,因为恐惧支配了贪婪,卖方比买方更为惊慌。

图 3-9. EURUSD 的蜡烛图显示了一个下降通道

我提到处理上升和下降通道时的一种偏见。我把这种偏见称为隐形之手。原因如下:

“趋势是你的朋友。” 这句话由马丁·兹威格创造,意味着在上升通道中,您需要更多关注每当市场回归支撑区域时的买入。这是因为您希望看涨压力的隐形之手能增加您获胜交易的概率。同样,在下降通道的情况下,更多地专注于在市场达到上限时进行卖空。兹威格定律的完整版本如下:“趋势是你的朋友,直到它弯曲的时候。” 这意味着在任何时候,市场都可能改变其走势,任何与趋势的友谊都将终止。总之,图表分析是主观的,并且更多依赖于交易者或分析师的经验。

值得一提的是,除了通过视觉估计来绘制它们以外,还有许多其他寻找支撑和阻力水平的方法:

斐波那契回撤

在这里,您可以使用斐波那契比率来确定反应性水平。通常会在上涨或下跌阶段计算斐波那契回撤,以便知道市场如果触及这些水平将会反转。这种方法的问题在于它非常主观,并且像任何其他技术一样,不是完美的。优点在于它提供了许多有趣的水平。

枢轴点

使用枢轴点可以利用简单的数学公式找到各个水平。基于昨天的交易活动,使用公式预测今天的未来支撑和阻力水平。然后,每当市场接近这些水平时,您尝试通过反向交易来抵消这一动向。

移动平均线

这些将在下一节中讨论。它们具有动态性质并跟随价格。您还可以使用它们来检测当前市场状态。

提示

找到支撑和阻力水平的最佳方法是尽可能多地结合技术,这样您就会对最初的想法更有信心。交易是一个数字游戏,尽可能多地增加胜算应该最终提高您的系统表现的机会。

指标分析

指标分析 是第二常用的技术分析工具。它通常伴随图表一起确认您的初始想法。您可以将指标视为助手。它们可以分为两种类型:

趋势跟随指标

用于检测并交易预期继续的趋势市场中的当前走势。因此,它们与走势持续性有关。

逆势指标

用于淡化行情走势,并且在横盘市场中最有效,因为它们通常标志着初始行情的结束。因此,它们与行情预期反转(因此与行情的反持续性)有关。

接下来的部分介绍技术分析的两个支柱,移动平均线(趋势跟随)和相对强弱指数(逆势)。

移动平均线

最著名的趋势跟随叠加指标是移动平均线。它的简单性使其无疑成为最受追捧的工具之一。移动平均线有助于确认和跟随趋势。您还可以使用它们来找到支撑和阻力水平、止损点和目标,以及理解潜在趋势。

有许多类型的移动平均线,但最常见的是简单移动平均线,其中您取最近收盘价的滚动均值,如下式所示:

Moving average i = Price i +Price i-1 +...+Price i-n n

这是你在统计学和生活中几乎任何其他部分使用的简单均值。它只是观察值的总和除以观察值的数量。²

图 3-10 显示了应用于美元/加元的 30 小时简单移动平均线。30 小时 表示我在每小时 K 线的情况下计算最近 30 个周期的移动平均值。

图 3-10. 美元/加元的蜡烛图与 30 小时简单移动平均线

移动平均线的经验法则包括以下内容:

  • 每当市场高于其移动平均线时,牛市动能正在进行中,并且最好寻找做多机会。

  • 每当市场低于其移动平均线时,熊市动能正在进行中,并且最好寻找做空机会。

  • 每当市场穿过其移动平均线时,可以说动能已经改变,市场可能正在进入新的状态(趋势)。

您还可以组合移动平均线,以便它们发出信号。例如,每当短期移动平均线穿过长期移动平均线时,将出现牛市交叉信号,市场可能会继续上涨。这也被称为金叉

相反,每当短期移动平均线穿过长期移动平均线下方时,就会出现熊市交叉,市场可能会继续下跌。这也被称为死亡交叉

图 3-11 显示了 USDCAD 的 10 小时(接近市场价格)和 30 小时移动平均线(远离市场价格)。您可以注意到,在开始阶段,出现了一个金叉信号,标志着一个牛市趋势的开始。

图 3-11. USDCAD 的蜡烛图与 30 小时和 10 小时简单移动平均线

要编写移动平均线,使用以下函数:

def ma(data, lookback, close, position): 

    data = add_column(data, 1)

    for i in range(len(data)):

            try:

                data[i, position] = (data[i - lookback + 1:i + 1, 
                                     close].mean())

            except IndexError:

                pass

    data = delete_row(data, lookback)

    return data

要计算收盘价格的 30 周期(或小时,取决于您的时间框架)移动平均线,您需要定义ma()函数,并按以下方式调用它:

`# Setting the lookback period`
lookback = 30

`# Setting the index of the close price column`
close_column = 3

`# Setting the index of the moving average column`
ma_column = 4

`# Calling the moving average function`
my_data = ma(my_data, lookback, close_column, ma_column)

相对强弱指数

现在让我们讨论反向指标。由 J. Welles Wilder Jr.首次引入,相对强弱指数(RSI)是最流行和多用途的有界指标之一。它主要用作反向指标,其中极端值信号表明可以利用的反应。

使用以下步骤来计算默认的 14 周期相对强弱指数(RSI):

  1. 计算前一个收盘价格的变动。

  2. 将正净变动与负净变动分开。

  3. 计算平滑后的正净变动和负净变动的绝对值的平均移动。

  4. 将平滑后的正变动除以平滑后的绝对负变动。将此计算称为相对强弱指数(RS)。

  5. 对每个时间步骤应用此归一化公式以获得 RSI:

R S I i = 100 - 100 1+RS i

注意

平滑移动平均线是 RSI 的创建者开发的一种特殊类型的移动平均线。它比简单移动平均线更平滑和稳定。

一般来说,RSI 默认使用 14 周期的回看期,尽管每位交易员可能有自己的偏好。以下是如何使用此指标:

  • 每当 RSI 显示 30 或更低的读数时,市场被认为是超卖状态,可能会发生向上的修正。

  • 每当 RSI 显示 70 或更高的读数时,市场被认为是超买状态,可能会发生向下的修正。

  • 每当 RSI 超过或突破 50 水平时,可能正在出现新的趋势,但这通常是一种较弱的假设,更多地是理论性质而非实践性质。

图 3-12 显示了 EURUSD 与其 14 周期 RSI 的对比情况,位于第二面板。指标应用于确认长期或短期偏向,并在市场状态的时机分析中非常有帮助。

图 3-12. 顶部面板显示的小时 EURUSD 值,底部面板显示的 14 期 RSI

要使用常规数组方法创建 RSI,首先定义平滑移动平均如下:

def smoothed_ma(data, alpha, lookback, close, position):

    lookback = (2 * lookback) - 1

    alpha = alpha / (lookback + 1.0)

    beta  = 1 - alpha

    data = ma(data, lookback, close, position)

    data[lookback + 1, position] = (data[lookback + 1, close] * alpha) + 
                                   (data[lookback, position] * beta)

    for i in range(lookback + 2, len(data)):

            try:

                data[i, position] = (data[i, close] * alpha) + 
                                    (data[i - 1, position] * beta)

            except IndexError:

                pass

    return data

现在,让我们使用以下函数编写 RSI:

def rsi(data, lookback, close, position):

    data = add_column(data, 5)

    for i in range(len(data)):

        data[i, position] = data[i, close] - data[i - 1, close]

    for i in range(len(data)):

        if data[i, position] > 0:

            data[i, position + 1] = data[i, position]

        elif data[i, position] < 0:

            data[i, position + 2] = abs(data[i, position])

    data = smoothed_ma(data, 2, lookback, position + 1, position + 3)
    data = smoothed_ma(data, 2, lookback, position + 2, position + 4)

    data[:, position + 5] = data[:, position + 3] / data[:, position + 4]

    data[:, position + 6] = (100 - (100 / (1 + data[:, position + 5])))

    data = delete_column(data, position, 6)
    data = delete_row(data, lookback)

    return data

总结一下,指标可以通过多种方式计算。最常用的两种是移动平均和 RSI。我稍后会在第十章和第十一章回到它们。目前,请确保掌握技术分析的概念。我们继续进行模式识别。

模式识别

模式是指反复出现的特定配置,显示随后移动的特定预测。模式可以分为以下类型:

经典价格模式

这些是众所周知的技术反转价格模式,由于难以在不考虑主观条件的情况下进行回测而被认为不可靠。然而,它们仍被许多交易员和分析师使用。

定时模式

基于时间和价格的组合。这些模式较少被人知晓,但在正确使用时可以强大且具有预测性。

蜡烛图案

这里使用 OHLC 数据来预测市场未来反应。蜡烛图是可视化图表的最佳方式之一,因为它们包含许多可能信号反转或确认移动的模式。后续章节将对其进行更深入的讨论。

经典价格模式是指理论配置,如双顶和矩形。它们通常是反转模式或续趋势模式:

续价格模式

这些是确认总体持续移动的配置。例如,矩形和三角形。

反转价格模式

这些是淡化总体持续移动的配置。例如,头肩顶和双底。

传统图表分析师熟悉双顶和双底,它们预示着反转并给出了反转的潜力。尽管简单,但它们是主观的,有些并不像其他那样明显可见。

这阻碍了了解它们是否增加了价值。图 3-13 展示了双顶的插图,验证模式后通常会给出一个看跌的倾向,这通常是打破两个顶部之间最低点连接线的验证。这条线称为颈线

图 3-13. 双顶插图

注意双顶中的三个重要元素:

领口线

这是连接两个峰值之间的最低点和模式开始/结束的线。它用于确定回撤水平,接下来我们将定义它。

回撤

打破颈线后,市场应该会试图朝向颈线做绝望的尝试,但由于卖方利用这一水平重新进场继续做空,市场未能继续上涨。因此,回撤水平是在确认双顶后的理论最佳卖出点。

潜力

这是双顶的目标。它被测量为模式顶部和向下投影的颈线之间的中点,从相同的颈线点开始。

双顶或双底可以有任何大小,但最好能够被大多数市场参与者看到,以便其影响更大。从心理学角度来说,这种模式的解释是,在第二个顶部或底部,市场未能推动价格超过第一个峰值,因此显示出弱势,这可能被卖方利用。

还有其他更客观的模式;即它们具有明确的检测和启动规则。这些模式都基于明确的客观条件,并不受分析师主观判断的影响。这有助于它们的反向测试和评估。

在结束这一入门章节之前,我想指出一些技术分析的误解和最佳实践,以便您能够正确开始。

技术分析的常见陷阱

尽管技术分析看起来很简单,但它可能会被误用,这不幸地加剧了有关其效用和相对基本分析位置的永恒争论。重要的是设定正确的期望,并保持在逻辑思维的范围内。本节讨论了技术分析的已知缺陷,您必须确保避免这些缺陷,以最大化您在金融丛林中的生存率。

渴望迅速致富

这个陷阱主要是心理上的,因为它涉及缺乏纪律和糟糕的管理。迫切想要尽快赚钱以取悦社会的需求,会促使交易者在交易和与交易相关的活动中做出情绪化和不良决策。

这也与需要赚钱的事实有关,你可能会不断改变自己的交易计划,因为你相信新计划是致富的更快途径。

当你对自己没有足够的信心,并认为其他人在赚钱方面比你更强时,你更有可能跟随他们,特别是因为他们提供了丰富的信息。除了你之外,没有人可以改变你的未来。

强迫模式

一种常见的心理缺陷,被称为确认偏见,阻止交易者看到与他们已经建立的观点相矛盾的信号。

有时,你对某些市场有初步看法,因此开始寻找任何符合这一观点的东西,这也可能迫使模式出现,尽管它们并无有效性。

警告

你必须在分析中始终保持中立,并谨慎对待各种元素,同时保持最大的客观性。当然,这说起来容易,做起来难,绝对中立的最佳替代方案是算法化的方法,虽然这要以牺牲人类智能因素为代价。

后见之明偏差,梦想粉碎者

技术分析在过去看起来很好。一切都显得显而易见,甚至使用非常基本的策略也很容易预测;然而,当你应用后者时,你会发现由于严酷的现实,你的大脑会欺骗你认为过去是完全可预测的。

这也是为什么回测结果几乎总是优于前向测试的原因。当你看过去的模式,并相信它们应该很容易被发现时,你表现出了“后见之明偏差”。要解决这个问题,你必须使用无偏算法进行回测。只有这样,你才能确定该模式是否具有价值。许多散户交易员陷入陷阱,只是简单地看待过去,来确定他们的策略的有效性,结果表现不佳。我在第十二章中更深入地讨论了这些偏见。

假设过去事件会有相同的未来结果

你一定听过这句话:“历史不会重演,但它确实经常押韵。” 这句话对理解市场如何运作至关重要。当你应用技术分析时,不要期望从过去的信号和模式中得到确切的结果。相反,你必须将它们作为指导和概率元素使用,这些元素表明市场可能会产生类似的反应,并且与过去的反应有相关性。

交易是一个数字游戏,你必须使赔率倾向于你的利益。当人们面对类似事件时,有时会表现出相似的行为方式。然而,随着不同的参与者不断进入和退出买卖活动,并且随着他们的动机不断变化,你可以肯定,在遇到类似于过去某种模式的市场反应之后,未来的反应不会完全相同,尽管它可能与过去“押韵”,意味着平均而言可能会有相关的反应。

话虽如此,不要期望每次看到明显的模式时都能完美地把握市场的时机。

把事情弄得比必要的更复杂

另一句话是“一切都应该尽可能简单,但不要太简单。” 这完美描述了你应该如何进行研究和交易。

金融市场是高度复杂的、半随机的环境,需要不简单的策略,但策略不能太复杂,以至于你陷入“过拟合”的陷阱,这是一个常见的问题,交易员完美预测过去,并假设未来会完全相同,从而导致过去的巨大纸面收益,但未来却遭受巨大的真实损失。

技术分析最佳实践

当你误用某物时,你倾向于避免责怪自己,而是安抚自己的自尊心,从而免除任何责任。让我们列出一些最佳实践,优化你技术上分析市场的方式。

发挥不同时间框架的力量

许多分析师和交易者仅通过短期视角来分析市场。一个例子是在小时图上寻找机会,而忽视了日图上的市场配置。有一条严格的规则要知道,长时间框架(周度和月度)始终比短时间框架更重要,这意味着如果在较高时间框架上有坚实的阻力,即使在较短时间框架上看到看涨配置,也不应该真的考虑在该水平附近买入。

你应该这样做的原因是尊重行星对齐原则,在这种情况下,它决定了确认彼此的元素越多,你对成功交易的概率的信心越大。一个良好的框架是至少在三个不同的时间框架内分析市场,从较长的时间视角开始向下分析。

使用多种策略或指标

一些分析师认为图表分析比指标或模式更有效,而其他人持相反意见。最好的解决方案是三者兼用,并创建结合各类分析的最佳形态的策略。既然可以选择多种方法,为何只用一种呢?记住,这是一个数字游戏,你需要尽可能多的观点。

选择适合当前市场条件的正确策略

如上所述,市场往往趋势或横向移动。两种主要类型的策略可以归类为反向或趋势跟随,前者依赖于均值回归的概念(消退总体移动),后者依赖于动量原则(跟随总体移动)。

在选择部署策略之前,你必须知道市场是处于趋势还是区间,这可以通过使用移动平均线或其他趋势识别技术来完成。

主要的一点是在强劲趋势市场中不要使用反向策略,在区间市场中不要使用趋势跟随策略。这说起来容易,但实际上非常困难,如果不是不可能的话,估计当前和下一个市场制度。

不要低估默认参数

默认参数实际上并没有像人们描绘的那么可怕,一个显而易见的原因是:它们对交易者和分析师的可见性最高。

举例来说,考虑相对强度指数。你可以说,使用 14 个周期作为回顾窗口的默认版本可能比使用 55 个周期的版本更可靠,假设采用相同的策略,这是因为更多的人关注默认版本并基于它做决策,而不是使用 55 个周期的版本。

交易中的关键点是确保其他参与者也能看到你看到的配置,这样你就能最大化赢利的机会。这并不意味着你必须完全使用其他人相同的技术,但有时不要对指标进行过多调整也是有帮助的。

¹ 这假设长期内显示出非随机概率,表现出确定性特征。

² 你也可以把均值看作是总和除以数量。

³ 参见 J. Welles Wilder Jr.(1978)的技术交易系统中的新概念,由 Trend Research 出版。

第四章:经典趋势跟随模式

趋势跟随,一个看似简单却又复杂的概念。或许放弃市场定时而只是顺势操作会更容易,但实际上,由于众多随机变量影响价格,这样做可能会面临挑战。一个稳步上升的市场可能会因一次经济或政治事件而动荡,但随后继续平稳前行,这会让在波动风暴中被止损的交易员感到困惑。

本章涵盖了经典的(在技术分析领域已广为人知和确立的)趋势跟随蜡烛图案。这些是简单和复杂的构型,自技术分析诞生以来就存在,并在许多初级和高级技术分析课程中教授。本章的目的是创建模式的客观条件并对其进行回测,以便您可以形成关于它们出现频率和可预测性的基本看法。有些模式可能经常出现,而其他模式可能极少出现,从而阻碍了对它们进行适当评估的能力。

如前所述,我回测图案的方法是假设在验证前一天收盘信号后,我会在下一个开盘时开启买入或卖出头寸。本书选择的回测时间框架是每小时。

光头蜡烛图案

第一个经典的趋势跟随模式是光头蜡烛。这个词源于日语,意为秃头或紧贴头皮,你很快就会明白为什么。

区分光头蜡烛图和普通蜡烛图相对较容易,因为光头蜡烛图没有任何影线。这意味着看涨的光头蜡烛图的开盘和最低价格相同,收盘和最高价格也相同。相反,看跌的光头蜡烛图的开盘和最高价格相同,收盘和最低价格也相同。图 4-1 展示了光头蜡烛图的这两个版本。

图 4-1. 左边是一个看涨的光头蜡烛图;右边是一个看跌的光头蜡烛图。

光头蜡烛图案通常出现在较短的时间框架内,因为蜡烛图的波动时间较短,难以超出它们的边界。这在分析 1 分钟和 5 分钟图表并将它们与日常图表进行比较时可以看出。

注意

当蜡烛图的开盘和收盘价格之间的时间较短时,蜡烛图更有可能形成光头蜡烛图案。

在使用任何模式之前,您必须理解其存在的原因。毕竟,模式不是随机发现的;它们必须有其背后的合理性。更重要的是,您需要知道在验证模式后为什么应该期望某种反应。

答案在市场心理学中,它无处不在,以至于专注和知识丰富的交易员可以捕捉到任何暗示并跟随(或反向操作)市场走势。

当市场处于强劲的上涨趋势时,很少会出现较低的新低点,并且由于对基础证券的需求强劲,通常会在接近高点处收盘。在一个蜡烛图中,需求的最大力量表现为不存在低点且收盘价位于高点,这正是独角兽阳线蜡烛图的核心。当资产以最高价位收盘时,你会得到买家渴望更多的信号,而当没有低点低于开盘价时,你应该更有信心地认为,没有人成功将价格推低到其开盘价以下。

同样地,当市场处于强劲的下跌趋势时,很少会出现较高的新高点,并且由于基础证券的供应压力巨大,通常会在接近低点处收盘。在一个蜡烛图中,供应的最大力量表现为不存在高点且收盘价位于低点,如熊市独角兽阴线蜡烛图所示。

注意

独角兽阳线蜡烛图(bullish Marubozu)是最强烈的牛市活动表现,而独角兽阴线蜡烛图(bearish Marubozu)则是最强烈的熊市活动表现。

在一些使用多个小数点的市场中,检测独角兽蜡烛图模式可能会有些棘手,这是由于其发生概率。例如,在货币市场上,如果使用五个小数点,你可能不会很快找到任何独角兽蜡烛图模式,但当你遵循通常的、不太精确的四个小数点惯例时(参见第二章),你会更频繁地看到这种模式。这对所有市场都适用;因此,为了能够分析这种模式,你需要对小数点进行一些微调。

图 4-2 显示了应用舍入函数后的 USDCHF 清晰图表:

my_data = rounding(my_data, 4)

图 4-2. USDCHF 的蜡烛图

下一步是编写信号功能的代码。

注意

信号功能是算法的核心,因为它负责找到模式并按时间顺序确定它们,因此根据特定条件输出买入和卖出信号。

在算法上,条件需如下:

  • 如果收盘价大于开盘价,则高价等于收盘价,低价等于开盘价,那么在买入信号列的下一行打印 1。

  • 如果收盘价低于开盘价,则高价等于开盘价,低价等于收盘价,然后在卖出信号列的下一行打印-1。

在 Python 语言中,使用以下语法编写独角兽蜡烛图信号功能:

def signal(data, open_column, high_column, low_column, close_column, 
           buy_column, sell_column):

    data = add_column(data, 5)    

    for i in range(len(data)):  

       try:

           `# Bullish pattern`
           if data[i, close_column] > data[i, open_column] and 
              data[i, high_column] == data[i, close_column] and \               data[i, low_column] == data[i, open_column] and 
              data[i, buy_column] == 0:

                    data[i + 1, buy_column] = 1 

           `# Bearish pattern`
           elif data[i, close_column] < data[i, open_column] and 
                data[i, high_column] == data[i, open_column] and \                 data[i, low_column] == data[i, close_column] and 
                data[i, sell_column] == 0:

                    data[i + 1, sell_column] = -1 

       except IndexError:

            pass

    return data

图 4-3 显示了根据函数给出的信号在 EURUSD 上生成的交易。

图 4-3. EURUSD 上的信号图表

向上箭头表示看涨信号,而向下箭头表示看跌信号。图 4-4 展示了美元瑞士法郎信号的另一个示例。

图 4-4. 美元瑞士法郎信号图

现在让我们使用第二章讨论的性能评估指标回测该模式。

表格 4-1 展示了丸坐模式的表现摘要。交易条件如下:

  • 在确认当前收盘价格信号后,入场价格为下一个开盘价格。

  • 出场时,获取任一方向(看涨或看跌)的另一个信号。

  • 性能评估指标中未包含交易成本。

  • 没有使用风险管理系统。

使用风险管理系统意味着采用简单和复杂的订单管理技术,以规范最大损失和预期目标。

表格 4-1. 丸坐模式:表现摘要表格

资产命中率盈利因子风险-收益比信号数
欧元美元47.14%1.061.19963
美元瑞士法郎47.01%0.951.071355
英镑美元47.27%0.790.89605
美元加元49.06%0.991.03958
比特币美元47.81%1.381.51433
以太坊美元39.76%0.931.413687
黄金43.33%0.961.258443
标普 50041.92%0.660.91260
富时 10050.00%1.091.0990

回测的资产显示,丸坐模式相对罕见,并且似乎并未增加很大价值。如命中率所示,所有预测都是随机的。命中率约为 50%意味着你的对错概率相当。

同样,盈利因子显示你几乎只能获得足以抵消总亏损的总收益。在计算交易成本时,这一点被放大。接近 1.00 的盈利因子无关紧要,不太可能提供有关模式表现良好与否的更多信息。

如果分析交易该模式时承担的风险,会发现这证实了该策略的随机性。平均值约为 1.00,基本上你冒着 1 美元风险去赚 1 美元,结果可能两样。最后,信号并不是非常频繁,但超过 30 的阈值是进行任何统计解释所需的。

主要结论是,丸坐模式单独使用不太可能提供盈利策略。尽管如此,还请记住以下几点进行回测:

  • 仅基于一个模式进行持仓,未与其他确认指标结合。通常情况下,将模式与其他技术和指标结合使用效果更佳。

  • 回测的市场范围有限;因此,该模式在其他市场上的表现可能会有所不同。

  • 回测的时间范围有限,因为结果仅反映了小时时间框架。请记住,还有其他时间框架,如五分钟、每日和每周。

  • 退出技术是多变的,并取决于下一个信号,这可能要等很长时间。其他更合适的退出技术可能包括固定退出或依赖波动性的退出。

因此,即使回测给出了关于模式能力的一般近似想法,您也必须始终了解其局限性。

三根蜡烛模式

三根蜡烛 模式是一种趋势确认配置,其信号在打印出三根颜色相同、具有最小指定大小的蜡烛之后给出。一个看涨的三根蜡烛模式也被称为三白兵,其由三根大涨的蜡烛组成,每根的收盘价都高于前一根。相反,一个看跌的三根蜡烛模式被称为三黑鸦,其由三根大跌的蜡烛组成,每根的收盘价都低于前一根。

注意

三白兵和三黑鸦的名称来源于蜡烛图曾经以黑白色图示,前者代表着看跌蜡烛,后者代表着看涨蜡烛。

图 4-5 展示了三白兵。请注意,每个收盘价都高于前一个。

图 4-5. 三白兵模式

图 4-6 展示了三黑鸦。请注意,每个收盘价都低于前一个。

图 4-6. 三黑鸦模式

该模式的直觉非常简单。它源于一种称为羊群效应 的心理偏倚,即市场参与者因为他人的行为而跟随趋势。这并不意味着该模式基于人类的缺陷和努力不足;它只是指人类常见的一种行为,他们倾向于跟随整体趋势。

注意

人们追随最新的时尚潮流是因为它带来心理奖励。趋势追随交易者追随最新的趋势,因为他们相信这会带来财务回报,或者因为他们有一种错失机会的恐惧形式(FOMO)。

金融羊群效应可以定义为跟随多数人的行为,试图从当前持续的动向中获利尽可能多。因此,本质上,羊群效应实际上是趋势追随。当您看到同一方向内的三根大蜡烛时,通常会将其解释为一种充满动力、信念和继续朝同一方向发展的健康行动。将这三根大蜡烛与小蜡烛的羞怯行动进行比较,后者的类型在涨跌之间交替。

人们被信心和力量所吸引,更有可能追随它。三根健康蜡烛图案体现了力量和信心。

发现这种模式很容易,但必须有严格的算法规则,不仅仅是三根相同颜色的大蜡烛;否则,你将会有大量信号。从算法的角度看,条件需要如下:

  • 如果最近三个收盘价分别高于其前一个收盘价,并且每个蜡烛图符合最小实体大小,则在下一行的买入信号列中打印 1。

  • 如果最近三个收盘价分别低于其前一个收盘价,并且每个蜡烛图符合最小实体大小,则在下一行的卖出信号列中打印-1。

蜡烛的实体是收盘价和开盘价之间的差异的绝对值。因此,要找到实体,请按照以下数学公式操作:

Candlestick body i = | Close price i - Open price i |

def signal(data, open_column, close_column, buy_column, sell_column):

    data = add_column(data, 5)

    for i in range(len(data)):

       try:

           `# Bullish pattern`
           if data[i, close_column] - data[i, open_column] > body and \               data[i - 1, close_column] - data[i - 1, open_column] > \               body and data[i - 2, close_column] - \
              data[i - 2, open_column] > body and data[i, close_column] > \               data[i - 1, close_column] and data[i - 1, close_column] > \               data[i - 2, close_column] and data[i - 2, close_column] > \               data[i - 3, close_column] and data[i, buy_column] == 0:

                    data[i + 1, buy_column] = 1

           `# Bearish pattern`
           elif data[i, close_column] - data[i, open_column] > body and \                 data[i - 1, close_column] - data[i - 1, open_column] > \                 body and data[i - 2, close_column] - \                 data[i - 2, open_column] > body and data[i, close_column] \ 
                < data[i - 1, close_column] and data[i - 1, close_column] \
                < data[i - 2, close_column] and data[i - 2, close_column] \
                < data[i - 3, close_column] and data[i, sell_column] == 0:

                    data[i + 1, sell_column] = -1

       except IndexError:

            pass

此函数根据先前显示的条件生成买入和卖出信号。由于蜡烛的实体可能根据波动性而异,实际上可能会在某种程度上影响该模式的客观性。理论上,三根蜡烛图案仅指“大”蜡烛,并不指示应如何调整大小;因此,我根据资产和小时时间框架使用固定值。表 4-2 显示了总结。

表 4-2. 三根蜡烛图案:蜡烛大小选择

资产Body类型
EURUSD0.0005美元
USDCHF0.0005瑞士法郎
GBPUSD0.0005美元
USDCAD0.0005加拿大元
BTCUSD50美元
ETHUSD10美元
黄金2美元
S&P50010点数
FTSE10010点数

你也可以根据波动性调整变量body,但我将展示另一种适应波动性的模式。目前,我将保持经典模式的理论条件不变,并将创造力留给第五章和第七章中的现代模式。

图 4-7 显示了 EURUSD 上的信号图表。

图 4-7. EURUSD 上的信号图表

表 4-3 显示了三根蜡烛图案的性能总结。

表 4-3. 三根蜡烛图案:性能总结表

| 资产 | 命中率 | 盈利因子 | 风险-收益比 | 信号数 | | --- | --- | --- | | EURUSD | 61.45% | 1.05 | 0.66 | 2672 | | USDCHF | 62.04% | 0.98 | 0.60 | 2005 | | GBPUSD | 61.53% | 0.96 | 0.60 | 3611 | | USDCAD | 60.97% | 0.97 | 0.62 | 2844 | | BTCUSD | 62.30% | 1.00 | 0.61 | 1085 | | ETHUSD | 61.22% | 0.96 | 0.61 | 392 | | 黄金 | 61.11% | 1.04 | 0.66 | 828 | | S&P500 | 65.99% | 1.10 | 0.57 | 741 | | FTSE100 | 64.54% | 0.97 | 0.53 | 1018 |

尽管结果显示相对于丸坐模式具有较高的命中率,但您必须小心,因为单独作为指标的命中率是没有用的。您必须将其与风险-回报比并行分析,以了解其预测能力是否有盈利可能。

你首先注意到的是关键低风险-回报值,其范围在 0.53 到 0.66 之间。这是第一个警告信号,说明命中率并不令人印象深刻。实际上,这甚至可能不足以产生正面结果。

田士模式

田士模式是一种趋势确认配置,市场在未填补的缺口后发出继续信号。在继续之前,您需要了解什么是缺口。

缺口构成了价格行为的重要组成部分。它们在市场中的罕见程度因市场而异。例如,在货币市场中,它们通常发生在周末后的零售市场开盘或重大公告时。在股票市场中,缺口从一天到另一天是相当常见的。

缺口是由于低流动性而导致两个连续收盘价格之间的不连续或空隙。当市场在100交易,突然在未报出100 交易,突然在未报出101 的情况下交易到$102 时,就形成了一个看涨的缺口。在图表中可以看到烛台之间似乎有一个缺失的部分。图 4-8 展示了一个看涨缺口。请注意烛台之间的空白区域。

图 4-8. 一个看涨缺口

缺口可能是由于基本和技术原因而发生,但无论出现原因如何,您都应该有兴趣识别并进行交易。在货币市场上,周末出现的明显缺口是看得见的。图 4-9 展示了一个看跌缺口。

图 4-9. 一个看跌缺口

缺口有不同类型,当它们出现时,由于事后的偏见,对它们进行分类可能很困难(事后偏见是一种认知偏差,因为分析师已经知道结果,所以会高估技术的预测能力):

常见缺口

这些通常发生在横盘市场中。它们可能会被填充,因为市场的均值回归动态。

脱离缺口

这些通常类似于常见的缺口,但缺口出现在图形阻力的上方或图形支撑的下方。它们标志着新趋势的加速。

逃跑缺口

这些通常发生在趋势内部,但更加确认它;因此,这是一种继续模式。

耗尽缺口

这些通常发生在趋势结束时,接近支撑或阻力水平。这是一个反转模式。

在缺口出现的那一刻确定其类型并不是确定的方法,但这对于检测田士模式并不重要。该模式以一种用于固定日本和服的配饰命名,但其用途不明确。

一个牛市塔斯基模式由三根蜡烛图组成,第一根是看涨的蜡烛图,第二根是另一根看涨的蜡烛图,其间跳空高于第一根蜡烛图,第三根蜡烛图是熊市的,但不会收于第一根蜡烛图的收盘价以下。

图 4-10 展示了一个牛市塔斯基。

图 4-10. 一个牛市塔斯基

熊市塔斯基模式(见图 4-11)是牛市塔斯基的镜像。它由三根蜡烛图组成,第一根是熊市蜡烛图,第二根是另一根熊市蜡烛图,其间跳空低于第一根蜡烛图,第三根蜡烛图是看涨的,但不会收于第一根蜡烛图的收盘价以上。

图 4-11. 一个熊市塔斯基

这种模式的直觉与突破原理相关,即突破某个阈值,无论是支撑还是阻力,都应该在释放到初始方向之前对其产生重力作用。

每当看到市场跳空上涨,后面紧随一个不完全填补跳空的熊市蜡烛图时,这可能是卖方不足以领先的信号,显示出看涨的倾向。同样地,每当看到市场跳空下跌,后面紧随一个不完全填补跳空的看涨蜡烛图时,这可能是买方不足以领先的信号,显示出看跌的倾向。

算法上,条件需要如下:

  • 如果前两个周期的收盘价高于前两个周期的开盘价,上一个周期的开盘价高于前两个周期的收盘价,上一个周期的收盘价高于上一个周期的开盘价,并且当前的收盘价高于前两个周期的收盘价,则在买入信号列的下一行打印 1。

  • 如果前两个周期的收盘价低于前两个周期的开盘价,上一个周期的开盘价低于前两个周期的收盘价,上一个周期的收盘价低于上一个周期的开盘价,并且当前的收盘价低于前两个周期的收盘价,则在卖出信号列的下一行打印−1。

def signal(data, open_column, close_column, buy_column, sell_column):

    data = add_column(data, 5)

    for i in range(len(data)):

       try:

      `# Bullish pattern`
           if data[i, close_column] < data[i, open_column] and \               data[i, close_column] < data[i - 1, open_column] and \               data[i, close_column] > data[i - 2, close_column] and \               data[i - 1, close_column] > data[i - 1, open_column] and \               data[i - 1, open_column] > data[i - 2, close_column] and \               data[i - 2, close_column] > data[i - 2, open_column]:

                    data[i + 1, buy_column] = 1

           `# Bearish pattern`
           elif data[i, close_column] > data[i, open_column] and \                 data[i, close_column] > data[i - 1, open_column] and \                 data[i, close_column] < data[i - 2, close_column] and \                 data[i - 1, close_column] < data[i - 1, open_column] and \                 data[i - 1, open_column] < data[i - 2, close_column] and \                 data[i - 2, close_column] < data[i - 2, open_column]:

                    data[i + 1, sell_column] = -1

       except IndexError:

            pass

    return data

是时候讨论回测结果了(见表 4-4)。请记住,该模式非常罕见,你应该对结果保持一种统计上的怀疑态度。

表 4-4. 塔斯基模式:性能摘要表

资产命中率盈利因子风险-收益比信号数
EURUSD58.06%0.970.7031
USDCHF59.46%1.921.3137
GBPUSD48.72%0.930.9839
USDCAD40.00%0.610.9140
BTCUSD53.57%0.890.7756
ETHUSD50.00%1.031.0330
GOLD51.49%1.010.95101
S&P50036.36%0.330.5811
富时 10050.00%1.561.5614

结果证实了最初的假设:这种模式非常罕见。在大约十年时间里,EURUSD 的小时值仅出现 31 次,这个数量极为有限。

罕见模式的问题在于不清楚它们是如何进入文献的,因为缺乏足够(现在也是如此)的数据来分析这些模式的结果和交易的可预测性。

结果大多是负面的,统计上的稳健性不高,因为交易次数较少。田口模式可能在逻辑上有其合理性,但缺乏硬数据支持。

三方法模式

三方法模式是一个复杂的配置,主要由五根蜡烛图组成。上升的三方法模式应该出现在牛市中,第一根蜡烛图是一个大阳线,接着是三根小阴线,通常都在第一根蜡烛图的范围内。要确认这一模式,最后一根大阳线必须收盘高于第一根蜡烛图的高点。这就像是一个小盘整的牛市突破。

图 4-12 描述了一个上升的三方法

图 4-12. 上升的三方法模式

下降的三方法模式应该出现在熊市中,第一根蜡烛图是一个大阴线,接着是三根小阳线,通常都在第一根蜡烛图的范围内。要确认这一模式,最后一根大阴线必须收盘低于第一根蜡烛图的低点。这就像是一个小盘整的熊市突破。图 4-13 描绘了一个下降的三方法

图 4-13. 下降的三方法模式

从心理学角度来看,这种模式涉及到超越或突破的概念,作为最初走势的确认。交易者通常会推高价格,直到他们开始获利了结并平仓。这种活动对价格有一种平滑作用,通过所谓的修正盘整阶段来稳定价格。

如果最初的交易者恢复他们的买入或卖出活动,并设法超越盘整的范围,你可以相当有信心认为这种走势会继续。

三方法蜡烛图模式的信号功能可以写成如下形式:

def signal(data, open_column, high_column, low_column, close_column, 
           buy_column, sell_column):

    data = add_column(data, 5)

    for i in range(len(data)):

       try:

  `# Bullish pattern`
           if data[i, close_column] > data[i, open_column] and\               data[i, close_column] > data[i - 4, high_column] and\               data[i, low_column] < data[i - 1, low_column] and\               data[i - 1, close_column] < data[i - 4, close_column] and\               data[i - 1, low_column] > data[i - 4, low_column] and\               data[i - 2, close_column] < data[i - 4, close_column] and\               data[i - 2, low_column] > data[i - 4, low_column] and\               data[i - 3, close_column] < data[i - 4, close_column] and\               data[i - 3, low_column] > data[i - 4, low_column] and\               data[i - 4, close_column] > data[i - 4, open_column]:

                    data[i + 1, buy_column] = 1

  `# Bearish pattern`
           elif data[i, close_column] < data[i, open_column] and\                 data[i, close_column] < data[i - 4, low_column] and\                 data[i, high_column] > data[i - 1, high_column] and\                 data[i - 1, close_column] > data[i - 4, close_column] and\                 data[i - 1, high_column] < data[i - 4, high_column] and\                 data[i - 2, close_column] > data[i - 4, close_column] and\                 data[i - 2, high_column] < data[i - 4, high_column] and\                 data[i - 3, close_column] > data[i - 4, close_column] and\                 data[i - 3, high_column] < data[i - 4, high_column] and\                 data[i - 4, close_column] < data[i - 4, open_column]:

                    data[i + 1, sell_column] = -1

       except IndexError:

            pass

    return data

图 4-14 展示了美元加元的信号图。

图 4-14. 美元加元的信号图

这种模式极为罕见,本身并没有显示出太多附加值。即使在放宽条件后,回测结果显示出的信号也很少;因此,这种模式依然神秘。

根据您的数据提供商,一些历史值可能不完全相同,因此,您可以看到过去模式在来自某个提供商的数据上验证,但在来自另一个提供商的数据上未验证。这通常解释了在切换提供商和经纪人时进行反向测试结果略有不同的原因。

Hikkake 模式

Hikkake是一个日语动词,意思是“欺骗”或“陷阱”,而此模式指的是一种实际的陷阱。该模式较为复杂,由大约五根蜡烛图组成,取决于文献(一些研究表明它可能是多个蜡烛图的组合,但没有具体指定蜡烛图的数量)。

牛市 Hikkake(参见图 4-15)以一根牛市蜡烛图开头,后跟一根完全嵌入在第一根蜡烛内部的熊市蜡烛图。接下来,必须出现两根蜡烛图,其高点不超过第二根蜡烛的高点。最后,出现一根大的牛市蜡烛图,其收盘价超过第二根蜡烛的高点。这证实了图案的有效性和上涨确认。

熊市 Hikkake以一根熊市蜡烛图开始,后跟一根完全嵌入在第一根蜡烛内部的牛市蜡烛图。接下来,必须出现两根蜡烛图,其低点不低于第二根蜡烛的低点。最后,必须出现一根大的熊市蜡烛图,其收盘价跌破第二根蜡烛的低点。这证实了图案的有效性和下跌确认。

尽管模式的心理学具有主观性,但并不难理解。术语entrapment源自这样一个事实,即牛市 Hikkake 在历史上是对那些认为市场已经见顶并且应该继续下跌的交易员的熊市陷阱。因此,每当看到最后的蜡烛图突破高点并验证配置时,您可以有一定的信心,预示着更多的力量即将到来。这是因为止损被触发,并且交易员正在看着一个理论上强势的牛市信号超过其阻力位。

图 4-15. 一种牛市 Hikkake

一种熊市 Hikkake(参见图 4-16)可与那些认为市场已经见底并且现在应该反弹的交易员形成对比。因此,每当看到最后的蜡烛图跌破低点并验证配置时,您可以开始改变偏见。

图 4-16. 一种熊市 Hikkake

信号功能可以写成如下形式:

def signal(data, open_column, high_column, low_column, close_column, 
           buy_signal, sell_signal):

    data = add_column(data, 5)    

    for i in range(len(data)):    

       try:

            `# Bullish pattern`
            if data[i, close_column] > data[i - 3, high_column] and \                data[i, close_column] > data[i - 4, close_column] and \                data[i - 1, low_column] < data[i, open_column] and \                data[i - 1, close_column] < data[i, close_column] and \                data[i - 1, high_column] <= data[i - 3, high_column] and \                data[i - 2, low_column] < data[i, open_column] and \                data[i - 2, close_column] < data[i, close_column] and \                data[i - 2, high_column] <= data[i - 3, high_column] and \                data[i - 3, high_column] < data[i - 4, high_column] and \                data[i - 3, low_column] > data[i - 4, low_column] and \                data[i - 4, close_column] > data[i - 4, open_column]: 

                   data[i + 1, buy_signal] = 1 

        `# Bearish pattern`
            elif data[i, close_column] < data[i - 3, low_column] and \                  data[i, close_column] < data[i - 4, close_column] and \                  data[i - 1, high_column] > data[i, open_column] and \                  data[i - 1, close_column] > data[i, close_column] and \                  data[i - 1, low_column] >= data[i - 3, low_column] and \                  data[i - 2, high_column] > data[i, open_column] and \                  data[i - 2, close_column] > data[i, close_column] and \                  data[i - 2, low_column] >= data[i - 3, low_column] and \                  data[i - 3, low_column] > data[i - 4, low_column] and \                  data[i - 3, high_column] < data[i - 4, high_column] and \                  data[i - 4, close_column] < data[i - 4, open_column]: 

                     data[i + 1, sell_signal] = -1

       except IndexError:

            pass

    return data

图 4-17 显示了 EURGBP 上的一个生成信号。显然,这种模式很少见,并且没有有意义的反向测试结果。

图 4-17. EURGBP 上的信号图表

由于该模式非常罕见,没有足够的数据进行正确评估。当然,可以放宽条件以增加信号出现的可能性,但理论上,如果这样做,你就必须给它起个别的名字。表格 4-5 总结了其表现。

表格 4-5. Hikkake 图案:绩效摘要表

资产命中率盈利因子风险-收益比信号数
EURUSD44.83%0.941.16116
USDCHF47.66%0.740.81107
GBPUSD49.57%1.551.57115
USDCAD55.14%0.840.68107
BTCUSD56.96%1.180.8979
ETHUSD60.00%1.240.8250
GOLD51.81%1.141.06166
S&P50083.33%6.011.2012
FTSE10053.33%2.061.8015

结果显示随机表现,这很可能意味着该模式没有预测能力。在标准普尔 500 指数上的命中率为 83.33% 可能在统计学上不正确,因为算法在过去几年仅检测到 12 个信号。

¹ 记住,三根蜡烛图案由三根大蜡烛组成。因此,您需要编写条件代码,确保它们具有最小尺寸。