Python-算法交易秘籍-全-

Python 算法交易秘籍(全)

原文:zh.annas-archive.org/md5/010eca9c9f84c67fe4f8eb1d9bd1d316

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

Python 是一种非常流行的语言,用于构建和执行算法交易策略。如果您想了解如何使用 Python 构建算法交易的坚实基础,本书可以帮助您。

从设置 Python 环境进行交易和与经纪人建立连接开始,您将了解金融市场的重要方面。随着您在这本算法交易书中的进展,您将学会获取金融工具,查询和计算各种类型的蜡烛图和历史数据,最后,计算和绘制技术指标。接下来,您将了解如何下达各种类型的订单,例如普通订单、套利订单和套单,并了解它们的状态转换。您还将揭示从零开始设计和执行强大的算法交易策略时所面临的挑战。后续章节将带您进行回测、模拟交易,最终进行真实交易,使用您从零开始创建的算法策略。您甚至将了解如何自动化交易,并找到进行有效决策的正确策略,这对人类交易者来说是不可能的。

本书结束时,你将能够通过实现 Python 库来进行算法交易,从而使用 Python 进行算法交易生态系统中的关键任务。

本书适合对象

如果你是金融分析师、金融交易员、数据分析师、算法交易员、交易爱好者或任何想要学习使用 Python 进行算法交易和重要技术以解决金融领域所面临挑战的人,这本书适合你。需要具备 Python 编程语言的基本工作知识。虽然对交易相关术语的一些基本知识将会有所帮助,但并非必须。

本书内容概述

第一章,处理和操作日期、时间和时间序列数据,详细介绍了 Python DateTime模块和 pandas DataFrame,这些是有效处理时间序列数据所需的。

第二章,股票市场 - 交易入门,介绍了如何设置 Python 与经纪人的连接,获取金融工具,并简要了解如何下达简单订单。您还将学习如何查询保证金和计算经纪费和政府税。

第三章,获取金融数据,深入讨论了金融工具。

第四章,计算蜡烛图和历史数据,解释了如何获取和理解历史数据,以及如何获取、计算和绘制各种蜡烛图模式,包括日本蜡烛图(OHLC)、Renko、线段破坏和平均蜡烛图。

第五章,计算和绘制技术指标,解释了如何计算和绘制包括趋势指标、动量指标、波动率指标和成交量指标在内的 10 种技术指标。

第六章,在交易所下常规订单,解释了如何在两种交易类型、两种订单代码和四种订单品种之间下 16 种常规订单。您将学习如何实时查询订单状态,同时还将了解经纪人支持的可能订单状态以及常规订单的订单生命周期。

第七章,在交易所下挂单和交易所下覆盖单,解释了如何在两种交易类型和多种订单品种之间下八种挂单和四种覆盖单。您将学习有关目标、止损和跟踪止损的信息,以及经纪人支持的可能订单状态以及挂单和覆盖单的订单生命周期。

第八章,算法交易策略 – 逐步编码,解释了如何使用两个策略编码示例从头开始编写您自己的算法交易策略,其中包括常规订单和挂单。

第九章,算法交易 – 回测,介绍了如何使用两个策略编码示例来回测您自己的算法交易策略,其中包括常规订单和挂单。您还将学习如何实时获取执行日志和各种类型的回测报告,包括盈亏报告、统计报告和订单历史日志。

第十章,算法交易 – 模拟交易,解释了如何在实时市场中使用两个策略编码示例来模拟交易您自己的算法交易策略,其中包括常规订单和挂单。您还将学习如何实时获取执行日志和各种类型的模拟交易报告,包括盈亏报告、统计报告和订单历史日志。

第十一章,算法交易 – 实盘交易,解释了如何在实时市场和真实资金中使用两个策略编码示例进行真实交易您自己的算法交易策略,其中包括常规订单和挂单。您还将学习如何实时获取执行日志和各种类型的真实交易报告,包括盈亏报告和统计报告。

要充分利用本书

本书适合任何对算法交易领域感兴趣的人士。您不需要有金融或算法交易背景。您需要有 Python 编程语言的基本知识。每一章都介绍算法交易中的一个新概念,并逐步引导您从零到高手。本书可以帮助您在使用 Python 进行算法交易方面建立坚实的基础。

您需要在计算机上安装最新版本的 Python 3。本书中的示例在 Python 3.8.2 上进行了测试,它们应该在任何将来的 Python 发布版本上都能正常工作。

您还需要在 Zerodha,一家现代经纪商,拥有一个经纪账户,以尝试大多数章节中涵盖的各种技术。附录 I提供了如何设置 Zerodha 账户的详细、逐步信息,以防您没有账户。

要执行交易策略,您还需要在 AlgoBulls 上拥有一个账户。附录 II提供了如何设置 AlgoBulls 账户的详细、逐步信息,以防您没有账户。

此外,几乎每一章都期望您已安装了其他 Python 包,例如 pyalgotrading。您可以使用 pip 进行安装。这在每一章的技术要求部分有解释。

本章中的所有示例都作为 Jupyter 笔记本提供在我们的 GitHub 存储库上:github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook。如果您想要直接尝试示例而无需输入任何代码,您也可以安装 Jupyter Notebook。您可以使用 pip 进行安装:pip install notebook

本书涵盖的软件/硬件 操作系统要求
Python 3.7+ (www.python.org/downloads/) 任何支持 Python 3.7+ 的操作系统;Linux、Windows、macOS X 等等。

每一章的要求总结在以下表格中:

章节编号 Zerodha 账户 AlgoBulls 账户
1
2
3
4
5
6
7
8
9
10
11

如果您使用本书的数字版本,我们建议您自己输入代码或通过 GitHub 存储库访问代码(链接在下一节中提供)。这样做可以帮助您避免与复制和粘贴代码相关的任何潜在错误。

下载示例代码文件

您可以从您在 www.packt.com 的账户中下载本书的示例代码文件。如果您在其他地方购买了本书,您可以访问 www.packtpub.com/support 并注册,文件将直接发送到您的邮箱。

您可以按照以下步骤下载代码文件:

  1. www.packt.com 登录或注册。

  2. 选择“支持”选项卡。

  3. 单击“Code Downloads”。

  4. 在搜索框中输入书名,并按照屏幕上的指示操作。

下载文件后,请确保使用最新版本的以下工具解压或提取文件夹:

  • 用于 Windows 的 WinRAR/7-Zip

  • 用于 Mac 的 Zipeg/iZip/UnRarX

  • 用于 Linux 的 7-Zip/PeaZip

本书的代码包也托管在 GitHub 上:github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook。如果代码有更新,将在现有的 GitHub 存储库上更新。

我们还有其他代码包,来自我们丰富的书籍和视频目录,可以在 github.com/PacktPublishing/ 查看!快去看看吧!

下载彩色图像

我们还提供了一个 PDF 文件,其中包含本书中使用的屏幕截图/图表的彩色图像。您可以在此处下载:static.packt-cdn.com/downloads/9781838989354_ColorImages.pdf

使用的约定

本书中使用了许多文本约定。

CodeInText:表示文本中的代码词、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄。以下是一个例子:“经纪人通常为每个客户提供唯一的密钥,通常是作为 api-keyapi-secret 键对。”

代码块设置如下:

>>> plot_candlestick_chart(historical_data, 
                           PlotType.OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:TATASTEEL | 1st Jan, 2020 | '
                           'Candle Interval: 1 Minute')

任何命令行输入或输出都按照以下方式编写:

$ pip install pyalgotrading

粗体:表示新术语、重要词或屏幕上看到的词。例如,菜单或对话框中的单词会像这样出现在文本中。以下是一个例子:“如果一个日本蜡烛的 收盘 价格高于其 开盘 价格,它的颜色就是绿色的。”

警告或重要说明会显示如下。

提示和技巧显示如下。

章节

在本书中,您会发现一些经常出现的标题(Getting readyHow to do it...How it works...There's more...See also)。

要清晰地说明如何完成配方,请按照以下部分进行操作:

准备工作

本节告诉您可以在配方中期望什么,并描述设置任何软件或任何配方所需的任何初步设置的方法。

如何实现…

本节包含完成配方所需的步骤。

工作原理…

本节通常包括对前一节发生的情况的详细解释。

还有更多…

本节包含有关配方的其他信息,以使您对配方更加了解。

另请参阅

本节提供有用的链接,指向配方的其他有用信息。

第一章:处理和操纵日期、时间和时间序列数据

当涉及到算法交易时,时间序列数据是无处不在的。因此,处理、管理和操纵时间序列数据对于成功执行算法交易至关重要。本章包含了各种食谱,演示了如何使用 Python 标准库和pandas来进行算法交易,pandas是一个 Python 数据分析库。

对于我们的上下文,时间序列数据是一系列数据,由等间隔的时间戳和描述特定时间段内交易数据的多个数据点组成。

处理时间序列数据时,您首先应该了解的是如何读取、修改和创建理解日期和时间的 Python 对象。Python 标准库包括了datetime模块,它提供了datetimetimedelta对象,可以处理关于日期和时间的所有内容。本章的前七个食谱讨论了这个模块。本章的剩余部分讨论了如何使用pandas库处理时间序列数据,pandas是一个非常高效的数据分析库。我们的食谱将使用pandas.DataFrame类。

以下是本章的食谱列表:

  • 创建日期时间对象

  • 创建时间差对象

  • 对日期时间对象进行操作

  • 修改日期时间对象

  • 将日期时间转换为字符串

  • 从字符串创建日期时间对象

  • 日期时间对象和时区

  • 创建一个 pandas.DataFrame 对象

  • DataFrame 操作——重命名、重新排列、反转和切片

  • DataFrame 操作——应用、排序、迭代和连接

  • 将 DataFrame 转换为其他格式

  • 从其他格式创建 DataFrame

技术要求

您将需要以下内容才能成功执行本章的食谱:

  • Python 3.7+

  • Python 包:

  • pandas ($ pip install pandas)

对于本章中的所有食谱,您将需要本章的 Jupyter 笔记本,位于github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/tree/master/Chapter01

您还可以打开一个新的 Jupyter 笔记本,并直接尝试食谱中显示的实践练习。请注意,对于其中一些食谱,您的输出可能会有所不同,因为它们取决于提供的日期、时间和时区信息。

创建日期时间对象

datetime模块提供了一个datetime类,它可以用于准确捕获与时间戳、日期、时间和时区相关的信息。在本食谱中,您将以多种方式创建datetime对象,并检查其属性。

如何做...

按照以下步骤执行本食谱:

  1. 从 Python 标准库中导入必要的模块:
>>> from datetime import datetime
  1. 使用now()方法创建一个持有当前时间戳的datetime对象并打印它:
>>> dt1 = datetime.now()
>>> print(f'Approach #1: {dt1}')

我们得到以下输出。您的输出将有所不同:

Approach #1: 2020-08-12 20:55:39.680195
  1. 打印与dt1相关的日期和时间属性:
>>> print(f'Year: {dt1.year}')
>>> print(f'Month: {dt1.month}')
>>> print(f'Day: {dt1.day}')
>>> print(f'Hours: {dt1.hour}')
>>> print(f'Minutes: {dt1.minute}')
>>> print(f'Seconds: {dt1.second}')
>>> print(f'Microseconds: {dt1.microsecond}')
>>> print(f'Timezone: {dt1.tzinfo}')

我们得到以下输出。您的输出可能会有所不同:

Year: 2020
Month: 8
Day: 12
Hours: 20
Minutes: 55
Seconds: 39
Microseconds: 680195
Timezone: None
  1. 创建一个持有 2021 年 1 月 1 日的时间戳的datetime对象:
>>> dt2 = datetime(year=2021, month=1, day=1)
>>> print(f'Approach #2: {dt2}')

您将得到以下输出:

Approach #2: 2021-01-01 00:00:00
  1. 打印与dt2相关的各种日期和时间属性:
>>> print(f'Year: {dt.year}')
>>> print(f'Month: {dt.month}')
>>> print(f'Day: {dt.day}')
>>> print(f'Hours: {dt.hour}')
>>> print(f'Minutes: {dt.minute}')
>>> print(f'Seconds: {dt.second}')
>>> print(f'Microseconds: {dt.microsecond}')
>>> print(f'Timezone: {dt2.tzinfo}')

您将得到以下输出:

Year: 2021
Month: 1
Day: 1
Hours: 0
Minutes: 0
Seconds: 0
Microseconds: 0
Timezone: None

工作原理...

步骤 1中,您从datetime模块中导入datetime类。在步骤 2中,您使用now()方法创建并打印一个datetime对象,并将其分配给dt1。该对象保存当前的时间戳信息。

一个datetime对象具有以下与日期、时间和时区信息相关的属性:

1 year 一个介于 0 和 23 之间的整数,包括 0 和 23
2 month 一个介于 1 和 12 之间的整数,包括 1 和 12
3 day 一个介于 1 和 31 之间的整数,包括 1 和 31
4 hour 一个介于 0 和 23 之间的整数,包括 0 和 23
5 minute 一个介于 0 和 59 之间的整数,包括 0 和 59
6 second 一个介于 0 和 59 之间的整数,包括 0 和 59
7 microsecond 一个介于 0 和 999999 之间的整数,包括 0 和 999999
8 tzinfo 一个timezone类的对象。(有关时区的更多信息,请参阅日期时间对象和时区示例。)

步骤 3中,这些属性被打印为dt1。您可以看到它们保存了当前时间戳信息。

步骤 4中,您创建并打印另一个datetime对象。这次您创建了一个特定的时间戳,即 2021 年 1 月 1 日,午夜。您将构造函数本身与参数一起调用——year2021month1day1。其他与时间相关的属性默认为0,时区默认为None。在步骤 5中,您打印了dt2的属性。您可以看到它们与您在步骤 4中传递给构造函数的值完全相同。

还有更多

您可以使用datetime对象的date()time()方法提取日期和时间信息,分别作为datetime.datedatetime.time类的实例:

  1. 使用date()方法从dt1中提取日期。注意返回值的类型。
>>> print(f"Date: {dt1.date()}")
>>> print(f"Type: {type(dt1.date())}")

您将得到以下输出。您的输出可能会有所不同:

Date: 2020-08-12
Type: <class 'datetime.date'>
  1. 使用time()方法从dt1中提取日期。注意返回值的类型。
>>> print(f"Time: {dt1.time()}")
>>> print(f"Type: {type(dt1.time())}")

我们得到以下输出。您的输出可能会有所不同:

Time: 20:55:39.680195
Type: <class 'datetime.time'>
  1. 使用date()方法从dt2中提取日期。注意返回值的类型。
>>> print(f"Date: {dt2.date()}")
>>> print(f"Type: {type(dt2.date())}")

我们得到以下输出:

Date: 2021-01-01
Type: <class 'datetime.date'>
  1. 使用time()方法从dt2中提取日期。注意返回值的类型。
>>> print(f"Time: {dt2.time()}")
>>> print(f"Type: {type(dt2.time())}")

我们得到以下输出:

Time: 00:00:00
Type: <class 'datetime.time'>

创建 timedelta 对象

datetime模块提供了一个timedelta类,可用于表示与日期和时间差异相关的信息。在本示例中,您将创建timedelta对象并对其执行操作。

如何做…

按照以下步骤执行此示例:

  1. 从 Python 标准库中导入必要的模块:
>>> from datetime import timedelta
  1. 创建一个持续时间为 5 天的timedelta对象。将其分配给td1并打印它:
>>> td1 = timedelta(days=5)
>>> print(f'Time difference: {td1}')

我们得到以下输出:

Time difference: 5 days, 0:00:00
  1. 创建一个持续 4 天的timedelta对象。将其赋值给td2并打印出来:
>>> td2 = timedelta(days=4)
>>> print(f'Time difference: {td2}')

我们得到以下输出:

Time difference: 4 days, 0:00:00
  1. td1td2相加并打印输出:
>>> print(f'Addition: {td1} + {td2} = {td1 + td2}')

我们得到以下输出:

Addition: 5 days, 0:00:00 + 4 days, 0:00:00 = 9 days, 0:00:00
  1. td2td1中减去并打印输出:
>>> print(f'Subtraction: {td1} - {td2} = {td1 - td2}')

我们将得到以下输出:

Subtraction: 5 days, 0:00:00 - 4 days, 0:00:00 = 1 day, 0:00:00
  1. td1乘以一个数字(一个浮点数):
>>> print(f'Multiplication: {td1} * 2.5 = {td1 * 2.5}')

我们得到以下输出:

Multiplication: 5 days, 0:00:00 * 2.5 = 12 days, 12:00:00

工作原理...

步骤 1中,您从datetime模块中导入timedelta类。在步骤 2中,您创建一个持有5 天时间差值的timedelta对象,并将其赋值给td1。您调用构造函数来创建具有单个属性days的对象。您在此处传递值为5。类似地,在步骤 3中,您创建另一个timedelta对象,其中包含4 天的时间差值,并将其赋值给td2

在接下来的步骤中,您对timedelta对象执行操作。在步骤 4中,您将td1td2相加。这将返回另一个timedelta对象,其中包含9 天的时间差值,这是由td1td2持有的时间差值的总和。在步骤 5中,您将td2td1中减去。这将返回另一个timedelta对象,其中包含1 天的时间差值,这是由td1td2持有的时间差值之间的差异。在步骤 6中,您将td1乘以2.5,一个浮点数。这再次返回一个timedelta对象,其中包含十二天半的时间差值。

还有更多内容

可以使用一个或多个可选参数创建timedelta对象:

1 weeks 一个整数,默认值为 0。
2 days 一个整数,默认值为 0。
3 hours 一个整数,默认值为 0。
4 minutes 一个整数,默认值为 0。
5 seconds 一个整数,默认值为 0。
6 milliseconds 一个整数,默认值为 0。
7 microseconds 一个整数,默认值为 0。

步骤 2步骤 3中,我们仅使用了days参数。您也可以使用其他参数。此外,这些属性在创建时被标准化。对timedelta对象的这种标准化是为了确保每个时间差值都有一个唯一的表示形式。以下代码演示了这一点:

  1. 创建一个小时为23,分钟为59,秒数为60timedelta对象。将其赋值给td3并打印出来。它将被标准化为一个timedelta对象,其中days1(其他日期和时间相关属性为0):
>>> td3 = timedelta(hours=23, minutes=59, seconds=60)
>>> print(f'Time difference: {td3}')

我们得到以下输出:

Time difference: 1 day, 0:00:00

timedelta对象有一个方便的方法,total_seconds()。该方法返回一个浮点数,表示timedelta对象持续的总秒数。

  1. td3上调用total_seconds()方法。您将得到86400.0作为输出:
>>> print(f'Total seconds in 1 day: {td3.total_seconds()}')

我们得到以下输出:

Total seconds in 1 day: 86400.0

时间对象上的操作

datetimetimedelta类支持各种数学操作,以获取未来或过去的日期。使用这些操作返回另一个datetime对象。在这个示例中,您将创建datetimedatetimetimedelta对象,并对它们执行数学运算。

如何做...

按照这些步骤执行此操作:

  1. 从 Python 标准库中导入必要的模块:
>>> from datetime import datetime, timedelta
  1. 获取今天的日期。将其赋值给date_today并打印出来:
>>> date_today = date.today()              
>>> print(f"Today's Date: {date_today}")

我们得到以下输出。您的输出可能有所不同:

Today's Date: 2020-08-12
  1. 使用timedelta对象将 5 天添加到今天的日期。将其赋值给date_5days_later并打印出来:
>>> date_5days_later = date_today + timedelta(days=5)
>>> print(f"Date 5 days later: {date_5days_later}")

我们得到以下输出。您的输出可能有所不同:

Date 5 days later: 2020-08-17
  1. 使用timedelta对象从今天的日期减去 5 天。将其赋值给date_5days_ago并打印出来:
>>> date_5days_ago = date_today - timedelta(days=5)
>>> print(f"Date 5 days ago: {date_5days_ago}")

我们得到以下输出。您的输出可能有所不同:

Date 5 days ago: 2020-08-07
  1. 使用>操作符将date_5days_laterdate_5days_ago进行比较:
>>> date_5days_later > date_5days_ago

我们得到以下输出:

True
  1. 使用<操作符将date_5days_laterdate_5days_ago进行比较:
>>> date_5days_later < date_5days_ago

我们得到以下输出:

False
  1. 使用>操作符将date_5days_laterdate_todaydate_5days_ago一起进行比较:
>>> date_5days_later > date_today > date_5days_ago

我们得到以下输出:

True
  1. 获取当前时间戳。将其赋值给current_timestamp
>>> current_timestamp = datetime.now()
  1. 获取当前时间。将其赋值给time_now并打印出来:
>>> time_now = current_timestamp.time()
>>> print(f"Time now: {time_now}")

我们得到以下输出。您的输出可能有所不同:

Time now: 20:55:45.239177
  1. 使用timedelta对象将 5 分钟添加到当前时间。将其赋值给time_5minutes_later并打印出来:
>>> time_5minutes_later = (current_timestamp + 
                                timedelta(minutes=5)).time()
>>> print(f"Time 5 minutes later: {time_5minutes_later}")

我们得到以下输出。您的输出可能有所不同:

Time 5 minutes later: 21:00:45.239177
  1. 使用timedelta对象从当前时间减去 5 分钟。将其赋值给time_5minutes_ago并打印出来:
>>> time_5minutes_ago = (current_timestamp - 
                            timedelta(minutes=5)).time()
>>> print(f"Time 5 minutes ago: {time_5minutes_ago}")

我们得到以下输出。您的输出可能有所不同:

Time 5 minutes ago: 20:50:45.239177
  1. 使用<操作符将time_5minutes_latertime_5minutes_ago进行比较:
>>> time_5minutes_later < time_5minutes_ago

我们得到以下输出。您的输出可能有所不同:

False
  1. 使用>操作符将time_5minutes_latertime_5minutes_ago进行比较:
>>> time_5minutes_later > time_5minutes_ago

我们得到以下输出。您的输出可能有所不同:

True
  1. 使用>操作符将time_5minutes_latertime_nowtime_5minutes_ago一起进行比较:
>> time_5minutes_later > time_now > time_5minutes_ago

我们得到以下输出。您的输出可能有所不同:

True

工作原理...

步骤 1中,您从datetime模块导入datedatetimetimedelta类。在步骤 2中,您使用类date提供的today() classmethod获取今天的日期,并将其赋值给一个新属性date_today。(classmethod允许您直接在类上调用方法而不创建实例。)返回的对象类型为datetime.date。在步骤 3中,您通过将持续时间为 5 天的timedelta对象添加到date_today来创建一个比今天晚 5 天的日期。您将此赋值给一个新属性date_5days_later。同样,在步骤 4中,您创建一个 5 天前的日期并将其赋值给一个新属性date_5days_ago

步骤 5步骤 6 中,你使用 >< 操作符分别比较 date_5days_laterdate_5days_ago。如果第一个操作数保存的日期在第二个操作数之后,则 > 操作符返回 True。类似地,如果第二个操作数保存的日期在第一个操作数之后,则 < 操作符返回 True。在 步骤 7 中,你比较到目前为止创建的所有三个日期对象。注意输出。

步骤 8步骤 14 执行与 步骤 2步骤 7 相同的操作,但这次是在datetime.time对象上——获取当前时间、获取当前时间之后的 5 分钟、获取当前时间之前的 5 分钟,并比较所有创建的datetime.time对象。无法直接将timedelta对象添加到datetime.time对象中以获取过去或未来的时间。为了克服这一点,你可以将timedelta对象添加到datetime对象中,然后使用time()方法从中提取时间。你在 步骤 10步骤 11 中执行此操作。

还有更多

本示例展示了对datetime对象的操作,这些操作可以类似地在datetime对象上执行。除了+-<>之外,你还可以在datetimedatetime对象上使用以下操作符:

>= 仅在第一个操作数保持的datetime/date/time晚于或等于第二个操作数时返回True
<= 仅在第一个操作数保持的datetime/date/time早于或等于第二个操作数时返回True
== 仅在第一个操作数保持的datetime/date/time等于第二个操作数时返回True

这不是允许的操作符的详尽列表。有关更多信息,请参阅datetime模块的官方文档:docs.python.org/3.8/library/datetime.html

修改 datetime 对象

通常,你可能希望修改现有的datetime对象以表示不同的日期和时间。本示例包括演示此操作的代码。

如何做…

按照以下步骤执行此示例:

  1. 从 Python 标准库中导入必要的模块:
>>> from datetime import datetime
  1. 获取当前时间戳。将其分配给dt1并打印:
>>> dt1 = datetime.now()
>>> print(dt1)

我们得到以下输出。你的输出会有所不同:

2020-08-12 20:55:46.753899
  1. 通过替换dt1yearmonthday属性来创建一个新的datetime对象。将其分配给dt2并打印:
>>> dt2 = dt1.replace(year=2021, month=1, day=1)
>>> print(f'A timestamp from 1st January 2021: {dt2}')

我们得到以下输出。你的输出会有所不同:

A timestamp from 1st January 2021: 2021-01-01 20:55:46.753899
  1. 通过直接指定所有属性来创建一个新的datetime对象。将其分配给dt3并打印它:
>>> dt3 = datetime(year=2021, 
                   month=1, 
                   day=1,
                   hour=dt1.hour,
                   minute=dt1.minute, 
                   second=dt1.second, 
                   microsecond=dt1.microsecond, 
                   tzinfo=dt1.tzinfo)
print(f'A timestamp from 1st January 2021: {dt3}')

我们得到以下输出。你的输出会有所不同:

A timestamp from 1st January 2021: 2021-01-01 20:55:46.753899
  1. 比较dt2dt3
>>> dt2 == dt3

我们得到以下输出。

True

工作原理...

步骤 1中,您从datetime模块中导入datetime类。在步骤 2中,您使用datetimenow()方法获取当前时间戳并将其赋值给新属性dt1。要从现有的datetime对象获取修改后的时间戳,可以使用replace()方法。在步骤 3中,您通过调用replace()方法从dt1创建一个新的datetime对象dt2。您指定要修改的属性,即yearmonthday。其余属性保持不变,即hourminutesecondmicrosecondtimezone。您可以通过比较步骤 2步骤 3的输出来确认这一点。在步骤 4中,您创建另一个datetime对象dt3。这次,您直接调用datetime构造函数。您将所有属性传递给构造函数,使创建的时间戳与dt2相同。在步骤 5中,您使用==运算符确认dt2dt3持有完全相同的时间戳,该运算符返回True

datetime对象转换为字符串

本配方演示了将datetime对象转换为字符串的过程,该过程在打印和日志记录中应用。此外,在通过 web API 发送时间戳时也很有帮助。

如何做…

执行此配方的以下步骤:

  1. 从 Python 标准库中导入必要的模块:
>>> from datetime import datetime
  1. 获取带有时区信息的当前时间戳。将其分配给now并打印出来:
>>> now = datetime.now().astimezone()
  1. now强制转换为字符串并打印出来:
>>> print(str(now))

我们得到以下输出。您的输出可能会有所不同:

2020-08-12 20:55:48.366130+05:30
  1. 使用strftime()now转换为具有特定日期时间格式的字符串并打印出来:
>>> print(now.strftime("%d-%m-%Y %H:%M:%S %Z"))

我们得到以下输出。您的输出可能会有所不同:

12-08-2020 20:55:48 +0530

如何运作...

步骤 1中,您从datetime模块中导入datetime类。在步骤 2中,您使用带有时区的当前时间戳并将其赋值给新属性nowdatetimenow()方法获取当前时间戳,但没有时区信息。这样的对象称为时区本地的datetime对象。astimezone()方法从此时区无关对象上添加系统本地时区的时区信息,从而将其转换为时区感知对象。(有关更多信息,请参阅datetime 对象和时区配方)。在步骤 3中,您将now转换为字符串对象并将其打印出来。请注意,输出的日期格式是固定的,可能不是您的选择。datetime模块有一个strftime()方法,它可以按需要将对象转换为特定格式的字符串。在步骤 4中,您将now转换为格式为DD-MM-YYYY HH:MM:SS +Z的字符串。步骤 4中使用的指令描述如下:

指令 意义
%d 以零填充的十进制数表示的月份中的一天
%m 以零填充的十进制月份
%Y 十进制数世纪年份
%H 小时(24 小时制)以零填充的十进制数
%M 分钟,以零填充的十进制数
%S 秒,以零填充的十进制数
%Z 时区名称(如果对象是无时区的,则为空字符串)

可以在docs.python.org/3.7/library/datetime.html#strftime-and-strptime-behavior找到可以提供给.strptime()的指令的完整列表。

从字符串创建 datetime 对象

此配方演示了将格式良好的字符串转换为datetime对象。这在从文件中读取时间戳时很有用。此外,在通过 Web API 接收时间戳作为 JSON 数据时也很有帮助。

如何做...

执行此配方的以下步骤:

  1. 从 Python 标准库中导入必要的模块:
>>> from datetime import datetime
  1. 创建一个包含日期、时间和时区的时间戳的字符串表示形式。将其赋值给now_str
>>> now_str = '13-1-2021 15:53:39 +05:30'
  1. now_str转换为now,一个datetime.datetime对象。打印出来:
>>> now = datetime.strptime(now_str, "%d-%m-%Y %H:%M:%S %z")
>>> print(now)

我们得到以下输出:

2021-01-13 15:53:39+05:30
  1. 确认 now 是datetime类型:
>>> print(type(now))

我们得到以下输出:

<class 'datetime.datetime'>

如何工作...

步骤 1中,你从datetime模块中导入datetime类。在步骤 2中,你创建一个包含有效时间戳的字符串,并将其赋值给一个新属性now_strdatetime模块有一个strptime()方法,可以将一个特定格式的字符串转换为datetime对象。在步骤 3中,你将now_str,一个格式为DD-MM-YYYY HH:MM:SS +Z的字符串,转换为now。在步骤 4中,你确认now确实是datetime类型的对象。在步骤 3中使用的指令与将 datetime 对象转换为字符串配方中描述的相同。

还有更多

当将字符串读入datetime对象时,应使用适当的指令消耗整个字符串。部分消耗字符串将引发异常,如下面的代码片段所示。错误消息显示了未转换的数据,并可用于修复提供给strptime()方法的指令。

尝试使用strptime()方法将now_str转换为datetime对象。只传递包含字符串日期部分指令的字符串。注意错误:

>>> now = datetime.strptime(now_str, "%d-%m-%Y")

输出如下:

# Note: It's expected to have an error below
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-96-dc92a0358ed8> in <module>
----> 1 now = datetime.strptime(now_str, "%d-%m-%Y")
      2 # Note: It's expected to get an error below

/usr/lib/python3.8/_strptime.py in _strptime_datetime(cls, data_string, format)
    566 """Return a class cls instance based on the input string and the
    567 format string."""
--> 568 tt, fraction, gmtoff_fraction = _strptime(data_string, format)
    569 tzname, gmtoff = tt[-2:]
    570 args = tt[:6] + (fraction,)

/usr/lib/python3.8/_strptime.py in _strptime(data_string, format)
    350 (data_string, format))
    351 if len(data_string) != found.end():
--> 352 raise ValueError("unconverted data remains: %s" %
    353 data_string[found.end():])
    354 

ValueError: unconverted data remains: 15:53:39 +05:30

datetime对象和时区

有两种类型的datetime对象——时区无关和时区感知。时区无关对象不包含时区信息,而时区感知对象包含时区信息。这个配方演示了在datetime对象上执行多个与时区相关的操作:创建时区无关和时区感知对象,向时区感知对象添加时区信息,从时区无关对象中删除时区信息,以及比较时区感知和时区无关对象。

如何做...

执行此配方的以下步骤:

  1. 从 Python 标准库中导入必要的模块:
>>> from datetime import datetime
  1. 创建一个时区无关的 datetime 对象。将其赋给 now_tz_naive 并打印它:
>>> now_tz_unaware = datetime.now()
>>> print(now_tz_unaware)

我们得到了以下输出。您的输出可能会有所不同:

2020-08-12 20:55:50.598800
  1. 打印 now_tz_naive 的时区信息。注意输出:
>>> print(now_tz_unaware.tzinfo)

我们得到了以下输出:

None
  1. 创建一个时区感知的 datetime 对象。将其赋给 now_tz_aware 并打印它:
>>> now_tz_aware = datetime.now().astimezone()
>>> print(now_tz_aware)

我们得到了以下输出。您的输出可能会有所不同:

2020-08-12 20:55:51.004671+05:30
  1. 打印 now_tz_aware 的时区信息。注意输出:
>>> print(now_tz_aware.tzinfo)

我们得到了以下输出。您的输出可能会有所不同:

IST
  1. 通过从 now_tz_aware 中添加时区信息创建一个新时间戳。将其赋给 new_tz_aware 并打印它:
>>> new_tz_aware = now_tz_naive.replace(tzinfo=now_tz_aware.tzinfo)
>>> print(new_tz_aware)

输出如下。您的输出可能会有所不同:

2020-08-12 20:55:50.598800+05:30
  1. 使用 tzinfo 属性打印 new_tz_aware 的时区信息。注意输出:
>>> print(new_tz_aware.tzinfo)

输出如下。您的输出可能会有所不同:

IST
  1. 通过从 new_tz_aware 中移除时区信息创建一个新的时间戳。将其赋给 new_tz_naive 并打印它:
>>> new_tz_naive = new_tz_aware.replace(tzinfo=None)
>>> print(new_tz_naive)

输出如下。您的输出可能会有所不同:

2020-08-12 20:55:50.598800
  1. 使用 tzinfo 属性打印 new_tz_naive 的时区信息。注意输出:
>>> print(new_tz_naive.tzinfo)

输出如下:

None

工作原理如下...

步骤 1 中,从 datetime 模块中导入 datetime 类。在 步骤 2 中,使用 now() 方法创建一个时区无关的 datetime 对象,并将其赋给一个新属性 now_tz_naive。在 步骤 3 中,使用 tzinfo 属性打印 now_tz_naive 所持有的时区信息。观察到输出为 None,因为这是一个时区无关的对象。

步骤 4 中,使用 now()astimezone() 方法创建了一个时区感知的 datetime 对象,并将其赋给一个新属性 now_tz_aware步骤 5 中,使用 tzinfo 属性打印了 now_tz_aware 所持有的时区信息。注意输出为 IST 而不是 None,因为这是一个时区感知对象。

步骤 6 中,通过向 now_tz_naive 添加时区信息来创建一个新的 datetime 对象。时区信息来自 now_tz_aware。你可以使用 replace() 方法实现这一点(有关更多信息,请参阅 修改 datetime 对象 配方)。将其赋给一个新变量 new_tz_aware。在 步骤 7 中,打印 new_tz_aware 所持有的时区信息。观察到它与 步骤 5 中的输出相同,因为你从 now_tz_aware 中取了时区信息。同样,在 步骤 8步骤 9 中,你创建了一个新的 datetime 对象 new_tz_naive,但这次你移除了时区信息。

还有更多

您只能在时区无关或时区感知的 datetime 对象之间使用比较运算符。你不能比较一个时区无关的 datetime 对象和一个时区感知的 datetime 对象。这样做会引发异常。这在以下步骤中得到了证明:

  1. 比较两个时区无关对象,new_tz_naivenow_tz_naive。注意输出:
>>> new_tz_naive <= now_tz_naive
  1. 比较两个时区感知对象,new_tz_awarenow_tz_aware。注意输出:
>>> new_tz_aware <= now_tz_aware

我们得到了以下输出:

True
  1. 比较一个时区感知对象和一个时区不感知对象,new_tz_awarenow_tz_naive。注意错误:
>>> new_tz_aware > now_tz_naive

我们得到以下输出:

-------------------------------------------------------------------
            TypeError Traceback (most recent call last)
<ipython-input-167-a9433bb51293> in <module>
----> 1 new_tz_aware > now_tz_naive
      2 # Note: It's expected to get an error below

TypeError: can't compare offset-naive and offset-aware datetimes

创建一个 pandas.DataFrame 对象

现在我们已经完成了日期和时间的处理,让我们转向处理时间序列数据。pandas库有一个pandas.DataFrame类,对于处理和操作这样的数据很有用。这个示例从创建这些对象开始。

如何做...

对于这个示例,执行以下步骤:

  1. 从 Python 标准库中导入必要的模块:
>>> from datetime import datetime
>>> import pandas
  1. 创建一个时间序列数据的示例,作为一个字典对象列表。将其分配给time_series数据:
>>> time_series_data = \
[{'date': datetime.datetime(2019, 11, 13, 9, 0),   
  'open': 71.8075, 'high': 71.845,  'low': 71.7775, 
  'close': 71.7925, 'volume': 219512},
{'date': datetime.datetime(2019, 11, 13, 9, 15),  
 'open': 71.7925, 'high': 71.8,    'low': 71.78,   
 'close': 71.7925, 'volume': 59252},
{'date': datetime.datetime(2019, 11, 13, 9, 30),  
 'open': 71.7925, 'high': 71.8125, 'low': 71.76,
 'close': 71.7625, 'volume': 57187},
{'date': datetime.datetime(2019, 11, 13, 9, 45),  
 'open': 71.76,   'high': 71.765,  'low': 71.735,  
 'close': 71.7425, 'volume': 43048}, 
{'date': datetime.datetime(2019, 11, 13, 10, 0),  
 'open': 71.7425, 'high': 71.78,   'low': 71.7425, 
 'close': 71.7775, 'volume': 45863},
{'date': datetime.datetime(2019, 11, 13, 10, 15), 
 'open': 71.775,  'high': 71.8225, 'low': 71.77,   
 'close': 71.815,  'volume': 42460},
{'date': datetime.datetime(2019, 11, 13, 10, 30), 
 'open': 71.815,  'high': 71.83,   'low': 71.7775, 
 'close': 71.78,   'volume': 62403},
{'date': datetime.datetime(2019, 11, 13, 10, 45), 
 'open': 71.775,  'high': 71.7875, 'low': 71.7475,
 'close': 71.7525, 'volume': 34090},
{'date': datetime.datetime(2019, 11, 13, 11, 0),  
 'open': 71.7525, 'high': 71.7825, 'low': 71.7475,
 'close': 71.7625, 'volume': 39320},
{'date': datetime.datetime(2019, 11, 13, 11, 15), 
 'open': 71.7625, 'high': 71.7925, 'low': 71.76,
 'close': 71.7875, 'volume': 20190}]
  1. time_series_data创建一个新的DataFrame。将其分配给df并打印它:
>>> df = pandas.DataFrame(time_series_data)
>>> df

我们得到以下输出:

                 date    open    high     low   close volume
0 2019-11-13 09:00:00 71.8075 71.8450 71.7775 71.7925 219512
1 2019-11-13 09:15:00 71.7925 71.8000 71.7800 71.7925  59252
2 2019-11-13 09:30:00 71.7925 71.8125 71.7600 71.7625  57187
3 2019-11-13 09:45:00 71.7600 71.7650 71.7350 71.7425  43048
4 2019-11-13 10:00:00 71.7425 71.7800 71.7425 71.7775  45863
5 2019-11-13 10:15:00 71.7750 71.8225 71.7700 71.8150  42460
6 2019-11-13 10:30:00 71.8150 71.8300 71.7775 71.7800  62403
7 2019-11-13 10:45:00 71.7750 71.7875 71.7475 71.7525  34090
8 2019-11-13 11:00:00 71.7525 71.7825 71.7475 71.7625  39320
9 2019-11-13 11:15:00 71.7625 71.7925 71.7600 71.7875  20190
  1. 获取df中的列列表:
>>> df.columns.tolist()

我们得到以下输出:

['date', 'open', 'high', 'low', 'close', 'volume']
  1. 再次使用time_series_data创建一个DataFrame对象。这次,按照你想要的顺序指定列:
>>> pandas.DataFrame(time_series_data, 
         columns=['close','date', 'open', 'high', 'low', 'volume'])

我们得到以下输出:

    close                date    open    high     low volume
0 71.7925 2019-11-13 09:00:00 71.8075 71.8450 71.7775 219512
1 71.7925 2019-11-13 09:15:00 71.7925 71.8000 71.7800  59252
2 71.7625 2019-11-13 09:30:00 71.7925 71.8125 71.7600  57187
3 71.7425 2019-11-13 09:45:00 71.7600 71.7650 71.7350  43048
4 71.7775 2019-11-13 10:00:00 71.7425 71.7800 71.7425  45863
5 71.8150 2019-11-13 10:15:00 71.7750 71.8225 71.7700  42460
6 71.7800 2019-11-13 10:30:00 71.8150 71.8300 71.7775  62403
7 71.7525 2019-11-13 10:45:00 71.7750 71.7875 71.7475  34090
8 71.7625 2019-11-13 11:00:00 71.7525 71.7825 71.7475  39320
9 71.7875 2019-11-13 11:15:00 71.7625 71.7925 71.7600  20190

它是如何工作的...

步骤 1中,从datetime模块导入datetime类和pandas包。在步骤 2中,创建一个时间序列数据,这通常由第三方 API 返回历史数据。这个数据是一个字典列表,每个字典有相同的键集——dateopenhighlowclosevolume。注意date键的值是一个datetime对象,其他键的值是float对象。

步骤 3中,通过直接调用构造函数并将time_series_data作为参数来创建一个 pandas DataFrame对象,并将返回数据分配给df。字典的键成为df的列名,值成为数据。在步骤 4中,使用columns属性和tolist()方法将df的列作为列表提取出来。您可以验证time_series_data中字典的键与列名相同。

步骤 5中,通过向构造函数传递columns参数以特定顺序的列来创建一个DataFrame,该参数是一个字符串列表。

还有更多

当创建一个DataFrame对象时,会自动分配一个索引,这是所有行的地址。前面示例中最左边的列是索引列。默认情况下,索引从0开始。可以通过向DataFrame构造函数传递一个index参数以迭代器的形式设置自定义索引。如下所示:

  1. time_series_data创建一个新的 DataFrame 对象,带有自定义索引:
>>> pandas.DataFrame(time_series_data, index=range(10, 20)) 

我们得到以下输出:

                  date    open    high     low   close volume
10 2019-11-13 09:00:00 71.8075 71.8450 71.7775 71.7925 219512
11 2019-11-13 09:15:00 71.7925 71.8000 71.7800 71.7925  59252
12 2019-11-13 09:30:00 71.7925 71.8125 71.7600 71.7625  57187
13 2019-11-13 09:45:00 71.7600 71.7650 71.7350 71.7425  43048
14 2019-11-13 10:00:00 71.7425 71.7800 71.7425 71.7775  45863
15 2019-11-13 10:15:00 71.7750 71.8225 71.7700 71.8150  42460
16 2019-11-13 10:30:00 71.8150 71.8300 71.7775 71.7800  62403
17 2019-11-13 10:45:00 71.7750 71.7875 71.7475 71.7525  34090
18 2019-11-13 11:00:00 71.7525 71.7825 71.7475 71.7625  39320
19 2019-11-13 11:15:00 71.7625 71.7925 71.7600 71.7875  20190

注意输出中的索引从10开始到19结束。默认索引值应该是从09

DataFrame 操作—重命名、重新排列、反转和切片

创建DataFrame对象后,你可以对其执行各种操作。本示例涵盖了对DataFrame对象进行以下操作。重命名列、重新排列列、反转DataFrame,以及对DataFrame进行切片以提取行、列和数据子集。

准备工作完成

确保df对象在你的 Python 命名空间中可用。请参考本章的创建 pandas.DataFrame 对象示例来设置该对象。

如何执行…

对这个示例执行以下步骤:

  1. dfdate列重命名为timestamp。打印它:
>>> df.rename(columns={'date':'timestamp'}, inplace=True)
>>> df

我们得到以下输出:

            timestamp    open    high     low   close volume
0 2019-11-13 09:00:00 71.8075 71.8450 71.7775 71.7925 219512
1 2019-11-13 09:15:00 71.7925 71.8000 71.7800 71.7925  59252
2 2019-11-13 09:30:00 71.7925 71.8125 71.7600 71.7625  57187
3 2019-11-13 09:45:00 71.7600 71.7650 71.7350 71.7425  43048
4 2019-11-13 10:00:00 71.7425 71.7800 71.7425 71.7775  45863
5 2019-11-13 10:15:00 71.7750 71.8225 71.7700 71.8150  42460
6 2019-11-13 10:30:00 71.8150 71.8300 71.7775 71.7800  62403
7 2019-11-13 10:45:00 71.7750 71.7875 71.7475 71.7525  34090
8 2019-11-13 11:00:00 71.7525 71.7825 71.7475 71.7625  39320
9 2019-11-13 11:15:00 71.7625 71.7925 71.7600 71.7875  20190
  1. 通过重新排列df中的列创建一个新的DataFrame对象:
>>> df.reindex(columns=[
               'volume', 
               'close', 
               'timestamp', 
               'high', 
               'open', 
               'low'
            ])

我们得到以下输出:

  volume   close           timestamp    high    open     low
0 219512 71.7925 2019-11-13 09:00:00 71.8450 71.8075 71.7775
1  59252 71.7925 2019-11-13 09:15:00 71.8000 71.7925 71.7800
2  57187 71.7625 2019-11-13 09:30:00 71.8125 71.7925 71.7600
3  43048 71.7425 2019-11-13 09:45:00 71.7650 71.7600 71.7350
4  45863 71.7775 2019-11-13 10:00:00 71.7800 71.7425 71.7425
5  42460 71.8150 2019-11-13 10:15:00 71.8225 71.7750 71.7700
6  62403 71.7800 2019-11-13 10:30:00 71.8300 71.8150 71.7775
7  34090 71.7525 2019-11-13 10:45:00 71.7875 71.7750 71.7475
8  39320 71.7625 2019-11-13 11:00:00 71.7825 71.7525 71.7475
9  20190 71.7875 2019-11-13 11:15:00 71.7925 71.7625 71.7600
  1. 通过反转df中的行创建一个新的DataFrame对象:
>>> df[::-1]

我们得到以下输出:

            timestamp    open    high     low   close volume
9 2019-11-13 11:15:00 71.7625 71.7925 71.7600 71.7875  20190
8 2019-11-13 11:00:00 71.7525 71.7825 71.7475 71.7625  39320
7 2019-11-13 10:45:00 71.7750 71.7875 71.7475 71.7525  34090
6 2019-11-13 10:30:00 71.8150 71.8300 71.7775 71.7800  62403
5 2019-11-13 10:15:00 71.7750 71.8225 71.7700 71.8150  42460
4 2019-11-13 10:00:00 71.7425 71.7800 71.7425 71.7775  45863
3 2019-11-13 09:45:00 71.7600 71.7650 71.7350 71.7425  43048
2 2019-11-13 09:30:00 71.7925 71.8125 71.7600 71.7625  57187
1 2019-11-13 09:15:00 71.7925 71.8000 71.7800 71.7925  59252
0 2019-11-13 09:00:00 71.8075 71.8450 71.7775 71.7925 219512
  1. df中提取close列:
>>> df['close']

我们得到以下输出:

0    71.7925
1    71.7925
2    71.7625
3    71.7425
4    71.7775
5    71.8150
6    71.7800
7    71.7525
8    71.7625
9    71.7875
Name: close, dtype: float64
  1. df中提取第一行:
>>> df.iloc[0]

我们得到以下输出:

timestamp    2019-11-13 09:00:00
open                     71.8075
high                      71.845
low                      71.7775
close                    71.7925
volume                    219512
Name: 10, dtype: object
  1. 提取一个2 × 2矩阵,只包括前两行和前两列:
>>> df.iloc[:2, :2]

我们得到以下输出:

            timestamp    open
0 2019-11-13 09:00:00 71.8075
1 2019-11-13 09:15:00 71.7925

它的工作原理...

重命名:在步骤 1 中,你使用 pandas 的 DataFrame 的rename()方法将date列重命名为timestamp。你通过将columns参数作为一个字典传递,其中要替换的现有名称作为键,其新名称作为相应的值。你还将inplace参数传递为True,以便直接修改df。如果不传递,其默认值为False,意味着将创建一个新的DataFrame而不是修改df

重新排列:在步骤 2 中,你使用reindex()方法从df创建一个新的DataFrame,重新排列其列。你通过传递columns参数以字符串列表的形式传递所需的顺序的列名。

反转:在步骤 3 中,你通过以一种特殊的方式使用索引运算符[::-1]df创建一个新的DataFrame,其中的行被反转。这类似于我们反转常规的 Python 列表的方式。

切片:在步骤 4 中,你使用df上的索引运算符提取列close。你在这里传递列名close作为索引。返回的数据是一个pandas.Series对象。你可以在 DataFrame 对象上使用iloc属性来提取行、列或子集 DataFrame 对象。在步骤 5 中,你使用iloc提取第一行,并使用0作为索引。返回的数据是一个pandas.Series对象。在步骤 6 中,你使用iloc提取从df中的(:2, :2)开始的 2x2 子集。这意味着提取直到索引 2(即 0 和 1)的所有行和直到索引 2(再次是 0 和 1)的所有列的数据。返回的数据是一个pandas.DataFrame对象。

在此示例中显示的所有操作中,返回一个新的DataFrame对象的地方,原始的DataFrame对象保持不变。

还有更多

.iloc()属性也可以用于从DataFrame中提取列。以下代码展示了这一点。

df中提取第四列。观察输出:

>>> df.iloc[:, 4]

我们得到以下输出:

0    71.7925
1    71.7925
2    71.7625
3    71.7425
4    71.7775
5    71.8150
6    71.7800
7    71.7525
8    71.7625
9    71.7875
Name: close, dtype: float64

注意,此输出和 步骤 4 的输出相同。

DataFrame 操作 — 应用、排序、迭代和连接

在上一个食谱的基础上,本食谱演示了可以对 DataFrame 对象执行的更多操作:对列中的所有元素应用函数、基于列进行排序、迭代行以及垂直和水平连接多个 DataFrame 对象。

准备工作

在尝试此食谱之前,请确保您已经完成了上一个食谱。确保您的 Python 命名空间中有来自上一个食谱的 df

如何做...

为此食谱执行以下步骤:

  1. 导入必要的模块
>>> import random
>>> import pandas
  1. 使用不同的日期和时间格式 DD-MM-YYYY HH:MM:SS 修改 df 的时间戳列中的值:
>>> df['timestamp'] = df['timestamp'].apply(
                        lambda x: x.strftime("%d-%m-%Y %H:%M:%S"))
>>> df

我们得到以下输出:

            timestamp    open    high     low   close volume
0 13-11-2019 09:00:00 71.8075 71.8450 71.7775 71.7925 219512
1 13-11-2019 09:15:00 71.7925 71.8000 71.7800 71.7925  59252
2 13-11-2019 09:30:00 71.7925 71.8125 71.7600 71.7625  57187
3 13-11-2019 09:45:00 71.7600 71.7650 71.7350 71.7425  43048
4 13-11-2019 10:00:00 71.7425 71.7800 71.7425 71.7775  45863
5 13-11-2019 10:15:00 71.7750 71.8225 71.7700 71.8150  42460
6 13-11-2019 10:30:00 71.8150 71.8300 71.7775 71.7800  62403
7 13-11-2019 10:45:00 71.7750 71.7875 71.7475 71.7525  34090
8 13-11-2019 11:00:00 71.7525 71.7825 71.7475 71.7625  39320
9 13-11-2019 11:15:00 71.7625 71.7925 71.7600 71.7875  20190
  1. 创建一个按照 close 列升序排列的新的 DataFrame 对象:
>>> df.sort_values(by='close', ascending=True)

我们得到以下输出:

            timestamp    open    high     low   close volume
3 13-11-2019 09:45:00 71.7600 71.7650 71.7350 71.7425  43048
7 13-11-2019 10:45:00 71.7750 71.7875 71.7475 71.7525  34090
2 13-11-2019 09:30:00 71.7925 71.8125 71.7600 71.7625  57187
8 13-11-2019 11:00:00 71.7525 71.7825 71.7475 71.7625  39320
4 13-11-2019 10:00:00 71.7425 71.7800 71.7425 71.7775  45863
6 13-11-2019 10:30:00 71.8150 71.8300 71.7775 71.7800  62403
9 13-11-2019 11:15:00 71.7625 71.7925 71.7600 71.7875  20190
0 13-11-2019 09:00:00 71.8075 71.8450 71.7775 71.7925 219512
1 13-11-2019 09:15:00 71.7925 71.8000 71.7800 71.7925  59252
5 13-11-2019 10:15:00 71.7750 71.8225 71.7700 71.8150  42460
  1. 创建一个按照 open 列降序排列的新的 DataFrame 对象:
>>> df.sort_values(by='open', ascending=False)

我们得到以下输出:

            timestamp    open    high     low   close volume
6 13-11-2019 10:30:00 71.8150 71.8300 71.7775 71.7800  62403
0 13-11-2019 09:00:00 71.8075 71.8450 71.7775 71.7925 219512
2 13-11-2019 09:30:00 71.7925 71.8125 71.7600 71.7625  57187
1 13-11-2019 09:15:00 71.7925 71.8000 71.7800 71.7925  59252
7 13-11-2019 10:45:00 71.7750 71.7875 71.7475 71.7525  34090
5 13-11-2019 10:15:00 71.7750 71.8225 71.7700 71.8150  42460
9 13-11-2019 11:15:00 71.7625 71.7925 71.7600 71.7875  20190
3 13-11-2019 09:45:00 71.7600 71.7650 71.7350 71.7425  43048
8 13-11-2019 11:00:00 71.7525 71.7825 71.7475 71.7625  39320
4 13-11-2019 10:00:00 71.7425 71.7800 71.7425 71.7775  45863
  1. 遍历 df 以找到每行的 openclosehighlow 的平均值:
>>> for _, row in df.iterrows():
       avg = (row['open'] + row['close'] + row['high'] + 
              row['low'])/4
       print(f"Index: {_} | Average: {avg}")

我们得到以下输出:

Index: 0 | Average: 71.805625
Index: 1 | Average: 71.79124999999999
Index: 2 | Average: 71.781875
Index: 3 | Average: 71.750625
Index: 4 | Average: 71.760625
Index: 5 | Average: 71.795625
Index: 6 | Average: 71.800625
Index: 7 | Average: 71.765625
Index: 8 | Average: 71.76124999999999
Index: 9 | Average: 71.775625
  1. 逐列迭代 df 的第一行的所有值:
>>> for value in df.iloc[0]:
        print(value)

我们得到以下输出:

13-11-2019 09:00:00
71.8075
71.845
71.7775
71.7925
219512
  1. 创建一个样本时间序列数据作为字典对象的列表。将其分配给 df_new
>>> df_new = pandas. DataFrame([
    {'timestamp': datetime.datetime(2019, 11, 13, 11, 30),
     'open': 71.7875,
     'high': 71.8075,
     'low': 71.77,
     'close': 71.7925,
     'volume': 18655},
    {'timestamp': datetime.datetime(2019, 11, 13, 11, 45),
     'open': 71.7925,
     'high': 71.805,
     'low': 71.7625,
     'close': 71.7625,
     'volume': 25648},
    {'timestamp': datetime.datetime(2019, 11, 13, 12, 0),
     'open': 71.7625,
     'high': 71.805,
     'low': 71.75,
     'close': 71.785,
     'volume': 37300},
    {'timestamp': datetime.datetime(2019, 11, 13, 12, 15),
     'open': 71.785,
     'high': 71.7925,
     'low': 71.7575,
     'close': 71.7775,
     'volume': 15431},
    {'timestamp': datetime.datetime(2019, 11, 13, 12, 30),
     'open': 71.7775,
     'high': 71.795,
     'low': 71.7725,
     'close': 71.79,
     'volume': 5178}])
>>> df_new

我们得到以下输出:

            timestamp    open    high     low   close volume
0 2019-11-13 11:30:00 71.7875 71.8075 71.7700 71.7925  18655
1 2019-11-13 11:45:00 71.7925 71.8050 71.7625 71.7625  25648
2 2019-11-13 12:00:00 71.7625 71.8050 71.7500 71.7850  37300
3 2019-11-13 12:15:00 71.7850 71.7925 71.7575 71.7775  15431
4 2019-11-13 12:30:00 71.7775 71.7950 71.7725 71.7900   5178
  1. 通过垂直连接 dfdf_new 创建一个新的 DataFrame:
>>> pandas.concat([df, df_new]).reset_index(drop=True)

我们得到以下输出:

             timestamp    open    high     low   close volume
0  13-11-2019 09:00:00 71.8075 71.8450 71.7775 71.7925 219512
1  13-11-2019 09:15:00 71.7925 71.8000 71.7800 71.7925  59252
2  13-11-2019 09:30:00 71.7925 71.8125 71.7600 71.7625  57187
3  13-11-2019 09:45:00 71.7600 71.7650 71.7350 71.7425  43048
4  13-11-2019 10:00:00 71.7425 71.7800 71.7425 71.7775  45863
5  13-11-2019 10:15:00 71.7750 71.8225 71.7700 71.8150  42460
6  13-11-2019 10:30:00 71.8150 71.8300 71.7775 71.7800  62403
7  13-11-2019 10:45:00 71.7750 71.7875 71.7475 71.7525  34090
8  13-11-2019 11:00:00 71.7525 71.7825 71.7475 71.7625  39320
9  13-11-2019 11:15:00 71.7625 71.7925 71.7600 71.7875  20190
10 2019-11-13 11:30:00 71.7875 71.8075 71.7700 71.7925  18655
11 2019-11-13 11:45:00 71.7925 71.8050 71.7625 71.7625  25648
12 2019-11-13 12:00:00 71.7625 71.8050 71.7500 71.7850  37300
13 2019-11-13 12:15:00 71.7850 71.7925 71.7575 71.7775  15431
14 2019-11-13 12:30:00 71.7775 71.7950 71.7725 71.7900   5178

它是如何工作的...

步骤 1 中,您导入 pandas 包。

应用:在 步骤 2 中,您通过使用 apply 方法修改 dftimestamp 列中的所有值。此方法接受要应用的函数作为输入。您在此处传递一个期望一个 datetime 对象作为单个输入的 lambda 函数,并使用 strftime() 将其转换为所需格式的字符串。(有关 strftime() 的更多详细信息,请参阅 将 datetime 对象转换为字符串 食谱)。apply 方法调用在 dftimestamp 列上,这是一个 pandas.Series 对象。lambda 函数应用于列中的每个值。此调用返回一个新的 pandas.Series 对象,您将其重新分配给 dftimestamp 列。注意,之后,dftimestamp 列保存的是字符串对象,而不是之前的 datetime 对象。

排序:在 步骤 3 中,您通过按照 dfclose 列升序排列来创建一个新的 DataFrame 对象。您使用 sort_values() 方法来执行排序。类似地,在 步骤 4 中,您通过按照 dfopen 列降序排列来创建一个新的 DataFrame 对象。

迭代:在步骤 5中,您使用iterrows()方法迭代df以找到并打印出每行的openclosehighlow的平均值。iterrows()方法将每行作为一个(index, pandas.Series)对进行迭代。在步骤 6中,您使用df.iloc[0]迭代df的第一行的所有值。您将第一行的timestampopenhighlowclosevolume列值作为输出。

连接:在步骤 6中,您创建了一个新的DataFrame,类似于创建 pandas.DataFrame 对象配方中创建的那个,并将其赋值给df_new。您使用pandas.concat()函数通过垂直连接dtdf_new来创建一个新的DataFrame。这意味着将创建一个新的DataFrame,其中df_new的行附加在df的行下面。您将包含dfdf_new的列表作为参数传递给pandas.concat()函数。另外,为了创建一个从0开始的新索引,您使用了reset_index()方法,并将参数 drop 传递为True。如果您不使用reset_index(),则连接的DataFrame的索引会看起来像这样—0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4。(有关DataFrame索引的更多信息,请参阅创建 pandas.DataFrame 对象配方。)

还有更多

您也可以使用pandas.concat()函数将两个DataFrame对象水平连接在一起,即列方向上,通过将axis参数传递给pandas.concat()方法一个值为1。这在以下步骤中显示:

  1. 从 Python 标准库导入random模块:
>>> import random
  1. 使用一个单列open和随机值创建一个DataFrame对象。将其赋值给df1并打印出来:
>>> df1 = pandas.DataFrame([random.randint(1,100) for i in 
                            range(10)], columns=['open'])
>>> df1

我们得到以下输出。您的输出可能会有所不同:

   open
0    99
1    73
2    16
3    53
4    47
5    74
6    21
7    22
8     2
9    30
  1. 使用一个单列close和随机值创建另一个DataFrame对象。将其赋值给df2并打印出来:
>>> df2 = pandas.DataFrame([random.randint(1,100) for i in 
                            range(10)], columns=['close'])
>>> df2

我们得到以下输出:

   close
0     63
1     84
2     44
3     56
4     25
5      1
6     41
7     55
8     93
9     82
  1. 通过水平连接df1df2创建一个新的DataFrame
>>> pandas.concat([df1, df2], axis=1)

我们得到以下输出。您的输出可能会有所不同:

    open  close
0     99     93
1     73     42
2     16     57
3     53     56
4     47     25
5     74      1
6     21     41
7     22     55
8      2     93
9     30     82

将 DataFrame 转换为其他格式

本配方演示了将DataFrame对象转换为其他格式,如.csv文件、json对象和pickle对象。将其转换为.csv文件可以使进一步使用电子表格应用程序处理数据变得更加容易。json格式对于通过网络 API 传输DataFrame对象非常有用。pickle格式对于通过套接字将一个 Python 会话中创建的DataFrame对象传输到另一个 Python 会话中而无需重新创建它们非常有用。

准备工作

确保在您的 Python 命名空间中可用对象df。请参阅本章的创建 pandas.DataFrame 对象配方来设置此对象。

如何做...

执行此配方的以下步骤:

  1. df转换并保存为 CSV 文件:
>>> df.to_csv('dataframe.csv', index=False)
  1. df转换为 JSON 字符串:
>>> df.to_json()

我们得到以下输出:

'{
    "timestamp":{
        "0":"13-11-2019 09:00:00","1":"13-11-2019 09:15:00",
        "2":"13-11-2019 09:30:00","3":"13-11-2019 09:45:00",
        "4":"13-11-2019 10:00:00","5":"13-11-2019 10:15:00",
        "6":"13-11-2019 10:30:00","7":"13-11-2019 10:45:00",
        "8":"13-11-2019 11:00:00","9":"13-11-2019 11:15:00"},
    "open":{
        "0":71.8075,"1":71.7925,"2":71.7925, "3":71.76,         
        "4":71.7425,"5":71.775,"6":71.815, "7":71.775,
        "8":71.7525,"9":71.7625},
    "high"{
        "0":71.845,"1":71.8,"2":71.8125,"3":71.765,
        "4":71.78,"5":71.8225,"6":71.83,"7":71.7875,
        "8":71.7825,"9":71.7925},
    "low":{
        "0":71.7775,"1":71.78,"2":71.76,"3":71.735,
        "4":71.7425,"5":71.77,"6":71.7775,"7":71.7475,
        "8":71.7475,"9":71.76},
    "close":{
        "0":71.7925,"1":71.7925,"2":71.7625,"3":71.7425,
        "4":71.7775,"5":71.815,"6":71.78,"7":71.7525,
        "8":71.7625,"9":71.7875},
    "volume":{
        "0":219512,"1":59252,"2":57187,"3":43048,
        "4":45863,"5":42460,"6":62403,"7":34090,
        "8":39320,"9":20190}}'
  1. df保存为一个文件:
>>> df.to_pickle('df.pickle')

工作原理...

步骤 1 中,你使用 to_csv() 方法将 df 保存为 .csv 文件。你将 dataframe.csv,一个生成 .csv 文件的文件路径,作为第一个参数传递,将索引设置为 False 作为第二个参数。将索引设置为 False 可以防止索引被转储到 .csv 文件中。如果你想将 DataFrame 与其索引一起保存,可以将索引设置为 True 传递给 to_csv() 方法。

步骤 2 中,你使用 to_json() 方法将 df 转换为 JSON 字符串。你没有向 to_json() 方法传递任何额外的参数。

步骤 3 中,你使用 to_pickle() 方法对对象进行 pickle(序列化)。同样,你没有向 to_pickle() 方法传递任何额外的参数。

方法 to_csv()to_json()to_pickle() 可以接受比本示例中显示的更多可选参数。有关这些方法的完整信息,请参阅官方文档:

从其他格式创建 DataFrame

在这个示例中,你将从其他格式(如 .csv 文件、.json 字符串和 pickle 文件)创建 DataFrame 对象。使用电子表格应用程序创建的 .csv 文件、通过 web API 接收的有效 JSON 数据或通过套接字接收的有效 pickle 对象都可以通过将它们转换为 DataFrame 对象来进一步处理。

从不受信任的来源加载 pickled 数据可能是不安全的。请谨慎使用 read_pickle()。你可以在这里找到更多详细信息:docs.python.org/3/library/pickle.html。如果你在之前的示例中使用此函数的 pickle 文件,那么使用 read_pickle() 是完全安全的。

准备工作

在开始此示例之前,请确保你已经按照上一个示例的步骤进行了操作。

如何操作…

执行以下步骤来完成这个示例:

  1. 通过读取 CSV 文件创建一个 DataFrame 对象:
>>> pandas.read_csv('dataframe.csv')

我们得到以下输出:

            timestamp    open    high     low   close volume
0 2019-11-13 09:00:00 71.8075 71.8450 71.7775 71.7925 219512
1 2019-11-13 09:15:00 71.7925 71.8000 71.7800 71.7925  59252
2 2019-11-13 09:30:00 71.7925 71.8125 71.7600 71.7625  57187
3 2019-11-13 09:45:00 71.7600 71.7650 71.7350 71.7425  43048
4 2019-11-13 10:00:00 71.7425 71.7800 71.7425 71.7775  45863
5 2019-11-13 10:15:00 71.7750 71.8225 71.7700 71.8150  42460
6 2019-11-13 10:30:00 71.8150 71.8300 71.7775 71.7800  62403
7 2019-11-13 10:45:00 71.7750 71.7875 71.7475 71.7525  34090
8 2019-11-13 11:00:00 71.7525 71.7825 71.7475 71.7625  39320
9 2019-11-13 11:15:00 71.7625 71.7925 71.7600 71.7875  20190
  1. 通过读取 JSON 字符串创建一个 DataFrame 对象:
>>> pandas.read_json("""{
        "timestamp": {
            "0":"13-11-2019 09:00:00", "1":"13-11-2019 09:15:00", 
            "2":"13-11-2019 09:30:00","3":"13-11-2019 09:45:00", 
            "4":"13-11-2019 10:00:00","5":"13-11-2019 10:15:00",
            "6":"13-11-2019 10:30:00","7":"13-11-2019 10:45:00",
            "8":"13-11-2019 11:00:00","9":"13-11-2019 11:15:00"},

        "open":{
            "0":71.8075,"1":71.7925,"2":71.7925,"3":71.76,
            "4":71.7425,"5":71.775,"6":71.815,"7":71.775,
            "8":71.7525,"9":71.7625},

        "high":{
            "0":71.845,"1":71.8,"2":71.8125,"3":71.765,"4":71.78,
            "5":71.8225,"6":71.83,"7":71.7875,"8":71.7825,
            "9":71.7925},

        "low":{
            "0":71.7775,"1":71.78,"2":71.76,"3":71.735,"4":71.7425,
            "5":71.77,"6":71.7775,"7":71.7475,"8":71.7475,
            "9":71.76},

        "close":{
            "0":71.7925,"1":71.7925,"2":71.7625,"3":71.7425,
            "4":71.7775,"5":71.815,"6":71.78,"7":71.7525,
            "8":71.7625,"9":71.7875},

        "volume":{
            "0":219512,"1":59252,"2":57187,"3":43048,"4":45863,
            "5":42460,"6":62403,"7":34090,"8":39320,"9":20190}}
            """)

我们得到以下输出:

            timestamp    open    high     low   close volume
0 2019-11-13 09:00:00 71.8075 71.8450 71.7775 71.7925 219512
1 2019-11-13 09:15:00 71.7925 71.8000 71.7800 71.7925  59252
2 2019-11-13 09:30:00 71.7925 71.8125 71.7600 71.7625  57187
3 2019-11-13 09:45:00 71.7600 71.7650 71.7350 71.7425  43048
4 2019-11-13 10:00:00 71.7425 71.7800 71.7425 71.7775  45863
5 2019-11-13 10:15:00 71.7750 71.8225 71.7700 71.8150  42460
6 2019-11-13 10:30:00 71.8150 71.8300 71.7775 71.7800  62403
7 2019-11-13 10:45:00 71.7750 71.7875 71.7475 71.7525  34090
8 2019-11-13 11:00:00 71.7525 71.7825 71.7475 71.7625  39320
9 2019-11-13 11:15:00 71.7625 71.7925 71.7600 71.7875  20190
  1. 通过取消反序列化 df.pickle 文件来创建一个 DataFrame 对象:
>>> pandas.read_pickle('df.pickle')

我们得到以下输出:

            timestamp    open    high     low   close volume
0 2019-11-13 09:00:00 71.8075 71.8450 71.7775 71.7925 219512
1 2019-11-13 09:15:00 71.7925 71.8000 71.7800 71.7925  59252
2 2019-11-13 09:30:00 71.7925 71.8125 71.7600 71.7625  57187
3 2019-11-13 09:45:00 71.7600 71.7650 71.7350 71.7425  43048
4 2019-11-13 10:00:00 71.7425 71.7800 71.7425 71.7775  45863
5 2019-11-13 10:15:00 71.7750 71.8225 71.7700 71.8150  42460
6 2019-11-13 10:30:00 71.8150 71.8300 71.7775 71.7800  62403
7 2019-11-13 10:45:00 71.7750 71.7875 71.7475 71.7525  34090
8 2019-11-13 11:00:00 71.7525 71.7825 71.7475 71.7625  39320
9 2019-11-13 11:15:00 71.7625 71.7925 71.7600 71.7875  20190

工作原理如下...

步骤 1 中,你使用 pandas.read_csv() 函数从 .csv 文件创建一个 DataFrame 对象。你将 dataframe.csv,即 .csv 文件应该读取的文件路径,作为参数传递。回想一下,在前一个示例的 步骤 1 中创建了 dataframe.csv

第 2 步中,你使用pandas.read_json()函数从有效的 JSON 字符串创建一个DataFrame对象。你将前一个示例中第 2 步的输出的 JSON 字符串作为此函数的参数传递。

第 3 步中,你使用pandas.read_pickle()方法从pickle文件创建一个DataFrame对象。你将df.pickle,即 pickle 文件应该读取的文件路径,作为此函数的参数传递。回忆一下,你在前一个示例的第 3 步中创建了df.pickle

如果你遵循了前一个示例,那么所有三个步骤的输出都将是相同的DataFrame对象。这与前一个示例中的df完全相同。

方法read_csv()read_json()read_pickle()可以接受比本示例中显示的更多的可选参数。请参考官方文档以获取有关这些方法的完整信息。

第二章:股票市场-交易入门

在构建算法交易系统时,有必要与提供用于程序化下单和查询交易的 API 的现代经纪人开设账户。这样我们就可以通过我们的 Python 脚本控制经纪账户,而传统上是通过经纪人的网站手动操作经纪账户,这个 Python 脚本将成为我们更大型算法交易系统的一部分。本章演示了一些基本的配方,介绍了开发完整的算法交易系统所需的基本经纪 API 调用。

本章涵盖以下配方:

  • 设置 Python 与经纪人的连接

  • 查询工具列表

  • 获取工具

  • 查询交易所列表

  • 查询段列表

  • 了解经纪人支持的其他属性

  • 下单普通订单

  • 下单布林订单

  • 下单交割订单

  • 下单当日订单

  • 查询保证金和资金

  • 计算经纪费

  • 计算政府征收的税费

让我们开始吧!

技术要求

要成功执行本章中的示例,您将需要以下内容:

  • Python 3.7+

  • Python 的pyalgotrading包($ pip install pyalgotrading

本章的最新 Jupyter 笔记本可以在 GitHub 上找到:github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/tree/master/Chapter02

本章演示了现代经纪人ZERODHA的 API,它受到pyalgotrading的支持。您也可以选择其他由pyalgotrading支持的经纪人。本章中的配方对于任何其他经纪人应该大致相同。pyalgotrading包将经纪 API 抽象为统一的接口,因此您不需要担心底层经纪 API 调用。

要设置与ZERODHA的经纪账户,请参阅附录 I中提供的详细步骤。

设置 Python 与经纪人的连接

设置与经纪人的连接的第一步是获取 API 密钥。经纪人通常为每个客户提供唯一的密钥,通常是作为api-keyapi-secret密钥对。这些 API 密钥通常是收费的,通常按月订阅。在开始本配方之前,您需要从经纪人的网站获取api-keyapi-secret的副本。有关更多详细信息,请参阅附录 I

怎么做…

我们执行以下步骤来完成此配方:

  1. 导入必要的模块:
>>> from pyalgotrading.broker.broker_connection_zerodha import BrokerConnectionZerodha
  1. 从经纪人那里获取api_keyapi_secret密钥。这些对您是唯一的,并将由经纪人用于识别您的 Demat 账户:
>>> api_key = "<your-api-key>"
>>> api_secret = "<your-api-secret>"
>>> broker_connection = BrokerConnectionZerodha(api_key, api_secret)

您将获得以下结果:

Installing package kiteconnect via pip...
Please login to this link to generate your request token: https://kite.trade/connect/login?api_key=<your-api-key>&v=3
  1. 从上述网址获取请求令牌:
>>> request_token = "<your-request-token>"
>>> broker_connection.set_access_token(request_token)

工作原理…

第一步 中,你从 pyalgotrading 导入 BrokerConnectionZerodha 类。BrokerConnectionZerodha 类提供了围绕特定经纪人 API 的抽象。对于 第二步,你需要经纪人的 API 密钥和 API 秘密。如果你没有它们,请参考 附录 I 获取详细的说明和带有截图的指导。在 第二步 中,你将你的 API 密钥和 API 秘密分配给新的 api_keyapi_secret 变量,并使用它们创建 broker_connectionBrokerConnectionZerodha 类的一个实例。如果你是第一次运行这个程序并且没有安装 kiteconnectpyalgotrading 将自动为你安装它。(kiteconnect 是官方的 Python 包,用于与 Zerodha 后端通信;BrokerConnectionZerodha 是在 kiteconnect 之上的一个封装。)第二步 生成一个登录链接。在这里,你需要点击链接并使用你的 Zerodha 凭据登录。如果认证过程成功,你将在浏览器地址栏中看到一个类似以下的链接:

https://127.0.0.1/?request_token=&action=login&status=success

例如,完整的链接将如下所示:

https://127.0.0.1/?request_token=H06I6Ydv95y23D2Dp7NbigFjKweGwRP7&action=login&status=success

复制字母数字令牌,H06I6Ydv95y23D2Dp7NbigFjKweGwRP7,并将其粘贴到 第三步request_token 中。broker_connection 实例现在已准备好执行 API 调用。

查询一组工具

一旦 broker_connection 句柄准备好,它就可以用来查询包含经纪人提供的所有金融工具的列表。

准备工作

确保 broker_connection 对象在你的 Python 命名空间中可用。请参考本章中前一个配方设置此对象。

如何做…

我们执行以下步骤来完成这个配方:

  1. 显示所有工具:
>>> instruments = broker_connection.get_all_instruments()
>>> instruments

你将会得到类似以下的输出。对你来说,确切的输出可能会有所不同:

  instrument_token exchange_token tradingsymbol name last_price expiry strike tick_size lot_size instrument_type segment exchange
0 267556358 1045142 EURINR20AUGFUT EURINR 0.0 2020-08-27 0.0 0.0025 1 FUT BCD-FUT BCD
1 268660998 1049457 EURINR20DECFUT EURINR 0.0 2020-12-29 0.0 0.0025 1 FUT BCD-FUT BCD
2 266440966 1040785 EURINR20JULFUT EURINR 0.0 2020-07-29 0.0 0.0025 1 FUT BCD-FUT BCD
3 266073606 1039350 EURINR20JUNFUT EURINR 0.0 2020-06-26 0.0 0.0025 1 FUT BCD-FUT BCD
4 265780742 1038206 EURINR20MAYFUT EURINR 0.0 2020-05-27 0.0 0.0025 1 FUT BCD-FUT BCD
... ... ... ... ... ... ... ... ... ... ... ... ...
64738 978945 3824 ZODJRDMKJ ZODIAC JRD-MKJ 0.0 0.0 0.0500 1 EQ NSE NSE
64739 2916865 11394 ZOTA ZOTA HEALTH CARE 0.0 0.0 0.0500 1 EQ NSE NSE
64740 7437825 29054 ZUARI-BE ZUARI AGRO CHEMICALS 0.0 0.0 0.0500 1 EQ NSE NSE
64741 979713 3827 ZUARIGLOB ZUARI GLOBAL 0.0 0.0 0.0500 1 EQ NSE NSE
64742 4514561 17635 ZYDUSWELL ZYDUS WELLNESS 0.0 0.0 0.0500 1 EQ NSE NSE

64743 rows × 12 columns
  1. 打印工具总数:
>>> print(f'Total instruments: {len(instruments)}')

我们得到以下输出(你的输出可能会有所不同):

Total instruments: 64743

工作原理…

第一步使用 broker_connectionget_all_instruments() 方法获取所有可用的金融工具。此方法返回一个 pandas.DataFrame 对象。此对象分配给一个新变量 instruments,显示在 第一步 的输出中。由于经常添加新的金融工具并定期过期现有的工具,这个输出可能对你来说有所不同。最后一步显示了经纪人提供的工具总数。

关于前面的 API 调用返回的数据的解释将在第三章中深入讨论,分析金融数据。对于这个配方,知道如何获取工具列表的方法就足够了。

获取一个工具

工具,也称为金融工具证券,是可以在交易所交易的资产。在交易所中,可以有数万种工具。本示例演示了如何根据其交易所交易符号获取工具。

准备就绪

确保 broker_connection 对象在你的 Python 命名空间中可用。请参考本章第一个示例设置此对象。

怎么做…

获取特定交易符号和交易所的工具:

>>> broker_connection.get_instrument(segment='NSE', tradingsymbol='TATASTEEL')

你将得到以下输出:

segment: NSE
exchange: NSE
tradingsymbol: TATASTEEL
broker_token: 895745
tick_size: 0.05
lot_size: 1
expiry: 
strike_price: 0.0

工作原理…

broker_connection 对象提供了一个方便的方法,get_instrument,用于获取任何金融工具。在返回工具之前,它以 segmenttradingsymbol 为属性。返回对象是 Instrument 类的一个实例。

查询交易所列表

交易所 是一个交易工具交易的市场。交易所确保交易过程公平且始终按规则进行。通常,经纪人支持多个交易所。本示例演示了如何查找经纪人支持的交易所列表。

准备就绪

确保 instruments 对象在你的 Python 命名空间中可用。请参考本章第二个示例以了解如何设置此对象。

怎么做…

显示经纪人支持的交易所:

>>> exchanges = instruments.exchange.unique()
>>> print(exchanges)

你将得到以下输出:

['BCD' 'BSE' 'NSE' 'CDS' 'MCX' 'NFO']

工作原理…

instruments.exchange 返回一个 pandas.Series 对象。其 unique() 方法返回一个由经纪人支持的唯一交易所组成的 numpy.ndarray 对象。

查询分段列表

一个分段实质上是根据其类型对工具进行分类。在交易所中常见的各种分段类型包括现金/股票、期货、期权、大宗商品和货币。每个分段可能有不同的运营时间。通常,经纪人支持多个交易所内的多个分段。本示例演示了如何查找经纪人支持的分段列表。

准备就绪

确保 instruments 对象在你的 Python 命名空间中可用。请参考本章第二个示例以了解如何设置此对象。

怎么做…

显示经纪人支持的分段:

>>> segments = instruments.segment.unique()
>>> print(segments)

你将得到以下输出:

['BCD-FUT' 'BCD' 'BCD-OPT' 'BSE' 'INDICES' 'CDS-FUT' 'CDS-OPT' 'MCX-FUT' 'MCX-OPT' 'NFO-OPT' 'NFO-FUT' 'NSE']

工作原理…

instruments.segment 返回一个 pandas.Series 对象。它的 unique 方法返回一个由经纪人支持的唯一分段组成的 numpy.ndarray 对象。

了解经纪人支持的其他属性

为了下订单,需要以下属性:订单交易类型、订单种类、订单类型和订单代码。不同的经纪人可能支持不同类型的订单属性。例如,一些经纪人可能仅支持普通订单,而其他经纪人可能支持普通订单和止损订单。可以使用 pyalgotrading 包提供的经纪人特定常量查询经纪人支持的每个属性的值。

如何做…

我们执行以下步骤来完成此配方:

  1. pyalgotrading模块中导入必要的类:
>>> from pyalgotrading.broker.broker_connection_zerodha import BrokerConnectionZerodha
  1. 列出订单交易类型:
>>> list(BrokerConnectionZerodha.ORDER_TRANSACTION_TYPE_MAP.keys())

我们将得到以下输出:

[<BrokerOrderTransactionTypeConstants.BUY: 'BUY'>,
 <BrokerOrderTransactionTypeConstants.SELL: 'SELL'>]
  1. 列出订单品种:
>>> list(BrokerConnectionZerodha.ORDER_VARIETY_MAP.keys())

我们将得到以下输出:

[<BrokerOrderVarietyConstants.MARKET: 'ORDER_VARIETY_MARKET'>,
 <BrokerOrderVarietyConstants.LIMIT: 'ORDER_VARIETY_LIMIT'>,
 <BrokerOrderVarietyConstants.STOPLOSS_LIMIT: 'ORDER_VARIETY_STOPLOSS_LIMIT'>,
 <BrokerOrderVarietyConstants.STOPLOSS_MARKET: 'ORDER_VARIETY_STOPLOSS_MARKET'>]
  1. 列出订单类型:
>>> list(BrokerConnectionZerodha.ORDER_TYPE_MAP.keys())

我们将得到以下输出:

[<BrokerOrderTypeConstants.REGULAR: 'ORDER_TYPE_REGULAR'>,
 <BrokerOrderTypeConstants.BRACKET: 'ORDER_TYPE_BRACKET'>,
 <BrokerOrderTypeConstants.COVER: 'ORDER_TYPE_COVER'>,
 <BrokerOrderTypeConstants.AMO: 'ORDER_TYPE_AFTER_MARKET_ORDER'>]
  1. 列出订单代码:
>>> list(BrokerConnectionZerodha.ORDER_CODE_MAP.keys())

我们将得到以下输出:

[<BrokerOrderCodeConstants.INTRADAY: 'ORDER_CODE_INTRADAY'>,
 <BrokerOrderCodeConstants.DELIVERY: 'ORDER_CODE_DELIVERY_T0'>]

它是如何工作的…

步骤 1 中,我们从pyalgotrading导入BrokerConnectionZerodha类。此类保存了pyalgotrading和特定经纪人常量之间的订单属性映射,作为字典对象。接下来的步骤获取并打印这些映射。步骤 2 显示您的经纪人支持BUYSELL订单交易类型。

步骤 3 显示您的经纪人支持MARKETLIMITSTOPLOSS_LIMITSTOPLOSS_MARKET订单品种。步骤 4 显示您的经纪人支持REGULARBRACKETCOVERAFTER_MARKET订单类型。步骤 5 显示您的经纪人支持INTRADAYDELIVERY订单代码。

输出可能因经纪人而异,因此如果您使用不同的经纪人,请查阅您的经纪人文档。所有这些类型参数的详细解释将在第六章 在交易所下订单 中涵盖。本配方仅概述这些参数,因为它们在本章后续配方中需要。

放置一个简单的常规订单

本配方演示了如何通过经纪人在交易所上放置REGULAR订单。REGULAR订单是最简单的订单类型。尝试完此配方后,通过登录经纪人网站检查您的经纪人账户;您会发现一个订单已经被放置在那里。您可以将订单 ID 与本配方中显示的最后一个代码片段中返回的订单 ID 匹配。

准备就绪

确保broker_connection对象在你的 Python 命名空间中可用。参考本章第一个配方,了解如何设置此对象。

如何做…

我们执行以下步骤来完成此配方:

  1. pyalgotrading中导入必要的常量:
>>> from pyalgotrading.constants import *
  1. 获取特定交易符号和交易所的金融工具:
>>> instrument = broker_connection.get_instrument(segment='NSE', 
                                        tradingsymbol='TATASTEEL')
  1. 放置一个简单的常规订单 - 一个BUYREGULARINTRADAYMARKET订单:
>>> order_id = broker_connection.place_order(
                   instrument=instrument, 
                   order_transaction_type= \
                       BrokerOrderTransactionTypeConstants.BUY,
                   order_type=BrokerOrderTypeConstants.REGULAR, 
                   order_code=BrokerOrderCodeConstants.INTRADAY,
                   order_variety= \
                       BrokerOrderVarietyConstants.MARKET, 
                   quantity=1)
>>> order_id

我们将得到以下输出:

191209000001676

它是如何工作的…

步骤 1 中,您从pyalgotrading导入常量。在步骤 2 中,您使用broker_connectionget_instrument()方法以segment = 'NSE'tradingsymbol = 'TATASTEEL'获取金融工具。在步骤 3 中,您使用broker_connectionplace_order()方法放置一个REGULAR订单。place_order()方法接受的参数描述如下:

  • instrument:必须放置订单的金融工具。应该是Instrument类的实例。您在这里传递instrument

  • order_transaction_type: 订单交易类型。应为BrokerOrderTransactionTypeConstants类型的枚举。在这里,你传递了BrokerOrderTransactionTypeConstants.BUY

  • order_type: 订单类型。应为BrokerOrderTypeConstants类型的枚举。在这里,你传递了BrokerOrderTypeConstants.REGULAR

  • order_code: 订单代码。应为BrokerOrderCodeConstants类型的枚举。在这里,你传递了BrokerOrderCodeConstants.INTRADAY

  • order_variety: 订单种类。应为BrokerOrderVarietyConstants类型的枚举。在这里,你传递了BrokerOrderVarietyConstants.MARKET

  • quantity: 要交易的股票数量。应为正整数。我们在这里传递了1

如果订单放置成功,该方法将返回一个订单 ID,您可以随时以后用于查询订单状态。

不同类型参数的详细解释将在第六章中介绍,在交易所上下订单。这个配方旨在让你了解如何下达REGULAR订单,这是各种可能订单类型之一的想法。

下达一个简单的 BRACKET 订单

这个配方演示了如何通过经纪人在交易所上下达一个BRACKET订单。BRACKET订单是两腿订单。一旦第一个订单执行完毕,经纪人会自动下达两个新订单 – 一个STOPLOSS订单和一个TARGET订单。在任何时候只有一个订单被执行;当第一个订单完成时,另一个订单将被取消。在尝试了此配方后,通过登录经纪人的网站,您可以在您的经纪账户中找到已下达的订单。您可以将订单 ID 与本配方中显示的最后一个代码片段中返回的订单 ID 进行匹配。

准备就绪

确保broker_connection对象在你的 Python 命名空间中可用。参考本章的第一个配方,学习如何设置此对象。

如何操作...

我们执行以下步骤完成此配方:

  1. 导入必要的模块:
>>> from pyalgotrading.constants import *
  1. 获取特定交易符号和交易所的工具:
>>> instrument = broker_connection.get_instrument(segment='NSE', 
                                        tradingsymbol='ICICIBANK')
  1. 获取工具的最新交易价格:
>>> ltp = broker_connection.get_ltp(instrument)
  1. 下达一个简单的BRACKET订单 – 一个BUYBRACKETINTRADAYLIMIT订单:
>>> order_id = broker_connection.place_order(
                   instrument=instrument,
                   order_transaction_type= \
                       BrokerOrderTransactionTypeConstants.BUY,
                   order_type=BrokerOrderTypeConstants.BRACKET, 
                   order_code=BrokerOrderCodeConstants.INTRADAY, 
                   order_variety=BrokerOrderVarietyConstants.LIMIT,
                   quantity=1, price=ltp-1, 
                   stoploss=2, target=2)
>>> order_id

我们将得到以下输出:

191212001268839

如果在执行此代码时收到以下错误,则意味着由于市场波动性较高,经纪人阻止了 Bracket 订单:

InputException: 由于市场预期波动性较高,Bracket 订单暂时被阻止。

当经纪人开始允许 Bracket 订单时,你应该稍后尝试该配方。你可以不时地查看经纪人网站以了解 Bracket 订单何时被允许。

工作原理...

步骤 1中,您从pyalgotrading导入常量。在步骤 2中,您使用broker_connectionget_instrument()方法获取segment = 'NSE'tradingsymbol = 'ICICBANK'的金融工具。在步骤 3中,您获取工具的最后交易价格LTP。(LTP 将在第三章的分析金融数据中更详细地解释。)在步骤 4中,您使用broker_connectionplace_order()方法放置一个BRACKET订单。place_order()方法接受的参数的描述如下:

  • instrument: 必须放置订单的金融工具。应该是Instrument类的实例。你在这里传递instrument

  • order_transaction_type: 订单交易类型。应该是BrokerOrderTransactionTypeConstants类型的枚举。你在这里传递BrokerOrderTransactionTypeConstants.BUY

  • order_type: 订单类型。应该是BrokerOrderTypeConstants类型的枚举。你在这里传递BrokerOrderTypeConstants.BRACKET

  • order_code: 订单代码。应该是BrokerOrderCodeConstants类型的枚举。你在这里传递BrokerOrderCodeConstants.INTRADAY

  • order_variety: 订单种类。应该是BrokerOrderVarietyConstants类型的枚举。你在这里传递BrokerOrderVarietyConstants.LIMIT

  • quantity: 给定工具要交易的股份数量。应为正整数。你在这里传递1

  • price: 应该放置订单的限价。你在这里传递ltp-1,这意味着低于ltp值的 1 个单位价格。

  • stoploss: 初始订单价格的价格差,应该放置止损订单的价格。应为正整数或浮点值。你在这里传递2

  • target: 初始价格的价格差,应该放置目标订单的价格。应为正整数或浮点值。你在这里传递2

如果订单放置成功,该方法会返回一个订单 ID,您可以随时稍后用于查询订单的状态。

对不同类型参数的详细解释将在第六章中进行,在交易所上放置交易订单。本示例旨在向您展示如何放置BRACKET订单,这是各种可能订单类型之一。

放置一个简单的 DELIVERY 订单

此示例演示了如何通过经纪人在交易所下达 DELIVERY 订单。DELIVERY 订单将传递到用户的 Demat 账户,并存在直到用户明确平仓为止。在交易会话结束时由交货订单创建的仓位将转移到下一个交易会话。它们不会由经纪人明确平仓。尝试完这个示例后,通过登录经纪人的网站检查你的经纪账户;你会发现已经有一个订单被下达了。你可以将订单 ID 与此示例中最后显示的代码片段返回的订单 ID 进行匹配。

准备工作

确保在你的 Python 命名空间中可用 broker_connection 对象。请参考本章第一个示例来学习如何设置此对象。

操作方法…

我们执行以下步骤来完成此示例:

  1. 导入必要的模块:
>>> from pyalgotrading.constants import *
  1. 获取特定交易符号和交易所的金融工具:
>>> instrument = broker_connection.get_instrument(segment='NSE', 
                                        tradingsymbol='AXISBANK')
  1. 下达一个简单的 DELIVERY 订单 - 一个 SELLREGULARDELIVERYMARKET 订单:
>>> order_id = broker_connection.place_order(
                   instrument=instrument,
                   order_transaction_type= \
                       BrokerOrderTransactionTypeConstants.SELL,
                   order_type=BrokerOrderTypeConstants.REGULAR,
                   order_code=BrokerOrderCodeConstants.DELIVERY,
                   order_variety= \
                       BrokerOrderVarietyConstants.MARKET, 
                    quantity=1)
>>> order_id

我们将得到以下输出:

191212001268956

工作原理…

第 1 步 中,你从 pyalgotrading 导入常量。在 第 2 步 中,你使用 broker_connectionget_instrument() 方法,通过 segment = 'NSE'tradingsymbol = 'AXISBANK' 获取金融工具。在 第 3 步 中,你使用 broker_connectionplace_order() 方法下达 DELIVERY 订单。此方法接受以下参数:

  • instrument:必须下订单的金融工具。应该是 Instrument 类的实例。你在这里传递 instrument

  • order_transaction_type:订单交易类型。应该是 BrokerOrderTransactionTypeConstants 类型的枚举。你在这里传递 BrokerOrderTransactionTypeConstants.SELL

  • order_type:订单类型。应该是 BrokerOrderTypeConstants 类型的枚举。你在这里传递 BrokerOrderTypeConstants.REGULAR

  • order_code:订单代码。应该是 BrokerOrderCodeConstants 类型的枚举。你在这里传递 BrokerOrderCodeConstants.DELIVERY

  • order_variety:订单类型。应该是 BrokerOrderVarietyConstants 类型的枚举。你在这里传递 BrokerOrderVarietyConstants.MARKET

  • quantity: 要为给定金融工具交易的股票数量。应该是正整数。我们在这里传递 1

如果订单下达成功,该方法会返回一个订单 ID,你随时可以使用它查询订单的状态。

关于不同类型参数的详细解释将在 第六章 在交易所上下达交易订单 中介绍。此示例旨在让你了解如何下达 DELIVERY 订单,这是各种可能订单中的一种。

下达一个简单的 INTRADAY 订单

此配方演示如何通过经纪人 API 下达 INTRADAY 订单。INTRADAY 订单不会传送到用户的 Demat 账户。由日内订单创建的头寸具有一天的生命周期。这些头寸在交易会话结束时由经纪人明确平仓,并不转入下一个交易会话。尝试完此配方后,通过登录经纪人网站查看您的经纪账户;您会发现已经有了一个订单。您可以将订单 ID 与此配方中显示的最后一个代码片段返回的订单 ID 进行匹配。

准备工作

确保 broker_connection 对象在您的 Python 命名空间中可用。请参考本章的第一个配方,了解如何设置此对象。

如何操作…

我们执行以下步骤来完成此配方:

  1. 导入必要的模块:
>>> from pyalgotrading.constants import *
  1. 获取特定交易符号和交易所的工具:
>>> instrument = broker_connection.get_instrument(segment='NSE', 
                                        tradingsymbol='HDFCBANK')
  1. 获取工具的最近成交价:
>>> ltp = broker_connection.get_ltp(instrument)
  1. 下达一个简单的 INTRADAY 订单 —— 一个 SELLBRACKETINTRADAYLIMIT 订单:
>>> order_id = broker_connection.place_order(
                   instrument=instrument,
                   order_transaction_type= \
                       BrokerOrderTransactionTypeConstants.SELL,
                   order_type=BrokerOrderTypeConstants.BRACKET,
                   order_code=BrokerOrderCodeConstants.INTRADAY, 
                   order_variety=BrokerOrderVarietyConstants.LIMIT,
                   quantity=1, price=ltp+1, stoploss=2, target=2)
>>> order_id

我们将获得以下输出:

191212001269042

如果在执行此代码时出现以下错误,则意味着经纪人由于市场波动性较高而阻止了 Bracket 订单:

InputException: 由于市场预期波动率较高,Bracket 订单暂时被阻止。

当经纪人开始允许 Bracket 订单时,您应该稍后尝试此配方。您可以不时地在经纪人网站上查看更新,了解何时允许 Bracket 订单。

工作原理…

步骤 1 中,您从 pyalgotrading 导入常量。在 步骤 2 中,您使用 broker_connectionget_instrument() 方法通过 segment = 'NSE'tradingsymbol = 'HDFCBANK' 获取金融工具。在 步骤 3 中,您获取该工具的 LTP。(LTP 将在 第三章 的 金融工具的最近成交价 配方中详细解释。)在 步骤 4 中,您使用 broker_connectionplace_order() 方法下达 BRACKET 订单。place_order() 方法接受的参数描述如下:

  • instrument:必须下达订单的金融工具。应为 Instrument 类的实例。在这里传递 instrument

  • order_transaction_type:订单交易类型。应为 BrokerOrderTransactionTypeConstants 类型的枚举。在这里传递 BrokerOrderTransactionTypeConstants.SELL

  • order_type:订单类型。应为 BrokerOrderTypeConstants 类型的枚举。在这里传递 BrokerOrderTypeConstants.BRACKET

  • order_code:订单代码。应为 BrokerOrderCodeConstants 类型的枚举。在这里传递 BrokerOrderCodeConstants.INTRADAY

  • order_variety:订单种类。应为 BrokerOrderVarietyConstants 类型的枚举。在这里传递 BrokerOrderVarietyConstants.LIMIT

  • quantity:给定工具要交易的股票数量。应该是正整数。这里你传递了1

  • price:应该下订单的限价。这里你传递了ltp+1,表示高于ltp值的 1 个单位价格。

  • stoploss:与初始订单价格的价格差,应在该价格处放置止损订单。应该是正整数或浮点数值。这里你传递了2

  • target:与初始订单价格的价格差,应在该价格处放置目标订单。应该是正整数或浮点数值。这里你传递了2

如果下单成功,该方法将返回一个订单 ID,您随时可以在以后的任何时间使用它来查询订单的状态。

不同类型参数的详细解释将在第六章,在交易所下订单 中介绍。本示例旨在让您了解如何下达 INTRADAY 订单,这是各种可能订单类型之一。

查询保证金和资金

在下单之前,重要的是要确保您的经纪账户中有足够的保证金和资金可用以成功下单。资金不足会导致经纪拒绝任何下单,这意味着其他人将永远不会在交易所下单。本示例向您展示了如何随时查找您的经纪账户中可用的保证金和资金。

准备就绪

确保 broker_connection 对象在您的 Python 命名空间中可用。请参考本章的第一个示例来学习如何设置它。

如何操作…

我们执行以下步骤完成此示例:

  1. 显示股票保证金:
>>> equity_margins = broker_connection.get_margins('equity')
>>> equity_margins

我们将得到以下输出(您的输出可能有所不同):

{'enabled': True,
 'net': 1623.67,
 'available': {'adhoc_margin': 0,
  'cash': 1623.67,
  'opening_balance': 1623.67,
  'live_balance': 1623.67,
  'collateral': 0,
  'intraday_payin': 0},
 'utilised': {'debits': 0,
  'exposure': 0,
  'm2m_realised': 0,
  'm2m_unrealised': 0,
  'option_premium': 0,
  'payout': 0,
  'span': 0,
  'holding_sales': 0,
  'turnover': 0,
  'liquid_collateral': 0,
  'stock_collateral': 0}}
  1. 显示股票资金:
>>> equity_funds = broker_connection.get_funds('equity')
>>> equity_funds

我们将得到以下输出(您的输出可能有所不同):

1623.67
  1. 显示商品保证金:
>>> commodity_margins = get_margins(commodity')
>>> commodity_margins

我们将得到以下输出(您的输出可能有所不同):

{'enabled': True,
 'net': 16215.26,
 'available': {'adhoc_margin': 0,
  'cash': 16215.26,
  'opening_balance': 16215.26,
  'live_balance': 16215.26,
  'collateral': 0,
  'intraday_payin': 0},
 'utilised': {'debits': 0,
  'exposure': 0,
  'm2m_realised': 0,
  'm2m_unrealised': 0,
  'option_premium': 0,
  'payout': 0,
  'span': 0,
  'holding_sales': 0,
  'turnover': 0,
  'liquid_collateral': 0,
  'stock_collateral': 0}}
  1. 显示商品资金:
>>> commodity_funds = broker_connection.get_funds('commodity')
>>> commodity_funds

我们将得到以下输出(您的输出可能有所不同):

0

工作原理…

broker_connection对象提供了用于获取经纪账户可用保证金和资金的方法:

  • get_margins()

  • get_funds()

经纪公司 Zerodha 分别跟踪 equitycommodity 产品的保证金和资金。如果您使用的是 pyalgotrading 支持的其他经纪公司,则可能会将资金和保证金分别跟踪 equitycommodity

步骤 1 展示了如何使用broker_connection对象的get_margins()方法查询equity产品的保证金,参数为equity步骤 2 展示了如何使用broker_connection对象的get_funds()方法查询equity产品的资金,参数为equity字符串。

步骤 34 展示了如何查询以commodity字符串为参数的commodity产品的保证金和资金情况。

计算收取的佣金

每次成功完成的订单,经纪人可能会收取一定的费用,这通常是买卖工具价格的一小部分。虽然金额看似不大,但重要的是要跟踪佣金,因为它最终可能会吃掉你一天结束时的可观利润的一大部分。

收取的佣金因经纪人而异,也因交易段而异。针对这个方案,我们将考虑佣金为 0.01%。

如何做…

我们执行以下步骤完成这个方案:

  1. 计算每笔交易收取的佣金:
>>> entry_price = 1245
>>> brokerage = (0.01 * 1245)/100
>>> print(f'Brokerage charged per trade: {brokerage:.4f}')

我们将获得以下输出:

Brokerage charged per trade: 0.1245
  1. 计算 10 笔交易的总佣金:
>>> total_brokerage = 10 * (0.01 * 1245) / 100
>>> print(f'Total Brokerage charged for 10 trades: \
            {total_brokerage:.4f}')

我们将获得以下输出:

Total Brokerage charged for 10 trades: 1.2450

工作原理…

第 1 步 中,我们从交易买入或卖出的价格entry_price开始。对于这个方案,我们使用了1245。接下来,我们计算价格的 0.01%,即0.1245。然后,我们计算 10 笔这样的交易的总佣金,结果为10 * 0.1245 = 1.245

每个订单,佣金都会收取两次。第一次是当订单进入持仓时,而第二次是当订单退出持仓时。要获取所收取的佣金的确切细节,请参考您的经纪人提供的费用清单。

计算收取的政府税费

对于每个成功完成的订单,政府可能会收取一定的费用,这是买卖工具价格的一小部分。虽然金额看似不大,但重要的是要跟踪政府税费,因为它最终可能会吃掉你一天结束时的可观利润的一大部分。

政府的收费取决于交易所的位置,并且从一个交易段到另一个交易段都有所不同。针对这个方案,我们将考虑政府税费的费率为 0.1%。

如何做…

我们执行以下步骤完成这个方案:

  1. 计算每笔交易收取的政府税费:
>>> entry_price = 1245
>>> brokerage = (0.1 * 1245)/100
>>> print(f'Government taxes charged per trade: {brokerage:.4f}')

我们将获得以下输出:

Government taxes charged per trade: 1.2450
  1. 计算 10 笔交易收取的总政府税费:
>>> total_brokerage = 10 * (0.1 * 1245) / 100
>>> print(f'Total Government taxes charged for 10 trades: \
            {total_brokerage:.4f}')

我们将获得以下输出:

Total Government taxes charged for 10 trades: 12.4500

工作原理…

第 1 步 中,我们从交易买入或卖出的价格entry_price开始。对于这个方案,我们使用了1245。接下来,我们计算价格的 0.1%,即1.245。然后,我们计算 10 笔这样的交易的总佣金,结果为10 * 1.245 = 12.245

对于每个订单,政府税费会收取两次。第一次是当订单进入持仓时,而第二次是当订单退出持仓时。要获取所收取的政府税费的确切细节,请参考交易所提供的政府税费清单。

第三章:获取金融数据

拥有便捷的金融数据对于进行算法交易至关重要。 金融数据可以是静态的,也可以是动态的。 静态金融数据是在交易时间内不会改变的数据。 静态数据包括金融工具列表、金融工具属性、金融工具的限价和上一个交易日的收盘价格。 动态金融数据是在交易时间内可能持续变化的数据。 动态数据包括市场深度、最后交易价格、金融工具的时间和数量,以及当日的最高和最低价格。 本章包括获取各种类型金融数据的配方。

以下是本章的配方列表:

  • 获取金融工具列表

  • 金融工具的属性

  • 金融工具的到期日

  • 金融工具的限价

  • 金融工具的市场深度

  • 金融工具的总待买数量

  • 金融工具的总待卖数量

  • 金融工具当日的总成交量

  • 金融工具的最后交易价格

  • 金融工具的最后交易时间

  • 金融工具的最后交易数量

  • 金融工具当日的开盘价格

  • 金融工具当日的最高价格

  • 金融工具当日的最低价格

  • 金融工具上一个交易日的收盘价格

技术要求

您将需要以下内容才能成功执行本章的配方:

  • Python 3.7+

  • pyalgotrading Python 包($ pip install pyalgotrading

本章的最新 Jupyter 笔记本可以在 GitHub 上找到,网址为github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook

以下代码将帮助您与 Zerodha 建立经纪人连接,这将在本章中的所有配方中使用。 请确保在尝试任何配方之前已经按照这些步骤进行了操作。

与经纪人建立连接的第一步是获取 API 密钥。 经纪人会为每位客户提供唯一的密钥,通常是api-keyapi-secret密钥对。 这些 API 密钥通常按月订阅收费。 在开始之前,您需要从经纪人网站上获取您的api-keyapi-secret的副本。 您可以参考附录 I了解更多详细信息。

执行以下步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.broker.broker_connection_zerodha import BrokerConnectionZerodha
  1. 从经纪人处获取api_keyapi_secret密钥。 这些是您独有的,经纪人将用它们来识别您的证券账户:
>>> api_key = "<your-api-key>"
>>> api_secret = "<your-api-secret>"
>>> broker_connection = BrokerConnectionZerodha(api_key, \
                                                api_secret)

您将获得以下输出:

Installing package kiteconnect via pip...
Please login to this link to generate your request token: https://kite.trade/connect/login?api_key=<your-api-key>&v=3

如果您是第一次运行此程序,且未安装kiteconnectpyalgotrading会自动为您安装它。第 2 步 的最终输出将是一个链接。点击链接并使用您的 Zerodha 凭据登录。如果身份验证成功,您将在浏览器地址栏中看到一个类似于以下内容的链接:https://127.0.0.1/?request_token=<alphanumeric-token>&action=login&status=success

以下是一个示例:

https://127.0.0.1/?request_token=H06I6Ydv95y23D2Dp7NbigFjKweGwRP7&action=login&status=success
  1. 复制字母数字令牌并粘贴到request_token中:
>>> request_token = "<your-request-token>"
>>> broker_connection.set_access_token(request_token)

broker_connection实例现已准备好执行 API 调用。

pyalgotrading包支持多个经纪人,并为每个经纪人提供一个连接对象类,具有相同的方法。它将经纪人 API 抽象在统一接口后面,因此您不必担心底层经纪人 API 调用,可以直接使用本章中的所有示例。设置经纪人连接的程序将因经纪人而异。如果您不是使用 Zerodha 作为经纪人,则可以参考 pyalgotrading 文档设置经纪人连接。对于 Zerodha 用户,之前的步骤就足够了。

获取金融工具列表

金融工具,也被称为证券,是可以在交易所交易的资产。在交易所中,可能有成千上万种金融工具。金融工具列表在静态性质上是不变的,因为它在实时交易时段不会改变。金融工具可能会随时改变,但绝不会在同一天内发生变化。拥有这些数据是算法交易的第一步。本文介绍如何获取金融工具列表。

准备工作

确保在 Python 命名空间中有 broker_connection 对象可用。请参考本章的技术要求部分进行设置。

如何做...

使用 broker_connection 获取并显示所有可用的金融工具:

>>> instruments = broker_connection.get_all_instruments()
>>> instruments

我们得到了以下输出(您的输出可能有所不同):

工作原理...

本文使用 broker_connectionget_all_instruments() 方法获取所有可用的金融工具列表,该方法返回一个 pandas.DataFrame 对象。此对象被赋值给一个新属性instruments,并在输出中显示。由于经常添加新的金融工具并定期更新现有的金融工具,因此此输出可能与您的输出不同。

金融工具的属性

金融工具具有各种属性,可以更深入地了解该工具,例如交易代码、交易所、交易段、最小变动价位等等。在下单时,也需要一些这些属性。本文列出并解释了经纪人支持的所有属性。所有这些属性都是静态的,意味着它们在实时交易时段不会改变。

准备工作

确保instruments对象在你的 Python 命名空间中可用。参考本章的 获取金融工具列表 配方来设置它。

如何操作…

列出经纪人提供的金融工具的所有属性:

>>> list(instruments.columns)

我们得到以下输出:

['instrument_token',
 'exchange_token',
 trading-symbol,
 'name',
 'last_price',
 'expiry',
 'strike',
 'tick_size',
 'lot_size',
 'instrument_type',
 'segment',
 exchange]

工作原理…

获取金融工具列表 配方将所有工具作为 pandas.DataFrame 对象获取。调用其 columns 属性返回所有可用的列。每一列都是每个金融工具的属性。你可以在kite.trade/docs/connect/v3/market-quotes/#csv-response-columns找到更多细节。

金融工具的到期日

金融工具可能有固定的到期日,也可能没有。如果有,它们在到期日最后可用于交易。通常,现金段中的工具不会过期,而衍生品工具(来自期货和期权段的工具)具有短暂的有效期,并在指定日期到期。本配方展示了两种类型的工具以及如何获取它们的到期日期。到期日是静态数据,意味着在交易时间内不会更改。

准备工作

确保broker_connectioninstruments 对象在你的 Python 命名空间中可用。参考本章的 技术要求 部分来设置 broker_connection。参考本章的第一个配方来设置 instruments

如何操作…

我们对这个配方执行以下步骤:

  1. 使用broker_connection获取一个工具对象:
>>> instrument1 = broker_connection.get_instrument('NSE', 
                                                   'TATASTEEL')
  1. 检查并打印instrument1是否会到期:
>>> print(f'Instrument expires: {instrument1.will_expire()}')

我们得到以下输出:

Instrument expires: False
  1. 使用broker_connection获取另一个工具对象:
>>> instrument2 = broker_connection.get_instrument('NFO-FUT', 
                                                TATASTEEL20AUGFUT)

在这里你不应该得到任何输出。这意味着你已成功获取了这个工具。

请注意,如果在此步骤中正确输入后仍然获得以下输出,请参考本章 获取金融工具列表 配方的输出表格,使用最新可用的 NFO-FUT 段脚本重试此步骤:

ERROR: Instrument not found. Either it is expired and hence not available, or you have misspelled the "segment" and "tradingsymbol" parameters.

这可能是因为带有 tradingsymbol TATASTEEL20AUGFUT 的工具在编写本书时可用,但自那时起已经到期,因此不再可用。

  1. 检查并打印instrument2是否会到期:
>>> print(f'Instrument expires: {instrument2.will_expire()}')

我们得到以下输出:

Instrument expires: True
  1. 打印instrument2的到期日期:
>>> print(f'Expiry date: {instrument2.expiry}')

我们得到以下输出(您的输出可能不同):

Expiry date: 2020-08-27

工作原理…

步骤 1使用BrokerConnectionZerodha类的get_instrument()方法获取一个工具并将其分配给一个新属性instrument1。该对象是Instrument类的一个实例。调用get_instrument所需的两个参数是交易所(NSE)和交易符号(TATASTEEL)。在步骤 2中,我们使用will_expire()方法检查该工具是否将过期。此步骤的输出为False。我们在步骤 3步骤 4中重复相同的过程,这次是为了另一个工具,分配给一个新属性instrument2,该方法对will_expire()方法返回True。这显示在步骤 4的输出中。最后,在步骤 5中,我们使用expiry属性获取instrument2的到期日期。

金融工具的电路限制

每种金融工具都有明确定义的价格范围。预计该工具价格在当天的价格范围内。在市场交易时间内,如果工具价格突破价格范围的上限或下限,交易所可能会暂停该工具的交易一段时间或整天。这是为了防止工具价格在一天内的突然上涨或下跌。价格范围的上限称为上限电路限制,下限称为下限电路限制。这些数据是静态的,意味着在一天内不会改变。但是,它们在一天到另一天之间可能会发生显着变化。本配方帮助查找金融工具的电路限制。

准备工作

确保broker_connectioninstrument1对象在您的 Python 命名空间中可用。请参阅本章的技术要求部分设置broker_connection。请参阅本章的金融工具属性配方设置instrument1

如何操作…

获取并打印instrument1的下限和上限电路限制:

>>> lower_circuit_limit, upper_circuit_limit = \
                        broker_connection.get_circuit_limits(instrument1)
>>> print(f'Lower circuit limit: {lower_circuit_limit}')
>>> print(f'Upper circuit limit: {upper_circuit_limit}')

我们得到以下输出(您的输出可能有所不同):

Lower circuit limit: 315.9
Upper circuit limit: 386

工作原理…

BrokerConnectionZerodha类的get_circuit_limits()方法将给定金融工具的下限和上限电路限制作为元组获取。此方法将Instrument类型的对象作为参数。我们在这里使用instrument1作为参数。

金融工具的市场深度

金融工具的市场深度是市场上买方和卖方的数据的时间顺序列表。买方列表是买方愿意以哪些价格及其相应数量购买该工具的价格和数量列表。类似地,卖方列表是卖方愿意以哪些价格及其相应数量出售该工具的价格和数量列表。如果您对市场深度的概念还不熟悉,本配方的工作原理…部分的解释将更清晰地解释。

市场深度有助于预测某一金融工具价格的走向。它还有助于了解具有大数量的订单是否会显著改变价格。市场深度具有动态性,意味着在实时交易时段内不断变化。此示例帮助实时查找金融工具的市场深度。

准备工作

确保 broker_connectioninstrument1 对象在您的 Python 命名空间中可用。请参阅本章的 技术要求 部分设置 broker_connection。请参阅本章的 金融工具属性 部分设置 instrument1

如何做…

获取并打印 instrument1 的买入市场深度和卖出市场深度:

>>> buy_market_depth, sell_market_depth = \
                            broker_connection.get_market_depth(instrument1)
>>> print(f'Buy Market Depth:\n{buy_market_depth}')
>>> print(f'Sell Market Depth:\n{sell_market_depth}')

我们得到以下输出(您的输出可能会有所不同):

Buy Market Depth:
  orders  price quantity
0      1 350.05        1
1     16 350.00    43294
2      5 349.95     1250
3      8 349.90     3134
4      5 349.85     1078

Sell Market Depth:
  orders  price quantity
0      1 350.10       25
1      7 350.15     1367
2     13 350.20     4654
3     13 350.25     2977
4     21 350.30     5798

它是如何工作的…

BrokerConnectionZerodha 类的 get_market_depth() 方法获取给定金融工具的市场深度。此方法以 Instrument 类型的对象作为参数。在此处我们使用 instrument1 作为参数。市场深度显示为买方卖方的不同表格。

买入市场深度 是一个包含五个条目或竞标的表格,按价格降序排列。每个条目表示市场上某一时刻可用的买家,给出的价格以及该价格可用的数量。

卖出市场深度 是一个包含五个条目或竞标的表格,按价格升序排列。每个条目表示市场上某一时刻已存在的卖家,给出的价格以及该价格可用的数量。

当买家和卖家匹配时,订单在交易所执行,条目从买方和卖方表格中删除。

金融工具的总待买入数量

金融工具的总待买入数量是在某一时刻所有待买入订单数量的总和。此数据具有动态性,并且在实时交易时段内随时可能发生变化。

准备工作

确保 broker_connectioninstrument1 对象在您的 Python 命名空间中可用。请参阅本章的 技术要求 部分设置 broker_connection。请参阅本章的 金融工具属性 部分设置 instrument1

如何做…

获取并打印 instrument1 的总待买入数量:

>>> total_pending_buy_quantity = \
            broker_connection.get_total_pending_buy_quantity(instrument1)
>>> print(f'Total pending BUY quantity: {total_pending_buy_quantity}')

我们得到以下输出(您的输出可能会有所不同):

Total pending BUY quantity: 1319590

它是如何工作的…

BrokerConnectionZerodha 类的 get_total_pending_buy_quantity() 方法在任意时刻获取给定金融工具的总买入数量。此方法以 Instrument 类型的对象作为参数。在此处我们使用 instrument1 作为参数。

金融工具的总待卖出数量

金融工具的总待售数量是在某一时刻所有待售订单数量的总和。此数据是动态的,可能在交易时间内的任何时刻变化。

准备工作

确保在你的 Python 命名空间中有broker_connectioninstrument1对象可用。参考本章的技术要求部分设置broker_connection。参考本章的金融工具属性配方设置instrument1

如何做…

获取并打印instrument1的总待售数量:

>>> total_pending_sell_quantity = \
            broker_connection.get_total_pending_sell_quantity(instrument1)
>>> print(f'Total pending SELL quantity: {total_pending_sell_quantity}')

我们得到以下输出(你的输出可能不同):

Total pending SELL quantity: 968602

工作原理…

BrokerConnectionZerodha类的get_total_pending_sell_quantity()方法在任何给定时刻获取给定金融工具的总卖出数量。此方法将Instrument类型的对象作为参数。我们在此处使用instrument1作为参数。

一天中金融工具的总交易量

金融工具的一天中总交易量是该天内所有交易数量(买入和卖出,但仅计算一次)的总和。例如,如果交易员 A交易员 B购买股票X的 10 数量,而交易员 C将相同股票X的 20 数量卖给交易员 D,那么X的总交易量将是 10 + 20 = 30。它不会是 10 + 10 + 20 + 20 = 60,因为交易对总交易量的贡献只计算一次。此数据是动态的,可能在交易时间内的任何时刻增加。

准备工作

确保在你的 Python 命名空间中有broker_connectioninstrument1对象可用。参考本章的技术要求部分设置broker_connection。参考本章的金融工具属性配方设置instrument1

如何做…

获取并打印一天中某个工具的总交易量:

>>> total_volume_day = broker_connection.get_total_volume_day(instrument1)
>>> print(f'Total Volume for the day so far: {total_volume_day}')

我们得到以下输出(你的输出可能不同):

Total Volume for the day so far: 24416975

工作原理…

BrokerConnectionZerodha类的get_total_volume_day()方法在一天中的任何给定时刻获取给定金融工具的总交易量。此方法将Instrument类型的对象作为参数。我们在此处使用instrument1作为参数。

金融工具的最新交易价格

金融工具的最新交易价格LTP)是执行该工具的订单的最新价格。它本质上是指可以购买或出售该工具的当前价格(假设流动性良好)。正如描述所示,此数据是动态的,可能在交易时间内持续变化。本配方展示了如何获取金融工具的 LTP。

准备工作

确保 broker_connectioninstrument1 对象在你的 Python 命名空间中可用。参考本章的技术要求部分设置 broker_connection。参考本章的金融工具属性示例设置 instrument1

如何做...

获取并打印 instrument1 的 LTP:

>>> ltp = broker_connection.get_ltp(instrument1)
>>> print(f'Last traded price: {ltp}')

我们得到以下输出(您的输出可能不同):

Last traded price: 350.95

工作原理...

BrokerConnectionZerodha 类的 get_ltp() 方法在任何给定时刻获取给定金融工具的 LTP。此方法将一个 Instrument 类型的对象作为参数。我们在此处使用 instrument1 作为参数。获取的数据类型为 float

金融工具的最后交易时间

金融工具的最后交易时间LTT)是该工具最后一次执行订单的最新时间。此数据具有动态性,因为它在交易时间内可能会持续变化。此示例帮助获取金融工具的 LTT。

准备工作

确保 broker_connectioninstrument1 对象在你的 Python 命名空间中可用。参考本章的技术要求部分设置 broker_connection。参考本章的金融工具属性示例设置 instrument1

如何做...

获取并打印 instrument1 的 LTT:

>>> ltt = broker_connection.get_ltt(instrument1)
>>> print(f'Last traded time: {ltt}')

我们得到以下输出(您的输出可能不同):

Last traded time: 2020-07-17 14:42:54

工作原理...

BrokerConnectionZerodha 类的 get_ltt() 方法在任何给定时刻获取给定金融工具的 LTT。此方法将一个 Instrument 类型的对象作为参数。我们在此处使用 instrument1 作为参数。获取的数据是 datetime.datetime 类的一个实例。

金融工具的最后交易数量

金融工具的最后交易数量LTQ)是该工具最后一次执行订单时交易的数量。此数据具有动态性,因为它在交易时间内可能会持续变化。此示例演示了如何获取金融工具的 LTQ。

准备工作

确保 broker_connectioninstrument1 对象在你的 Python 命名空间中可用。参考本章的技术要求部分设置 broker_connection。参考本章的金融工具属性示例设置 instrument1

如何做...

获取并打印 instrument1 的 LTQ:

>>> ltq = broker_connection.get_ltq(instrument1)
>>> print(f'Last traded quantity: {ltq}')

我们得到以下输出(您的输出可能不同):

Last traded quantity: 19

工作原理...

BrokerConnectionZerodha 类的 get_ltq() 方法在任何给定时刻获取给定金融工具的 LTQ。此方法将一个 Instrument 类型的对象作为参数。我们在此处使用 instrument1 作为参数。获取的数据类型为 int

金融工具当天的记录开盘价

通常,交易策略在做出新交易决策之前使用金融工具当日的开盘价格作为首要合格条件之一。将当日的开盘价格与前一日的收盘价格进行比较,可能会暗示市场价格在当天对于某个工具是上涨还是下跌。如果开盘价格明显高于前一日的收盘价格,则价格可能会继续上涨。同样,如果开盘价格明显低于前一日的收盘价格,则价格可能会继续下跌。记录的开盘价格数据是静态的,意味着它在交易小时内不会改变。本教程演示了如何获取金融工具当日的开盘价格。

准备工作

确保在你的 Python 命名空间中可用broker_connectioninstrument1对象。请参考本章节的 技术要求 部分设置broker_connection。请参考本章节的 金融工具属性 食谱设置instrument1

如何做...

获取并打印一天中instrument1的开盘价格:

>>> open_price_day = broker_connection.get_open_price_day(instrument1)
>>> print(f'Open price today: {open_price_day}')

我们得到以下输出(你的输出可能会有所不同):

Open price today: 346

工作原理如下...

BrokerConnectionZerodha类的get_open_price_day()方法获取给定金融工具当日的开盘价格。此方法将Instrument类型的对象作为参数。我们在这里使用instrument1作为参数。获取的数据为float类型。

金融工具当日的记录最高价格

在做出新交易决策之前,交易策略通常使用金融工具当日的最高价格作为一个合格条件之一。这些数据是动态的,因为它们可能在交易小时内不断变化。本教程演示了如何获取金融工具当日的最高记录价格。

准备工作

确保在你的 Python 命名空间中可用broker_connectioninstrument1对象。请参考本章节的 技术要求 部分设置broker_connection。请参考本章节的 金融工具属性 食谱设置instrument1

如何做...

获取并打印一天中instrument1的记录最高价格:

>>> high_price_day = broker_connection.get_high_price_day(instrument1)
>>> print(f'Highest price today: {high_price_day}')

我们得到以下输出。你的输出可能会有所不同:

Highest price today: 356.8

工作原理如下...

BrokerConnectionZerodha类的get_high_price_day()方法获取给定金融工具当日的最高记录价格。此方法将Instrument类型的对象作为参数。我们在这里使用instrument1作为参数。获取的数据为float类型。

金融工具当日的记录最低价格

通常,交易策略在做出新交易决策之前会将金融工具当天最低价格作为符合条件之一。这些数据是动态的,因为它们可能在实时交易时间内持续变化。本文演示了如何获取金融工具当天最低记录价格。

准备工作

确保 broker_connectioninstrument1 对象在您的 Python 命名空间中可用。请参考本章的 技术要求 部分设置 broker_connection。请参考本章的 金融工具属性 配方设置 instrument1

操作方法…

获取并打印 instrument1 当天最低价格的记录:

>>> low_price_day = broker_connection.get_low_price_day(instrument1)
>>> print(f'Lowest price today: {low_price_day}')

我们得到以下输出(您的输出可能有所不同):

Lowest price today: 345.15

工作原理是…

BrokerConnectionZerodha 类的 get_low_price_day() 方法获取给定金融工具当天的最低记录价格。此方法以 Instrument 类型的对象作为参数。我们在此处将 instrument1 用作参数。获取的数据是 float 类型的。

金融工具上一交易日的记录收盘价

通常,交易策略在做出交易决策之前会使用前一交易日的金融工具收盘价作为首要条件之一。将当天的开盘价与前一天的收盘价进行比较,可能会暗示市场价格今天是否会上涨或下跌。如果开盘价显著高于前一天的收盘价,则价格可能会继续上涨。同样,如果开盘价显著低于前一天的收盘价,则价格可能会继续下跌。记录的收盘价数据是静态的,意味着在实时交易时间内不会发生变化。本文介绍了如何获取金融工具的前一日收盘价。

准备工作

确保 broker_connectioninstrument1 对象在您的 Python 命名空间中可用。请参考本章的 技术要求 部分设置 broker_connection。请参考本章的 金融工具属性 配方设置 instrument1

操作方法…

获取并打印 instrument1 上一交易日的记录收盘价:

>>> close_price_last_day = \
                broker_connection.get_close_price_last_day(instrument1)
>>> print(f'Close price of last trading day: {close_price_last_day}')

我们得到以下输出(您的输出可能有所不同):

Close price of last trading day: 341.65

工作原理是…

BrokerConnectionZerodha 类的 get_close_price_day() 方法获取给定金融工具的前一交易日收盘价。此方法以 Instrument 类型的对象作为参数。我们在此处将 instrument1 用作参数。获取的数据是 float 类型的。

第四章:计算蜡烛图案和历史数据

金融工具的历史数据是指金融工具过去所有买入或卖出价格的数据。算法交易策略总是在历史数据上进行虚拟执行,以评估其在实际投资前的过去表现。这个过程被称为回测。历史数据对于回测至关重要(在第八章,回测策略中有详细介绍)。此外,历史数据还需要用于计算技术指标(在第五章,计算和绘制技术指标中有详细介绍),这有助于在实时进行买卖决策。蜡烛图案是股票分析中广泛使用的工具。分析师通常使用各种类型的蜡烛图案。本章提供了一些示例,展示了如何使用经纪人 API 获取历史数据,如何获取和计算多个蜡烛图案 – 日本(开-高-低-收OHLC)、线段、Renko 和 Heikin-Ashi – 以及如何使用第三方工具获取历史数据。

在本章中,我们将介绍以下示例:

  • 利用经纪人 API 获取历史数据

  • 利用日本(OHLC)蜡烛图案获取历史数据

  • 利用蜡烛间隔变化的日本蜡烛图案获取数据

  • 利用线段烛台图案获取历史数据

  • 利用 Renko 蜡烛图案获取历史数据

  • 利用 Heikin-Ashi 蜡烛图案获取历史数据

  • 使用 Quandl 获取历史数据

让我们开始吧!

技术要求

您将需要以下内容才能成功执行本章中的示例:

  • Python 3.7+

  • Python 包:

  • pyalgotrading$ pip install pyalgotrading

  • quandl ($pip install quandl)这是可选的,仅用于最后一个示例

本章的最新 Jupyter 笔记本可在 GitHub 上找到:github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/tree/master/Chapter04

以下代码将帮助您设置与 Zerodha 的经纪人连接,这将被本章中的所有示例使用。请确保在尝试任何提供的示例之前已经执行了这些步骤。

设置与经纪人的连接的第一件事是收集所需的 API 密钥。经纪人为每个客户提供唯一的密钥,通常是api-keyapi-secret密钥对。这些 API 密钥通常是按月付费的。在开始本章之前,您需要从经纪人网站获取您的api-keyapi-secret的副本。您可以参考附录 I了解更多详情。

执行以下步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.broker.broker_connection_zerodha import BrokerConnectionZerodha
  1. 从经纪人处获取 api_keyapi_secret。这些对你是唯一的,经纪人将使用它们来识别你的证券账户:
>>> api_key = "<your-api-key>"
>>> api_secret = "<your-api-secret>"
>>> broker_connection = BrokerConnectionZerodha(api_key, 
                                                api_secret)

你将得到以下链接:

Installing package kiteconnect via pip...
Please login to this link to generate your request token: https://kite.trade/connect/login?api_key=<your-api-key>&v=3

如果你是第一次运行此程序,并且尚未安装 kiteconnectpyalgotrading 将自动为你安装。步骤 2 的最终输出将是一个链接。点击该链接并使用你的 Zerodha 凭证登录。如果认证成功,你将在浏览器地址栏中看到一个类似于 https://127.0.0.1/?request_token=&action=login&status=success 的链接。

例如:

https://127.0.0.1/?request_token=H06I6Ydv95y23D2Dp7NbigFjKweGwRP7&action=login&status=success
  1. 复制字母数字令牌并粘贴到 request_token 中:
>>> request_token = "<your-request-token>"
>>> broker_connection.set_access_token(request_token)

broker_connection 实例现在已经准备好执行 API 调用了。

pyalgotrading 包支持多个经纪人,并为每个经纪人提供一个连接对象类,其方法相同。它将经纪人 API 抽象在一个统一的接口后面,使用户无需担心底层经纪人 API 调用,并可以直接使用本章中的所有示例。

仅经纪人连接设置的过程会因经纪人而异。如果你不使用 Zerodha 作为你的经纪人,你可以参考 pyalgotrading 文档来学习如何设置经纪人连接。对于 Zerodha 用户,上述步骤就足够了。

使用经纪人 API 获取历史数据

金融工具的历史数据是过去时间戳的时间序列数据。可以使用经纪人 API 获取给定时段的历史数据。本篇示例演示了如何建立经纪人连接以及如何为金融工具获取单日历史数据的过程。

准备工作

确保 broker_connection 对象在你的 Python 命名空间中可用。请参考本章节的技术要求部分了解如何设置它。

如何执行…

执行以下步骤完成本篇示例:

  1. 获取一个金融工具的历史数据:
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'TATASTEEL')
>>> historical_data = broker_connection.get_historical_data(
                            instrument=instrument, 
                            candle_interval='minute', 
                            start_date='2020-01-01', 
                            end_date='2020-01-01')
>>> historical_data

你将得到以下输出:

  1. 打印 historical_data DataFrame 的可用列:
>>> historical_data.columns

你将得到以下输出:

>>> Index(['timestamp', 'open', 'high', 'low', 'close', 'volume'], 
            dtype='object')

工作原理…

步骤 1 中,你使用 broker_connectionget_instrument() 方法来获取一个金融工具,并将其赋值给一个新属性 instrument。这个对象是 Instrument 类的一个实例。调用 get_instrument() 需要两个参数,交易所('NSE')和交易标志('TATASTEEL')。接下来,你使用 get_historical_data() 方法获取 instrument 的历史数据。这个方法接受四个参数,描述如下:

  • instrument:必须放置历史数据的金融工具。应该是 Instrument 类的一个实例。在这里传递 instrument

  • candle_interval: 一个有效的字符串,表示历史数据中每个蜡烛图的持续时间。你在这里传递minute。(可能的值可以是minute3minute5minute10minute30minute60minuteday。)

  • start_date: 截取历史数据的开始日期。应该是YYYY-MM-DD格式的字符串。你在这里传递2020-01-01

  • end_date: 截取历史数据的截止日期,包括该日期。应该是YYYY-MM-DD格式的字符串。你在这里传递2020-01-01

步骤 2中,你获取并打印historical_data的可用列。你得到的列是timestampopenhighlowclosevolume

更多有关蜡烛图案的信息将在下一篇配方使用日本(OHLC)蜡烛图案获取历史数据以及本章的第三篇配方获取具有蜡烛间隔变化的日本蜡烛图案中介绍。

使用日本(OHLC)蜡烛图案获取历史数据

金融工具的历史数据是一个蜡烛图数组。历史数据中的每个条目都是一个单独的蜡烛图。有各种各样的蜡烛图案。

本配方演示了最常用的蜡烛图案——日本蜡烛图案。它是一种蜡烛图案,每个蜡烛图案持有一个持续时间,并指示在该持续时间内工具可能会取得的所有价格。这些数据使用四个参数表示——开盘价、最高价、最低价和收盘价。可以描述如下:

  • Open: 蜡烛持续时间开始时金融工具的价格

  • High: 蜡烛整个持续时间内金融工具的最高记录价格

  • Low: 蜡烛整个持续时间内金融工具的最低记录价格

  • Close: 蜡烛持续时间结束时金融工具的价格

根据这些参数,日本蜡烛图案也被称为OHLC 蜡烛图案。日本蜡烛图案中的所有时间戳都是等距的(在市场开放时间内)。例如,一个交易日的时间戳看起来像是上午 9:15、9:16、9:17、9:18 等等,对于 1 分钟的蜡烛间隔,每个时间戳都是在 1 分钟的间隔内等距分布的。

准备工作

确保broker_connectionhistorical_data对象在你的 Python 命名空间中可用。参考本章的技术要求部分设置broker_connection。参考上一篇配方设置historical_data

操作步骤…

我们执行以下步骤来进行这个配方:

  1. 导入必要的模块:
>>> from pyalgotrading.utils.func import plot_candlestick_chart, PlotType
  1. historical_data的一行创建一个绿色蜡烛图:
>>> candle_green = historical_data.iloc[:1,:]    
# Only 1st ROW of historical data
>>> plot_candlestick_chart(candle_green, 
                           PlotType.JAPANESE, 
                           "A 'Green' Japanese Candle")

你将得到以下输出:

  1. historical_data的一行创建一个红色蜡烛图:
# A 'Red' Japanese Candle
>>> candle_red = historical_data.iloc[1:2,:]     
# Only 2nd ROW of historical data
>>> plot_candlestick_chart(candle_red, 
                           PlotType.OHLC, 
                           "A 'Red' Japanese Candle")

这将给您以下输出:

  1. 绘制一个仪器历史数据的图表:
>>> plot_candlestick_chart(historical_data, 
                           PlotType.OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:TATASTEEL | 1st Jan, 2020 | '
                           'Candle Interval: 1 Minute')

这将给您以下输出:

  1. 绘制另一个仪器历史数据的图表:
>>> instrument2 = broker_connection.get_instrument('NSE', 'INFY')
>>> historical_data = \
        broker_connection.get_historical_data(instrument2, 
                                              'minute', 
                                              '2020-01-01', 
                                              '2020-01-01')
>>> plot_candlestick_chart(historical_data, 
                           PlotType.OHLC,
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:INFY | 1st Jan, 2020 | '
                           'Candle Interval: 1 Minute')

这将给您以下输出:

  1. 绘制另一个仪器历史数据的图表:
>>> instrument3 = broker_connection.get_instrument('NSE',
                                                   'ICICIBANK')
>>> historical_data = 
            broker_connection.get_historical_data(instrument3, 
                                                  'minute', 
                                                  '2020-01-01', 
                                                  '2020-01-01')
>>> plot_candlestick_chart(historical_data, PlotType.OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:ICICIBANK | 1st Jan, 2020 | '
                           'Candle Size: 1 Minute')

这将给您以下输出:

工作原理…

步骤 1中,您导入plot_candlestick_chart,这是一个用于绘制蜡烛图表的快速实用函数,以及PlotType,一个用于各种蜡烛图案类型的枚举。接下来的两步介绍了两种蜡烛图,或简称为蜡烛——一个绿色蜡烛和一个红色蜡烛。正如我们之前提到的,历史数据中的每个条目都是一个蜡烛。这两个步骤有选择地从数据中提取绿色和红色蜡烛。(请注意,如果您选择了与使用经纪人 API 获取历史数据配方中不同的持续时间historical_data,则传递给historical_data.iloc的索引将不同)。如果一个日本蜡烛的收盘价高于其开盘价,它的颜色将是绿色。绿色蜡烛也称为看涨蜡烛,因为它表明价格在那段时间内看涨,即上涨。如果一个日本蜡烛的收盘价低于其开盘价,它的颜色将是红色。红色蜡烛也称为看跌蜡烛,因为它表明价格在那段时间内看跌,即下跌。

步骤 4中,您使用plot_candlestick_chart()函数绘制了historical_data持有的完整历史数据。图表是多个蜡烛图的组合,每个蜡烛图的长度都不同。因此,这样的图表被称为蜡烛图案图表。请注意,蜡烛间隔为 1 分钟,意味着时间戳在 1 分钟间隔内等间距排列。步骤 5步骤 6演示了相似的 1 分钟蜡烛间隔蜡烛图案图表,分别用于NSE:INFYNSE:ICICIBANK仪器。

如果您是蜡烛图表的新手,我建议您与本章的 Jupyter Notebook 中的图表进行交互,网址为github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/blob/master/Chapter04/CHAPTER%204.ipynb。尝试悬停在多个蜡烛图上以查看它们的值,并放大/缩小或移动到各种持续时间以更清晰地查看蜡烛图。尝试将这些蜡烛图的颜色与本食谱中的描述联系起来。如果由于某种原因 Jupyter Notebook 中的图表没有自动呈现给您,您可以下载此 html 文件,该文件是相同 Jupyter Notebook 的文件,将其在浏览器中打开并与其进行交互:github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/blob/master/Chapter04/CHAPTER%204.ipynb

获取以不同蜡烛图间隔为特征的日本蜡烛图案:

金融工具的历史数据可以以不同的蜡烛图间隔形式进行分析。经纪人通常支持 1 分钟、3 分钟、5 分钟、10 分钟、15 分钟、30 分钟、1 小时、1 天等蜡烛图间隔。较短的蜡烛图间隔暗示着局部价格运动趋势,而较大的蜡烛图间隔则表示整体价格运动趋势。根据算法交易策略的不同,您可能需要较短的蜡烛图间隔或较大的蜡烛图间隔。1 分钟的蜡烛图间隔通常是最小的可用蜡烛图间隔。此示例演示了金融工具一天的历史数据在各种蜡烛图间隔下的情况。

准备就绪

确保broker_connection对象在您的 Python 命名空间中可用。请参考本章的技术要求部分,了解如何设置broker_connection

如何执行…

我们执行此食谱的以下步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.utils.func import plot_candlestick_chart, PlotType
  1. 获取一个工具:
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'TATASTEEL')
  1. 绘制仪器历史数据的图表,间隔为 1 分钟的蜡烛图:
>>> historical_data_1minute = \
        broker_connection.get_historical_data(instrument, 
                                              'minute', 
                                              '2020-01-01', 
                                              '2020-01-01')
>>> plot_candlestick_chart(historical_data_1minute, 
                           PlotType.OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           '1st Jan, 2020 | '
                           'Candle Interval: 1 Minute')

您将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 3 分钟的蜡烛图:
>>> historical_data_3minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '3minute', 
                                                  '2020-01-01', 
                                                  '2020-01-01')
>>> plot_candlestick_chart(historical_data_3minutes, 
                           PlotType.OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           '1st Jan, 2020 | '
                           'Candle Interval: 3 Minutes')

您将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 5 分钟的蜡烛图:
>>> historical_data_5minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '5minute', 
                                                  '2020-01-01', 
                                                  '2020-01-01')
>>> plot_candlestick_chart(historical_data_5minutes, 
                           PlotType.OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           '1st Jan, 2020 | '
                           'Candle Interval: 5 Minutes')

您将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 10 分钟的蜡烛图:
>>> historical_data_10minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '10minute', 
                                                  '2020-01-01', 
                                                  '2020-01-01')
>>> plot_candlestick_chart(historical_data_10minutes, 
                           PlotType.OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           '1st Jan, 2020 | '
                           'Candle Interval: 10 Minutes')

您将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 15 分钟的蜡烛图:
>>> historical_data_15minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '15minute', 
                                                  '2020-01-01', 
                                                  '2020-01-01')
>>> plot_candlestick_chart(historical_data_15minutes, 
                           PlotType.OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           '1st Jan, 2020 | '
                           'Candle Interval: 15 Minutes')

您将获得以下输出:

  1. 用 30 分钟蜡烛图间隔绘制仪器的历史数据图表:
>>> historical_data_30minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '30minute', 
                                                  '2020-01-01', 
                                                  '2020-01-01')
>>> plot_candlestick_chart(historical_data_30minutes, 
                           PlotType.OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           '1st Jan, 2020 | '
                           'Candle Interval: 30 Minutes')

您将获得以下输出:

  1. 用 1 小时蜡烛图间隔绘制仪器的历史数据图表:
>>> historical_data_1hour = \
            broker_connection.get_historical_data(instrument, 
                                                  'hour', 
                                                  '2020-01-01', 
                                                  '2020-01-01')
>>> plot_candlestick_chart(historical_data_1hour, 
                           PlotType.OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           '1st Jan, 2020 | '
                           'Candle Interval: 1 Hour')
  1. 用 1 天蜡烛图间隔绘制仪器的历史数据图表:
>>> historical_data_day = \
            broker_connection.get_historical_data(instrument, 
                                                  'day', 
                                                  '2020-01-01', 
                                                  '2020-01-01')
>>> plot_candlestick_chart(historical_data_day, 
                           PlotType.OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           '1st Jan, 2020 | '
                           'Candle Interval: Day')

您将获得以下输出,这是一个单一的蜡烛:

它的工作原理...

步骤 1 中,您导入了plot_candlestick_chart,这是一个用于绘制蜡烛图案图表的快速实用函数,以及PlotType,一个用于各种蜡烛图案类型的枚举。 在步骤 2 中,使用broker_connectionget_instrument()方法获取一个仪器并将其分配给一个新属性instrument。 此对象是Instrument类的一个实例。 调用get_instrument()所需的两个参数是交易所('NSE')和交易符号('TATASTEEL')。 步骤 34 获取并绘制了蜡烛图间隔的历史数据;即,1 分钟,3 分钟,5 分钟,10 分钟,15 分钟,30 分钟,1 小时和 1 天。 您使用get_historical_data()方法获取相同仪器和相同开始和结束日期的历史数据,只是蜡烛间隔不同。 您使用plot_candlestick_chart()函数绘制日本蜡烛图案图表。 随着蜡烛间隔的增加,您可以观察到以下图表之间的差异:

  • 蜡烛总数减少了。

  • 图表中由于突然的价格波动而出现的尖峰被最小化了。 较小的蜡烛间隔图表具有更多的尖峰,因为它们关注局部趋势,而较大的蜡烛间隔图表具有较少的尖峰,并且更平滑。

  • 股价的长期趋势变得明显。

  • 决策可能会变慢,因为您必须等待更长的时间才能获取新的蜡烛数据。 较慢的决策可能是期望的,也可能不是,这取决于策略。 例如,为了确认趋势,使用较小蜡烛间隔的数据,比如 3 分钟,和较大蜡烛间隔的数据,比如 15 分钟,将是期望的。 另一方面,对于在日内交易中抓住机会,不希望使用较大蜡烛间隔的数据,比如 1 小时或 1 天。

  • 相邻蜡烛的价格范围(y 轴范围)可能重叠,也可能不重叠。

  • 所有时间戳在时间上等间隔(在市场营业时间内)。

使用线形蜡烛图案获取历史数据

金融工具的历史数据可以以 Line Break 蜡烛图案的形式进行分析,这是一种专注于价格运动的蜡烛图案。这与专注于时间运动的日本蜡烛图案不同。经纪人通常不会通过 API 提供 Line Break 蜡烛图案的历史数据。经纪人通常使用日本蜡烛图案提供历史数据,需要将其转换为 Line Break 蜡烛图案。较短的蜡烛间隔暗示着局部价格运动趋势,而较长的蜡烛间隔则表示整体价格运动趋势。根据你的算法交易策略,你可能需要蜡烛间隔较小或较大。1 分钟的蜡烛间隔通常是最小的可用蜡烛间隔。

Line Break 蜡烛图案的工作方式如下:

  1. 每个蜡烛只有openclose属性。

  2. 用户定义一个线数n)设置,通常取为3

  3. 在每个蜡烛间隔结束时,如果股价高于前n个 Line Break 蜡烛中的最高价,则形成一个绿色蜡烛。

  4. 在每个蜡烛间隔结束时,如果股价低于前n个 Line Break 蜡烛中的最低价,则形成一个红色蜡烛。

  5. 在每个蜡烛间隔结束时,如果既不满足点 3 也不满足点 4,则不形成蜡烛。因此,时间戳不需要等间距。

这个配方展示了我们如何使用经纪人 API 获取历史数据,将历史数据转换为 Line Break 蜡烛图案,并进行绘图。这是针对多个蜡烛间隔进行的。

准备就绪

确保broker_connection对象在你的 Python 命名空间中可用。参考本章的技术要求部分,了解如何设置broker_connection

如何操作…

对于这个配方,我们执行以下步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.utils.func import plot_candlestick_chart, PlotType
>>> from pyalgotrading.utils.candlesticks.linebreak import Linebreak
  1. 获取一个工具的历史数据并将其转换为 Line Break 数据:
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'TATASTEEL')
>>> historical_data_1minute = \
            broker_connection.get_historical_data(instrument, 
                                                  'minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_1minute_linebreak = \
                              Linebreak(historical_data_1minute)
>>> historical_data_1minute_linebreak

你将得到以下输出:

       close       open                   timestamp
0     424.00     424.95   2019-12-02 09:15:00+05:30
1     424.50     424.00   2019-12-02 09:16:00+05:30
2     425.75     424.80   2019-12-02 09:17:00+05:30
3     423.75     424.80   2019-12-02 09:19:00+05:30
4     421.70     423.75   2019-12-02 09:20:00+05:30 
        …         …                ....
1058  474.90     474.55   2019-12-31 10:44:00+05:30
1059  471.60     474.55   2019-12-31 11:19:00+05:30
1060  471.50     471.60   2019-12-31 14:19:00+05:30
1061  471.35     471.50   2019-12-31 15:00:00+05:30
1062  471.00     471.35   2019-12-31 15:29:00+05:30
  1. historical_data的一行创建一个绿色 Line Break 蜡烛:
>>> candle_green_linebreak = historical_data_1minute_linebreak.iloc[1:2,:]            
# Only 2nd ROW of historical data
>>> plot_candlestick_chart(candle_green_linebreak, 
                           PlotType.LINEBREAK, 
                           "A 'Green' Line Break Candle")

你将得到以下输出:

  1. historical_data的一行创建一个红色 Line Break 蜡烛:
>>> candle_red_linebreak = historical_data_1minute_linebreak.iloc[:1,:]            
# Only 1st ROW of historical data
>>> plot_candlestick_chart(candle_red_linebreak, 
                           PlotType.LINEBREAK, 
                           "A 'Red' Line Break Candle")

你将得到以下输出:

  1. 为仪器的历史数据绘制一个 1 分钟蜡烛间隔的图表:
>>> plot_candlestick_chart(historical_data_1minute_linebreak, 
                           PlotType.LINEBREAK, 
                           'Historical Data | '
                           'Line Break Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 1 Minute', True)

你将得到以下输出:

  1. 为仪器的历史数据绘制一个 3 分钟蜡烛间隔的图表:
>>> historical_data_3minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '3minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_3minutes_linebreak = \
                    Linebreak(historical_data_3minutes)
>>> plot_candlestick_chart(historical_data_3minutes_linebreak, 
                           PlotType.LINEBREAK, 
                           'Historical Data | '
                           'Line Break Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 3 Minutes', True)

你将得到以下输出:

  1. 为仪器的历史数据绘制一个 5 分钟蜡烛间隔的图表:
>>> historical_data_5minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '5minute', 
                                                  '2019-12-01', 
                                                  '2020-01-10')
>>> historical_data_5minutes_linebreak = \
                            Linebreak(historical_data_5minutes)
>>> plot_candlestick_chart(historical_data_5minutes_linebreak, 
                           PlotType.LINEBREAK, 
                           'Historical Data | '
                           'Line Break Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 5 Minutes', True)

你将得到以下输出:

  1. 绘制该工具的历史数据图,每根蜡烛间隔为 10 分钟:
>>> historical_data_10minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '10minute', 
                                                  '2019-12-01', 
                                                  '2020-01-10')
>>> historical_data_10minutes_linebreak = \
                            Linebreak(historical_data_10minutes)
>>> plot_candlestick_chart(historical_data_10minutes_linebreak, 
                           PlotType.LINEBREAK, 
                           'Historical Data | '
                           'Line Break Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 10 Minutes', True)

你将会得到以下输出:

  1. 绘制该工具的历史数据图,每根蜡烛间隔为 15 分钟:
>>> historical_data_15minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '15minute', 
                                                  '2019-12-01', 
                                                  '2020-01-10')
>>> historical_data_15minutes_linebreak = \
                            Linebreak(historical_data_15minutes)
>>> plot_candlestick_chart(historical_data_15minutes_linebreak, 
                           PlotType.LINEBREAK, 
                           'Historical Data | '
                           'Line Break Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 15 Minutes', True)

你将会得到以下输出:

  1. 绘制该工具的历史数据图,每根蜡烛间隔为 30 分钟:
>>> historical_data_30minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '30minute', 
                                                  '2019-12-01', 
                                                  '2020-01-10')
>>> historical_data_30minutes_linebreak = \
                            Linebreak(historical_data_30minutes)
>>> plot_candlestick_chart(historical_data_30minutes_linebreak, 
                           PlotType.LINEBREAK, 
                           'Historical Data | '
                           'Line Break Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 30 Minutes', True)

你将会得到以下输出:

  1. 绘制该工具的历史数据图,每根蜡烛间隔为 1 小时:
>>> historical_data_1hour = \
            broker_connection.get_historical_data(instrument, 
                                                  'hour', 
                                                  '2019-12-01', 
                                                  '2020-01-10')
>>> historical_data_1hour_linebreak = \
                                Linebreak(historical_data_1hour)
>>> plot_candlestick_chart(historical_data_1hour_linebreak, 
                            PlotType.LINEBREAK, 
                           'Historical Data | '
                           'Line Break Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 1 Hour', True)

你将会得到以下输出:

  1. 绘制该工具的历史数据图,每根蜡烛间隔为 1 天:
>>> historical_data_day = \
            broker_connection.get_historical_data(instrument, 
                                                  'day', 
                                                  '2019-12-01', 
                                                  '2020-01-10')
>>> historical_data_day_linebreak = \
                                Linebreak(historical_data_day)
>>> plot_candlestick_chart(historical_data_day_linebreak, 
                           PlotType.LINEBREAK, 
                           'Historical Data | '
                           'Line Break Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 1 Day', True)

你将会得到以下输出:

它是如何工作的...

步骤 1 中,你导入 plot_candlestick_chart,一个用于绘制蜡烛图表的快捷实用函数,PlotType,一个用于各种蜡烛图案的枚举,以及 Linebreak 函数,该函数可以将日本蜡烛图案的历史数据转换成线条蜡烛图案。在 步骤 2 中,你使用 broker_connectionget_instrument() 方法来获取一个工具,并将其赋值给一个新属性 instrument。这个对象是 Instrument 类的一个实例。调用 get_instrument() 需要的两个参数是交易所('NSE')和交易符号('TATASTEEL')。接下来,你使用 broker_connection 对象的 get_historical_data() 方法获取工具的历史数据,时段为 2019 年 12 月,蜡烛间隔为 1 分钟。返回的时间序列数据以日本蜡烛图案的形式呈现。Linebreak() 函数将此数据转换为线条蜡烛图案,另一个 pandas.DataFrame 对象。你将其赋值给 historical_data_1minute_linebreak。注意到 historical_data_1minute_linebreak 只有 timestampopenclose 列。另外,请注意时间戳不是等距的,因为线条蜡烛是基于价格变动而不是时间的。在 步骤 3步骤 4 中,你从数据中选择性地提取了一个绿色和一个红色蜡烛。(请注意,如果您选择了第一章中获取的 historical_data 的不同持续时间,传递给 historical_data.iloc 的索引将不同。)请注意,蜡烛没有影子(延伸在主要蜡烛体两侧的线)因为蜡烛只有 open 和 close 属性。在 步骤 5 中,你使用 plot_candlestick_chart() 函数绘制了 historical_data 持有的完整历史数据。

步骤 612 中,您使用日本烛台图案获取历史数据,将其转换为 Line Break 烛台图案,并绘制烛台间隔为 3 分钟、5 分钟、10 分钟、15 分钟、30 分钟、1 小时和 1 天的转换数据。随着烛台间隔的增加,观察以下图表之间的差异和相似之处:

  • 烛台总数减少。

  • 由于突然的价格变动,图表中的尖峰被最小化。较小的烛台间隔图表具有更多尖峰,因为它们关注局部趋势,而较大的烛台间隔图表具有较少的尖峰,并且更加平滑。

  • 股价的长期趋势变得可见。

  • 决策可能变得较慢,因为您必须等待更长时间才能获取新的烛台数据。较慢的决策可能是可取的,也可能不是,这取决于策略。例如,为了确认趋势,使用较小烛台间隔的数据(例如 3 分钟)和较大烛台间隔的数据(例如 15 分钟)的组合是可取的。另一方面,为了抓住日内交易的机会,不希望使用较大烛台间隔(例如 1 小时或 1 天)的数据。

  • 两个相邻烛台的价格范围(y 轴跨度)不会重叠。相邻的烛台始终共享其中一个端点。

  • 与日本烛台图案不同,时间戳无需等间隔(烛台是基于价格变动而不是时间变动形成的)。

如果您对查找数学和实现 Line Break 烛台感兴趣,请参阅位于 github.com/algobulls/pyalgotrading/blob/master/pyalgotrading/utils/candlesticks/linebreak.pypyalgotrading 包中的源代码。

使用 Renko 砖块图案获取历史数据

金融工具的历史数据可以以 Renko 砖块图案的形式进行分析,这是一种关注价格变动的烛台图案。这与关注时间变动的日本烛台图案不同。经纪人通常不通过 API 提供 Renko 砖块图案的历史数据。经纪人通常通过使用需要转换为 Renko 砖块图案的日本烛台图案来提供历史数据。较短的烛台间隔暗示着局部价格变动趋势,而较大的烛台间隔则表示整体价格变动趋势。根据您的算法交易策略,您可能需要烛台间隔小或大。1 分钟的烛台间隔通常是最小的可用烛台间隔。

Renko 砖块图案的工作原理如下:

  1. 每个烛台仅具有 openclose 属性。

  2. 您可以定义一个砖块计数 (b) 设置,通常设置为 2

  3. 每个蜡烛始终是固定的,并且等于Brick Count。因此,此处也将蜡烛称为砖块

  4. 在每个蜡烛间隔结束时,如果股价比前一个砖的最高价高出b个点,则形成绿色砖块。如果价格在单个蜡烛间隔内上涨超过b个点,将形成足够多的砖块以适应价格变动。

    例如,假设价格比前一砖的高点高出 21 个点。如果砖块大小为2,将形成 10 个具有相同时间戳的砖块以适应 20 点的变动。对于剩余的 1 点变化(21-20),直到价格至少再上涨 1 点之前,不会形成任何砖块。

  5. 在每个蜡烛间隔结束时,如果股价比前一个砖的最低价低b个点,则形成红色蜡烛。如果价格在单个蜡烛间隔内下跌超过b个点,将形成足够多的砖块以适应价格变动。

    例如,假设价格比前一个砖的最高价低 21 个点。如果砖块大小为2,将形成 10 个具有相同时间戳的砖块以适应 20 点的变动。对于剩余的 1 点变化(21-20),直到价格至少再下跌 1 点之前,不会形成任何砖块。

  6. 没有两个相邻的蜡烛重叠在一起。相邻的蜡烛始终共享它们的一端。

  7. 没有任何时间戳需要等间隔(不像日本蜡烛图案),因为蜡烛是基于价格运动而不是时间运动形成的。此外,与其他图案不同,可能会有多个具有相同时间戳的蜡烛。

本食谱展示了如何使用经纪人 API 获取历史数据作为日本蜡烛图案,以及如何使用砖块蜡烛图案转换和绘制不同蜡烛间隔的历史数据。

准备工作

确保在 Python 命名空间中可用的broker_connection对象。请参阅本章的技术要求部分,了解如何设置broker_connection

如何做…

我们按照以下步骤执行此处方:

  1. 导入必要的模块:
>>> from pyalgotrading.utils.func import plot_candlestick_chart, PlotType
>>> from pyalgotrading.utils.candlesticks.renko import Renko
  1. 获取仪器的历史数据并将其转换为砖块数据:
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'TATASTEEL')
>>> historical_data_1minute = \
            broker_connection.get_historical_data(instrument, 
                                                  'minute', 
                                                  '2019-12-01', 
                                                  '2020-01-10')
>>> historical_data_1minute_renko = Renko(historical_data_1minute)
>>> historical_data_1minute_renko

您将获得以下输出:

      close     open                     timestamp
0     424.0   424.95     2019-12-02 09:15:00+05:30
1     422.0   424.00     2019-12-02 09:20:00+05:30
2     426.0   424.00     2019-12-02 10:00:00+05:30
3     422.0   424.00     2019-12-02 10:12:00+05:30
4     420.0   422.00     2019-12-02 15:28:00+05:30
       ...     ...          ...        ...
186   490.0   488.00     2020-01-10 10:09:00+05:30
187   492.0   490.00     2020-01-10 11:41:00+05:30
188   488.0   490.00     2020-01-10 13:31:00+05:30
189   486.0   488.00     2020-01-10 13:36:00+05:30
190   484.0   486.00     2020-01-10 14:09:00+05:30
  1. historical_data的行中创建绿色砖块:
>>> candle_green_renko = historical_data_1minute_renko.iloc[2:3,:]            
# Only 3rd ROW of historical data
>>> plot_candlestick_chart(candle_green_renko, 
                           PlotType.RENKO, 
                           "A Green 'Renko' Candle")

您将获得以下输出:

  1. historical_data的行中创建红色砖块:
>>> plot_candlestick_chart(historical_data_1minute_renko, 
                           PlotType.RENKO, 
                           'Historical Data | '
                           'Renko Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 1 Minute', True)

您将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 1 分钟蜡烛:
>>> historical_data_3minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '3minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_3minutes_renko = \
                                Renko(historical_data_3minutes)
>>> plot_candlestick_chart(historical_data_3minutes_renko, 
                           PlotType.RENKO, 
                           'Historical Data | '
                           'Renko Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 3 Minutes', True)

您将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 3 分钟蜡烛:
>>> historical_data_5minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '5minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_5minutes_renko = \
                                Renko(historical_data_5minutes)
>>> plot_candlestick_chart(historical_data_5minutes_renko, 
                           PlotType.RENKO, 
                           'Historical Data | '
                           'Renko Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 5 Minutes', True)

您将获得以下输出:

  1. 用 5 分钟蜡烛间隔绘制工具的历史数据图表:
>>> historical_data_10minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '10minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_10minutes_renko = \
                                Renko(historical_data_10minutes)
>>> plot_candlestick_chart(historical_data_10minutes_renko, 
                           PlotType.RENKO, 
                           'Historical Data | '
                           'Renko Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 10 Minutes', True)

你将会得到以下输出:

  1. 用 10 分钟蜡烛间隔绘制工具的历史数据图表:
>>> historical_data_15minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '15minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_15minutes_renko = \
                               Renko(historical_data_15minutes)
>>> plot_candlestick_chart(historical_data_15minutes_renko, 
                           PlotType.RENKO, 
                           'Historical Data | '
                           'Renko Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 15 Minutes', True)

你将会得到以下输出:

  1. 用 15 分钟蜡烛间隔绘制工具的历史数据图表:
>>> historical_data_15minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '15minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_15minutes_renko = \
                                Renko(historical_data_15minutes)
>>> plot_candlestick_chart(historical_data_15minutes_renko, 
                           PlotType.RENKO, 
                           'Historical Data | '
                           'Renko Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 15 Minutes', True)

你将会得到以下输出:

  1. 用 30 分钟蜡烛间隔绘制工具的历史数据图表:
>>> historical_data_30minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '30minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_30minutes_renko = \
                                Renko(historical_data_30minutes)
>>> plot_candlestick_chart(historical_data_30minutes_renko, 
                           PlotType.RENKO, 
                           'Historical Data | '
                           'Renko Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 30 Minutes', True)

你将会得到以下输出:

  1. 用 1 小时蜡烛间隔绘制工具的历史数据图表:
>>> historical_data_1hour = \
            broker_connection.get_historical_data(instrument, 
                                                  'hour', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_1hour_renko = Renko(historical_data_1hour)
>>> plot_candlestick_chart(historical_data_1hour_renko, 
                           PlotType.RENKO, 
                           'Historical Data | '
                           'Renko Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 1 Hour', True)

你将会得到以下输出:

  1. 用 1 天蜡烛间隔绘制工具的历史数据图表:
>>> historical_data_day = \
            broker_connection.get_historical_data(instrument, 
                                                  'day', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_day_renko = Renko(historical_data_day)
>>> plot_candlestick_chart(historical_data_day_renko, 
                           PlotType.RENKO, 
                           'Historical Data | '
                           'Renko Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: Day', True)

你将会得到以下输出:

工作原理…

步骤 1 中,你导入了plot_candlestick_chart,一个用于绘制蜡烛图表的快速实用函数,PlotType,用于各种蜡烛图案的枚举,以及Renko函数,该函数可以将日本蜡烛图案的历史数据转换为 Renko 蜡烛图案。在 步骤 2 中,你使用broker_connectionget_instrument()方法来获取一个工具,并将其赋值给一个新属性instrument。这个对象是Instrument类的一个实例。调用 get_instrument()所需的两个参数是交易所('NSE')和交易符号('TATASTEEL')。接下来,你使用broker_connection对象的get_historical_data()方法来获取 2019 年 12 月的历史数据,蜡烛间隔为 1 分钟。返回的时间序列数据以日本蜡烛图案的形式呈现。Renko()函数将此数据转换为 Renko 蜡烛图案,另一个pandas.DataFrame对象。你将其赋值给historical_data_1minute_renko。请注意,historical_data_1minute_renko具有timestampopenclose列。同时请注意,时间戳不是等距的,因为 Renko 蜡烛是基于价格变动而不是时间的。在 步骤 34 中,你选择性地从数据中提取一个绿色蜡烛和一个红色蜡烛(请注意,传递给historical_data.iloc的索引是从本章第一个配方中获取的)。请注意,蜡烛没有影子(延伸在主要蜡烛体两侧的线),因为蜡烛只有openclose属性。在 步骤 5 中,你使用plot_candlestick_chart()函数绘制historical_data中保存的完整历史数据。

步骤 612中,您使用日本蜡烛图形态获取历史数据,将其转换为 Renko 蜡烛图形态,并绘制蜡烛间隔为 3 分钟、5 分钟、10 分钟、15 分钟、30 分钟、1 小时和 1 天的转换数据。观察随着蜡烛间隔增加而图表之间的以下差异和相似之处:

  • 蜡烛图形态的总数量减少。

  • 由于突然的价格波动,图表中的尖峰被最小化。较小的蜡烛间隔图表具有更多尖峰,因为它们关注局部趋势,而较大的蜡烛间隔图表则具有较少的尖峰,并且更平滑。

  • 股价的长期趋势变得可见。

  • 决策可能会变慢,因为您必须等待更长时间才能获得新的蜡烛数据。根据策略,较慢的决策可能是可取或不可取的。例如,为了确认趋势,使用较小的蜡烛间隔数据(如 3 分钟)和较大的蜡烛间隔数据(如 15 分钟)的组合将是可取的。另一方面,为了抓住日内交易中的机会,不希望使用较大蜡烛间隔(如 1 小时或 1 天)的数据。

  • 两个相邻蜡烛的价格范围(y 轴跨度)不会互相重叠。相邻蜡烛总是共享其中一个端点。

  • 没有必要让所有时间戳等间隔排列(不像日本蜡烛图形态那样),因为蜡烛是基于价格运动而不是时间运动形成的。

如果您对研究 Renko 蜡烛图的数学和实现感兴趣,请参考 github.com/algobulls/pyalgotrading/blob/master/pyalgotrading/utils/candlesticks/renko.pypyalgotrading 包中的源代码。

使用平均-足蜡烛形态获取历史数据

金融工具的历史数据可以以平均-足烛形态的形式进行分析。经纪人通常不会通过 API 提供使用平均-足烛形态的历史数据。经纪人通常提供使用日本蜡烛图形态的历史数据,需要将其转换为平均-足烛形态。较短的蜡烛间隔暗示着局部价格走势,而较长的蜡烛间隔则表示整体价格走势。根据您的算法交易策略,您可能需要蜡烛间隔较小或较大。1 分钟的蜡烛间隔通常是可用的最小蜡烛间隔。

平均-足蜡烛形态的工作原理如下:

  • 每根蜡烛都有 收盘价开盘价最高价最低价 属性。对于每根蜡烛,会发生以下情况:

  • 收盘价 计算为当前日本蜡烛的 开盘价最高价最低价 和 收盘价 属性的平均值。

  • 开盘价 为前一个平均-足蜡烛的 开盘价 和 收盘价 属性的平均值。

  •  为:

  • 当前平均蜡烛的Open

  • 当前平均蜡烛的Close

  • 当前日本蜡烛的High

  • Low是:

  • 当前平均蜡烛的Open

  • 当前平均蜡烛的Close

  • 当前日本蜡烛的Low

  • Close高于Open时形成绿色蜡烛。(与日本蜡烛图案中的绿色蜡烛相同。)

  • Close低于Open时形成红色蜡烛。(与日本蜡烛图案中的红色蜡烛相同。)

  • 所有时间戳均等间隔(在市场营业时间内)。

此示例向您展示了在使用经纪人 API 时如何使用日本蜡烛图案获取历史数据,以及如何转换和绘制各种蜡烛间隔的历史数据使用平均蜡烛图案。

准备就绪

确保在你的 Python 命名空间中可用 broker_connection 对象。请参考本章的技术要求部分了解如何设置 broker_connection

如何实现…

我们对这个配方执行以下步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.utils.func import plot_candlestick_chart, PlotType
>>> from pyalgotrading.utils.candlesticks.heikinashi import HeikinAshi
  1. 获取仪器的历史数据并将其转换为平均蜡烛数据:
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'TATASTEEL')
>>> historical_data_1minute = \
            broker_connection.get_historical_data(instrument, 
                                                  'minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_1minute_heikinashi = \
                            HeikinAshi(historical_data_1minute)
>>> historical_data_1minute_heikinashi

你将获得以下输出:

  1. 创建一行数据的绿色平均蜡烛:
>>> candle_green_heikinashi = \
            historical_data_1minute_heikinashi.iloc[2:3,:]            
# Only 3rd ROW of historical data
>>> plot_candlestick_chart(candle_green_heikinashi, 
                           PlotType.HEIKINASHI, 
                           "A 'Green' HeikinAshi Candle")

你将获得以下输出:

  1. 创建一行数据的红色平均蜡烛:
# A 'Red' HeikinAshi Candle
>>> candle_red_heikinashi = \
            historical_data_1minute_heikinashi.iloc[4:5,:]            
# Only 1st ROW of historical data
>>> plot_candlestick_chart(candle_red_heikinashi, 
                           PlotType.HEIKINASHI, 
                           "A 'Red' HeikinAshi Candle")

你将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 1 分钟:
>>> plot_candlestick_chart(historical_data_1minute_heikinashi, 
                           PlotType.HEIKINASHI, 
                           'Historical Data | '
                           'Heikin-Ashi Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 1 minute', True)

你将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 3 分钟:
>>> historical_data_3minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '3minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_3minutes_heikinashi = \
                            HeikinAshi(historical_data_3minutes)
>>> plot_candlestick_chart(historical_data_3minutes_heikinashi, 
                           PlotType.HEIKINASHI, 
                           'Historical Data | '
                           'Heikin-Ashi Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 3 minutes', True)

你将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 5 分钟:
>>> historical_data_5minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '5minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_5minutes_heikinashi = \
                           HeikinAshi(historical_data_5minutes)
>>> plot_candlestick_chart(historical_data_5minutes_heikinashi, 
                            PlotType.HEIKINASHI, 
                           'Historical Data | '
                           'Heikin-Ashi Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 5 minutes', True)

你将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 10 分钟:
>>> historical_data_10minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '10minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_10minutes_heikinashi = \
                            HeikinAshi(historical_data_10minutes)
>>> plot_candlestick_chart(historical_data_10minutes_heikinashi, 
                           PlotType.HEIKINASHI, 
                           'Historical Data | '
                           'Heikin-Ashi Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 10 minutes', True)

你将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 15 分钟:
>>> historical_data_15minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '15minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_15minutes_heikinashi = \
                           HeikinAshi(historical_data_15minutes)
>>> plot_candlestick_chart(historical_data_15minutes_heikinashi, 
                           PlotType.HEIKINASHI, 
                           'Historical Data | '
                           'Heikin-Ashi Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 15 minutes', True)

你将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 30 分钟:
>>> historical_data_30minutes = \
            broker_connection.get_historical_data(instrument, 
                                                  '30minute', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_30minutes_heikinashi = \
                           HeikinAshi(historical_data_30minutes)
>>> plot_candlestick_chart(historical_data_30minutes_heikinashi, 
                           PlotType.HEIKINASHI, 
                           'Historical Data | '
                           'Heikin-Ashi Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 30 minutes', True)

你将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 1 小时:
>>> historical_data_1hour = 
        broker_connection.get_historical_data(instrument, 
                                              'hour', 
                                              '2019-12-01', 
                                              '2019-12-31')
>>> historical_data_1hour_heikinashi = \
                           HeikinAshi(historical_data_1hour)
>>> plot_candlestick_chart(historical_data_1hour_heikinashi, 
                           PlotType.HEIKINASHI, 
                           'Historical Data | '
                           'Heikin-Ashi Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: 1 Hour', True)

你将获得以下输出:

  1. 绘制仪器历史数据的图表,间隔为 1 天:
>>> historical_data_day = \
            broker_connection.get_historical_data(instrument, 
                                                  'day', 
                                                  '2019-12-01', 
                                                  '2019-12-31')
>>> historical_data_day_heikinashi = \
                                HeikinAshi(historical_data_day)
>>> plot_candlestick_chart(historical_data_day_heikinashi, 
                           PlotType.HEIKINASHI, 
                           'Historical Data | '
                           'Heikin-Ashi Candlesticks Pattern | '
                           'NSE:TATASTEEL | '
                           'Dec, 2019 | '
                           'Candle Interval: Day', True)

你将获得以下输出:

工作原理…

步骤 1中,您导入plot_candlestick_chart,一个用于绘制蜡烛图模式图表的快捷工具函数,PlotType,一个用于各种蜡烛图模式的枚举,以及HeikinAshi函数,该函数可以将日本蜡烛图模式的历史数据转换为适用于平均阴阳蜡烛图模式的数据。在步骤 2中,您使用broker_connectionget_instrument()方法获取一个工具,并将其分配给一个新属性instrument。这个对象是Instrument类的一个实例。调用get_instrument()所需的两个参数是交易所('NSE')和交易符号('TATASTEEL')。接下来,您使用broker_connection对象的get_historical_data()方法获取 2019 年 12 月的历史数据,蜡烛间隔为 1 分钟。返回的时间序列数据以日本蜡烛图模式的形式返回。HeikinAshi()函数将这些数据转换为平均阴阳蜡烛图模式,另一个pandas.DataFrame对象。您将其分配给historical_data_1minute_heikinashi。注意historical_data_1minute_heikinashi具有timestampcloseopenhighlow列。还请注意,时间戳是等距的,因为平均阴阳蜡烛图是基于日本蜡烛的平均值。在步骤 3步骤 4中,您从数据中选择性地提取绿色和红色蜡烛。(请注意,如果您选择本章第一个配方中获取的historical_data的不同持续时间,则传递给historical_data.iloc的索引将不同。)请注意,蜡烛具有阴影(延伸在主蜡烛体两侧的线),因为蜡烛具有highlow属性,以及openclose属性。在步骤 5中,您使用plot_candlstick_charts()函数绘制historical_data保存的完整历史数据。

步骤 6步骤 12之间,您使用日本蜡烛图模式获取历史数据,将其转换为平均阴阳蜡烛图模式,并分别为 3 分钟、5 分钟、10 分钟、15 分钟、30 分钟、1 小时和 1 天的蜡烛间隔绘制转换后的数据图表。随着蜡烛间隔的增加,观察以下图表之间的差异和相似之处:

  • 蜡烛总数减少。

  • 由于突然的价格波动,图表中的尖峰被最小化。较小的蜡烛间隔图表具有更多的尖峰,因为它们专注于局部趋势,而较大的蜡烛间隔图表具有较少的尖峰,更加平滑。

  • 股价的长期趋势变得可见。

  • 决策可能会变慢,因为你必须等待更长时间才能获取新的蜡烛数据。决策速度变慢可能是好事,也可能不是,这取决于策略。例如,为了确认趋势,使用较小蜡烛间隔的数据(例如 3 分钟)和较大蜡烛间隔的数据(例如 15 分钟)会是理想的。另一方面,为了抓住日内交易的机会,较大蜡烛间隔(例如 1 小时或 1 天)的数据则不理想。

  • 相邻蜡烛的价格范围(y 轴跨度)可能重叠,也可能不重叠。

  • 所有的时间戳在时间上是均匀分布的(在市场开放时间内)。

如果你对 Heikin-Ashi 蜡烛图的数学和实现感兴趣,请参考 pyalgotrading 包中的源代码:github.com/algobulls/pyalgotrading/blob/master/pyalgotrading/utils/candlesticks/heikinashi.py

使用 Quandl 获取历史数据

到目前为止,在本章的所有配方中,你都使用了经纪连接来获取历史数据。在这个配方中,你将使用第三方工具 Quandl (www.quandl.com/tools/python) 来获取历史数据。它有一个免费使用的 Python 版本,可以使用 pip 轻松安装。这个配方演示了使用 quandl 来获取 FAAMG 股票价格(Facebook、亚马逊、苹果、微软和谷歌)的历史数据。

准备工作

确保你已安装了 Python 的 quandl 包。如果没有,你可以使用以下 pip 命令进行安装:

$ pip install quandl 

如何做…

我们对此配方执行以下步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.utils.func import plot_candlestick_chart, PlotType
>>> import quandl
  1. 绘制 Facebook 的历史数据图表,蜡烛间隔为 1 天:
>>> facebook = quandl.get('WIKI/FB', 
                           start_date='2015-1-1', 
                           end_date='2015-3-31')
>>> plot_candlestick_chart(facebook, 
                           PlotType.QUANDL_OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'FACEBOOK | '
                           'Jan-March 2015 | '
                           'Candle Interval: Day', True)

你将得到以下输出:

  1. 绘制亚马逊的历史数据图表,蜡烛间隔为 1 天:
>>> amazon = quandl.get('WIKI/AMZN', 
                         start_date='2015-1-1', 
                         end_date='2015-3-31')
>>> plot_candlestick_chart(amazon, 
                           PlotType.QUANDL_OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'AMAZON | '
                           'Jan-March 2015 | '
                           'Candle Interval: Day', True)

你将得到以下输出:

  1. 绘制苹果的历史数据图表,蜡烛间隔为 1 天:
>>> apple = quandl.get('WIKI/AAPL', 
                        start_date='2015-1-1', 
                        end_date='2015-3-31')
>>> plot_candlestick_chart(apple, 
                           PlotType.QUANDL_OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'APPLE | '
                           'Jan-March 2015 | '
                           'Candle Interval: Day', True)

你将得到以下输出:

  1. 绘制微软的历史数据图表,蜡烛间隔为 1 天:
>>> microsoft = quandl.get('WIKI/MSFT', 
                            start_date='2015-1-1', 
                            end_date='2015-3-31')
>>> plot_candlestick_chart(microsoft, 
                           PlotType.QUANDL_OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'MICROSOFT | '
                           'Jan-March 2015 | '
                           'Candle Interval: Day', True)

你将得到以下输出:

  1. 绘制谷歌的历史数据图表,蜡烛间隔为 1 天:
>>> google = quandl.get('WIKI/GOOGL', 
                         start_date='2015-1-1', 
                         end_date='2015-3-31')
>>> plot_candlestick_chart(google, 
                           PlotType.QUANDL_OHLC, 
                           'Historical Data | '
                           'Japanese Candlesticks Pattern | '
                           'GOOGLE | '
                           'Jan-March 2015 | '
                           'Candle Interval: Day', True)

你将得到以下输出:

工作原理…

第一步中,你需要导入plot_candlestick_chart,这是一个用于绘制蜡烛图表的快速实用函数,还有PlotType,用于表示各种蜡烛图案的枚举,以及quandl模块。在其余的步骤中,使用quandl.get()获取 Facebook、Amazon、Apple、Microsoft 和 Google 股票的历史数据,并使用plot_candlestick_chart()方法进行绘制。quandl返回的数据格式是 OHLC(开盘价、最高价、最低价、收盘价)格式。

这种第三方模块的好处是它们是免费的,而且你不需要建立经纪人连接来获取历史数据。但缺点是来自免费包的数据有其局限性。例如,无法实时获取数据,也无法获取日内交易的数据(1 分钟蜡烛、3 分钟蜡烛等)。

因此,是否要使用这些数据取决于你的需求。它可能适用于测试或更新现有代码库,但不足以提供实时数据源,这在实际交易会话期间是需要的。

第五章:计算和绘制技术指标

技术分析是交易中使用数学函数(称为技术指标)来预测并找到股票市场中盈利机会的一门学科。 技术指标分析基于金融工具的过去和现在的价格和成交量的数据,并提供统计信息。 这有助于预测金融工具未来的价格可能会上涨或下跌。 作为交易者,您可以在交易时做出知情决策,从而提高成功的几率。

技术指标不考虑底层金融工具业务的任何基本方面,如收入、收益、利润等。 但是,它们确实考虑了过去和现在的价格和成交量,这有助于预测短期价格变动。

大多数经纪人提供实时在历史数据图上叠加技术指标图表。 这有助于直观地预测价格变动的趋势。 但是,仅进行视觉分析也有一些限制:

  • 您一次只能查看和分析少量图表,而您可能希望分析数千张图表以帮助寻找盈利机会。

  • 对多个图表进行视觉分析是繁琐的,并且容易出现延迟和人为错误。 当我们希望立即准确抓住一个良好的交易机会时,延迟和错误是不可行的。

因此,最好让计算机实时分析大量金融工具的历史数据。 出于这个原因,学习如何使用其历史数据计算给定金融工具的技术指标非常重要。 本章提供了使用 Python 计算各种技术指标的示例代码。

可能会出现您希望绘制复杂图表的情况。 但是,使用大多数经纪人提供的工具可能不可能这样做。 例如,您可能希望在历史数据的收盘价上绘制简单移动平均线SMA)以及其相对强度指数RSI)上的线(数学上是SMA(RSI(close, timeperiod=10), timeperiod=5)),并在某个时期内进行分析,比如说 3 个月,以帮助制定您的交易策略。 在这些情况下,了解如何为给定的金融工具绘制技术指标将会有所帮助。 本章的示例还包括使用 Python 绘制技术指标的代码。

每个技术指标都属于以下两类之一:

  • 领先:这类指标在趋势即将开始或即将发生反转时提供交易信号。 换句话说,它们引领了趋势。 因此,这些指标有助于预测即将到来的趋势。(如果价格上涨,则趋势可能是看涨的,如果价格下跌,则可能是看跌的。)

  • 滞后:这类指标在趋势开始后或逆转发生后提供交易信号。因此,这些指标有助于找出当前趋势。

技术指标也可以根据它们提供的见解广泛分类为四种类型:

  • 趋势指标 或振荡器:这些指标指示市场的趋势,如果有的话。这些指标也被称为振荡器,因为它们通常随着时间在高值和低值之间振荡,就像一个振荡的波一样。这种指标通常是滞后的,但有时也可以是领先的。

  • 动量指标:这些指标告诉我们当前趋势有多强劲,以及当前趋势是否可能发生逆转。这些指标通常是领先的。

  • 波动率指标:这些指标衡量价格变动的速率,无论方向如何(即看跌看涨)。这些指标帮助我们了解价格变动的速度快慢。一个非常波动的市场可能不适合你的交易策略,因为在你查询市场并以特定价格下订单时,价格可能已经明显偏离指定价格。这些指标通常是滞后指标。

  • 成交量指标:这些指标指示成交量随时间的变化速度。成交量越大,当前趋势就越强劲,因此这些指标有助于找出当前趋势的强度。这些指标既可以是领先也可以是滞后

本章讨论了所有先前提到的类别和类型中的 10 个技术指标。每个配方都会执行以下操作:

  1. 介绍了一个新的技术指标

  2. 展示如何使用给定的历史数据在 Python 上计算它

  3. 展示如何使用 Python 在日本蜡烛图模式图表上绘制它

  4. 解释了从图表中提供的指标的见解

在本章中,我们将介绍以下配方:

  • 趋势指标 – 简单移动平均线

  • 趋势指标 – 指数移动平均线

  • 趋势指标 – 移动平均收敛差离

  • 趋势指标 – 抛物线转向止损

  • 动量指标 – 相对强弱指数

  • 动量指标 - 随机振荡器

  • 波动率指标 – 布林带

  • 波动率指标 – 平均真实范围

  • 成交量指标 – 在平衡成交量

  • 成交量指标 – 成交量加权平均价格

本章的主要重点是演示如何计算和绘制最常用的技术指标。尽管每个技术指标在每个示例的开头都有介绍,但深入理解它们超出了本书的范围。如果您对此感兴趣,请参考杰克·施瓦格(Jack Schwager)、马丁·普林格(Martin Pring)、约翰·墨菲(John Murphy)、史蒂夫·尼森(Steve Nison)和托马斯·布尔科夫斯基(Thomas Bulkowski)等知名人士的作品。您也可以使用广泛接受的网络资源,例如 www.investopedia.com/

技术要求

为了成功执行本章的示例,您将需要以下内容:

  • Python 3.7+

  • 以下 Python 包:

  • pyalgotrading$ pip install pyalgotrading

  • TA-Lib$ pip install TA-Lib

如果在安装TA-Lib时遇到错误,主要是由于缺少依赖项。您可以按照以下说明解决问题:

  • 对于 Mac OS X,请使用以下
$ brew install ta-lib
  • 对于 Windows,请使用以下说明

您可以根据您的 Windows 版本(32 位/64 位)和 Python 版本从 www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib 下载最新的TA-Lib二进制文件。因此,例如,此网站上的此链接 TA_Lib‑0.4.18‑cp38‑cp38‑win_amd64.whl,适用于TA-Lib版本 0.4.18(TA_Lib-0.4.18)和 Python 版本 3.8(cp38),是 Windows 64 位兼容的(win_amd64)。

  • 对于 Linux,请执行以下步骤

prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz 下载gzip文件,并从您的 Linux 终端运行以下命令:

  1. 解压下载的包含gzip文件的TA-Lib源代码:
$ tar -xzf ta-lib-0.4.0-src.tar.gz 
  1. 将当前工作目录更改为已提取的文件夹:
$ cd ta-lib/
  1. 运行configure命令为您的机器配置TA-Lib
$ ./configure --prefix=/usr 
  1. 运行make命令从下载的源代码构建TA-Lib
$ make
  1. 运行install命令以将构建的可执行文件和库安装到机器上的特定目录:
$ sudo make install

如果这没有帮助,您仍然遇到错误,请参阅官方的TA-Lib GitHub 页面 github.com/mrjbq7/ta-lib#dependencies

本章的最新 Jupyter 笔记本可以在 GitHub 上找到 github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/tree/master/Chapter05

建议您在 Jupyter 笔记本中尝试本章的示例。所有示例都有一个图表作为输出。您可以使用 Jupyter Notebook 的功能(如选择、平移、缩放等)方便地与这些图表交互。

与经纪人建立连接所需的第一件事是获取 API 密钥。经纪人将为每个客户提供唯一的密钥,通常作为一个api-keyapi-secret密钥对。这些 API 密钥通常是按月订阅的方式收费的。在开始之前,您需要从经纪人网站获取您的api-keyapi-secret。您可以参考附录 I以获取更多详情。

以下步骤将帮助您导入必要的模块,设置与 Zerodha 的经纪人连接,并获取并保留一些历史数据,这些数据将由本章中的所有配方使用。请确保在尝试任何配方之前已经执行了这些步骤:

  1. 导入必要的模块:
>>> import pandas as pd
>>> import talib
>>> from pyalgotrading.broker.broker_connection_zerodha import BrokerConnectionZerodha
>>> from pyalgotrading.utils.func import plot_candlesticks_chart, PlotType

这些模块将在本章中需要。

plot_candlesticks_chart函数在每个配方中都使用。它接受以下参数:

  • data:要绘制的历史数据,应为具有timestampopenhighlowclose列的pandas.DataFrame对象。

  • plot_type:一个pyalgotrading.plot_type枚举类的实例,指定蜡烛图模式图表的类型。

  • indicators(可选):一个字典列表,指定应与蜡烛图模式图表一起绘制的指标。每个dict应具有以下键值对:

  • name:图例中的图的名称。

  • data:表示要绘制的指标数据的pandas.Series对象

  • extra(可选):属性的字典,将传递给plotly.graph_objects.Scatter构造函数(关于此类的更多信息可以在plot.ly/python-api-reference/generated/plotly.graph_objects.Scatter.html找到)。

  • plot_indicators_separately(可选):如果为False,则指标将绘制在与历史数据相同的图上。如果为True,则指标将单独绘制。默认值为False

  • caption(可选):向图添加字符串标题。

  1. 从经纪人那里获取api_keyapi_secret密钥。这些对您来说是唯一的,并且经纪人将使用它们来识别您的证券账户:
>>> api_key = "<your-api-key>"
>>> api_secret = "<your-api-secret>"
>>> broker_connection = BrokerConnectionZerodha(api_key, 
                                                api_secret)

我们得到以下输出:

Installing package kiteconnect via pip. This may take a while...
Please login to this link to generate your request token: https://kite.trade/connect/login?api_key=<your-api-key>&v=3

如果您是第一次运行此程序并且未安装kiteconnectpyalgotrading将自动为您安装。第 2 步的最终输出将是一个链接。点击链接并使用您的 Zerodha 凭据登录。如果身份验证成功,您将在浏览器的地址栏中看到一个类似于https://127.0.0.1/?request_token=<aplphanumeric-token>&action=login&status=success的链接,例如https://127.0.0.1/?request_token=H06I6Ydv95y23D2Dp7NbigFjKweGwRP7&action=login&status=success

  1. 复制字母数字令牌并将其粘贴到request_token中:
>>> request_token = "<your-request-token>"
>>> broker_connection.set_access_token(request_token)
  1. 获取并打印一个工具的历史数据,并将其分配给historical_data
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'TATASTEEL')
>>> historical_data = \
        broker_connection.get_historical_data(
                                instrument=instrument, 
                                candle_interval='minute', 
                                start_date='2020-01-01 12:00:00', 
                                end_date='2020-01-01 14:00:00')
>>> historical_data

我们得到以下输出:

                    timestamp   open   high    low  close volume
  0 2020-01-01 12:00:00+05:30 467.00 467.30 467.00 467.15   5694
  1 2020-01-01 12:01:00+05:30 467.15 467.50 467.10 467.35  10852
  2 2020-01-01 12:02:00+05:30 467.35 467.45 467.20 467.45   4171
  3 2020-01-01 12:03:00+05:30 467.50 467.50 467.35 467.45   2897
...                       ...    ...    ...    ...    ...    ...
117 2020-01-01 13:57:00+05:30 469.70 469.70 469.55 469.60   9442
118 2020-01-01 13:58:00+05:30 469.60 469.70 469.50 469.60   7609
119 2020-01-01 13:59:00+05:30 469.60 469.60 469.50 469.50   8155
120 2020-01-01 14:00:00+05:30 469.50 469.60 469.45 469.60   6973

此步骤使用BrokerConnectionZerodha类的get_instrument()方法获取一个工具并将其分配给一个新属性instrument。该对象是Instrument类的一个实例。调用get_instrument所需的两个参数是交易所('NSE')和交易符号('TATASTEEL')。接下来,使用get_historical_data()方法为instrument获取并打印历史数据。此方法接受四个参数,描述如下:

  • instrumentInstrument):由broker_connectionget_instrument()方法返回的对象。

  • candle_intervalstr):表示历史数据中每个蜡烛图的持续时间的有效字符串。可能的值可以是'minute''3minute''5minute''10minute''15minute''30minute''60minute''day'。我们在步骤 4中将'minute'传递给此参数。

  • start_datestr):历史数据将从此时间戳开始获取。我们在步骤 4中将'2020-01-01 12:00:00'传递给此参数。

  • end_datestr):历史数据将在此时间戳之前获取。我们在步骤 4中将'2020-01-01 14:00:00'传递给此参数。

在本章中将需要historical_data对象。

pyalgotrading包支持多个经纪人,并为每个经纪人提供一个连接对象类,具有相同的方法。它在统一接口后面抽象了经纪人 API,因此您无需担心底层经纪人 API 调用,并且可以像使用现有的方法一样使用本章中的所有示例。从经纪人到经纪人的经纪人连接设置程序将有所不同。如果您不是使用 Zerodha 作为经纪人,则可以参考pyalgotrading文档了解有关设置经纪人连接的信息。对于 Zerodha 用户,前面部分提到的步骤就足够了。

趋势指标 - 简单移动平均线

SMA 是滞后趋势指标。它用于通过消除噪音平滑价格数据并因此识别趋势。

SMA 是移动平均线的最简单形式。每个输出值是历史数据的前n个值的平均值。您可以定义n的值,也称为时间周期。在 SMA 中,时间段内的每个值具有相同的权重,并且不包括时间段之外的值。这使其对数据中的最近变化的响应较低,因此对于平滑价格数据非常有用。SMA 的连续上升表明明显的牛市趋势,而连续下降则表明熊市趋势。因此,它是趋势指标。此外,由于它指示趋势在开始之后,它是滞后指标

SMA 在技术分析中被广泛使用。它也用于计算其他技术指标,无论是与自身还是其他指标结合使用,时间周期相同或不同。

计算 SMA 的公式如下:

n >= 1),这里,n是时间段,必须由用户定义。

虽然了解其背后的数学原理是个好主意,但这个方法不要求你理解或记住给定的公式。我们使用第三方 Python 包talib,它提供了一个用于计算 SMA 的准备好的函数。

入门指南

确保你的 Python 命名空间具有以下对象:

  1. talib(软件包)

  2. plot_candlesticks_chart(函数)

  3. PlotType(枚举)

  4. historical_data(一个pandas DataFrame)

参考本章的技术要求部分设置这些对象。

如何做…

我们将执行以下步骤来完成这个方法:

  1. historical_data上计算 SMA。将其分配给sma_9并打印它:
>>> sma_9 = talib.SMA(historical_data['close'], 
                      timeperiod=9)
>>> sma_9

我们得到以下输出:

0             NaN
1             NaN
2             NaN
3             NaN
4             NaN
5             NaN
6             NaN
7             NaN
8      467.927778
9      468.100000
10     468.211111
11     468.400000
          ...    
117    469.738889
118    469.744444
119    469.716667
120    469.716667
  1. historical_data上绘制sma_9
>>> indicators = [
    {
       'name': 'SMA 9',
       'data': sma_9,
       'extra': {
           'mode': 'lines',
           'line': {
               'color': 'gray'
            }
        }
    }
]
>>> plot_candlesticks_chart(data=historical_data,
                            plot_type=PlotType.JAPANESE, 
                           indicators=indicators, 
                           caption='Trend Indicator: '
                           'Simple Moving Average | '
                           'NSE:TATASTEEL | '
                           '1st Jan, 2020 | '
                           'Candle Interval: 1 Minute')

我们得到以下输出:

绘制图表需要plotly Python 包(github.com/plotly/plotly.py)。如果你还没有安装它,plot_candlesticks_chart函数将为你安装它。

工作原理...

talib软件包提供了一个可用的talib.SMA函数。我们在步骤 1中使用它来计算historical_data上的 SMA,并将其分配给一个新属性sma_9。除了historical_data的 close 系列外,此函数还接受timeperiod作为参数,它应该是一个int值。我们在这里使用9作为参数。sma_9对象是一个pandas.Series对象。这在步骤 1中打印出来。我们使用plot_candlesticks_chart函数在historical_data上绘制sma_9

关于 SMA 指标值和图表的注意事项如下:

  • 由于输出值为NaN(索引从0开始,因此索引07是前八个值),在前八个时间戳值中缺少 SMA 图表。这是因为talib.SMA函数需要至少一个时间段数量的条目来计算 SMA,在我们的情况下为9。从第九行开始,我们可以看到计算的简单移动平均SMA)的值以及historical_data对象的相应时间戳。

  • 当价格上涨时,简单移动平均(SMA)增加,当价格下跌时减少,尽管不会立即在下一个时间戳中体现。

  • SMA 图表的上升或下降遵循相应价格的上升和下降。因此,它是一种滞后指标。换句话说,它不会提前预测趋势结果。

  • SMA 图表是平滑的,没有突然的尖峰,不像历史数据图表那样。因此,SMA 经常用于平滑价格。

趋势指标 – 指数移动平均

EMA 是一种滞后的趋势指标。它用于通过消除噪音平滑价格数据,从而识别趋势,对最近的值赋予更多的权重。

EMA 技术指标的计算是累积的,并且包括所有数据,其权重递减。过去的值对平均值的贡献较小,而最近的值贡献较大。值与当前时间越远,其贡献越小。因此,EMA 是一种移动平均,对数据的最近变化更为敏感。

EMA 技术指标不像 SMA 技术指标,其中时间周期内的每个值具有相等的权重,而时间周期外的值不包括在计算中。

EMA 在技术分析中被广泛使用。它还用于计算其他技术指标,无论是与自身还是其他指标组合使用,时间周期相同或不同。

用于计算 EMA 的递归公式如下:

(n >= 1),这里,n时间周期,必须由用户定义。K 有时被称为 平滑度加权系数

虽然了解其原理的数学原理是个好主意,但是这个示例不要求你理解或记住给定的公式。我们使用第三方 Python 包 talib,它提供了一个用于计算 EMA 的即用型函数。

入门

确保你的 Python 命名空间包含以下对象:

  1. talib(包)

  2. plot_candlesticks_chart(函数)

  3. PlotType(枚举)

  4. historical_data(一个 pandas DataFrame)

参考本章的 技术要求 部分来设置这些对象。

如何操作…

我们将对这个示例执行以下步骤:

  1. 计算 historical_data 上的 EMA。将其赋值给 ema_9 并打印出来:
>>> ema_9 = talib.EMA(historical_data['close'], 
                      timeperiod=9)
>>> ema_9

我们得到以下输出:

0             NaN
1             NaN
2             NaN
3             NaN
4             NaN
5             NaN
6             NaN
7             NaN
8      467.927778
9      468.082222
10     468.135778
11     468.338622
          ...    
117    469.728790
118    469.703032
119    469.662426
120    469.649941
  1. historical_data 上绘制 ema_9
>>> indicators = [
    {
        'name': 'EMA 9',
        'data': ema_9, 
        'extra': {
            'mode': 'lines',
            'line': {
                'color': 'gray'
            }
        }
    }
]
>>> plot_candlesticks_chart(data=historical_data, 
                            plot_type=PlotType.JAPANESE, 
                            indicators=indicators, 
                            caption='Trend Indicator: '
                            'Exponential Moving Average | '
                            'NSE:TATASTEEL | '
                            '1st Jan, 2020 | '
                            'Candle Interval: 1 Minute')

我们得到以下输出:

工作原理…

talib 包提供了一个即用型的 talib.EMA 函数。我们在 step 1 中使用它来计算 historical_data 上的 EMA,并将其赋值给一个新属性 ema_9。除了 historical_data 的收盘价序列外,这个函数还接受一个 timeperiod 作为参数,这应该是一个 int 值。我们在这里使用 9 作为参数。ema_9 对象是一个 pandas.Series 对象。这在 step 1 中打印出来。我们在 step 2 中使用 plot_candlesticks_chart 函数在 historical_data 上绘制 ema_9

观察 EMA 指标值和图表的以下要点:

  • 由于输出值为 NaN(索引从 0 开始,因此索引 07 是前八个值),第一批时间戳值的 EMA 图表缺失。这是因为 talib.EMA 函数需要至少一个时间周期的条目才能计算 EMA,在我们的情况下是 9。从第九行开始,我们可以看到计算的 EMA,每个条目都是 historical_data 对象相应时间戳的 EMA。

  • 随着价格的上涨,EMA 增加,随着价格的下跌而减少,紧密跟随价格。因此,它是一种趋势指标。

  • EMA 绘图的上升或下降跟随相应价格的上升和下降。因此,它是一个滞后指标。换句话说,它不会提前预测趋势结果。

  • EMA 绘图平滑,没有任何突然的波动,不像历史数据图。因此,EMA 用于平滑价格。

  • Plotting trend indicator – simple moving average 配方中的 SMA 绘图相比,EMA 绘图更紧密地跟随价格趋势。这是因为 EMA 给予最近的值更多的权重,而 SMA 中用于计算的每个数据都具有相等的权重。

有关plot_candlesticks_chart函数的使用方法,请参阅本章的How it works… 部分中的 Plotting trend indicator – simple moving average 配方。

趋势指标 – 移动平均线收敛散度

移动平均线收敛散度MACD)是一个滞后的趋势指标。MACD 有三个组成部分:MACD 线、MACD 信号和 MACD 直方图。MACD 线有助于识别趋势变化,因为它信号着新趋势方向的开始。MACD 线的大正值表明较短的 EMA 远大于较长的 EMA。这表明市场处于超买状态,这意味着价格将上涨。同样,MACD 线的大负值表明较短的 EMA 远小于较长的 EMA。这表明市场处于超卖状态,这意味着价格将下跌。当 MACD 线上穿 MACD 信号并且为正时,会产生一个买入信号;当 MACD 线下穿 MACD 信号并且变为负时,会产生一个卖出信号。

计算 MACD 的三个组成部分的公式如下:

  • MACD 线是两个不同时间段 EMA 之间的差值 —— 较短时间段 m 的 EMA 和较长时间段 n 的 EMA:

  • MACD 信号是 MACD 线的 EMA,时间段为 p

  • MACD 直方图是 MACD 线和 MACD 信号之间的差值:

MACD 线的时间周期通常给定为 12m)和 26n),MACD 信号的时间周期通常给定为 9p)。

虽然了解其工作原理的数学是个好主意,但是这个配方不要求你理解或记住给定的公式。我们使用第三方 Python 包talib,它提供了一个用于计算 MACD 的准备好的函数。

入门指南

确保你的 Python 命名空间具有以下对象:

  1. talib(包)

  2. pd(模块)

  3. plot_candlesticks_chart(函数)

  4. PlotType(枚举)

  5. historical_data(一个 pandas DataFrame)

请参考本章的 技术要求 部分设置这些对象。

如何做到…

我们将为此配方执行以下步骤:

  1. historical_data 上计算 MACD。将其分配给 macd_linemacd_signalmacd_histogram。另外,打印它:
>>> macd_line, macd_signal, macd_histogram = \
                            talib.MACD(historical_data['close'],
                                       fastperiod=12,
                                       slowperiod=26,
                                       signalperiod=9)
>>> pd.DataFrame({
    'Line': macd_line,
    'Signal': macd_signal, 
    'Histogram': macd_histogram
})

我们得到以下输出:

        Line   Signal Histogram
0        NaN      NaN       NaN
1        NaN      NaN       NaN
2        NaN      NaN       NaN
3        NaN      NaN       NaN
4        NaN      NaN       NaN
...      ...      ...       ...
116 0.075136 0.087038  -0.011901
117 0.057580 0.081146  -0.023566
118 0.043170 0.073551  -0.030381
119 0.023410 0.063523  -0.040113
120 0.015639 0.053946  -0.038307
  1. 绘制 macd_linemacd_signalmacd_histogram,以及 historical_data
>>> indicators = [
    {
        'name': 'MACD Line',
        'data': macd_line,
        'extra': {
            'mode': 'lines',
            'line': {
                'width': 1
            }
        }
    },
    {
        'name': 'MACD Signal',
        'data': macd_signal,
        'extra': {
            'mode': 'lines',
            'line': {
                'width': 1
            }
        }
    },
    {
        'name': 'MACD Histogram',
        'data': macd_histogram,
        'extra': {
            'mode': 'lines',
            'line': {
                'dash': 'dot',
                'width': 2
            }
        }
    }
]

>>> plot_candlesticks_chart(data=historical_data, 
                            plot_type=PlotType.JAPANESE, 
                            indicators=indicators,
                            plot_indicators_separately=True, 
                            caption='Trend Indicator: Moving '
                            'Average Convergence/Divergence | '
                            'NSE:TATASTEEL | '
                            '1st Jan, 2020 | '
                            'Candle Interval: 1 Minute', 
                            plot_height=700)

我们得到以下输出:

运作原理…

talib 包提供了一个可供使用的 talib.MACD 函数。我们在 步骤 1 中使用它来计算 historical_data 上的 MACD。除了 historical_data 的收盘序列外,此函数还接受 fastperiodslowperiodsignalperiod 作为参数,所有这些参数都应该是 int 类型的对象。在这里,我们将分别使用 26129 作为参数。talib.MACD 函数返回三个 pandas.Series 对象,我们将它们分配给新的属性:macd_linemacd_signalmacd_histogram。这三个对象被连接成一个 pandas.DataFrame 对象,并在 步骤 1 中打印出来。我们使用 plot_candlesticks_chart 函数在 步骤 2 中将 macd_linemacd_signalmacd_histogramhistorical_data 一起绘制。

注意以下关于 MACD 指标值和图表的要点:

  • MACD 图表在前 34 个时间戳值处缺失,仅在第 35 个时间戳处开始出现。这是因为第一个长期 EMA 数据需要 26 个数据点才能出现(短期 EMA 数据在前 12 个数据点出现),并且需要 9 个这些点才能出现 MACD 信号。因此,26 + 9 使其达到 35 个数据点。

  • 当价格上涨时,MACD 线为负数,当价格下跌时,MACD 线为正数。因此,它是一个趋势指标。

  • MACD 线图的上升或下降跟随相应价格的上升和下降。因此,它是一个滞后指标。换句话说,它不能预测趋势结果。

  • MACD 线图平稳,没有突然的波动,不像历史数据图。MACD 信号更加平滑,因为它是 MACD 线的 EMA。

  • 当 MACD 柱状图为正时,趋势为 牛市,这意味着价格上涨。当 MACD 柱状图为负时,趋势为 熊市,这意味着价格下跌。

如何使用 plot_candlesticks_chart 函数,请参考本章的 How it works… 部分的 绘制趋势指标 - 简单移动平均 配方。

趋势指标 - 抛物线停止和反转

抛物 停止和反转 (SAR) 是一个领先的趋势指标。

抛物线 SAR 为每个数据点计算一个跟踪止损。由于数据点是止损点,在趋势时它们远离价格,在趋势反转时穿过价格线。抛物线 SAR 接受两个参数作为输入:加速因子最大 点。

计算抛物线 SAR 的公式不直接,因此此处未提及。如果您对底层数学感兴趣,请参阅 www.tadoc.org/indicator/SAR.htm 上抛物线 SAR 的 TA-Lib 官方文档。虽然了解其工作原理的数学是个好主意,但本食谱不要求您理解或记住给定的公式。我们使用第三方 Python 包 talib,它提供了一个用于计算抛物线 SAR 的现成函数。

开始

确保您的 Python 命名空间具有以下对象:

  1. talib(包)

  2. plot_candlesticks_chart(函数)

  3. PlotType(枚举)

  4. historical_data(一个pandas DataFrame)

请参阅本章的技术要求部分设置这些对象。

如何做...

我们将为此食谱执行以下步骤:

  1. historical_data上计算抛物线 SAR。将其赋值给psar并打印出来:
>>> psar = talib.SAR(historical_data['high'], 
                     historical_data['low'], 
                     acceleration=0.02, 
                     maximum=0.2)
>>> psar

我们得到以下输出:

0             NaN
1      467.000000
2      467.010000
3      467.019800
4      467.029404
          ...    
116    469.175426
117    469.208409
118    469.240073
119    469.270470
120    469.299651
  1. historical_data上绘制psar
>>> indicators = [
    {
        'name': 'PSAR',
        'data': psar,
        'extra': {
            'mode': 'lines',
            'line': {
                'dash': 'dot',
                'width': 2,
                'color': 'purple'
            }
        }
    }
]

>>> plot_candlesticks_chart(data=historical_data, 
                            plot_type=PlotType.JAPANESE,
                            indicators=indicators,
                            caption='Trend Indicator: '
                            'Parabolic Stop and Reverse | '
                            'NSE:TATASTEEL | '
                            '1st Jan, 2020 | '
                            'Candle Interval: 1 Minute')

我们得到以下输出:

它是如何工作的...

talib 包提供了一个现成的 talib.SAR 函数。我们在步骤 1中使用它来计算historical_data上的抛物线 SAR,并将其分配给一个新属性psar。除了historical_data的高低系列外,此函数还接受加速度最大值作为参数,这两个参数都应该是float类型的对象。我们在这里分别使用0.020.2作为参数。psar对象是一个pandas.Series对象。这在步骤 1中打印出来。我们在步骤 2中使用plot_candlesticks_chart函数绘制psar以及historical_data

关于抛物线 SAR 指标值和图表的以下观察点:

  • 抛物线 SAR 被绘制为离散点,因为每个点代表止损点。止损点每次都会变化。所以,它是一个追踪止损。

  • 当抛物线 SAR 图在 OHLC 图下方时,趋势是看涨的;当它在 OHLC 图上方时,趋势是看跌的。因此,它是一个趋势指标。

有关plot_candlesticks_chart函数用法的更多信息,请参阅本章绘制趋势指标 - 简单移动平均食谱中的它是如何工作的...部分。

动量指标 - 相对强度指数

RSI 是一个领先的动量指标。RSI 是最近上升价格运动与绝对价格运动的比率。RSI 始终在0100之间。当值高于70时,它可以解释为表示超买条件,当值低于30时,它可以解释为表示超卖条件。当价格正在创造新的高点或新的低点时,RSI 表示反转。

计算 RSI 的公式并不直接,因此在这里没有提及。如果您对底层数学感兴趣,请参考官方 TA-Lib 关于 RSI 的文档 www.tadoc.org/indicator/RSI.htm。尽管了解这种工作方式的数学是一个好主意,但此示例不需要您理解或记住给定的公式。我们使用第三方 Python 包 talib,它提供了一个计算 RSI 的现成函数。

入门

确保你的 Python 命名空间具有以下对象:

  1. talib(包)

  2. plot_candlesticks_chart(函数)

  3. PlotType(枚举)

  4. historical_data(一个 pandas DataFrame)

请参考本章的 技术要求 部分设置这些对象。

如何执行…

我们将执行以下步骤来完成此过程:

  1. 计算 historical_data 上的 RSI。将其分配给 rsi_14 并打印出来:
>>> rsi_14 = talib.RSI(historical_data['close'], 
                       timeperiod=14)
>>> rsi_14

我们得到以下输出:

0            NaN
1            NaN
2            NaN
3            NaN
          ...
12           NaN
13           NaN
14     70.886076
15     69.932757
16     69.932757
17     64.873530
18     61.976413
         ...    
116    48.449209
117    48.449209
118    48.449209
119    45.997672
120    48.788323
  1. 绘制 rsi_14historical_data 的图表:
>>> indicators = [
    {
        'name': 'RSI (14)', 
        'data': rsi_14,
        'extra': {
            'mode': 'lines',
            'line': {
                'width': 2,
                'color': 'purple'
            }
        }
    }
]

>>> plot_candlesticks_chart(data=historical_data, 
                            plot_type=PlotType.JAPANESE, 
                            indicators=indicators, 
                            plot_indicators_separately=True,
                            caption='Momentum Indicator: '
                            'Relative Strength Index | '
                            'NSE:TATASTEEL | '
                            '1st Jan, 2020 | '
                            'Candle Interval: 1 Minute')

我们得到以下输出:

示例图片

工作原理…

talib 包提供了一个现成的 talib.RSI 函数。我们在 步骤 1 中使用它来计算 historical_data 上的 RSI,并将其赋值给一个新属性 rsi_14。除了 historical_data 的收盘价系列外,此函数还接受 timeperiod 作为参数,该参数应为一个 int 值。我们在此处使用 14 作为参数。rsi_14 对象是一个 pandas.Series 对象。这在 步骤 1 中打印出来。我们使用 plot_candlesticks_chart 函数在 步骤 2 中绘制 rsi_14historical_data 上的图表。

观察关于 RSI 指标值和图表的以下要点:

  • 输出中的前 13 个值为 NaN(索引从 0 开始,因此索引 012 是前 13 个值),因为函数至少需要一个时间段的条目来计算 RSI,在我们的情况下是 14。从第 14 行开始,我们可以看到计算的 RSI,每个条目都是 historical_data 对象对应时间戳的 RSI。

  • RSI 始终介于 0100 之间。

  • 对于给定的图表,价格在下午 12:45 至 1:00 之间突然达到峰值,并且 RSI 移动到 70 以上。因此,它正确地指示了 超买 条件。另外,由于它指示了价格走势的强度,它是一个动量指标。

有关 plot_candlesticks_chart 函数的更多信息,请参考本章 绘制趋势指标 – 简单移动平均 食谱中的 工作原理… 部分。

动量指标 – 随机震荡器

随机震荡器是一种领先的动量指标。它也被简称为 STOCH。STOCH 比较最新的收盘价与最近的交易范围。Fast K 是一个比率,取值在 0100 之间。Fast K 可以有杂乱的运动,因此它使用移动平均进行平滑,即 slow KSlow K 进一步使用另一个移动平均进行平滑,即 slow DSlow K 大于 75 的值表示超买条件,而小于 25 的值表示超卖条件。当 slow K 穿过 slow D 向上时,被认为是一个 买入 信号。类似地,当 slow K 穿过 slow D 向下时,被认为是一个 卖出 信号。

计算 STOCH 的公式如下:

MA 代表 移动平均,可以是 SMA 或 EMA。对于这个配方,我们使用了 SMA。这个公式需要三个时间段:其中一个是 n,另外两个是 MAs 的时间段。我们分析数据的范围由 n 定义。

尽管了解这个工作原理的数学是个好主意,但这个配方不要求你理解或记住给定的公式。我们使用了一个第三方的 Python 包,talib,它提供了一个用于计算 STOCH 的现成函数。

入门

确保你的 Python 命名空间具有以下对象:

  1. talib(包)

  2. pd(模块)

  3. plot_candlesticks_chart(函数)

  4. PlotType(枚举)

  5. historical_data(一个 pandas DataFrame)

请参考本章的 技术要求 部分设置这些对象。

如何做...

我们将按照以下步骤执行这个配方:

  1. historical_data 上计算随机震荡器。将其赋值给 slowkslowd。并打印出来:
>>> slowk, slowd = talib.STOCH(historical_data['high'], 
                               historical_data['low'], 
                               historical_data['close'], 
                               fastk_period=5, 
                               slowk_period=3, 
                               slowk_matype=0, 
                               slowd_period=3, 
                               slowd_matype=0)
>>> pd.DataFrame({
    'Slow K': slowk, 
    'Slow D': slowd
})

我们得到了以下输出:

       Slow K    Slow D
  0       NaN       NaN
  1       NaN       NaN
  2       NaN       NaN
  3       NaN       NaN
  4       NaN       NaN
  5       NaN       NaN
  6       NaN       NaN
  7       NaN       NaN
  8 70.514283 69.296302
  9 71.113411 70.921500
 10 61.606578 67.744757
 11 67.613252 66.777747
 12 52.662272 60.627367
...       ...       ...
116 63.626374 77.374847
117 44.102564 64.420024
118 20.000000 42.576313
119 13.333333 25.811966
120 15.757576 16.363636
  1. 绘制slowkslowd,以及historical_data
>>> indicators = [
    {
        'name': 'Slow K',
        'data': slowk, 
        'extra': {
            'mode':'lines', 
            'line': {
                'width': 2
            }
        }
    },
    {
        'name': 'Slow D',
        'data': slowd, 
        'extra': {
            'mode': 'lines',
            'line': {
                'width': 2
            }
        }
    }
]

>>> plot_candlesticks_chart(data=historical_data, 
                            plot_type=PlotType.JAPANESE, 
                            indicators=indicators, 
                            plot_indicators_separately=True, 
                            caption='Trend Indicator: '
                            'Stochastic Oscillator (Slow) | '
                            'NSE:TATASTEEL | '
                            '1st Jan, 2020 | '
                            'Candle Interval: 1 Minute', 
                            plot_height=700)

我们得到了以下输出:

工作原理是这样的...

talib 包提供了一个现成的 talib.STOCH 函数。我们在 步骤 1 中使用它来计算 historical_data 上的随机震荡器。除了historical_data的高、低和收盘系列外,此函数还接受以下参数:

  • fastk_periodint):我们分析数据的范围。在这里,我们将值取为 5

  • slowk_periodint):计算 fast K 上的移动平均的时间段。在这里,我们将值取为 3

  • slowk_matypeint):移动平均类型。值为 0 表示 SMA,值为 1 表示 EMA。在这里,我们将值取为 0

  • slowd_periodint):计算 slow K 上的移动平均的时间段。在这里,我们将值取为 3

  • slowd_matypeint):移动平均类型。值为 0 表示 SMA,值为 1 表示 EMA。在这里,我们将值取为 0

talib.STOCH函数返回两个pandas.Series对象,我们将它们分配给新属性:slowkslowd。这两个对象被连接成一个pandas.DataFrame对象,并在步骤 1中打印出来。我们使用plot_candlesticks_chart函数在步骤 2中绘制slowkslowd,以及historical_data

观察以下关于 STOCH 指标值和图表的要点:

  • 输出中的前八个值为NaN(索引从0开始,因此索引07是前 8 个值)。这是因为获取快速 K需要前五个值,获取慢 K需要三个快速 K,获取慢 D需要三个慢 K。因此,这是 5 +(3-1)+(2-1)= 9。(我们两次减去1,因为先前计算的最后一个值是下一个计算的第一个值,因此已经计算了一次。)从第九行开始,我们可以看到慢 K慢 D的计算值以及historical_data对象的相应时间戳。

  • 慢 K慢 D的值始终在0100之间。

  • 慢 K慢 D图的上升或下降随后往往伴随着相应价格的上升和下降,尤其是在下午 12:45 后的图表中尤为明显。因此,它是一个领先指标。换句话说,它可以提前预测趋势结果。

  • 由于它是一个领先指标,它对价格的反应很快。这经常导致错误信号,就像在下午 12:30 到 12:45 之间的图表中所看到的那样。(为了保护自己免受这些情况的影响,您可以在策略中使用更多的指标来获取趋势或反转的额外确认。)

波动率指标 - 布林带

布林带是滞后的波动率指标。布林带由三条线或带组成 - 中带下带上带。当价格波动性较高时,带之间的间隙扩大,而价格波动性较低时,带之间的间隙缩小。

布林带(Bollinger Bands)是一种超买或超卖条件的指标。当价格接近上带或下带时,该指标预测反转很快就会发生。中带充当支撑或阻力水平。

上带和下带也可以解释为价格目标。当价格从上带反弹并穿过中带时,下带成为价格目标,反之亦然。

计算布林带的公式如下。

布林带将典型价格TP)定义为蜡烛的高、低和收盘价的平均值。TP 用于计算中带、下带和上带:

中带是 TP 的 SMA:

上带和下带是中带上下标准差的整数(F)倍数。F的典型值为 2:

尽管了解这是如何运作的数学知识是个好主意,但这个示例不要求你理解或记住给定的公式。我们使用第三方 Python 软件包talib,它提供了一个用于计算布林带的现成函数。

入门指南

确保你的 Python 命名空间中有以下对象:

  1. talib(软件包)

  2. pd(模块)

  3. plot_candlesticks_chart(函数)

  4. PlotType(枚举)

  5. historical_data(一个pandas DataFrame)

请参考本章的技术要求部分设置这些对象。

如何操作…

对于这个示例,我们执行以下步骤:

  1. 计算historical_data上的布林带。将其分配给upperbandmiddlebandlowerband。同时,打印出来:
>>> upperband, middleband, lowerband = talib.BBANDS(
                                        historical_data['close'], 
                                        timeperiod=5, 
                                        nbdevup=2, 
                                        nbdevdn=2, 
                                        matype=0)
>>> pd.DataFrame({
    'Upperband': upperband,
    'Middleband': middleband,
    'Lowerband': lowerband
})

我们得到以下输出:

      Upperband Middleband   Lowerband
  0         NaN        NaN         NaN
  1         NaN        NaN         NaN
  2         NaN        NaN         NaN
  3         NaN        NaN         NaN
  4  468.138749     467.50  466.861251
...         ...        ...         ...
116  470.071661     469.83  469.588339
117  470.080666     469.78  469.479334
118  470.020666     469.72  469.419334
119  469.959839     469.65  469.340161
120  469.660000     469.58  469.500000
  1. historical_data上绘制upperbandmiddlebandlowerband
>>> indicators = [
    {
        'name': 'Upperband',
        'data': upperband, 
        'extra': {
            'mode': 'lines', 
            'line': {
                'width': 1
            }
        }
    },
    {
        'name': 'Middleband',
        'data': middleband, 
        'extra': {
            'mode':'lines',
            'line': {
                'width': 1
            }
        }
    },
    {
        'name': 'Lowerband',
        'data': lowerband, 
        'extra': {
            'mode': 'lines',
            'line': {
                'width': 1
            }
        }
    }
]

>>> plot_candlesticks_chart(data=historical_data, 
                            plot_type=PlotType.JAPANESE, 
                            indicators=indicators, 
                            caption='Volatility Indicator: '
                            'Bollinger Bands | '
                            'NSE:TATASTEEL | '
                            '1st Jan, 2020 | '
                            'Candle Interval: 1 Minute')

我们得到以下输出:

运作原理…

talib软件包提供了一个现成的talib.BBANDS函数。我们在步骤 1中使用它来计算historical_data上的布林带。除了historical_data的收盘系列之外,此函数还采用以下参数:

  • timeperiodint):计算 TP 上的 SMA 的时间段。TP 是高、低和收盘价格的平均值。在这里,我们将值设为5

  • nbdevupint):上带的无偏标准差数量。在这里,我们将值设为2

  • nbdevdnint):下带的无偏标准差数量。在这里,我们将值设为2

  • matypeint):移动平均类型。0表示 SMA,1表示 EMA。在这里,我们将值设为0

talib.BBANDS函数返回三个pandas.Series对象,我们将其分配给新属性:upperbandmiddlebandlowerband。这三个对象被连接成一个pandas.DataFrame对象,并在步骤 1中打印出来。我们使用plot_candlesticks_chart函数在步骤 2中在historical_data上绘制upperbandmiddlebandlowerband

观察布林带指标值和图表的以下几点:

  • 输出的前四个值是NaN(索引从0开始,所以索引03是前四个值),因为talib.BBANDS函数需要至少一个时间段的条目数来计算布林带,我们的情况下是5。从第五行开始,我们可以看到所有三个带的计算值和historical_data对象的相应时间戳。

  • 带的升降跟相应价格的升降一致。因此,布林带是一种滞后指标。换句话说,它们不能提前预测趋势结果。

  • 在图表中的下午 12:45,我们看到带变窄了。这是由于那时的低波动性(价格变化的缓慢速率)。

  • 在图中 1 点前,我们看到带宽之间的差距急剧扩大。这是因为那个时间点周围的波动性很高(价格变化速度快)。

  • 大多数情况下,当价格触及上轨时,它开始向下移动(相反的方向)。你可以将这些情况用作你的策略的卖出信号。

  • 大多数情况下,当价格触及下轨时,它开始向上移动(相反的方向)。你可以将这些情况用作你的策略的买入信号。

波动性指标 – 平均真实波动幅度

平均真实波动幅度ATR)是一种滞后的波动性指标。ATR 是波动性的一种度量。高 ATR 值表示高波动性,低值表示低波动性。

计算 ATR 的公式不是直接的,因此此处未提及。如果你对底层数学感兴趣,请参考TA-Lib关于 ATR 的官方文档,网址为www.tadoc.org/indicator/ATR.htm。虽然了解这个工作原理的数学是个好主意,但这个示例不需要你理解或记住给定的公式。我们使用第三方 Python 包talib,它提供了一个用于计算 ATR 的现成函数。

入门

确保你的 Python 命名空间中有以下对象:

  1. talib(包)

  2. pd(模块)

  3. plot_candlesticks_chart(函数)

  4. PlotType(枚举)

  5. historical_data(一个pandas DataFrame)

请参考本章的技术要求部分来设置这些对象。

如何执行…

我们将执行以下步骤来完成这个示例:

  1. historical_data上计算 ATR。将其赋值给atr_14并打印出来:
>>> atr_14 = talib.ATR(historical_data['high'], 
                       historical_data['low'], 
                       historical_data['close'], 
                       timeperiod=14)
>>> atr_14

我们得到以下输出:

0           NaN
1           NaN
2           NaN
3           NaN
4           NaN
5           NaN
6           NaN
7           NaN
8           NaN
9           NaN
10          NaN
11          NaN
12          NaN
13          NaN
14     0.575000
15     0.555357
16     0.562117
17     0.550538
18     0.529071
         ...   
116    0.375902
117    0.359766
118    0.348354
119    0.330614
120    0.317713
  1. 绘制atr_14,以及historical_data
>>> indicators = [
    {
        'name': 'ATR (14)',
        'data': atr_14,
        'extra': {
            'mode': 'lines',
            'line': {
                'width': 2,
                'color': 'purple'
            }
        }
    }
]

>>> plot_candlesticks_chart(data=historical_data,
                            plot_type=PlotType.JAPANESE,
                            indicators=indicators,
                            plot_indicators_separately=True, 
                            caption='Volatility Indicator: '
                            'Average True Range | '
                            'NSE:TATASTEEL | '
                            '1st Jan, 2020 | '
                            'Candle Interval: 1 Minute', 
                            plot_height=700)

我们得到以下输出:

如何运作…

talib包提供了一个现成的talib.ATR函数。我们在步骤 1中使用它来计算historical_data上的 ATR,并将其赋值给一个新属性,atr_14。除了historical_data的高、低和收盘系列之外,该函数还将timeperiod作为参数,它应该是一个int值。我们在这里使用14作为参数。rsi_14对象是一个pandas.Series对象。这在步骤 1中打印出来。我们使用plot_candlesticks_chart函数在步骤 2中将atr_14绘制在historical_data上。

观察关于 ATR 指标值和图表的以下要点:

  • 输出中的前 14 个值是NaN(索引从0开始,所以索引013是前 14 个值),因为talib.ATR函数需要至少比时间周期数多一个条目来计算 ATR,而在我们的情况下是14。从第 15 行开始,我们可以看到所有计算出的 ATR 值以及historical_data对象的相应时间戳。

  • 当波动性较高(价格变化速度快)时,ATR 开始上升。这可以在图表中午 1 点左右看到。

  • 当波动性较低(价格变化速度较慢)时,ATR 开始下降。这可以在图表末尾附近看到。

成交量指标 – 平衡量

平衡量OBV)是一个领先的量度指标。OBV 是上升量和下降量的累积总和。当收盘价高于前一个收盘价时,将成交量加到累积总和中,而当收盘价低于前一个收盘价时,则将成交量从累积总和中减去。

要解释 OBV,可以观察 OBV 和价格的运动。如果价格在 OBV 之前移动,则它是一个非确认的移动。OBV 中一系列上升峰或下降谷指示着强劲的趋势。如果 OBV 是平的,则市场没有趋势。

计算 OBV 的公式如下:

  • 如果 close > close[-1],那么 OBV = OBV[-1] + volume

  • 如果 close < close[-1],那么 OBV = OBV[-1] - volume

  • 如果 close = close[-1],那么 OBV = OBV[-1]

虽然了解其工作原理的数学知识是个好主意,但本示例不要求你理解或记住给定的公式。我们使用第三方 Python 包talib,它提供了一个用于计算 OBV 的现成函数。

入门

确保你的 Python 命名空间拥有以下对象:

  1. talib(包)

  2. pd(模块)

  3. plot_candlesticks_chart(函数)

  4. PlotType(枚举)

  5. historical_data(一个pandas DataFrame)

请参考本章的技术要求部分设置这些对象。

如何做…

我们将为此示例执行以下步骤:

  1. 计算historical_data上的 OBV。将其赋值给obv并打印出来:
>>> obv = talib.OBV(historical_data['close'], 
                    historical_data['volume'])
>>> obv

我们得到以下输出:

0        5694.0
1       16546.0
2       20717.0
3       20717.0
4      211302.0
         ...   
116    406508.0
117    406508.0
118    406508.0
119    398353.0
120    405326.0
  1. 在图中绘制obv,以及historical_data
>>> indicators = [
    {
        'name': 'On Balance Volume',
        'data': obv,
        'extra': {
            'mode': 'lines',
            'line': {
                'width': 2,
                'color': 'purple'
            }
        }
    }
]

>>> plot_candlesticks_chart(data=historical_data, 
                            plot_type=PlotType.JAPANESE, 
                            indicators=indicators,
                            plot_indicators_separately=True, 
                            caption='Volume Indicator: '
                            'On Balance Volume | '
                            'NSE:TATASTEEL | '
                            '1st Jan, 2020 | '
                            'Candle Interval: 1 Minute', 
                            plot_height=700)

我们得到以下输出:

工作原理…

talib包提供了一个现成的talib.OBV函数。我们在步骤 1中使用它来计算historical_data上的 OBV,并将其赋值给一个新的属性obv。该函数以historical_data的收盘价和成交量序列作为参数。obv对象是一个pandas.Series对象。这在步骤 1中打印出来。我们使用plot_candlesticks_chart函数在步骤 2中绘制obvhistorical_data

关于 OBV 指标值和图表的观察要点如下:

  • 在表中没有NaN输出。从第一行起,我们就可以看到所有计算得出的 OBV 值以及historical_data对象的相应时间戳。一个数据点就足以计算 OBV。

  • 这些值始终为正数。

  • OBV 图的上升或下降通常紧随相应价格的上升和下降。因此,它是一个领先的指标。换句话说,它提前预测趋势结果。(由于它是一个领先的指标,它会迅速反应价格。这经常导致错误信号。为了保护自己免受这些情况的影响,您可以在策略中使用更多的指标来获得趋势或反转的额外确认。)

成交量指标 - 成交量加权平均价格

成交量加权平均价格VWAP)是一种滞后的成交量指标。VWAP 是一种加权移动平均,其使用成交量作为加权因子,使较高成交量的日子具有更高的权重。它是一个非累积移动平均,因此仅使用时间段内的数据进行计算。

尽管这个函数在talib中可用,但我们将向您展示如何在此处手动计算指标,以创建其公式。这将帮助您在可能使用客户技术指标或从talib中缺失的不太流行的指标时创建自己的指标。

计算 VWAP 的公式如下:

这里,n时间段,必须由用户定义。

入门指南

确保您的 Python 命名空间具有以下对象:

  1. pd(模块)

  2. plot_candlesticks_chart(函数)

  3. PlotType(枚举)

  4. historical_data(一个pandas DataFrame)

请参考本章的技术要求部分设置这些对象。

如何做到这一点…

对于这个配方,我们将执行以下步骤:

  1. 定义一个计算VWAP的函数:
>>> def VWAP(hist_data_df):
        """
        Returns VWAP computed over the given historical data
        hist_data_df: A pandas DataFrame of historical data with 
                        columns
        'timestamp', 'high', 'low', 'close' and 'volume'
        """
        hist_data_df['date'] = \
               hist_data_df['timestamp'].apply(lambda x: x.date())
        unique_dates = sorted(set(hist_data_df['date']))
        vwap = []

        """
        Compute vwap for each day's data and append it to vwap 
        variable
        """
        for i, date in enumerate(unique_dates):
            day_df = hist_data_df.loc[hist_data_df['date'] == date]
            typical_price_day_df = (day_df.high + day_df.low + 
                                    day_df.close)/3
            vwap_day = list(((typical_price_day_df * 
                              day_df.volume).cumsum()) /                     
                              day_df.volume.cumsum())
            vwap += vwap_day

        return pd.Series(vwap)
  1. historical_data上计算VWAP。将其分配给vwap并打印它:
>>> vwap = VWAP(historical_data)
>>> vwap

我们得到以下输出:

0      467.150000
1      467.259311
2      467.280925
3      467.299623
4      468.085910
          ...    
116    468.965162
117    468.967599
118    468.969499
119    468.971309
120    468.972893
  1. vwaphistorical_data一起绘制:
>>> indicators = [
    {
        'name': 'VWAP',
        'data': vwap, 
        'extra': {
            'mode': 'lines',
            'line': {
                'width': 2, 
                'color': 'purple'
            }
        }
    }
]

>>> plot_candlesticks_chart(data=historical_data, 
                            plot_type=PlotType.JAPANESE, 
                            indicators=indicators,
                            plot_indicators_separately=True,
                            caption='Volume Indicator: '
                            'Volume Weighted Average Price | '
                            'NSE:TATASTEEL | '
                            '1st Jan, 2020 | '
                            'Candle Interval: 1 Minute',
                            plot_height=700)

我们得到以下输出:

工作原理是…

我们定义一个函数,根据给定的历史数据作为pandas.DataFrame对象计算VWAP。它的工作方式如下:

  1. 在历史数据中找到所有唯一的日期

  2. 遍历所有唯一的日期:

  • 提取day_df,一个pandas.DataFrame对象,其中包含落在唯一日期上的historical_data的条目

  • 计算typical_price_day_df,即当天高、低和收盘价的平均典型价格

  • 计算vwap_day,这是day_df中所有条目的典型价格加权平均的列表

  1. 将所有的vwap_day值作为一个pandas.Series对象附加在一起返回

我们在historical_data上使用VWAP函数计算步骤 2中的VWAP,并将其分配给一个新属性vwapvwap对象是一个pandas.Series对象。我们在步骤 3中使用plot_candlesticks_chart函数将vwaphistorical_data一起绘制。

关于VWAP指标值和图表,请注意以下要点:

  • 表格中没有NaN输出。从第一行开始,我们就可以看到VWAP的所有计算值以及historical_data对象的相应时间戳。一个单独的数据点足以计算VWAP

  • 值始终为正。

  • VWAP图的上升或下降遵循相应价格的上升和下降。因此,它是滞后指标。换句话说,它不会提前预测趋势结果。

第六章:在交易所上下常规订单

本章介绍了通过经纪人 API 可以在交易所上下的各种类型的常规订单。这些示例包括关于下订单、查询订单状态以及退出已完成订单的代码。这些示例将是你算法交易策略的基础部分。了解所有类型的订单并知道在特定需求下应该下哪种订单对于建立成功的交易策略至关重要。

每个订单都有多个属性,如下列表所述:

  • 订单交易类型:此属性简单地定义订单是 BUY 交易还是 SELL 交易。可能的值显然可以是 BUYSELL 中的一个。

  • 订单类型:此属性定义订单的类型,这将暗示订单的高级行为。常用的订单类型包括 REGULAR 订单、BRACKET 订单、COVER 订单等等。你的经纪人可能定义了更多类型的订单。本章包括 REGULAR 订单的示例。

  • 订单代码:此属性定义订单是否在当天交易结束时结算(即退出)或持续到下一个交易日。可能的值可以是 INTRADAYDELIVERY 中的一个。如其名,INTRADAY 订单的寿命只有一天,如果未在当天结束前退出,经纪人会在当天结束时退出。另一方面,DELIVERY 订单交付到用户的证券账户,并在用户明确结算前存在。DELIVERY 订单在最终交付给用户的证券账户之前可能会经历多个状态,就像这个例子中一样:

  • DELIVERY T+0(下单当天)

  • DELIVERY T+1(下单后的下一个交易日)

  • DELIVERY T+2(下单后的第二个交易日及以后)

这取决于基础段。例如,股票段工具的 DELIVERY 订单会经历这些状态。期货和期权段工具的 DELIVERY 订单不会经历这些状态。

  • 订单种类:此属性与订单的定价和激活有关。可能的值可以是以下之一:

  • MARKET:订单立即以最佳可用市场价格下单。用户下订单时不需要指定价格。

  • LIMIT:订单以指定价格下单,该价格要么低于最近交易价格LTP)(对于 BUY 订单),要么高于 LTP(对于 SELL 订单)。用户下订单时应指定一个限价限价将是用户打算买入/卖出工具的价格。

  • STOPLOSS_LIMIT:订单以指定价格下单,该价格要么高于 LTP(对于“买入”订单),要么低于 LTP(对于“卖出”订单)。用户应指定触发价格限价。当LTP穿过触发价格时,订单被激活,并以指定的限价下单。

  • STOPLOSS_MARKET:订单以指定价格下单,该价格要么高于 LTP(对于“买入”订单),要么低于 LTP(对于“卖出”订单)。用户应指定触发价格。当 LTP 穿过触发价格时,订单被激活,并以市价下单。

所有这些属性一起定义了一个完整的订单。为了下订单,四个属性都应该被精确知道。

上述属性,即订单交易类型、订单类型、订单代码和订单种类,由pyalgotrading软件包定义。您选择的经纪人可能会以不同的方式命名相同的属性。pyalgotrading软件包在内部处理这样的转换。

每个放置在交易所的订单在其生命周期内都经历各种状态。本章使用的经纪人支持每个订单的以下状态:

  • PUT ORDER REQ RECEIVED

  • VALIDATION PENDING

  • OPEN PENDING

  • TRIGGER PENDING

  • CANCEL PENDING

  • COMPLETE

  • CANCELLED

  • OPEN

  • REJECTED

本章的示例为常规订单的各种类型的状态转换提供了详细的状态机图。

如果您使用的是不同的经纪人,则经纪人可能支持不同的订单状态或以不同的方式命名订单状态。您可以查阅经纪人 API 文档以了解每个状态的含义。

在本章中,我们将介绍以下示例:

  • 放置常规市价订单

  • 放置常规限价订单

  • 放置常规止损限价订单

  • 放置常规止损市价订单

请确保您在活跃市场时间内,经纪账户余额充足时尝试所有这些示例。如果在非市场时间或资金不足时尝试这些示例,您的订单将被经纪人拒绝。这意味着订单将永远不会到达交易所,您将无法获得预期的响应。

技术要求

要成功执行本章中的示例,您需要以下内容:

  • Python 3.7+

  • Python 软件包:pyalgotrading$ pip install pyalgotrading

本章的最新 Jupyter 笔记本可以在 GitHub 上找到:github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/tree/master/Chapter06

与经纪人建立连接的第一件事是获取 API 密钥。经纪人会向每位客户提供独特的密钥,通常作为一个api-keyapi-secret密钥对。这些 API 密钥通常是收费的,通常是按月订阅的方式收费。在开始之前,您需要从经纪人网站获取您的api-keyapi-secret的副本。您可以参考附录 I了解更多详情。

以下步骤将帮助您建立与 Zerodha 的经纪人连接,这将被本章中的所有配方使用。请确保在尝试任何配方之前已经执行了这些步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.broker.broker_connection_zerodha import BrokerConnectionZerodha
>>> from pyalgotrading.constants import *

所有pyalgotrading常量现在都可在您的 Python 命名空间中使用。

  1. 从经纪人处获取api_keyapi_secret密钥。这些对您是唯一的,并将被经纪人用于识别您的证券账户:
>>> api_key = "<your-api-key>"
>>> api_secret = "<your-api-secret>"
>>> broker_connection = BrokerConnectionZerodha(api_key, 
                                                api_secret)

我们得到以下输出:

Installing package kiteconnect via pip. This may take a while...
Please login to this link to generate your request token: https://kite.trade/connect/login?api_key=<your-api-key>&v=3

如果您第一次运行此程序且未安装kiteconnectpyalgotrading将自动为您安装它。第 2 步的最终输出将是一个链接。单击该链接并使用您的 Zerodha 凭据登录。如果验证成功,您将在浏览器地址栏中看到一个类似于https://127.0.0.1/?request_token=<alphanimeric-toke>&action=login&status=success的链接。

我们有以下示例:

https://127.0.0.1/?request_token=H06I6Ydv95y23D2Dp7NbigFjKweGwRP7&action=login&status=success
  1. 复制字母数字令牌并将其粘贴到request_token中:
>>> request_token = "<your-request-token>"
>>> broker_connection.set_access_token(request_token)

broker_connection实例现在已准备好执行 API 调用。

pyalgotrading包支持多个经纪人,并为每个经纪人提供一个连接对象类,具有相同的方法。它将经纪人 API 抽象在统一接口之后,因此用户无需担心底层经纪人 API 调用,并且可以像本章中的所有配方一样使用。仅经纪人连接设置程序会因经纪人而异。如果您不是使用 Zerodha 作为您的经纪人,您可以参考pyalgotrading文档以设置经纪人连接。对于 Zerodha 用户,前面部分提到的步骤就足够了。

下达常规市价订单

常规市价订单是最简单的订单类型。该订单类型用于立即以最佳可用市价下达单一订单。市价等同于 LTP(如第三章中金融工具的最新交易价格配方所述)。

下达常规市价订单后,它会在最终达到终结状态(COMPLETEREJECTED)之前经过各种中间状态。常规市价订单立即转移到终结状态,而无需等待任何中间状态。下面的状态机图演示了常规市价订单在其生命周期内的各种状态:

此配方演示了下面的常规市价订单的下达和查询其状态:

  • BUYREGULARINTRADAYMARKET 订单

  • SELLREGULARINTRADAYMARKET 订单

  • BUYREGULARDELIVERYMARKET 订单

  • SELLREGULARDELIVERYMARKET 订单

准备工作

确保 broker_connection 对象和 pyalgotrading 包中的常量在您的 Python 命名空间中可用。请参考本章节的技术要求部分设置此对象。

如何操作…

我们为此示例执行以下步骤:

  1. 获取一个金融工具并将其分配给 instrument
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'HDFCBANK')
  1. 下单 BUYREGULARINTRADAYMARKET 并显示订单 ID:
>>> order1_id = broker_connection.place_order(
                instrument=instrument, 
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.BUY,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.INTRADAY,
                order_variety=BrokerOrderVarietyConstants.MARKET,
                quantity=1)
>>> order1_id

我们获得以下输出(您的输出可能会有所不同):

'200304002243710'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们获得以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以在以下截图中找到您的订单详情(一些数据可能与您的实际情况不同):

  1. 下单 SELLREGULARINTRADAYMARKET 并显示订单 ID:
>>> order2_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.SELL,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.INTRADAY,
                order_variety=BrokerOrderVarietyConstants.MARKET,
                quantity=1)
>>> order2_id

我们获得以下输出(您的输出会有所不同):

'200304002244044'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们获得以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以在以下截图中找到您的订单详情(一些数据可能与您的实际情况不同):

  1. 下单 BUYREGULARDELIVERYMARKET 并显示订单 ID:
>>> order3_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.BUY,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.DELIVERY,
                order_variety=BrokerOrderVarietyConstants.MARKET,
                quantity=1)
>>> order3_id

我们获得以下输出(您的输出可能会有所不同):

'200304002244263'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order3_id)

我们获得以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以在以下截图中找到您的订单详情(一些数据可能与您的实际情况不同):

  1. 下单 SELLREGULARDELIVERYMARKET 并显示订单 ID:
>>> order4_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.SELL,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.DELIVERY,
                order_variety=BrokerOrderVarietyConstants.MARKET,
                quantity=1)
>>> order4_id

我们获得以下输出(您的输出可能会有所不同):

'200304002244333'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order4_id)

我们获得以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以在以下截图中找到您的订单详情(一些数据可能与您的实际情况不同):

工作原理…

步骤 1中,您使用 BrokerConnectionZerodha 类的 get_instrument() 方法获取一个工具并将其分配给一个新属性 instrument。此对象是 Instrument 类的一个实例。调用 get_instrument 所需的两个参数是交易所('NSE')和交易符号('HDFCBANK')。

步骤 2中,您使用 BrokerConnection 类的 place_order 方法在交易所上下单 BUYREGULARINTRADAYMARKET 订单。place_order 方法是经纪人特定的下单 API 的包装器。它接受以下属性:

  • instrument:这是必须下单的金融工具,应该是 Instrument 类的实例。我们在这里传递 instrument

  • order_transaction_type:这是订单交易类型,应该是 BrokerOrderTransactionTypeConstants 类型的枚举。我们在这里传递 BrokerOrderTransactionTypeConstants.BUY

  • order_type:这是订单类型,应该是 BrokerOrderTypeConstants 类型的枚举。我们在这里传递 BrokerOrderTypeConstants.REGULAR

  • order_code:这是订单代码,应该是 BrokerOrderCodeConstants 类型的枚举。我们在这里传递 BrokerOrderCodeConstants.INTRADAY

  • order_variety:这是订单变体,应该是 BrokerOrderVarietyConstants 类型的枚举。我们在这里传递 BrokerOrderVarietyConstants.MARKET

  • quantity:这是交易给定金融工具的股票数量,应该是正整数。我们在这里传递 1

(传递给 place_order 方法的属性是经纪人无关的常量,之前从 pyalgotrading.constants 模块导入。)

步骤 2 中下单后,你会从经纪人那里获得一个订单 ID,然后将其赋给一个新属性 order1_idorder1_id 对象是 string 类型。如果由于某种原因订单未成功下达,可能不会获得订单 ID。在 步骤 3 中,你使用 broker_connection 对象的 get_order_status() 方法获取已下达订单的状态。你将 order1_id 作为 get_order_status() 方法的参数传递。你会得到订单状态为 'COMPLETE',类型为 string。你随时可以使用 order1_id 获取已下达订单的状态。

你也可以通过登录经纪人网站并检查订单部分来验证订单的成功下达。你应该看到类似于步骤 3 输出的屏幕截图中显示的数据。

此食谱中的其他步骤遵循相同的模式:下单并获取其状态,但使用不同的属性组合:

  • 步骤 45SELLREGULARINTRADAYMARKET 订单

  • 步骤 67BUYREGULARDELIVERYMARKET 订单

  • 步骤 89SELLREGULARDELIVERYMARKET 订单

经纪人返回的订单 ID 对于所有客户都是唯一的。你永远不会再次获得相同的订单 ID,也永远不会获得已被其他人下单的订单 ID。

下达常规限价订单

常规限价订单是一种在特定价格下单的订单类型。与常规市价订单不同,这不是市价。要下达此订单,需要一个特定的参数,称为 限价。此参数应满足以下条件:

  • 限价 应低于 BUY 订单的市价。

  • 限价 应高于 SELL 订单的市价。

如果这些条件不满足,则订单可能会以市场价格下单,从而将其转换为常规市价订单,或者经纪人可能会将其拒绝为无效订单。

下单常规限价订单时,它会在最终到达终态(完成取消拒绝)之前经历各种中间状态。在有利的市场条件实现之前,常规限价订单可能会在开放状态停留一段时间,然后转移到完成状态。以下状态机图演示了常规限价订单在其生命周期中的各种状态:

此示例演示了下列常规限价订单的下单及查询其状态:

  • 购买常规即日交易限价订单

  • 卖出常规即日交易限价订单

  • 购买常规交割限价订单

  • 卖出常规交割限价订单

准备就绪

确保您的 Python 命名空间中可用来自pyalgotrading包的broker_connection对象和常量。请参考本章的技术要求部分设置此对象。

如何操作...

我们为此示例执行以下步骤:

  1. 获取一个金融工具并将其分配给instrument
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'ICICIBANK')
  1. 获取最新成交价。下单购买常规即日交易限价并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order1_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.BUY,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.INTRADAY,
                order_variety=BrokerOrderVarietyConstants.LIMIT,
                quantity=1,
                price=ltp-1)
>>> order1_id

我们得到以下输出(您的输出可能有所不同):

'200303003518407'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出(您的输出可能有所不同):

'OPEN'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详情,如下图所示(您的一些数据可能不同):

  1. 过一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出(您的输出可能有所不同):

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详情,如下图所示(您的一些数据可能不同):

  1. 获取最新成交价。下单卖出常规即日交易限价并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order2_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.SELL,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.INTRADAY,
                order_variety=BrokerOrderVarietyConstants.LIMIT,
                quantity=1,
                price=ltp+1)
>>> order2_id

我们得到以下输出(您的输出可能有所不同):

'200303003243352'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下结果:

'OPEN'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详情,如下图所示(您的一些数据可能不同):

  1. 过一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出(您的输出可能有所不同):

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详情,如下图所示(您的一些数据可能不同):

  1. 获取 LTP。下一个BUYREGULARDELIVERYLIMIT订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order3_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.BUY,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.DELIVERY,
                order_variety=BrokerOrderVarietyConstants.LIMIT,
                quantity=1,
                price=ltp-1)
>>> order3_id

我们得到以下输出(您的输出可能有所不同):

'200303003266560'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order3_id)

我们得到以下输出:

'OPEN'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下图所示(您的数据可能有所不同):

  1. 一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order3_id)

我们得到以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下图所示(您的数据可能有所不同):

  1. 获取 LTP。下一个SELLREGULARDELIVERYLIMIT订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order4_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.SELL,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.DELIVERY,
                order_variety=BrokerOrderVarietyConstants.LIMIT,
                quantity=1,
                price=ltp+1)
>>> order4_id

我们得到以下输出(您的输出可能有所不同):

'200303003280699'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order4_id)

我们得到以下输出:

'OPEN'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下图所示(您的数据可能有所不同):

  1. 一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order4_id)

我们得到以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下图所示(您的数据可能有所不同):

工作原理...

步骤 1中,您使用BrokerConnectionZerodha类的get_instrument()方法获取一个工具并将其分配给一个新属性instrument。该对象是Instrument类的实例。调用get_instrument所需的两个参数是交易所('NSE')和交易符号('ICICIBANK')。

步骤 2中,您使用BrokerConnectionZerodha类的get_ltp()方法获取工具的 LTP,并将其分配给一个新属性ltp。这里将instrument对象作为参数传递。接下来,您使用broker_connection对象的place_order方法在交易所上下订单。place_order方法是经纪人特定的下订单 API 的包装器。它接受以下属性:

  • instrument:这是必须下订单的金融工具,应该是Instrument类的实例。我们在这里传递instrument

  • order_transaction_type:这是订单交易类型,应该是BrokerOrderTransactionTypeConstants类型的枚举。我们在这里传递BrokerOrderTransactionTypeConstants.BUY

  • order_type:这是订单类型,应该是BrokerOrderTypeConstants类型的枚举。我们在这里传递BrokerOrderTypeConstants.REGULAR

  • order_code:这是订单代码,应该是BrokerOrderCodeConstants类型的枚举。我们在这里传递BrokerOrderCodeConstants.INTRADAY

  • order_variety:这是订单类型,应该是BrokerOrderVarietyConstants类型的枚举。我们在这里传递BrokerOrderVarietyConstants.LIMIT

  • quantity:要交易的股票数量,应为正整数。我们在这里传递1

  • price:这是应该下订单的限价。我们在这里传递ltp-1,这意味着低于ltp1 单位价格。

(传递给place_order方法的属性是经纪人无关的常量,之前从pyalgotrading.constants模块导入。)

步骤 2 中下订单时,您将从经纪人那里获得一个订单 ID,将其分配给一个新属性,order1_idorder1_id 对象是一个字符串。如果由于某种原因订单未成功下达,则可能不会获得订单 ID。请注意,价格参数传递了值ltp-1。这意味着订单被放置在市场价格下方,这是放置BUY LIMIT订单的必要条件之一。

步骤 3 中,您使用broker_connection对象的get_order_status()方法获取已下订单的状态。您将order1_id作为参数传递给get_order_status()方法。您将订单状态获取为'OPEN',一个字符串。您随时可以使用order1_id来获取已下订单的状态。在步骤 4 中,您再次获取订单状态,如果订单已完成,您将获取订单状态为'COMPLETE'

您还可以通过登录经纪人网站并检查订单部分来验证您的订单成功下达。您应该看到与步骤 3步骤 4 的输出中显示的屏幕截图类似的数据。

步骤 3 中,如果您看到状态为'COMPLETE'而不是'OPEN',这可能是由于市场波动性过高。如果您希望订单在'OPEN'状态下保持一段时间,请尝试将订单放置在远离市场价格的位置。

此配方中的其他步骤遵循相同的模式,用于放置订单和获取其状态,用于不同属性组合:

  • 步骤 567SELLREGULARINTRADAYLIMIT订单

  • 步骤 8910BUYREGULARDELIVERYLIMIT订单

  • 步骤 111213SELLREGULARDELIVERYLIMIT订单

下达常规止损限价订单

常规止损限价订单是一种在特定价格下下达单一订单的订单类型。与常规市价订单不同,这不是市场价格。要下达此订单,需要两个特定参数,即触发价格限价。这些参数应满足以下条件:

  • 对于BUY订单,我们需要遵循以下规则:

  • 触发价格限价应高于市场价格。

  • 限价应大于触发价格

  • 对于SELL订单,应该观察以下内容:

  • 触发价 和限价 应低于市场价格。

  • 限价 应低于触发价

如果这些条件不满足,订单可能会以市价成交,从而转换为普通市价订单,或者经纪人可能会拒绝订单,认为它是无效订单。

在下普通止损限价订单时,订单会经历各种中间状态,最终达到最终状态(COMPLETECANCELLEDREJECTED)。普通止损限价订单可能会在TRIGGER_PENDING状态停留一段时间,直到市场条件有利才会转移到COMPLETE状态。以下状态机图表明了普通止损限价订单在其生命周期中的各种状态:

下单后,订单会保持TRIGGER_PENDING状态,直到市场价格突破触发价但未突破限价。此时订单激活并发送到交易所。然后,订单以最佳可用市场价格执行,该价格介于触发价限价之间。订单状态从TRIGGER_PENDING转移到OPENCOMPLETE状态。如果市场过于波动,市场价格同时突破触发价限价,订单将保持TRIGGER_PENDING状态。

此配方演示了下列普通止损限价订单的下单和查询其状态:

  • BUYREGULARINTRADAYSTOPLOSS_LIMIT订单

  • SELLREGULARINTRADAYSTOPLOSS_LIMIT订单

  • BUYREGULARDELIVERYSTOPLOSS_LIMIT订单

  • SELLREGULARDELIVERYSTOPLOSS_LIMIT订单

准备就绪...

确保pyalgotrading包中的broker_connection对象和常量在您的 Python 命名空间中可用。请参考本章的技术要求部分设置此对象。

如何操作…

我们按照以下步骤执行此配方:

  1. 获取一个金融工具并将其分配给instrument
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'AXISBANK')
  1. 获取最新交易价。下BUYREGULARINTRADAYSTOPLOSS_LIMIT订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order1_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.BUY,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.INTRADAY,
                order_variety= \
                    BrokerOrderVarietyConstants.STOPLOSS_LIMIT,
                quantity=1,
                price=ltp+1,
                trigger_price=ltp+1)
>>> order1_id

我们得到以下输出(您的输出可能会有所不同):

'200303003296676'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'TRIGGER PENDING'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如以下截图所示(某些数据可能与您的情况不同):

  1. 过一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如以下截图所示(某些数据可能与您的情况不同):

  1. 拉取 LTP。下达一个SELLREGULARINTRADAYSTOPLOSS_LIMIT订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order2_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.SELL,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.INTRADAY,
                order_variety= \
                    BrokerOrderVarietyConstants.STOPLOSS_LIMIT,
                quantity=1,
                price=ltp-1,
                trigger_price=ltp-1)
>>> order2_id

我们得到以下输出(您的输出可能会有所不同):

'200303003576828'
  1. 拉取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'TRIGGER PENDING'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(您的某些数据可能会有所不同):

  1. 一段时间后再次拉取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(您的某些数据可能会有所不同):

  1. 拉取 LTP。下达一个BUYREGULARDELIVERYSTOPLOSS_LIMIT订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order3_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.BUY,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.DELIVERY,
                order_variety= \
                    BrokerOrderVarietyConstants.STOPLOSS_LIMIT,
                quantity=1,
                price=ltp+1,
                trigger_price=ltp+1)
>>> order3_id

我们得到以下输出(您的输出可能会有所不同):

'200303003308116'
  1. 拉取并显示订单状态:
>>> broker_connection.get_order_status(order3_id)

我们得到以下输出:

'TRIGGER PENDING'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(您的某些数据可能会有所不同):

  1. 一段时间后再次拉取并显示订单状态:
>>> broker_connection.get_order_status(order3_id)

我们得到以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(您的某些数据可能会有所不同):

  1. 拉取 LTP。下达一个SELLREGULARDELIVERYSTOPLOSS_LIMIT订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order4_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.SELL,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.DELIVERY,
                order_variety= \
                    BrokerOrderVarietyConstants.STOPLOSS_LIMIT,
                quantity=1,
                price=ltp-1,
                trigger_price=ltp-1)
>>> order4_id

我们得到以下输出(您的输出可能会有所不同):

'200303003312976'
  1. 拉取并显示订单状态:
>>> broker_connection.get_order_status(order4_id)

我们得到以下输出:

'TRIGGER PENDING'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(您的某些数据可能会有所不同):

  1. 一段时间后再次拉取并显示订单状态:
>>> broker_connection.get_order_status(order4_id)

我们得到以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(您的某些数据可能会有所不同):

工作原理…

步骤 1中,您使用BrokerConnectionZerodha类的get_instrument()方法拉取一个工具,并将其分配给一个新属性instrument。这个对象是Instrument类的一个实例。调用get_instrument所需的两个参数是交易所('NSE')和交易符号('AXISBANK')。

步骤 2中,你使用BrokerConnectionZerodha类的get_ltp()方法获取工具的 LTP,并将其分配给一个新属性ltp。在这里,将instrument对象作为参数传递。接下来,你使用broker_connection对象的place_order方法在交易所下单BUYREGULARINTRADAYSTOPLOSS_LIMITplace_order方法是经纪人特定的下单 API 的包装器。它接受以下属性:

  • instrument:这是必须下单的金融工具,应该是Instrument类的实例。我们在这里传递instrument

  • order_transaction_type:这是订单交易类型,应该是BrokerOrderTransactionTypeConstants类型的枚举。我们在这里传递BrokerOrderTransactionTypeConstants.BUY

  • order_type:这是订单类型,应该是BrokerOrderTypeConstants类型的枚举。我们在这里传递BrokerOrderTypeConstants.REGULAR

  • order_code:这是订单代码,应该是BrokerOrderCodeConstants类型的枚举。我们在这里传递BrokerOrderCodeConstants.INTRADAY

  • order_variety:这是订单种类,应该是BrokerOrderVarietyConstants类型的枚举。我们在这里传递BrokerOrderVarietyConstants.STOPLOSS_LIMIT

  • quantity:这是要交易的股票数量,应该是正整数。我们在这里传递1

  • price:这是下单的限价。我们在这里传递ltp+1,表示高于ltp1单位价格。

  • trigger_price:这是下单的触发价格。我们在这里传递ltp+1,表示高于ltp1单位价格。

(传递给place_order方法的属性是与经纪人无关的常量,之前从pyalgotrading.constants模块导入。)

步骤 2中,下单后你会从经纪人那里获得一个订单 ID,将其分配给一个新属性order1_idorder1_id对象是一个字符串。如果由于某种原因下单失败,你可能不会获得订单 ID。请注意,价格和trigger_price参数被赋予了ltp+1的值。这意味着订单是在市价上方下单,这是放置BUY STOPLOSS_LIMIT订单的必要条件。

步骤 3中,你使用broker_connection对象的get_order_status()方法获取已下单的状态。你将order1_id作为get_order_status()方法的参数传递。你将订单状态作为字符串'TRIGGER PENDING'获取。你也可以随时使用order1_id获取已下单订单的状态。在步骤 4中,你再次获取订单状态,如果订单完成,你将订单状态获取为'COMPLETE'

您还可以通过登录经纪人网站并检查那里的订单部分来验证订单的成功下达。您应该看到类似于步骤 3步骤 4输出中显示的屏幕截图的数据。

步骤 3中,如果状态显示为'COMPLETE'而不是'TRIGGER PENDING',这可能是由于高波动性引起的。如果您希望订单在'OPEN'状态保持一段时间,请尝试将订单放置在市价之外的位置。

本配方的其他步骤遵循相同的模式,下达订单并获取其状态,但使用不同组合的属性:

  • 步骤 567SELLREGULARINTRADAYSTOPLOSS_LIMIT 订单

  • 步骤 8910BUYREGULARDELIVERYSTOPLOSS_LIMIT 订单

  • 步骤 111213SELLREGULARDELIVERYSTOPLOSS_LIMIT 订单

下达常规止损市价订单

常规止损市价订单是一种单一价格下单类型的订单。与常规市价订单不同,这不是市价。为了下达此订单,需要一个称为触发价格的特定参数。此参数应满足以下条件:

  • 触发价格对于BUY订单应高于市价。

  • 触发价格对于SELL订单应低于市价。

如果这些条件不满足,则订单可能以市价下单,从而基本上将其转换为常规市价单,或者经纪人可能将其拒绝为无效订单。

在下达常规止损市价订单时,它会在最终达到终止状态(COMPLETECANCELLEDREJECTED)之前经历各种中间状态。一个常规止损市价订单可能会在TRIGGER_PEDNING状态下保持一段时间,直到有利的市场条件达到,然后转移到COMPLETE状态。

以下状态机图演示了常规止损市价订单在其生命周期中的各种状态:

订单下达后,它将保持在TRIGGER_PENDING状态,直到市价突破触发价格。这时订单被激活并发送到交易所。订单然后以最佳可用市场价格执行。订单状态从TRIGGER_PENDING转换到OPEN再转换到COMPLETE状态。

一个常规止损市价单的行为与常规止损限价单类似(参见下达常规止损限价单配方),除了一个区别——止损市价单只需要触发价格,而不需要限价,而止损限价单则需要这两个参数。你可以将止损市价单视为具有无限制限价的止损限价单。

本配方演示了下列常规止损市价订单的下达和查询其状态:

  • BUYREGULARINTRADAYSTOPLOSS_MARKET 订单

  • SELLREGULARINTRADAYSTOPLOSS_MARKET订单

  • BUYREGULARDELIVERYSTOPLOSS_MARKET订单

  • SELLREGULARDELIVERYSTOPLOSS_MARKET订单

准备就绪

确保pyalgotrading包中的broker_connection对象和常量在您的 Python 命名空间中可用。请参考本章的技术要求部分设置此对象。

如何操作…

我们为这个菜谱执行以下步骤:

  1. 获取一个金融工具并将其分配给instrument
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'KOTAKBANK')
  1. 获取 LTP。下达一个BUYREGULARINTRADAYSTOPLOSS_MARKET订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order1_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.BUY,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.INTRADAY,
                order_variety= \
                    BrokerOrderVarietyConstants.STOPLOSS_MARKET,
                quantity=1,
                trigger_price=ltp+1)
>>> order1_id

我们获得以下输出(您的输出可能会有所不同):

'200727003362763'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们获得以下输出:

'TRIGGER PENDING'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(某些数据可能对您不同):

  1. 过一段时间再次获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们获得以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(某些数据可能对您不同):

  1. 获取 LTP。下达一个SELLREGULARINTRADAYSTOPLOSS_MARKET订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order2_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.SELL,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.INTRADAY,
                order_variety= \
                    BrokerOrderVarietyConstants.STOPLOSS_MARKET,
                quantity=1,
                trigger_price=ltp-1)
>>> order2_id

我们获得以下输出(您的输出可能会有所不同):

'200303003345436'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们获得以下输出:

'TRIGGER PENDING'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(某些数据可能对您不同):

  1. 过一段时间再次获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们获得以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(某些数据可能对您不同):

  1. 获取 LTP。下达一个BUYREGULARDELIVERYSTOPLOSS_MARKET订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order3_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.BUY,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.DELIVERY,
                order_variety= \
                    BrokerOrderVarietyConstants.STOPLOSS_MARKET,
                quantity=1,
                trigger_price=ltp+1)
>>> order3_id

我们获得以下输出(您的输出可能会有所不同):

'200727003580657'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order3_id)

我们获得以下输出:

'TRIGGER PENDING'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(某些数据可能对您不同):

  1. 过一段时间再次获取并显示订单状态:
>>> broker_connection.get_order_status(order3_id)

我们获得以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(某些数据可能对您不同):

  1. 获取 LTP。下达一个SELLREGULARDELIVERYSTOPLOSS_MARKET订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument.segment)
>>> order4_id = broker_connection.place_order(
                instrument=instrument,
                order_transaction_type= \
                    BrokerOrderTransactionTypeConstants.SELL,
                order_type=BrokerOrderTypeConstants.REGULAR,
                order_code=BrokerOrderCodeConstants.DELIVERY,
                order_variety= \
                    BrokerOrderVarietyConstants.STOPLOSS_MARKET,
                quantity=1,
                trigger_price=ltp-1)
>>> order4_id

我们得到以下输出(您的输出可能不同):

'200727003635594'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order4_id)

我们得到以下输出:

'TRIGGER PENDING'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详情,如下面的屏幕截图所示(您的一些数据可能不同):

  1. 过一段时间再次获取并显示订单状态:
>>> broker_connection.get_order_status(order4_id)

我们得到以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以找到您的订单详情,如下面的屏幕截图所示(您的一些数据可能不同):

如何工作…

步骤 1中,您使用BrokerConnectionZerodha类的get_instrument()方法获取一个工具并将其分配给一个新的属性instrument。此对象是Instrument类的一个实例。调用get_instrument所需的两个参数是交易所('NSE')和交易符号('KOTAKBANK')。

步骤 2中,您使用BrokerConnectionZerodha类的get_ltp()方法获取工具的 LTP,并将其分配给一个新的属性ltp。此处将instrument对象作为参数传递。接下来,您使用broker_connection对象的place_order方法在交易所上下达BUYREGULARINTRADAYSTOPLOSS_MARKET订单。place_order方法是经纪人特定的下单 API 的包装器。它接受以下属性:

  • instrument:这是必须下订单的金融工具,应该是Instrument类的实例。我们在这里传递instrument

  • order_transaction_type:这是订单交易类型,应该是BrokerOrderTransactionTypeConstants类型的枚举。我们在这里传递BrokerOrderTransactionTypeConstants.BUY

  • order_type:这是订单类型,应该是BrokerOrderTypeConstants类型的枚举。我们在这里传递BrokerOrderTypeConstants.REGULAR

  • order_code:这是订单代码,应该是BrokerOrderCodeConstants类型的枚举。我们在这里传递BrokerOrderCodeConstants.INTRADAY

  • order_variety:这是订单种类,应该是BrokerOrderVarietyConstants类型的枚举。我们在这里传递BrokerOrderVarietyConstants.STOPLOSS_MARKET

  • quantity:这是要交易的股票数量,并且应该是正整数。我们在这里传递1

  • trigger_price:这是订单应该下达的触发价格。我们在这里传递ltp+1,这意味着ltp上方1单位价格。

(传递给place_order方法的属性是经纪人无关的常量,之前从pyalgotrading.constants模块导入。)

第二步下单时,您将从经纪人那里获得一个订单 ID,然后将其分配给一个新属性order1_idorder1_id对象是一个字符串。如果由于某种原因下单失败,您可能不会得到订单 ID。请注意,trigger_price参数被赋予了ltp+1的值。这意味着订单被放置在市场价格之上,这是放置BUY STOPLOSS_MARKET订单的必要条件。

第三步,您使用broker_connection对象的get_order_status()方法获取已下订单的状态。您将order1_id作为get_order_status()方法的参数传递。您将订单状态作为'TRIGGER PENDING'的字符串获得。您还可以随时使用order1_id获取已下订单的状态。在第四步,您再次获取订单状态,如果订单已完成,您将获得订单状态为'COMPLETE'

您还可以通过登录经纪人网站并在那里检查订单部分来验证您的订单已成功下单。您应该看到类似于第三步第四步输出中显示的屏幕截图的数据。

第三步,如果状态显示为'COMPLETE'而不是'TRIGGER PENDING',这可能是由于高波动性造成的。如果你希望订单在'OPEN'状态保持一段时间,尝试将订单放置在市场价格较远的地方。

这个配方中的其他步骤都遵循相同的模式,即下订单并获取其状态,只是属性组合不同:

  • 步骤 567SELLREGULARINTRADAYSTOPLOSS_MARKET订单

  • 步骤 8910BUYREGULARDELIVERYSTOPLOSS_MARKET订单

  • 步骤 111213SELLREGULARDELIVERYSTOPLOSS_MARKET订单

第七章:在交易所放置括号和覆盖订单

本章介绍了可以通过经纪人 API 在交易所上放置的各种类型的括号和覆盖订单。这些配方包括用于放置 12 种类型的订单、查询它们的状态、取消未完成的订单和退出已完成订单的代码。这些配方将是您算法交易策略的基本组成部分。了解所有订单类型并知道为给定要求放置哪种订单对于构建成功的交易策略至关重要。

每个订单有四个属性,这四个属性共同完整定义了订单:

  • 订单交易类型

  • 订单类型

  • 订单代码

  • 订单种类

要下订单,所有四个属性都应该被准确知道。要了解更多关于这些属性的信息,请参考第六章的介绍,在交易所放置常规订单

本章中的配方为每种订单类型提供了详细的流程图。在交易所上下的每个订单在其生命周期中经历各种状态。要了解更多关于本章使用的经纪人支持的订单状态,请参考第六章的介绍,在交易所放置常规订单

在本章中,我们将涵盖以下配方:

  • 放置括号限价单

  • 放置括号止损限价单

  • 放置带有跟踪止损的括号限价单

  • 放置带有跟踪止损的括号止损限价单

  • 放置覆盖市价单

  • 放置覆盖限价单

请确保您在实时市场时间内具有足够的余额在您的经纪帐户中尝试所有这些配方。如果在非市场时间尝试这些配方或者余额不足,您的订单将被经纪人拒绝。这意味着订单永远不会达到交易所,您将无法获得预期的响应。

技术要求

您需要以下内容才能成功执行本章的配方:

  • Python 3.7+

  • Python 软件包:pyalgotrading$ pip install pyalgotrading

这一章的最新 Jupyter 笔记本可以在 GitHub 上找到,网址为 github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/tree/master/Chapter07

与经纪人建立连接的第一件事就是获取 API 密钥。经纪人将为每个客户提供唯一的密钥,通常是作为一个 api-keyapi-secret 密钥对。这些 API 密钥通常是收费的,通常是按月订阅的。您需要在开始之前从经纪人网站获取您的 api-keyapi-secret 的副本。您可以参考 附录 I 获取更多详细信息。

以下步骤将帮助您与 Zerodha 建立经纪人连接,该连接将被本章中的所有配方使用。请确保在尝试任何配方之前已经完成了这些步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.broker.broker_connection_zerodha import BrokerConnectionZerodha
>>> from pyalgotrading.constants import *

所有pyalgotrading常量现在都可在您的 Python 命名空间中使用。

  1. 从经纪人那里获取api_keyapi_secret密钥。这些对你来说是唯一的,并且经纪人将用它们来识别你的证券账户:
>>> api_key = "<your-api-key>"
>>> api_secret = "<your-api-secret>"
>>> broker_connection = BrokerConnectionZerodha(api_key, 
                                                api_secret)

我们得到以下输出:

Installing package kiteconnect via pip. This may take a while...
Please login to this link to generate your request token: https://kite.trade/connect/login?api_key=<your-api-key>&v=3

如果您是第一次运行此代码,并且没有安装kiteconnectpyalgotrading将自动为您安装它。步骤 2 的最终输出将是一个链接。点击链接并使用您的 Zerodha 凭据登录。如果验证成功,您将在浏览器的地址栏中看到一个类似于https://127.0.0.1/?request_token=<字母数字令牌>&action=login&status=success的链接。

我们有以下示例:

https://127.0.0.1/?request_token=H06I6Ydv95y23D2Dp7NbigFjKweGwRP7&action=login&status=success
  1. 复制字母数字令牌并粘贴到request_token中:
>>> request_token = "<your-request-token>"
>>> broker_connection.set_access_token(request_token)

broker_connection实例现在已准备好执行 API 调用。

pyalgotrading包支持多个经纪人,并为每个经纪人提供一个连接对象类,具有相同的方法。它将经纪人 API 抽象为统一接口,因此用户无需担心底层经纪人 API 调用,可以直接使用本章中的所有示例。只有设置经纪人连接的过程会因经纪人而异。如果您不是使用 Zerodha 作为您的经纪人,则可以参考 pyalgotrading 文档来设置经纪人连接。对于 Zerodha 用户,前一节中提到的步骤就足够了。

放置一个括号限价单

括号订单是一种复杂的订单,旨在在交易变得有利时帮助盈利,或在变得不利时限制损失,具有预定义的值。括号订单本质上是三个常规订单的组合——初始订单、目标订单和止损订单——这三个订单共同起作用,以帮助实现指定的利润或限制损失。除了常规订单参数外,括号订单还接受附加参数——targetstoplosstrailing stoploss(可选)。这三个常规订单描述如下:

  • 初始订单:此订单相当于常规限价单或常规止损限价单。一旦下单,它将保持'OPEN'状态,直到市价达到其触发价格值。一旦市场越过触发价格值,此订单将从'OPEN'状态移至'COMPLETE'状态,并且将放置目标和止损订单,下面描述这些订单。

  • 目标订单:此订单相当于常规限价单,其触发价格为指定的目标值,并且交易类型与初始订单相反。对于买入初始订单,目标订单以比初始订单更高的价格下单。对于卖出初始订单,则相反。数量与初始订单相匹配。因此,如果此订单执行,它将退出由初始订单创建的头寸。

  • 止损订单:此订单相当于常规止损限价订单,其指定的stoploss值为其触发价格,并且交易类型与初始订单相反。对于买入初始订单,止损订单放置在低于初始订单的价格处。对于卖出初始订单,情况则相反。数量与初始订单相匹配。因此,如果此订单执行,则退出初始订单创建的仓位。如果指定了trailing stoploss参数,每当初始订单价格朝着目标订单价格的方向移动时,止损订单就会按照trailing stoploss值的大小修改,朝着初始订单价格的方向进行修改。这有助于在初始订单价格运动方向发生变化时进一步减少损失。

由于目标订单和止损订单放置在初始订单的相对两侧,它们围绕初始订单形成了一个括号,因此这个订单称为括号订单。此外,由于目标订单和止损订单位于相对两侧,因此在给定时间内只有一个会执行(这意味着它的状态会从'OPEN'变为'COMPLETE'),并且当它执行时,该订单(无论是止损订单还是目标订单)都会自动取消。目标订单和止损订单也被称为初始订单的子订单,而初始订单则称为后者的父订单

除非经纪人另有支持,否则括号订单通常用于日内交易。如果初始订单或子订单在交易会话结束时未完成,则经纪人会自动取消或退出它们。

以下流程图解释了括号订单的工作原理:

以下是括号限价订单状态机图的参考:

  • 初始订单:请参考前一章节中下达常规限价订单配方的状态机图。

  • 目标订单:请参考前一章节中下达常规限价订单配方的状态机图。

  • 止损订单:请参考前一章节中下达常规止损限价订单配方的状态机图。

当买入括号订单必须低于市场价格放置,或卖出括号订单必须高于市场价格放置时,可以使用括号限价订单。

本配方演示了以下括号限价订单的下单和查询其状态:

  • BUYBRACKETINTRADAYLIMIT 订单(不带跟踪止损)

  • SELLBRACKETINTRADAYLIMIT 订单(不带跟踪止损)

准备工作

确保在你的 Python 命名空间中可用 broker_connection 对象和 pyalgotrading 包中的常量。请参考本章节的技术要求部分设置此对象。

如何实现…

我们对这个配方执行以下步骤:

  1. 获取一个金融工具并将其赋值给instrument
>>> instrument = broker_connection.get_instrument('NSE', 'SBIN')
  1. 获取 LTP。下一个BUYBRACKETINTRADAYLIMIT 订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order1_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type= \
                        BrokerOrderTransactionTypeConstants.BUY,
                    order_type=BrokerOrderTypeConstants.BRACKET,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.LIMIT,
                    quantity=1,
                    price=ltp-1,
                    stoploss=2,
                    target=2)
>>> order1_id

我们得到以下输出(您的输出可能会有所不同):

'2003030003491923'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'OPEN'

如果您使用凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(您的一些数据可能会有所不同):

  1. 过一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'COMPLETE'

如果您使用凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(您的一些数据可能会有所不同):

  1. 获取 LTP。下一个SELLBRACKETINTRADAYLIMIT 订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order2_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type= \
                        BrokerOrderTransactionTypeConstants.SELL,
                    order_type=BrokerOrderTypeConstants.BRACKET,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.LIMIT,
                    quantity=1,
                    price=ltp+1,
                    stoploss=2,
                    target=2)
>>> order2_id

我们得到以下输出(您的输出可能会有所不同):

'200303003639902'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'OPEN'

如果您使用凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(您的一些数据可能会有所不同):

  1. 过一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'COMPLETE'

如果您使用凭据登录经纪人网站并转到订单部分,您可以找到您的订单详细信息,如下面的屏幕截图所示(您的一些数据可能会有所不同):

工作原理…

步骤 1 中,您使用 BrokerConnectionZerodha 类的 get_instrument() 方法获取一个工具并将其赋值给一个新属性 instrument。这个对象是 Instrument 类的一个实例。调用 get_instrument 需要的两个参数是交易所('NSE')和交易符号('SBI')。

步骤 2 中,您使用 BrokerConnectionZerodha 类的 get_ltp() 方法获取工具的 LTP,并将其赋值给新属性 ltp。这里将 instrument 对象作为参数传递。接下来,您使用 broker_connection 对象的 place_order() 方法在交易所上下一个 BUYBRACKETINTRADAYLIMIT 订单。 place_order() 方法是经纪人特定的下单 API 的包装器。它接受以下属性:

  • instrument:这是必须下订单的金融工具,应该是 Instrument 类的实例。我们在这里传递 instrument

  • order_transaction_type:这是订单交易类型,应该是 BrokerOrderTransactionTypeConstants 类型的枚举。我们在这里传递 BrokerOrderTransactionTypeConstants.BUY

  • order_type:这是订单类型,应该是 BrokerOrderTypeConstants 类型的枚举。我们在这里传递 BrokerOrderTypeConstants.BRACKET

  • order_code: 这是订单代码,应为BrokerOrderCodeConstants类型的枚举。我们在这里传递BrokerOrderCodeConstants.INTRADAY

  • order_variety: 这是订单类型,应为BrokerOrderVarietyConstants类型的枚举。我们在这里传递BrokerOrderVarietyConstants.LIMIT

  • quantity: 这是要交易的股票数量,应为正整数。我们传递1

  • price: 这是应该下单的限价。我们在这里传递ltp-1,意味着低于ltp1单位价格。

  • stoploss: 这是与初始订单价格的价格差,应该放置止损订单的价格。它应该是正整数或浮点数值。我们在这里传递2

  • target: 这是与初始订单价格的价格差,应该放置目标订单的价格。它应该是正整数或浮点数值。我们在这里传递2

(传递给place_order()方法的属性是与经纪人无关的常量,之前从pyalgotrading.constants模块导入的。)

第 2 步中下单后,您会从经纪人那里获得一个订单 ID,您将其分配给一个新属性order1_idorder1_id对象是一个字符串。如果由于某种原因订单未能成功下达,则您可能不会获得订单 ID。请注意,价格参数传递了一个值为ltp-1。这意味着订单是在市场价格下方下达的,这是下达买入限价订单的必要条件。stoploss参数指定为2。这意味着止损订单将以比初始订单的执行价格低两个价格单位的价格下达。同样,target参数指定为2。这意味着目标订单将以比初始订单的执行价格高两个价格单位的价格下达。

第 3 步中,您使用broker_connection对象的get_order_status()方法获取下单状态。您将order1_id作为get_order_status()方法的参数传递。您得到的订单状态为'OPEN',是一个字符串。您还可以在以后的任何时间使用order1_id来获取已下单的状态。

第 4 步中,您再次获取订单状态,如果订单已完成,则将订单状态作为'COMPLETE'。此后立即放置目标和止损订单,价格如前所述。目标订单执行为常规限价订单。止损订单执行为常规止损限价订单。当它们中的一个被执行并达到'COMPLETE'状态时,经纪人会自动取消另一个订单,因此它进入'CANCELLED'状态。请注意,目标和止损订单在初始订单的相反方向上,因此目标和止损订单不能同时执行。

您还可以通过登录经纪网站并检查订单部分来验证订单的成功下达。您应该会看到与 步骤 3步骤 4 的输出中显示的屏幕截图类似的数据。

步骤 3 中,如果您看到状态为 'COMPLETE' 而不是 'OPEN';这可能是由于市场波动较大。如果您希望订单保持在 'OPEN' 状态一段时间,请尝试将订单放置在市场价格之外。

下面是有关执行初始订单、目标订单和止损订单的更多详细信息的参考资料:

  • 初始订单:参考上一章节中的 下达常规限价订单 部分。

  • 目标订单:参考上一章节中的 下达常规限价订单 部分。

  • 止损订单:参考上一章节中的 下达常规止损限价订单 部分。

本配方中的其他步骤遵循相同的模式,即放置订单并获取其不同属性组合的状态:

  • 步骤 567SELLBRACKETINTRADAYLIMIT 订单

还有更多…

您可以通过退出其中一个子订单来退出框架订单。您退出的子订单将以市场价格执行并转移到 COMPLETE 状态。另一个子订单将转移到 CANCELLED 状态。

例如,假设您退出了止损订单。在这种情况下,目标订单将被取消,并且将转移到 CANCELLED 状态。止损订单将以市场价格执行,并且将转移到 COMPLETE 状态。如果您使用您的凭据登录经纪站点并转到订单部分,则可以找到子订单详细信息,如以下屏幕截图所示。您的一些数据可能会有所不同。

以下是退出框架订单前放置在 步骤 2 中的初始订单的目标订单:

以下是退出框架订单后的目标订单:

此屏幕截图显示了在退出之前放置在 步骤 2 中的初始订单的止损订单:

退出后的止损订单如下图所示:

下达框架止损限价订单

框架订单是复杂的订单,旨在在交易有利时帮助赚取利润,或在交易不利时限制损失,具有预定义的值。框架订单本质上是三个常规订单的组合 ——初始订单、目标订单和止损订单,它们共同起作用,帮助实现指定的利润或限制损失。除了常规订单参数外,框架订单还需要额外的参数 ——targetstoplosstrailing stoploss(可选)。

请参考下达一个 bracket 限价订单配方的介绍,深入理解 bracket 订单的工作原理。如果您想要在市价上方下达买入 bracket 订单或在市价下方下达卖出 bracket 订单,可以使用 bracket stoploss-limit 订单。

该配方演示了下述 bracket stoploss-limit 订单的下达和查询其状态:

  • BUYBRACKETINTRADAYSTOPLOSS_LIMIT订单(不带跟踪止损)

  • SELLBRACKETINTRADAYSTOPLOSS_LIMIT订单(不带跟踪止损)

以下是用于 bracket stoploss-limit 订单的状态机图参考:

  • 初始订单:请参考上一章节中下达一个常规止损限价订单配方的状态机图。

  • 目标订单:请参考上一章节中下达常规限价订单配方的状态机图。

  • 止损订单:请参考上一章节中下达一个常规止损限价订单配方的状态机图。

准备就绪

确保pyalgotrading包中的broker_connection对象和常量在你的 Python 命名空间中可用。请参考本章的技术要求部分设置该对象。

如何操作…

我们对本配方执行以下步骤:

  1. 获取一个金融工具并将其分配给instrument
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'INDUSINDBK')
  1. 获取最新成交价(LTP)。下达一个BUYBRACKETINTRADAYSTOPLOSS_LIMIT订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument.segment)
>>> order1_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type=\
                        BrokerOrderTransactionTypeConstants.BUY,
                    order_type=BrokerOrderTypeConstants.BRACKET,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.STOPLOSS_LIMIT,
                    quantity=1,
                    price=ltp+1,
                    trigger_price=ltp+1,
                    stoploss=2,
                    target=2)
>>> order1_id

我们得到以下输出(你的输出可能会有所不同):

'200226003619998'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'TRIGGER PENDING'
  1. 过一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'COMPLETE'
  1. 获取最新成交价(LTP)。下达一个SELLBRACKETINTRADAYSTOPLOSS_LIMIT订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order2_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type= \
                        BrokerOrderTransactionTypeConstants.SELL,
                    order_type=BrokerOrderTypeConstants.BRACKET,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.STOPLOSS_LIMIT,
                    quantity=1,
                    price=ltp-1,
                    trigger_price=ltp-1,
                    stoploss=2,
                    target=2)
>>> order2_id

我们得到以下输出(你的输出可能会有所不同):

'200226003620002'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'TRIGGER PENDING'
  1. 过一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'COMPLETE'

工作原理…

步骤 1中,您使用BrokerConnectionZerodha类的get_instrument()方法获取一个金融工具并将其分配给一个新的属性instrument。该对象是Instrument类的一个实例。调用get_instrument所需的两个参数是交易所('NSE')和交易符号('INDUSINDBK')。

步骤 2中,您使用BrokerConnectionZerodha类的get_ltp()方法获取该工具的最新成交价,并将其分配给一个新的属性ltp。这里的参数是instrument对象。接下来,您使用broker_connection对象的place_order()方法在交易所下达一个BUYBRACKETINTRADAYSTOPLOSS_LIMIT订单。place_order()方法是一个特定于经纪商的下单 API 的封装。它接受以下属性:

  • instrument:这是必须下订单的金融工具,应该是Instrument类的一个实例。我们在这里传递instrument

  • order_transaction_type:这是订单交易类型,应该是类型为BrokerOrderTransactionTypeConstants的枚举。我们在这里传递 BrokerOrderTransactionTypeConstants.BUY

  • order_type:这是订单类型,应该是类型为BrokerOrderTypeConstants的枚举。我们在这里传递 BrokerOrderTypeConstants.BRACKET

  • order_code:这是订单代码,应该是类型为BrokerOrderCodeConstants的枚举。我们在这里传递 BrokerOrderCodeConstants.INTRADAY

  • order_variety:这是订单种类,应该是类型为BrokerOrderVarietyConstants的枚举。我们在这里传递 BrokerOrderVarietyConstants.STOPLOSS_LIMIT

  • quantity:这是要交易的股票数量,应该是一个正整数。我们在这里传递 1

  • price:这是订单应该下达的限价。我们在这里传递 ltp+1,这意味着高于ltp 1 个单位的价格。

  • trigger_price:这是订单应该下达的触发价格。我们在这里传递 ltp+1,这意味着高于ltp 1 个单位的价格。

  • stoploss:这是与初始订单价格的价格差,止损订单应该被下达的价格。它应该是一个正的intfloat值。我们在这里传递2

  • target:这是与初始订单价格的价格差,目标订单应该被下达的价格。它应该是一个正的intfloat值。我们在这里传递 2

(传递给place_order()方法的属性是经纪人无关的常量,之前从pyalgotrading.constants模块导入。)

步骤 2中下单后,您将从经纪人那里获得一个订单 ID,将其分配给一个新属性,order1_idorder1_id对象是一个字符串。如果由于某种原因订单未能成功下达,您可能无法获得订单 ID。请注意,pricetrigger_price参数被赋予了ltp+1的值。这意味着订单的价格高于市场价格,这是下达买入止损限价订单的必要条件。止损参数被指定为2。这意味着止损订单将以比初始订单执行价格低 2 个价格单位的价格下达。同样,目标参数被指定为2。这意味着目标订单将以比初始订单执行价格高 2 个价格单位的价格下达。

第 3 步中,您使用broker_connection对象的get_order_status()方法获取已下达订单的状态。您将order1_id作为get_order_status()方法的参数传递。您将订单状态获取为'TRIGGER PENDING',一个字符串。您还可以在任何后续时间点使用order1_id获取已下达订单的状态。在第 4 步中,您再次获取订单状态,如果订单已完成,则将订单状态获取为'COMPLETE'。紧接着,按照之前提及的价格下达目标订单和止损订单。目标订单作为常规限价订单执行。止损订单作为常规止损限价订单执行。当其中一个执行并达到'COMPLETE'状态时,另一个订单会被经纪人自动取消,因此它转移到'CANCELLED'状态。请注意,目标订单和止损订单位于初始订单的相对方向,因此目标订单和止损订单不能同时执行。

第 3 步中,如果您看到状态为'COMPLETE'而不是'TRIGGER PENDING',这可能是由于高波动性引起的。如果您希望订单在一段时间内保持'OPEN'状态,请尝试将订单价格进一步设置远离市场价格。

以下是关于初始订单、目标订单和止损订单执行更多细节的参考:

  • 初始订单:参考上一章节中的下达常规止损限价订单一节。

  • 目标订单:参考上一章节中的下达常规限价订单一节。

  • 止损订单:参考上一章节中的下达常规止损限价订单一节。

您可以通过登录经纪网站并检查订单部分来验证您的订单成功下达。您应该看到类似于在交易所上下达括号限价订单一节中显示的屏幕截图的数据。

本配方中的其他步骤遵循相同的模式,用于不同属性组合的下单和获取其状态。

  • 步骤 567SELLBRACKETINTRADAYSTOPLOSS_LIMIT订单

下达带移动止损的括号限价订单

括号订单是复杂订单,旨在在交易有利时获利或在交易不利时限制损失,具有预定义值。括号订单基本上是三个常规订单的组合——初始订单、目标订单和止损订单,它们共同作用以帮助实现指定的利润或限制损失。除了常规订单参数外,括号订单还接受额外参数——targetstoplosstrailing stoploss(可选)。

请参考下达括号限价订单一节的介绍,深入了解括号订单的工作原理。

如果你想在市价以下下达一个买入交易限价订单或者在市价以上下达一个卖出交易限价订单,你可以使用一个买入交易限价订单。跟踪止损功能通过将止损订单的价格修改为与初始订单价格朝着目标订单价格方向移动的点数相同的方式,改进了止损订单的定位。每当初始订单价格朝着目标订单价格方向移动时,都会发生这种情况。这有助于进一步减少初始订单价格移动方向发生变化时的损失。

本配方演示了下面带有跟踪止损的两个交易限价订单的下单及查询其状态:

  • 带有跟踪止损的BUYBRACKETINTRADAYLIMIT订单

  • 带有跟踪止损的SELLBRACKETINTRADAYLIMIT订单

下面是关于买入交易限价订单的状态机图的参考:

  • 初始订单:参考上一章节中的下达普通限价订单配方中的状态机图。

  • 目标订单:参考上一章节中的下达普通限价订单配方中的状态机图。

  • 止损订单:参考上一章节中的下达普通止损限价订单配方中的状态机图。

准备就绪

确保你的 Python 命名空间中有来自pyalgotrading包的broker_connection对象和常量。请参考本章节的技术要求部分来设置这个对象。

如何操作…

对于这个配方,我们执行以下步骤:

  1. 获取一个金融工具并将其赋值给instrument
>>> instrument = broker_connection.get_instrument('NSE', 'FEDERALBNK')
  1. 获取最新成交价。下达一个BUYBRACKETINTRADAYLIMIT订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order1_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type= \
                        BrokerOrderTransactionTypeConstants.BUY,
                    order_type=BrokerOrderTypeConstants.BRACKET,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.LIMIT,
                    quantity=1,
                    price=ltp-1,
                    trigger_price=ltp-1,
                    stoploss=2,
                    target=2,
                    trailing_stoploss=1)
>>> order1_id

我们得到以下输出(你的输出可能不同):

'200226003620004'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'OPEN'
  1. 过一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'COMPLETE'
  1. 获取最新成交价。下达一个SELLBRACKETINTRADAYLIMIT订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order2_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type= \
                        BrokerOrderTransactionTypeConstants.SELL,
                    order_type=BrokerOrderTypeConstants.BRACKET,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.LIMIT,
                    quantity=1,
                    price=ltp+1,
                    trigger_price=ltp+1,
                    stoploss=2,
                    target=2,
                    trailing_stoploss=1)
>>> order1_id

我们得到以下输出(你的输出可能不同):

'200226003620009'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'OPEN'
  1. 过一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'COMPLETE'

工作原理...

第 1 步中,你使用BrokerConnectionZerodha类的get_instrument()方法获取一个金融工具并将其赋值给一个新的属性instrument。这个对象是Instrument类的一个实例。调用get_instrument所需的两个参数是交易所('NSE')和交易符号('FEDERALBNK')。

步骤 2 中,您可以使用 BrokerConnectionZerodha 类的 get_ltp() 方法获取金融工具的 LTP,并将其分配给一个新的属性 ltp。这里将 instrument 对象作为参数传递。接下来,您可以使用 broker_connection 对象的 place_order() 方法在交易所上放置 BUYBRACKETINTRADAYLIMIT 订单。place_order() 方法是对特定于经纪商的放置订单 API 的封装。它接受以下属性:

  • instrument: 这是必须下订单的金融工具,应该是 Instrument 类的实例。我们在这里传递 instrument

  • order_transaction_type: 这是订单交易类型,应该是 BrokerOrderTransactionTypeConstants 类型的枚举。我们在这里传递 BrokerOrderTransactionTypeConstants.BUY

  • order_type: 这是订单类型,应该是 BrokerOrderTypeConstants 类型的枚举。我们在这里传递 BrokerOrderTypeConstants.BRACKET

  • order_code: 这是订单代码,应该是 BrokerOrderCodeConstants 类型的枚举。我们在这里传递 BrokerOrderCodeConstants.INTRADAY

  • order_variety: 这是订单种类,应该是 BrokerOrderVarietyConstants 类型的枚举。我们在这里传递 BrokerOrderVarietyConstants.LIMIT

  • quantity: 这是要交易给定金融工具的股票数量,应该是正整数。我们在这里传递 1

  • price: 这是应该下订单的限价。我们在这里传递 ltp-1,意思是低于 ltp1 单位价格。

  • stoploss: 这是距离初始订单价格的价格差异,应该放置止损订单的位置。它应该是一个正整数或浮点值。我们在这里传递 2

  • target: 这是距离初始订单价格的价格差异,应该放置目标订单的位置。它应该是一个正整数或浮点值。我们在这里传递 2

  • trailing_stoploss: 这是每当市场价格朝向目标订单移动时应修改的止损订单的价格差异。我们在这里传递 1

(传递给 place_order() 方法的属性是从 pyalgotrading.constants 模块中导入的与经纪商无关的常量。)

步骤 2 中放置订单时,你从经纪人那里获得一个订单 ID,并将其分配给一个新的属性 order1_idorder1_id 对象是一个字符串。如果由于某种原因订单未能成功下达,你可能不会得到订单 ID。请注意,price 参数被赋予了一个值 ltp-1。这意味着订单被放置在市场价格之下,这是放置购买限价订单的必要条件。stoploss 参数被指定为 2。这意味着止损订单将被放置在比初始订单执行价格低两个价格单位的价格上。类似地,目标参数被指定为 2。这意味着目标订单将被放置在比初始订单执行价格高两个价格单位的价格上。最后,trailing_stoploss 参数被指定为 1。这意味着,在放置止损订单之后,每当市场价格以一个单位的倍数增加时,止损订单都将被修改并放置在比前一价格高一个单位的价格上。

例如,假设工具的市场价格在下单时为 100,那么目标和止损订单将分别放置在 102 和 98。假设市场价格达到 101,即比 100 高一个单位,则止损订单将被修改并放置在 99,即比其前一个价格高一个单位。通过这样做,你已将最大亏损从 2 减少到 1。

步骤 3 中,你使用 broker_connection 对象的 get_order_status() 方法获取已下单订单的状态。你将 order1_id 作为参数传递给 get_order_status() 方法。你将订单状态作为 'OPEN' 的字符串获取到。你也可以在以后的任何时间点使用 order1_id 获取已下单订单的状态。在步骤 4 中,你再次获取订单状态,如果订单已完成,则订单状态为 'COMPLETE'。在此之后,立即放置目标和止损订单,价格与之前提到的相同。目标订单执行为常规限价订单。止损订单执行为常规止损限价订单。当其中一个订单被执行并达到 COMPLETE 状态时,另一个订单会被经纪人自动取消,因此它转移到 CANCELLED 状态。回想一下,目标订单和止损订单位于初始订单的相反方向,因此目标订单和止损订单不能同时执行。如前所述,止损订单可能会被修改一个价格单位。

步骤 3 中,如果你看到状态为 COMPLETE 而不是 OPEN,这可能是由于高波动性造成的。如果你希望订单在一段时间内保持 OPEN 状态,请尝试将订单放置得离市场价格更远。

以下是关于初始订单、目标订单和止损订单执行的更多详细信息的参考资料:

  • 初始订单:参考前一章节中的放置常规限价单配方。

  • 目标订单:参考前一章节中的放置常规限价单配方的状态机图。

  • 止损订单:参考前一章节中的放置常规止损限价单配方。

您可以通过登录经纪网站并在那里检查订单部分来验证订单成功的放置。您应该看到类似于在交易所上放置括号限价订单配方中显示的屏幕截图的数据。

此配方中的其他步骤遵循相同的模式,即放置订单并获取其状态,用于不同属性组合的情况:

  • 步骤 567:带有跟踪止损的卖出括号即日限价订单

放置带有跟踪止损的括号止损限价单

括号订单是复杂订单,旨在在交易有利时帮助赚取利润,或在不利时限制损失,具有预定义值。括号订单本质上是三个常规订单的组合——一个初始订单、一个目标订单和一个止损订单,它们共同作用以帮助实现指定的利润或限制损失。除了常规订单参数外,括号订单还接受额外的参数——目标止损跟踪止损(可选)。

请参考放置括号限价单配方的介绍,以深入了解括号订单的工作原理。

如果您想在市价上方放置买入括号订单或在市价下方放置卖出括号订单,您可以使用括号止损限价订单。跟踪止损通过将其价格沿着初始订单价格的方向修改多少点,每当初始订单价格朝着目标订单价格的方向移动时,来改进止损订单的定位。这有助于在初始订单价格方向的价格变动改变时进一步减少损失。

此配方演示了以下带有跟踪止损的括号止损限价单的放置以及查询其状态:

  • 带有跟踪止损的买入括号即日止损限价订单

  • 带有跟踪止损的卖出括号即日止损限价订单

以下是关于带有跟踪止损的括号止损限价单的状态机图的参考:

  • 初始订单:参考前一章节中的放置常规止损限价单配方的状态机图。

  • 目标订单:参考前一章节中的放置常规限价单配方的状态机图。

  • 止损订单:参考前一章节中的放置常规止损限价单配方的状态机图。

准备工作

确保 pyalgotrading 包中的 broker_connection 对象和常量在您的 Python 命名空间中可用。请参考本章的 技术要求 部分设置此对象。

如何做…

我们为此配方执行以下步骤:

  1. 获取一个金融工具并将其分配给 instrument
>>> instrument = broker_connection.get_instrument('NSE', 'RBLBANK')
  1. 获取 LTP。下一个 BUYBRACKETINTRADAYSTOPLOSS_LIMIT 订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order1_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type= \
                        BrokerOrderTransactionTypeConstants.BUY,
                    order_type=BrokerOrderTypeConstants.BRACKET,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.STOPLOSS_LIMIT,
                    quantity=1,
                    price=ltp+1,
                    trigger_price=ltp+1,
                    stoploss=2,
                    target=2,
                    trailing_stoploss=1)
>>> order1_id

我们得到以下输出(您的输出可能会有所不同):

'200226003620011'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'TRIGGER PENDING'
  1. 再过一段时间后,重新获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'COMPLETE'
  1. 获取 LTP。下一个 SELLBRACKETINTRADAYSTOPLOSS_LIMIT 订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order2_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type= \
                        BrokerOrderTransactionTypeConstants.SELL,
                    order_type=BrokerOrderTypeConstants.BRACKET,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.STOPLOSS_LIMIT,
                    quantity=1,
                    price=ltp-1,
                    trigger_price=ltp-1,
                    stoploss=2,
                    target=2,
                    trailing_stoploss=1)
>>> order2_id

我们得到以下输出(您的输出可能会有所不同):

'200226003620023'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'TRIGGER PENDING'
  1. 再过一段时间后,重新获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'COMPLETE'

它是如何工作的...

步骤 1 中,您使用 BrokerConnectionZerodha 类的 get_instrument() 方法获取一个工具,并将其分配给一个新属性 instrument。该对象是 Instrument 类的一个实例。调用 get_instrument 需要的两个参数是交易所('NSE')和交易符号('RBLBANK')。

步骤 2 中,您使用 BrokerConnectionZerodha 类的 get_ltp() 方法获取工具的 LTP,并将其分配给一个新属性 ltp。在此处将 instrument 对象作为参数传递。接下来,您使用 broker_connection 对象的 place_order() 方法在交易所上放置 BUYREGULARINTRADAYSTOPLOSS_LIMIT 订单。place_order() 方法是特定于经纪人的放置订单 API 的包装器。它接受以下属性:

  • instrument: 这是必须放置订单的金融工具,并且应该是 Instrument 类的一个实例。我们在这里传递 instrument

  • order_transaction_type: 这是订单交易类型,应该是 BrokerOrderTransactionTypeConstants 类型的枚举。我们在这里传递 BrokerOrderTransactionTypeConstants.BUY

  • order_type: 这是订单类型,应该是 BrokerOrderTypeConstants 类型的枚举。我们在这里传递 BrokerOrderTypeConstants.BRACKET

  • order_code: 这是订单代码,应该是 BrokerOrderCodeConstants 类型的枚举。我们在这里传递 BrokerOrderCodeConstants.INTRADAY

  • order_variety: 这是订单类型,应该是 BrokerOrderVarietyConstants 类型的枚举。我们在这里传递 BrokerOrderVarietyConstants.STOPLOSS_LIMIT

  • quantity: 这是要交易的股票数量,并应该是一个正整数。我们在这里传递 1

  • price: 这是应该放置订单的限价。我们在这里传递 ltp+1,意味着比 ltp1 单位的价格。

  • trigger_price:这是应该放置订单的触发价格。我们在这里传递ltp+1,这意味着高出ltp的 1 个价格单位。

  • stoploss:这是与初始订单价格的价格差异,应该在其处放置止损订单。它应该是一个正整数或浮点数值。我们在这里传递2

  • target:这是目标订单应该放置的与初始订单价格的价格差异。它应该是一个正整数或浮点数值。我们在这里传递2

  • trailing_stoploss:这是每当市场价格朝着目标订单的方向移动时应该修改止损订单的价格差异。我们在这里传递1

(传递给place_order()方法的属性是与经纪人无关的常量,之前从pyalgotrading.constants模块导入。)

步骤 2中下单后,您将从经纪人那里获得一个订单 ID,然后将其分配给一个新属性order1_idorder1_id对象是一个字符串。如果由于某种原因下单未成功,您可能不会获得订单 ID。请注意,pricetrigger_price参数传递了一个值ltp+1。这意味着订单价格高于市场价格,这是下买入止损限价订单的必要条件。stoploss参数被指定为2。这意味着止损订单将被放置在比初始订单执行价格低两个价格单位的价格处。同样,target参数被指定为2。这意味着目标订单将被放置在比初始订单执行价格高两个价格单位的价格处。最后,trailing_stoploss参数被指定为1。这意味着,在放置止损订单后,每当市场价格以初始订单价格的价格单位倍数增加时,止损订单将被修改并放置在比先前价格高一个单位的价格处。

因此,例如,假设在下单时工具的市场价格为 100,那么目标和止损订单将分别放置在 102 和 98 处。假设市场价格达到 101,比 100 高一个单位,那么止损订单将被修改并放置在 99 处,这再次比其先前价格高一个单位。通过这样做,您已将最大损失从 2 减少到 1。

步骤 3中,您使用broker_connection对象的get_order_status()方法获取已下达订单的状态。您将order1_id作为参数传递给get_order_status()方法。您会得到订单状态为'TRIGGER PENDING'的结果,一个字符串。您可以在以后的任何时间使用order1_id来获取已下达订单的状态。在步骤 4中,您再次获取订单状态,如果订单已完成,您会得到订单状态为'COMPLETE'。在此之后立即,目标和止损订单按照先前提及的价格下达。目标订单作为常规限价订单执行。止损订单作为常规止损限价订单执行。当其中一个执行并达到'COMPLETE'状态时,另一个订单会被经纪人自动取消,因此它会进入'CANCELLED'状态。请记住,目标订单和止损订单位于初始订单的相对位置,因此目标订单和止损订单不能同时执行。如前所述,止损订单可能会被调整一个价格单位。

步骤 3中,如果您看到状态为'COMPLETE'而不是'TRIGGER PENDING',这可能是由于高波动性引起的。如果您希望订单保持在'TRIGGER PENDING'状态一段时间,请尝试将订单放置在离市价较远的位置。

以下是有关执行目标订单和止损订单更多详细信息的参考:

  • 初始订单:请参考前一章节中的下达常规止损限价订单

  • 目标订单:请参考前一章节中的下达常规限价订单

  • 止损订单:请参考前一章节中的下达常规止损限价订单

您可以通过登录经纪网站并检查订单部分来验证订单的成功下达。您应该看到类似于在交易所上下达括号限价订单一节中显示的截图数据。

本配方中的其他步骤遵循相同的模式,用于不同属性组合的下达订单和获取其状态:

  • 步骤 567SELLBRACKETINTRADAYSTOPLOSS_LIMIT订单

下达平仓市价订单

平仓订单是复杂的订单,旨在帮助将交易的损失限制在预定义的值范围内,如果交易变得不利。平仓订单实质上是两个常规订单的组合——初始订单和止损订单:

  • 初始订单:此订单可以等同于常规市价订单或常规限价订单,具体取决于您是下达市价平仓订单还是限价平仓订单。一旦订单进入'COMPLETE'状态,下一个步骤是下达止损订单,接下来会进行描述。

  • 止损订单:该订单等同于常规止损市价订单(前一章的下达常规止损市价订单处方),其触发价值为指定的触发价,交易类型与初始订单相反。对于买入初始订单,止损订单的价格低于初始订单。对于卖出初始订单,情况将反之。数量与初始订单相匹配。因此,如果此订单执行,则退出初始订单创建的仓位。

由于止损订单是为了防止初始订单造成意外损失而下达的,因此该订单称为覆盖订单。通常情况下,经纪人不允许取消止损订单一旦它被下达。它只能通过完成退出。

除非经纪人另有支持,否则覆盖订单通常用于日内交易。如果初始订单或止损订单在交易会话结束时尚未完成,则经纪人将自动取消或退出它们。

下图总结了前面的要点并解释了止损订单的工作原理:

当必须以市价下达覆盖订单时,可以使用覆盖市价订单。

此处方演示了下达以下覆盖市价订单并查询其状态:

  • BUYCOVERINTRADAYMARKET订单

  • SELLCOVERINTRADAYMARKET订单

以下是覆盖市价订单的状态机图的参考:

  • 初始订单:请参考前一章中下达常规市价订单处方的状态机图。

  • 止损订单:请参考前一章中下达常规止损市价订单处方的状态机图。

准备工作

确保在您的 Python 命名空间中可用pyalgotrading包中的broker_connection对象和常量。请参考本章的技术要求部分设置此对象。

如何操作……

我们执行以下步骤进行此处方:

  1. 获取金融工具并将其分配给instrument
>>> instrument = broker_connection.get_instrument('NSE', 
                                                  'BANKBARODA')
  1. 获取最新价格。下达BUYCOVERINTRADAYMARKET订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order1_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type=\
                        BrokerOrderTransactionTypeConstants.BUY,
                    order_type=BrokerOrderTypeConstants.COVER,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.MARKET,
                    quantity=1,
                    trigger_price=ltp-1)
>>> order1_id

我们得到以下输出(您的输出可能会有所不同):

'200303003717532'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'COMPLETE'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以在以下截图中找到您的订单详细信息(您的数据可能会有所不同):

  • 以下截图显示了初始订单:

  • 以下截图显示了止损订单:

  1. 获取最新价格。下达SELLCOVERINTRADAYMARKET订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order2_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type= \
                        BrokerOrderTransactionTypeConstants.SELL,
                    order_type=BrokerOrderTypeConstants.COVER,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.MARKET,
                    quantity=1,
                    trigger_price=ltp+1)
>>> order2_id

我们得到以下输出(您的输出可能会有所不同):

'200303003732941'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'TRIGGER PENDING'

如果您使用您的凭据登录到经纪人网站并转到订单部分,您可以在以下截图中找到您的订单详细信息(对于您可能会有些数据不同):

  • 以下截图显示了初始订单:

  • 以下截图显示了止损订单:

工作原理...

步骤 1 中,您使用BrokerConnectionZerodha类的get_instrument()方法获取一个工具并将其分配给一个新属性instrument。此对象是Instrument类的一个实例。调用get_instrument所需的两个参数是交易所('NSE')和交易符号('BANKBARODA')。

步骤 2 中,您使用BrokerConnectionZerodha类的get_ltp()方法获取工具的最新交易价格(LTP),并将其分配给一个新属性ltp。这里将instrument对象作为参数传递。接下来,您使用broker_connection对象的place_order()方法在交易所上下一个BUYCOVERINTRADAYMARKET订单。place_order()方法是经纪人特定的下单 API 的包装器。它接受以下属性:

  • instrument:这是必须下单的金融工具,应该是Instrument类的一个实例。我们在这里传递了instrument

  • order_transaction_type:这是订单交易类型,应该是BrokerOrderTransactionTypeConstants类型的枚举。我们在这里传递了BrokerOrderTransactionTypeConstants.BUY

  • order_type:这是订单类型,应该是BrokerOrderTypeConstants类型的枚举。我们在这里传递了BrokerOrderTypeConstants.COVER

  • order_code:这是订单代码,应该是BrokerOrderCodeConstants类型的枚举。我们在这里传递了BrokerOrderCodeConstants.INTRADAY

  • order_variety:这是订单种类,应该是BrokerOrderVarietyConstants类型的枚举。我们在这里传递了BrokerOrderVarietyConstants.MARKET

  • quantity:这是要交易的股票数量,应该是正整数。我们在这里传递了1

  • trigger_price:这是止损订单的触发价格。我们在这里传递了ltp-1,这意味着低于ltp一个单位价格。

(传递给place_order()方法的属性是经纪人不可知的常量,之前从pyalgotrading.constants模块导入。)

步骤 2 中下订单时,您从经纪人那里获得一个订单 ID,将其分配给一个新属性order1_idorder1_id对象是一个字符串。如果由于某种原因下单失败,则可能不会收到订单 ID。请注意,trigger_price参数传递了一个ltp-1的值。这意味着止损订单放在市价之下,这是放置卖出止损市价订单的必要条件。

步骤 3 中,您使用 broker_connection 对象的 get_order_status() 方法获取下达订单的状态。将 order1_id 作为参数传递给 get_order_status() 方法。您将订单状态作为字符串 'COMPLETE' 立即在此之后,以前述价格下达止损订单。然后,该订单被执行为常规止损-市价订单。

如果止损订单在任何时间点被执行,这意味着您的交易已经产生了损失,但已经保护您免受进一步的损失。止损订单转换为 'COMPLETE' 状态,并退出由盖板订单创建的头寸。您也可以通过登录经纪网站并检查订单部分来验证订单成功下达。您应该看到类似于 步骤 3 输出中显示的屏幕截图的数据。

以下是有关止损订单执行更多细节的参考:

  • 初始订单:参考前一章节中 下达常规市价订单 配方。

  • 止损订单:参考前一章节中 下达常规止损-市价 配方。

本配方中的其他步骤遵循相同的模式,用于不同属性组合的下单和获取其状态:

  • 步骤 45SELLCOVERINTRADAYMARKET 订单

下达盖板限价订单

盖板订单是旨在在交易不利的情况下帮助限制损失的复杂订单。盖板订单实质上是两个常规订单的组合——一个初始订单和一个止损订单,它们一起协同工作以帮助在交易不利时限制损失。

请参考 下达盖板市价订单 配方的介绍,深入了解盖板订单的工作原理。如果您想在市场价格下方下达买盖板订单或在市场价格上方下达卖盖板订单,则可以使用盖板限价订单。本配方演示以下盖板限价订单的下达和查询其状态:

  • BUYCOVERINTRADAYLIMIT 订单

  • SELLCOVERINTRADAYLIMIT 订单

以下是盖板限价订单状态机图的参考:

  • 初始订单:参考前一章节中 下达常规限价订单 配方的状态机图。

  • 止损订单:参考前一章节中 下达常规止损-市价 配方的状态机图。

准备工作

确保 pyalgotrading 包中的 broker_connection 对象和常量可在您的 Python 命名空间中使用。请参阅本章的 技术要求 部分设置此对象。

如何执行…

我们对这个配方执行以下步骤:

  1. 获取金融工具并将其分配给 instrument
>>> instrument = broker_connection.get_instrument('NSE', 'YESBANK')
  1. 获取 LTP。下达 BUYCOVERINTRADAYLIMIT 订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order1_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type= \
                        BrokerOrderTransactionTypeConstants.BUY,
                    order_type=BrokerOrderTypeConstants.COVER,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.LIMIT,
                    quantity=1,
                    price=ltp-0.5,
                    trigger_price=ltp-1)
>>> order1_id

我们得到以下输出:

'200303003749622’
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'OPEN'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以像下面的屏幕截图所示找到您的订单详细信息(您的数据可能会有所不同):

  • 以下屏幕截图显示了初始订单:

  • 以下屏幕截图显示了止损订单:

  1. 过一段时间后再次获取并显示订单状态:
>>> broker_connection.get_order_status(order1_id)

我们得到以下输出:

'COMPLETE'
  1. 获取 LTP。放置一个SELLCOVERINTRADAYLIMIT订单并显示订单 ID:
>>> ltp = broker_connection.get_ltp(instrument)
>>> order2_id = broker_connection.place_order(
                    instrument=instrument,
                    order_transaction_type=\
                        BrokerOrderTransactionTypeConstants.SELL,
                    order_type=BrokerOrderTypeConstants.COVER,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety= \
                        BrokerOrderVarietyConstants.LIMIT,
                    quantity=1,
                    price=ltp+0.5,
                    trigger_price=ltp+1)
>>> order2_id

我们得到以下输出(您的输出可能会有所不同):

'200303003751757'
  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'OPEN'

如果您使用您的凭据登录经纪人网站并转到订单部分,您可以像下面的屏幕截图所示找到您的订单详细信息(您的数据可能会有所不同):

  • 以下屏幕截图显示了初始订单:

  • 以下屏幕截图显示了止损订单:

  1. 获取并显示订单状态:
>>> broker_connection.get_order_status(order2_id)

我们得到以下输出:

'COMPLETE'

工作原理...

步骤 1中,您使用BrokerConnectionZerodha类的get_instrument()方法获取一个工具并将其分配给一个新属性,instrument。这个对象是Instrument类的一个实例。调用get_instrument所需的两个参数是交易所('NSE')和交易符号('YESBANK')。

步骤 2中,您使用BrokerConnectionZerodha类的get_ltp()方法获取工具的 LTP,并将其分配给一个新属性,ltp。在这里,将instrument对象作为参数传递。接下来,您使用broker_connection对象的place_order()方法在交易所上放置一个BUYCOVERINTRADAYLIMIT订单。place_order()方法是经纪人特定的下单 API 的包装器。它接受以下属性:

  • instrument: 这是必须放置订单的金融工具,应该是Instrument类的一个实例。我们在这里传递instrument

  • order_transaction_type: 这是订单交易类型,应该是类型的枚举,BrokerOrderTransactionTypeConstants。我们在这里传递BrokerOrderTransactionTypeConstants.BUY

  • order_type: 这是订单类型,应该是类型的枚举,BrokerOrderTypeConstants。我们在这里传递BrokerOrderTypeConstants.COVER

  • order_code: 这是订单代码,应该是类型的枚举,BrokerOrderCodeConstants.。我们在这里传递BrokerOrderCodeConstants.INTRADAY

  • order_variety: 这是订单种类,应该是类型的枚举,BrokerOrderVarietyConstants。我们在这里传递BrokerOrderVarietyConstants.LIMIT

  • quantity: 这是要交易给定工具的股票数量,应该是一个正整数。我们在这里传递1

  • price:这是初始订单的限价。我们在这里传递ltp-0.5,这意味着低于ltp 0.5 单位的价格。

  • trigger_price:这是止损订单的触发价格。我们在这里传递ltp-1,这意味着低于ltp一单位的价格。

(传递给place_order()方法的属性是来自pyalgotrading.constants模块之前导入的与经纪人无关的常量。)

第 2 步放置订单时,您将从经纪人那里获得一个订单 ID,将其分配给一个新属性order1_idorder1_id对象是一个字符串。如果由于某种原因订单未成功放置,您可能不会获得订单 ID。请注意,price参数被赋予一个值为ltp-0.5。这意味着初始订单被放置在市场价格之下,这是放置购买限价订单的必要条件。另请注意,trigger_price参数被赋予一个值为ltp-1。这意味着止损订单被放置在price之下(这将是放置止损订单时的市场价格),这是放置卖出止损市价订单的必要条件。

步骤 3中,您使用broker_connection对象的get_order_status()方法获取已放置订单的状态。您将order1_id作为参数传递给get_order_status()方法。您将订单状态获取为'OPEN',一个字符串。您还可以随时使用order1_id获取已放置订单的状态。在步骤 4中,您再次获取订单状态,如果订单已完成,您将获得订单状态为'COMPLETE'。在此之后立即放置止损订单,以前述价格。然后,此订单将作为常规止损市价订单执行。

如果在任何时间点执行了止损订单,这将意味着您的交易已经产生了损失,但是已经保护您免受进一步的损失。止损订单转移到'COMPLETE'状态,并且由覆盖订单创建的持仓被退出。您还可以通过登录经纪网站并检查订单部分来验证订单的成功放置。您应该看到类似于步骤 3输出中显示的屏幕截图的数据。

下面是关于初始订单和止损订单执行更多细节的参考:

  • 初始订单:请参阅前一章中的放置常规限价订单

  • 止损订单:请参阅前一章中的放置常规止损市价订单

此食谱中的其他步骤遵循相同的模式,为不同的属性组合放置订单并获取其状态:

  • 步骤 45SELLCOVERINTRADAYLIMIT订单

第八章:逐步编码算法交易策略

构建自己的算法交易策略是一项复杂的任务。需要一个具有许多组件的交易平台来测试和运行您的策略。其中一些组件包括计算引擎、实时数据源、经纪人连接、交易记录、基金经理、时钟、虚拟订单管理系统等等。

本章将使用由 AlgoBulls 提供的服务,这是一个算法交易平台(algobulls.com)。该平台提供了一个名为pyalgotrading的 Python 包(github.com/algobulls/pyalgotrading)。您将通过继承该包中提供的StrategyBase抽象类来编写您的策略作为 Python 类。该抽象类充当了以最小的努力快速开发和验证新策略的模板。您可以使用 AlgoBulls 平台来执行您的策略的回测、纸上交易和实盘交易。pyalgotrading包帮助我们专注于开发策略,并负责与 AlgoBulls 平台进行通信以执行目的。

本章介绍了两种策略:

  • EMA-Regular-Order 策略:该策略基于技术指标指数移动平均线。它使用常规订单。

  • MACD-Bracket-Order 策略:该策略基于技术指标移动平均线收敛与发散。它使用括号订单。

最初,您可能会发现策略编码是一项艰巨的任务。因此,编码部分被分为五个步骤。每个步骤演示了StrategyBase类强制执行的一个或多个方法。第六个步骤演示了如何将策略上传到 AlgoBulls 平台。

策略的框架如下所示:

class MyCustomStrategy(StrategyBase):
    def __init__(self, *args, **kwargs): # [Recipes 1, 7]
        ...
    def name(): # [Recipes 1, 7]
        …
    def versions_supported(): # [Recipes 1, 7]
        ...
    def initialize(self): # [Recipes 1, 7]
        ...
    def strategy_select_instruments_for_entry(self, candle, 
                                              instruments_bucket):
        … # [Recipes 2, 8]
    def strategy_enter_position(self, candle, instrument, sideband_info):
        … # [Recipes 3, 9]
    def strategy_select_instruments_for_exit(self, candle, 
                                             instruments_bucket):
        … # [Recipes 4, 10]
    def strategy_exit_position(self, candle, instrument, sideband_info):
        … # [Recipes 5, 11]

AlgoBulls 核心引擎是推动 AlgoBulls 平台的交易引擎。它负责读取您的策略并对其进行回测、纸上交易和实盘交易执行。AlgoBulls 核心引擎使用以下流程图成功执行您的策略:

本章我们将介绍以下几个步骤:

  • EMA-Regular-Order 策略 – 编写 init、initialize、name 和 versions_supported 方法

  • EMA-Regular-Order 策略 – 编写 strategy_select_instruments_for_entry 方法

  • EMA-Regular-Order 策略 – 编写 strategy_enter_position 方法

  • EMA-Regular-Order 策略 – 编写 strategy_select_instruments_for_exit 方法

  • EMA-Regular-Order 策略 – 编写 strategy_exit_position 方法

  • EMA-Regular-Order 策略 – 将策略上传到 AlgoBulls 交易平台

  • MACD-Bracket-Order 策略 – 编写 init、initialize、name 和 versions_supported 方法

  • MACD-Bracket-Order 策略 – 编写 strategy_select_instruments_for_entry 方法

  • MACD-Bracket-Order 策略 – 编写 strategy_enter_position 方法

  • MACD-Bracket-Order 策略 – 编写 strategy_select_instruments_for_exit 方法

  • MACD-Bracket-Order 策略 – 编写 strategy_exit_position 方法

  • MACD-Bracket-Order 策略 – 将策略上传到 AlgoBulls 交易平台

技术要求

您将需要以下内容来执行本章中的配方:

  • Python 3.7+

  • Python 包:

  • pyalgotrading ($ pip install pyalgotrading)

  • pyalgostrategypool ($ pip install pyalgostrategypool)

  • TA-Lib ($ pip install TA-Lib)

如果在安装 TA-Lib 时遇到错误,大多数情况下是由于缺少依赖项。您可以按照以下说明解决问题:

  • 对于 Mac OS X,请使用以下
$ brew install ta-lib
  • 对于 Windows,请使用以下说明

您可以根据您的 Windows 构建(32 位/64 位)和 Python 版本从 www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib 安装最新的 TA-Lib 二进制文件。例如,该网站上的此链接 TA_Lib‑0.4.18‑cp38‑cp38‑win_amd64.whlTA-Lib 版本 0.4.18 (TA_Lib-0.4.18) 和 Python 版本 3.8 (cp38) 的链接,且适用于 Windows 64 位 (win_amd64)。

  • 对于 Linux,请执行以下步骤

下载此gzip文件—prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz—并从您的 Linux 终端运行以下命令:

  1. 提取下载的包含 TA-Lib 源代码的 gzip 文件:
$ tar -xzf ta-lib-0.4.0-src.tar.gz
  1. 将当前工作目录更改为提取的文件夹:
$ cd ta-lib/
  1. 运行configure命令以配置 TA-Lib 适合您的计算机:
$ ./configure --prefix=/usr
  1. 运行make命令以从下载的源代码构建 TA-Lib
$ make
  1. 运行install命令以将构建的可执行文件和库安装到您的计算机上的特定目录:
$ sudo make install 

如果这不起作用并且您仍然遇到错误,请参考官方 TA-Lib GitHub 页面 github.com/mrjbq7/ta-lib#dependencies。本章的最新 Jupyter 笔记本可以在 GitHub 上找到 github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/tree/master/Chapter08

以下代码将帮助您导入本章中所有配方使用的必要模块。请确保在尝试任何配方之前已执行此步骤:

>>> from pyalgotrading.strategy.strategy_base import StrategyBase
>>> from pyalgotrading.constants import *

在前五个配方中,您将基于 EMA 技术指标创建一个完整的算法交易策略。此策略称为 EMA-Regular-Order 策略,并描述如下:

  • 技术指标

  • EMA(timeperiod=4)EMA4

  • EMA(timeperiod=9)EMA9

虽然时间周期的典型值为49,但两个时间周期都作为参数,因此可以在运行时进行更改,而无需重新创建策略。这在本章的第一个配方中进行了更多讨论。

  • 订单属性

  • 订单交易类型:BUYSELL

  • 订单类型:Regular

  • 订单代码:INTRADAY

  • 订单品种:Market

  • 策略算法

  • 每当EMA4向上穿越EMA9时,请注意以下内容:

  • 如果有的话,应退出之前的SHORT头寸。

  • 策略生成了一个BUY信号,应输入一个新的LONG头寸。

  • 每当EMA4向下穿越EMA9时,请注意以下内容:

  • 如果有的话,应退出之前的LONG头寸。

  • 策略生成了一个SELL信号,应输入一个新的SHORT头寸。

您将把整个逻辑编码为一个名为StrategyEMARegularOrder的 Python 类。这个类将是pyalgotrading包中StrategyBase的子类。在 AlgoBulls 平台上上传StrategyEMARegularOrder后,您可以在该策略上进行回测(参考第九章的前六个配方,回测策略)、模拟交易(参考第十章的前五个配方,模拟交易)和实际交易(参考第十一章的前五个配方,实际交易)。

在第七至第十一个配方中,您将基于 MACD 技术指标创建一个完整的算法交易策略。这个策略称为MACD-Bracket-Order策略,描述如下:

  • 技术指标

  • MACD:这个技术指标有三个组成部分:MACD 线、MACD 信号和 MACD 柱状图。对于这个策略,我们只关注 MACD 线和 MACD 信号组件。

  • 订单属性

  • 订单交易类型:BUYSELL

  • 订单类型:Bracket

  • 订单代码:INTRADAY

  • 订单品种:Limit

  • 策略算法

  • 每当 MACD 线向上穿越 MACD 信号时,请注意以下内容:

  • 如果有的话,应退出之前的SHORT头寸。

  • 策略生成了一个BUY信号,应输入一个新的LONG头寸。

  • 每当 MACD 线向下穿越 MACD 信号时,请注意以下内容:

  • 如果有的话,应退出之前的LONG头寸。

  • 策略生成了一个SELL信号,应输入一个新的SHORT头寸。

您将把整个逻辑编码为一个名为StrategyMACDBracketOrder的 Python 类。这个类将是pyalgotrading包中StrategyBase的子类。在 AlgoBulls 平台上上传StrategyMACDBracketOrder后,您可以对这个策略进行回测(参见第九章的第七到第十二个食谱,《算法交易 - 回测》), 模拟交易(参见第十章的第七到第十二个食谱,《算法交易 - 模拟交易》)和真实交易(参见第十一章的第七到第十一个食谱,《算法交易 - 真实交易》)。

有关这些主题的更多信息,请参阅以下对应章节:

  • 技术指标:第五章,《计算和绘制技术指标》

  • 订单属性:第六章,《在交易所上下常规订单》和第七章,《在交易所上下 Bracket 和 Cover Orders》。

您需要在 AlgoBulls 平台(algobulls.com)上设置您的帐户以获取 API 令牌。设置帐户是免费的。根据您的使用情况,使用其服务可能会产生费用。您可以从该网站上的免费套餐开始。更多细节请参考附录 II

EMA-Regular-Order strategy – 编写 init、initialize、name 和 versions_supported 方法

这个示例演示了StrategyEMARegularOrder类的初始编码。完整的类将在本章的EMA-Regular-Order strategy – coding the strategy_exit_position method食谱结束时编码完成。在这个示例中,您将编写以下方法:

  • __init__()

  • initialize()

  • name()

  • versions_supported()

要了解更多关于 EMA 技术指标的信息,请参阅第五章的Trend indicator – exponential moving average食谱,《计算和绘制技术指标》。

请参考本章介绍中的流程图,了解 AlgoBulls 核心引擎在策略执行过程中如何调用__init__()initialize()方法。

准备工作

确保您的 Python 命名空间中有StrategyBasepyalgotrading常量。请参考本章的技术要求部分进行设置。

怎么做…

创建一个名为StrategyEMARegularOrder的新类,它将是StrategyBase的子类,然后定义所需的四种方法:

class StrategyEMARegularOrder(StrategyBase):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.timeperiod1 = self.strategy_parameters['timeperiod1']
        self.timeperiod2 = self.strategy_parameters['timeperiod2']
        self.main_order = None
    def initialize(self):
        self.main_order = {}
    @staticmethod
    def name():
        return 'EMA Regular Order Strategy'
    @staticmethod
    def versions_supported():
        return AlgoBullsEngineVersion.VERSION_3_2_0

工作原理…

在这个示例中,我们创建了StrategyEMARegularOrder类,它是从StrategyBase派生的子类。我们为这个类定义了四种方法,并描述如下:

  • __init__() 方法:这是创建新策略时要做的第一件事情。首先,你需要创建这个方法,并使用super()调用父类的__init__()方法。这有助于 AlgoBulls 核心引擎创建进一步开发策略所需的必要数据结构。接下来,你从self.strategy_parameters中创建两个新属性——self.timeperiod1self.timeperiod2self.strategy_parameters是从StrategyBase派生的每个策略子类都可用的字典对象。(第八章的第二个配方讨论了这些值如何在运行时传递给self.strategy_parameters。)你将使用这些参数作为下一个配方中两个 EMA 的时间段。

最后,你创建一个新属性,self.main_order,它是一个空字典。我们将用它来保存在执行此策略期间放置的所有未完成订单的句柄。

  • initialize() 方法:这个方法在每个交易日的开头被调用,用于将任何内部变量初始化为它们的默认状态。对于实际交易和模拟交易,这个方法只调用一次。对于多日回测,这个方法会被多次调用,在每个新的交易日的开头调用一次。在这个方法中,你将self.main_order初始化为空字典。

  • name() 方法:这是一个静态方法,返回此策略的名称。在使用此策略进行回测、模拟交易和实际交易时会用到。在这个方法中,你只需返回一个字符串,指数移动平均线常规委托。你可以返回任何你选择的字符串。

  • versions_supported() 方法:这个静态方法用于返回创建此策略的 AlgoBulls 核心引擎版本。通常情况下,随着 AlgoBulls 核心引擎的新升级,可能会引入一些不兼容的变化。这个方法有助于确保此策略始终在正确的 AlgoBulls 核心引擎版本上运行。在这个方法中,你需要从常量模块中返回最高可用版本,目前写这一章时的版本是 VERSION_3_2_0。

这四个方法是强制性的;它们由StrategyBase基类强制执行,不能被跳过。

EMA-Regular-Order 策略 - 编写 strategy_select_instruments_for_entry 方法

在本配方中,你将继续编写StrategyEMARegularOrder类。在这里,你将编写strategy_select_instruments_for_entry()方法,这是由StrategyBase基类强制执行的强制性方法。AlgoBulls 核心引擎会在每个新的蜡烛上调用此方法,用于回测、模拟交易和实际交易服务。

请参考本章介绍中的流程图,了解 AlgoBulls 核心引擎在策略执行期间如何调用strategy_select_instruments_for_entry()方法。

准备工作

确保您在开始此配方之前已经按照前面的配方创建了StrategyEMARegularOrder类。

如何做…

继续编写StrategyEMARegularOrder类。我们需要定义两个新方法——一个用于获取 MACD 线和 MACD 历史信号之间的交叉值,另一个用于根据计算的交叉值从instruments_bucket中选择金融工具以进入新仓位:

class StrategyEMARegularOrder(StrategyBase):
    # Previous methods not shown
    def get_crossover_value(self, instrument):
        hist_data = self.get_historical_data(instrument)
        ema_x = talib.EMA(hist_data['close'], timeperiod=self.timeperiod1)
        ema_y = talib.EMA(hist_data['close'], timeperiod=self.timeperiod2)
        crossover_value = self.utils.crossover(ema_x, ema_y)
        return crossover_value
    def strategy_select_instruments_for_entry(self, candle,
                                              instruments_bucket):
        selected_instruments_bucket = []
        sideband_info_bucket = []
        for instrument in instruments_bucket:
            crossover_value = self.get_crossover_value(instrument)
            if crossover_value == 1:
                selected_instruments_bucket.append(instrument)
                sideband_info_bucket.append({'action': 'BUY'})
            elif crossover_value == -1:
                if self.strategy_mode is StrategyMode.INTRADAY:
                    selected_instruments_bucket.append(instrument)
                    sideband_info_bucket.append({'action': 'SELL'})
        return selected_instruments_bucket, sideband_info_bucket

工作原理…

在这个配方中,我们继续编写StrategyEMARegularOrder类的代码。我们为这个类定义了两个新方法,描述如下:

  • get_crossover_value()方法:这个方法以instrument作为参数(以及self)。这是需要计算交叉值的金融工具。您使用self.get_historical_data()方法获取最新的历史数据,并将其赋值给一个新属性hist_data。我们将instrument作为这个方法的参数。hist_data属性是一个pandas.DataFrame对象,具有timestampopenhighlowclosevolume列。获取的历史数据的默认持续时间是最近的 15 天。

您使用talib.EMA函数在hist_data的收盘价上计算 EMA,时间周期为self.timeperiod1,并将其赋值给ema_x。这个数据是一个pandas.Series对象。(更多关于 EMA 计算的细节,请参考第五章的第二个配方,计算和绘制技术指标。)类似地,您使用self.timeperiod2的时间周期在hist_data的收盘价上计算 EMA,并将其赋值给ema_y。这个返回数据也是一个pandas.Series对象。

您使用self.utils.crossover(ema_x, ema_y)计算ema_xema_y之间的交叉值,并将其赋值给一个新属性crossover_valuecrossover()函数调用如下:

  • 它以两个可迭代对象作为输入。我们在这里传递ema_xema_y

  • 如果ema_x向上穿越ema_y,交叉函数返回1

  • 如果ema_x向下穿越ema_y,交叉函数返回-1

  • 如果ema_xema_y之间没有交叉,则交叉函数返回0

最后,您返回crossover_value

  • strategy_select_instruments_for_entry()方法:这个方法除了self之外,还接受两个参数——candleCandleTime类型的对象,其中包含当前蜡烛的时间戳,以及instruments_bucketSetInstruments类型的对象,其中包含用于创建新仓位的所有金融工具。我们在策略执行时传递这些数据。您创建两个空列表,selected_instruments_bucketsideband_info_bucket。然后您在instruments_bucket上运行一个for循环,并且对于每个金融工具,您调用self.get_crossover_value()并将其值保存到一个新属性crossover_value。根据crossover_value的值,您做出决定,如下所示:

  • 如果crossover_value1,这意味着策略正在发出BUY信号。你需要执行以下操作:

  • instrument附加到selected_instruments_bucket

  • sideband_info_bucket属性附加一个{'action': 'BUY'}字典。

  • 如果crossover_value-1,这意味着策略正在发出SELL信号。你需要执行以下操作:

  • instrument附加到selected_instruments_bucket

  • sideband_info_bucket属性附加一个{'action': 'SELL'}字典。

  • 如果crossover_value既不是1也不是-1,这意味着策略没有发出任何信号。在这里你什么也不做。

最后,你需要返回两个属性:selected_instruments_bucketsideband_info_bucket。这些属性可能已经被填充,也可能保持为空列表。

请回忆strategy_select_instruments_for_entry()方法是为每个蜡烛调用的,因此前面的步骤会针对每个新蜡烛重复执行。在适当的蜡烛中,你会得到一个BUYSELL信号,而在其他蜡烛中,你将不会得到任何信号。根据信号,你可以下达适当的订单,这将在下一个示例中讨论。

strategy_select_instruments_for_entry()方法是由StrategyBase基类强制执行的,每个策略都必须定义该方法。

get_crossover_value()方法是一个辅助方法,意味着它不受StrategyBase基类的强制。你可以选择不定义这个方法,或者定义多个这样的辅助函数。

EMA-Regular-Order 策略 - 编写 strategy_enter_position 方法

在这个示例中,你将继续编写StrategyEMARegularOrder类的代码。在这里,你将编写strategy_enter_position()方法,这是StrategyBase基类强制执行的一个必需方法。这个方法由 AlgoBulls 核心引擎在每次strategy_select_instruments_for_entry方法返回非空数据时调用。这个方法在回测、模拟交易和实际交易服务中可能不会为每个新蜡烛调用。

请参考本章介绍中的流程图,了解 AlgoBulls 核心引擎在策略执行期间如何调用strategy_enter_position()方法。

准备就绪

确保在开始本示例之前已经按照前面的示例操作。

如何操作...

继续编写StrategyEMARegularOrder类的代码。我们需要定义一个方法为给定的工具打新订单并进入一个新的头寸:

class StrategyEMARegularOrder(StrategyBase):
    # Previous methods not shown
    def strategy_enter_position(self, candle, instrument, sideband_info):
        if sideband_info['action'] == 'BUY':
            qty = self.number_of_lots * instrument.lot_size
            self.main_order[instrument] = \
                self.broker.BuyOrderRegular(instrument=instrument,
                          order_code=BrokerOrderCodeConstants.INTRADAY,
                          order_variety=BrokerOrderVarietyConstants.MARKET,
                          quantity=qty)
        elif sideband_info['action'] == 'SELL':
            qty = self.number_of_lots * instrument.lot_size
            self.main_order[instrument] = \
                self.broker.SellOrderRegular(instrument=instrument,
                          order_code=BrokerOrderCodeConstants.INTRADAY,
                          order_variety=BrokerOrderVarietyConstants.MARKET,
                          quantity=qty)
        else:
            raise SystemExit(f'Got invalid sideband_info value: 
                              {sideband_info}')
        return self.main_order[instrument]

工作原理...

在本示例中,我们将继续编写StrategyEMARegularOrder类的代码。我们为该类定义了一个新方法,strategy_enter_position(),如下所述:

  • 此方法除了self之外还接受三个参数:

  • candle:包含当前蜡烛时间戳的CandleTime类型的对象。

  • instrument:代表金融工具的Instrument类型的对象。

  • sideband_info:一个字典对象,保存了与instrument属性相关的交易信息。这个对象看起来像{'action': [action_value]},其中[action_value]可以是'BUY''SELL'

  • 通过将self.number_of_lotsinstrument.lot_size相乘,并将结果赋值给一个新属性qty来计算下单数量。self.number_of_lots属性保存了交易的手数信息,你可以在执行这个策略时传递它。instrument.lot_size属性保存了instrument的手数大小,它是一个正整数。例如,如果手数数为 2,而 instrument 的手数大小为 10,那么订单的数量将是 2 * 10 = 20。

  • 如果sideband_info{'action': 'BUY'},则通过创建self.broker.BuyOrderRegular类的实例(第六章的第一个配方,在交易所上放置常规订单)并将其值赋给self.main_order[instrument],来下达BUY交易类型的Regular订单。

  • 如果sideband_info{'action': 'SELL'},则通过创建self.broker.SellOrderRegular类的实例(第六章的第一个配方,在交易所上放置常规订单)并将其值赋给self.main_order[instrument],来下达SELL交易类型的Regular订单。

在这两种情况下,self.main_order字典对象将instrumentorder实例作为键值对保存。这将在稍后(在EMA-Regular-Order 策略 - 编写 strategy_exit_position 方法配方中)对通过此方法创建的头寸进行退出时非常有用。

self.broker属性在运行时由 AlgoBulls 核心引擎替换为适当的经纪人实例。因此,相同的代码可以在 AlgoBulls 平台支持的所有经纪人上运行。

EMA-Regular-Order 策略 - 编写 strategy_select_instruments_for_exit 方法

在这个配方中,你将继续编写StrategyEMARegularOrder类的代码。在这里,你编写strategy_select_instruments_for_exit()方法,这是StrategyBase基类强制执行的一个必需方法。如果有任何未平仓头寸,AlgoBulls 核心引擎将为每个新的蜡烛进行回测、纸上交易和真实交易服务调用此方法。

请参考本章介绍中的流程图,了解 AlgoBulls 核心引擎在策略执行期间如何调用strategy_select_instruments_for_exit()方法。

准备工作

在开始本配方之前,请确保您已经按照前面的配方进行了操作。

如何做...

继续编写StrategyEMARegularOrder类。我们需要定义一个新的方法,根据交叉值的计算从instruments_bucket中选择工具以退出现有头寸:

class StrategyEMARegularOrder(StrategyBase):
    # Previous methods not shown
    def strategy_select_instruments_for_exit(self, candle, 
                                             instruments_bucket):
        selected_instruments_bucket = []
        sideband_info_bucket = []
        for instrument in instruments_bucket:
            if self.main_order.get(instrument) is not None:
                crossover_value = self.get_crossover_value(instrument)
                if crossover_value in [1, -1]:
                    selected_instruments_bucket.append(instrument)
                    sideband_info_bucket.append({'action': 'EXIT'})
        return selected_instruments_bucket, sideband_info_bucket

它是如何工作的...

在本配方中,我们继续编写StrategyEMARegularOrder类。我们为此类定义一个新方法,strategy_select_instruments_for_exit(),如下所述:

  • 除了self之外,此方法还接受两个参数:

  • candleCandleTime类型的对象,包含当前蜡烛的时间戳。

  • instruments_bucketSetInstruments类型的对象。该对象保存了之前通过strategy_enter_position()方法输入仓位的金融工具。

  • 创建两个空列表,selected_instruments_bucketsideband_info_bucket

  • instruments_bucket进行for循环。对于每个工具,使用'if self.main_order.get(instrument) is not None:'行检查是否已输入给定工具的仓位。只有在已存在仓位的情况下才继续。

  • 您调用self.get_crossover_value()并将其值保存到一个新属性crossover_value中。根据crossover_value的值,您做出决定,如下所示:

  • 如果crossover_value1-1,则表示发生了交叉。你执行以下操作:

  • instrument属性追加到selected_instruments_bucket

  • {'action': 'EXIT'}字典追加到sideband_info_bucket属性。

  • 如果crossover_value既不是1也不是-1,则表示策略未发出任何信号。这里不做任何操作。

  • 最后,您返回两个属性:selected_instruments_bucketsideband_info_bucket。这些属性可能已填充,也可能保持为空列表。

请记住,strategy_select_instruments_for_exit()方法将针对每个蜡烛调用,因此前述步骤将针对每个新蜡烛重复。在适当的蜡烛中,如果存在仓位,您可能会收到EXIT信号,在其他情况下,您将不会收到任何信号。根据信号,您可以通过放置适当的订单退出仓位,这将在下一个配方中讨论。

EMA-Regular-Order 策略 - 编写strategy_exit_position方法

在本配方中,您将继续编写StrategyEMARegularOrder类的代码。在这里,您将编写strategy_exit_position()方法,这是StrategyBase基类强制执行的最后一个必需方法。此方法由 AlgoBulls 核心引擎在每次strategy_select_instruments_for_exit方法返回非空数据时调用。完成本配方后,您将完成编写StrategyEMARegularOrder类。

请参考本章介绍中的流程图,了解 AlgoBulls 核心引擎在策略执行期间如何调用strategy_select_instruments_for_exit()方法。

准备就绪

在开始本配方之前,请确保您已按照上一个配方进行了操作。

如何做…

继续编写StrategyEMARegularOrder类。为给定工具基于sideband_info定义一个退出仓位的方法:

class StrategyEMARegularOrder(StrategyBase):
    # Previous methods not shown
    def strategy_exit_position(self, candle, instrument, sideband_info):
        if sideband_info['action'] == 'EXIT':
            self.main_order[instrument].exit_position()
            self.main_order[instrument] = None
            return True
        return False

工作原理…

在此示例中,我们继续编写StrategyEMARegularOrder类。我们为此类定义一个新方法,strategy_exit_position(),如下所述:

  • 此方法除了self之外还接受三个参数:

  • candle:一个包含当前蜡烛时间戳的CandleTime类型的对象。

  • instrument:一个表示金融工具的Instrument类型的对象。

  • sideband_info:一个包含有关要为instrument属性放置交易的信息的字典对象。此对象类似于{'action': 'EXIT'}

  • 如果sideband_info{'action': 'EXIT'},则执行以下操作:

  • 您使用self.main_order[instrument]获取订单(请回想一下,self.main_order是一个将工具和相应订单实例作为键值对保存的字典)。

  • 您通过调用其exit_position()方法退出此订单的持仓。

  • 您将self.main_order中对应于键instrument的值重置为None。这表明不再存在与instrument相对应的持仓。

  • 返回True,向 AlgoBulls 核心引擎表明此次调用中已经退出了instrument的持仓。

  • 如果sideband_info不是{'action': 'EXIT'},则返回False,向 AlgoBulls 核心引擎表明在此调用中未退出instrument的持仓。

self.broker 属性会在 AlgoBulls 核心引擎运行时被适当的经纪人实例替换。因此,相同的代码可以在 AlgoBulls 平台支持的所有经纪人上运行。

您现在已经完成了StrategyEMARegularOrder类的编码。

EMA-Regular-Order 策略 – 在 AlgoBulls 交易平台上上传策略

在此示例中,您将在 AlgoBulls 交易平台上上传策略类StrategyEMARegularOrder,该类是您在前面的示例中创建的。一旦上传完成,您可以在相同的代码库上进行回测、模拟交易和真实交易。

准备就绪

确保您已在 AlgoBulls 平台(algobulls.com)上设置了您的帐户以获取您的 API 令牌。设置帐户是免费的。使用其服务可能会产生费用,具体取决于您的使用情况。您可以从网站上的免费套餐开始。有关更多详细信息,请参阅附录 II

如何执行…

我们为此示例执行以下步骤:

  1. 导入必要的模块:
>>> import inspect
>>> from pyalgotrading.algobulls import AlgoBullsConnection
>>> from pyalgostrategypool.strategy_ema_regular_order import StrategyEMARegularOrder
  1. 创建一个新的 AlgoBulls 连接对象:
>>> algobulls_connection = AlgoBullsConnection()
  1. 获取授权 URL:
>>> algobulls_connection.get_authorization_url()

我们得到以下输出:

Please login to this URL with your AlgoBulls credentials and get your developer access token: https://app.algobulls.com/user/login
'https://app.algobulls.com/user/login'
  1. 使用您的 AlgoBulls 凭据登录到上述链接,获取您的令牌,并在此处设置它(有关更多详细信息,请参阅附录 II):
>>> algobulls_connection.set_access_token('80b7a69b168c5b3f15d56688841a8f2da5e2ab2c')
  1. 在上传策略之前,您可以检查您的策略代码,以确保您上传了正确的策略:
>>> print(inspect.getsource(StrategyEMARegularOrder))

我们得到以下输出:

class StrategyEMARegularOrder(StrategyBase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.timeperiod1 = self.strategy_parameters['timeperiod1']
        self.timeperiod2 = self.strategy_parameters['timeperiod2']

        self.main_order = None

    def initialize(self):
        self.main_order = {}

    @staticmethod
    def name():
        return 'EMA Regular Order Strategy'
    ...
    def strategy_exit_position(self, candle, instrument, 
                               sideband_info):
        if sideband_info['action'] == 'EXIT':
            self.main_order[instrument].exit_position()
            self.main_order[instrument] = None
            return True
        return False

这里没有显示完整的输出。请访问以下链接以阅读完整的输出:

github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/strategy_ema_regular_order.py

  1. StrategyEMARegularOrder上传到 AlgoBulls 平台。 这将为您的 AlgoBulls 帐户创建一个新策略:
>>> algobulls_connection.create_strategy(StrategyEMARegularOrder)

我们获得以下输出(您的输出可能会有所不同):

Validating Strategy...
{'details': `'strategy_code': '49287246f9704bbcbad76ade9e2091d9'}

工作原理如下...

我们在步骤 1中导入所需的模块。 在步骤 2中,创建了AlgoBullsConnection类的实例,命名为algobulls_connection。 在步骤 3中,使用algobulls_connection对象的get_authorization_url()方法获取授权 URL。 您应该从 Web 浏览器访问此 URL 以登录到 AlgoBulls 平台并获取您的开发者访问令牌。 (您可以在 AlgoBulls 平台上的附录 II中找到有关从 AlgoBulls 平台获取开发者访问令牌的详细信息和截图。) 您复制访问令牌并在步骤 4中使用algobulls_connectionset_access_token()方法设置它。 如果令牌被接受,则与 AlgoBulls 平台建立了成功的连接。

我们在步骤 15中编写的StrategyEMARegularOrder策略类也在pyalgostrategypool包中可用。 我们在步骤 1中导入此类。 或者,您也可以将您的策略类保存在单独的 Python 模块中,并在步骤 1中导入它,而不是从pyalgostrategypool导入它。

您使用algobulls_connectionupload_strategy()方法将StrategyEMARegularOrder策略类作为参数上传。 如果上传成功,您将收到带有strategy_code的成功消息,这是一个唯一的字符串。 strategy_code可以稍后用于执行与策略相关的所有操作,例如编辑策略,执行回测(第九章,算法交易 - 回测),执行模拟交易(第十章,算法交易 - 模拟交易),以及执行实际交易(第十一章,算法交易 - 实际交易)。

还有更多...

如果在上传后对策略进行了更改,您可以使用algobulls_connectionupload_strategy()方法将更新后的类和overwrite=True作为参数更新 AlgoBulls 平台上的策略。 如果更改成功上传,您将收到成功消息。

修改已上传的策略:

>>> algobulls_connection.create_strategy(StrategyEMARegularOrder, 
                                         overwrite=True)

我们获得以下输出:

Validating Strategy…
{'details': 'success'}

AlgoBulls 平台不允许具有相同名称(由name()方法返回)的多个策略。 如果存在具有相同名称的现有策略,则overwrite=True参数将更新该策略。 如果未将overwrite=True传递给create_strategy()方法,则默认值为False,这意味着它尝试在 AlgoBulls 平台上创建一个新策略。

MACD-Bracket-Order 策略 – 编码 __init__initializenameversions_supported 方法

本配方演示了 StrategyMACDBracketOrder 类的初始编码。完整的类将在本章的第十一个配方结束时编码。在本配方中,您将编写以下方法:

  • __init__()

  • initialize()

  • name()

  • versions_supported()

要了解更多关于 MACD 技术指标的信息,请参考 Chapter 5 中的Trend indicator – moving average convergence divergence配方,计算和绘制技术指标

请参考本章介绍中的流程图,了解 AlgoBulls 核心引擎在策略执行过程中如何调用 __init__()initialize() 方法。

准备工作

确保你的 Python 命名空间中有 StrategyBasepyalgotrading 常量。请参考本章的技术要求部分进行设置。

如何做…

创建一个名为 StrategyMACDBracketOrder 的新类。从 StrategyBase 派生它。定义所需的四种方法:

class StrategyMACDBracketOrder(StrategyBase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fastMA_period = self.strategy_parameters['fastma_period']
        self.slowMA_period = self.strategy_parameters['slowma_period']
        self.signal_period = self.strategy_parameters['signal_period']
        self.stoploss = self.strategy_parameters['stoploss_trigger']
        self.target = self.strategy_parameters['target_trigger']
        self.trailing_stoploss = \
                      self.strategy_parameters['trailing_stoploss_trigger']

        self.main_order = None

    def initialize(self):
        self.main_order = {}

    @staticmethod
    def name():
        return 'MACD Bracket Order Strategy'
    @staticmethod
    def versions_supported():
        return VERSION_3_2_0

工作原理…

在本配方中,我们将创建 StrategyEMARegularOrder 类,它是从 StrategyBase 派生的子类。我们将为该类定义四种方法,如下所述:

  • __init__() 方法:这是创建新策略时的第一步。首先,您创建此方法并使用 super() 调用父类 __init__() 方法。这有助于 AlgoBulls 核心引擎创建进一步开发策略所需的必要数据结构。接下来,您从 self.strategy_parameters 创建六个属性:

  • self.fastMA_period

  • self.slowMA_period

  • self.signal_period

  • self.stoploss

  • self.target

  • self.trailing_stoploss

self.strategy_parameters 是从 StrategyBase 派生的每个策略的字典对象。 (第九章的第七个配方 Chapter 9,算法交易 – **回测,讨论了这些值如何在运行时传递给 self.strategy_parameters。) 这些参数将在本章的下一个配方中作为 MACD 技术指标的参数使用。最后,创建一个新属性,self.main_order,为空字典。我们将用它来保存在执行此策略期间放置的所有挂单的句柄。

  • initialize() 方法:这个方法在每个交易日的开始时调用,将内部变量初始化为它们的默认状态。对于实盘交易和模拟交易,此方法只调用一次。对于多日回测,此方法会多次调用,每个交易日开始时调用一次。在这个方法中,您将 self.main_order 初始化为空字典。

  • name() 方法:这是一个静态方法,返回此策略的名称。在使用此策略进行回测、模拟交易和实盘交易服务时使用。在此方法中,你简单地返回一个字符串,MACD Bracket Order。你可以在此方法中返回任何你选择的字符串。

  • versions_supported() 方法:此静态方法用于返回此策略已创建的 AlgoBulls 核心引擎版本。通常,随着 AlgoBulls 核心引擎的新升级,可能会引入一些向后不兼容的更改。此方法有助于确保此策略始终在 AlgoBulls 核心引擎的正确版本上运行。在此方法中,你从常量模块中返回最高可用版本,即在编写本章时为 VERSION_3_2_0。

这四个方法是强制性的;它们由StrategyBase基类强制执行,不能跳过。

MACD-Bracket-Order 策略 - 编写 strategy_select_instruments_for_entry 方法

在这个示例中,你将继续编写StrategyMACDBracketOrder类。在这里,你将编写strategy_select_instruments_for_entry()方法,这是StrategyBase基类强制执行的一个必要方法。此方法由 AlgoBulls 核心引擎在每个新蜡烛上调用,用于回测、模拟交易和实盘交易服务。

请参考本章介绍中的流程图,了解 AlgoBulls 核心引擎在策略执行期间如何调用strategy_select_instruments_for_entry()方法。

准备工作

在开始这个示例之前,请确保你已经按照上一个示例创建了StrategyMACDBracketOrder类。

如何操作…

继续编写StrategyMACDBracketOrder类。定义两个新方法——一个用于获取 MACD 线和 MACD 历史信号之间的交叉值的方法,另一个用于根据计算的交叉值从instruments_bucket中选择进入新仓位的工具:

class StrategyMACDBracketOrder(StrategyBase):
    # Note: Some methods are not shown here    
    def get_crossover_value(self, instrument):
        hist_data = self.get_historical_data(instrument)
        macdline, macdsignal, _ = talib.MACD(hist_data['close'], 
                                           fastperiod=self.fastMA_period, 
                                           slowperiod=self.slowMA_period, 
                                           signalperiod=self.signal_period)
        crossover_value = self.utils.crossover(macdline, macdsignal)
        return crossover_value
    def strategy_select_instruments_for_entry(self, candle, 
                                              instruments_bucket):
        selected_instruments_bucket = []
        sideband_info_bucket = []
        for instrument in instruments_bucket:
            crossover_value = self.get_crossover_value(instrument)
            if crossover_value == 1:
                selected_instruments_bucket.append(instrument)
                sideband_info_bucket.append({'action': 'BUY'})
            elif crossover_value == -1:
                if self.strategy_mode is StrategyMode.INTRADAY:
                    selected_instruments_bucket.append(instrument)
                    sideband_info_bucket.append({'action': 'SELL'})
        return selected_instruments_bucket, sideband_info_bucket   

工作原理…

在这个示例中,我们继续编写StrategyMACDBracketOrder类。我们为这个类定义了两个新方法,描述如下:

  • get_crossover_value() 方法:这是一个辅助方法。它以instrument作为参数(以及self)。这是需要计算交叉值的金融工具。你使用self.get_historical_data()方法获取最新的历史数据,并将其赋值给一个新属性hist_data。我们将instrument作为此方法的参数传递。hist_data属性是一个pandas.DataFrame对象,具有timestampopenhighlowclosevolume列。

获取的历史数据的默认持续时间是过去的 15 天。你使用talib.MACD函数在hist_data的收盘价上计算 MACD。它接受以下附加参数:

  • fastperiod:我们在这里传递了self.fastMA_period

  • slowperiod:我们在这里传递了self.slowMA_period

  • signalperiod:我们在这里传递self.signal_period

此计算得到的 MACD 数据是一个pandas.Series对象的元组,你将其分配给macdlinemacdsignal_(最后一个对象分配给_,因为它不需要)。(有关 MACD 计算的更多详细信息,请参阅第五章的第三篇,计算和绘制技术指标。)你使用self.utils.crossover(macdline, macdsignal)计算macdlinemacdsignal之间的交叉值,并将其分配给一个新属性crossover_valuecrossover()函数调用如下:

  • 它需要两个可迭代对象作为输入。我们在这里传递macdlinemacdsignal

  • 如果macdline向上穿过macdsignal,交叉函数返回1

  • 如果macdline向下穿过macdsignal,交叉函数返回-1

  • 如果macdlinemacdsignal之间没有交叉,则交叉函数返回0

最后,你返回crossover_value

  • strategy_select_instruments_for_entry()方法:此方法除了self之外,还接受两个参数:

  • candle:一个CandleTime类型的对象,其中包含当前蜡烛的时间戳。

  • instruments_bucket:一个SetInstruments类型的对象,其中包含用于创建新头寸的所有可用金融工具。我们在策略执行时(第八章的第二篇)传递此数据,回测策略

你创建两个空列表,selected_instruments_bucketsideband_info_bucket。你对instruments_bucket运行一个for循环。对于每个工具,你调用self.get_crossover_value()并将其值保存到一个新属性crossover_value中。根据crossover_value的值,你做出以下决定:

  • 如果crossover_value1,表示策略发出了BUY信号。你执行以下操作:

  • instrument附加到selected_instruments_bucket

  • 将一个{'action': 'BUY'}字典附加到sideband_info_bucket属性。

  • 如果crossover_value-1,表示策略发出了SELL信号。你执行以下操作:

  • instrument附加到selected_instruments_bucket

  • 将一个{'action': 'SELL'}字典附加到sideband_info_bucket属性。

  • 如果crossover_value既不是1也不是-1,表示策略没有发出信号。你在这里不做任何操作。

  • 最后,你返回这两个属性:selected_instruments_bucketsideband_info_bucket。这些属性可能已被填充,也可能保持为空列表。

回想一下,strategy_select_instruments_for_entry()方法是为每个蜡烛调用的,因此前面的步骤对于每个新蜡烛都会重复。在适当的蜡烛上,你会得到一个BUYSELL信号,在其他蜡烛上,你不会得到任何信号。根据信号,你可以下达适当的订单,这在下一篇中有所讨论。

strategy_select_instruments_for_entry()方法是由StrategyBase基类强制执行的,必须为每个策略定义。get_crossover_value()方法是一个辅助方法,意味着它不是由StrategyBase基类强制执行的。您可以选择不定义或定义多个这样的辅助函数。

MACD-Bracket-Order 策略 - 编写strategy_enter_position方法的代码

在这个示例中,您将继续编写StrategyMACDBracketOrder类的代码。在这里,您将编写strategy_enter_position()方法,这是由StrategyBase基类强制执行的必需方法。该方法由 AlgoBulls 核心引擎在每次strategy_select_instruments_for_entry()方法返回非空数据时调用。对于每个新的蜡烛,这个方法可能不会被调用,用于回测、模拟交易和实盘交易服务。

请参考本章介绍中的流程图,了解 AlgoBulls 核心引擎在执行策略时如何调用strategy_enter_position()方法。

准备工作

在开始这个示例之前,请确保您已经按照上一个示例进行了操作。

如何操作…

继续编写StrategyMACDBracketOrder类。定义一个方法,为给定的金融工具下新订单并进入新头寸:

class StrategyMACDBracketOrder(StrategyBase):
    # Note: Some methods are not shown here 
    def strategy_enter_position(self, candle, instrument, sideband_info):
        if sideband_info['action'] == 'BUY':
            qty = self.number_of_lots * instrument.lot_size
            ltp = self.broker.get_ltp(instrument)
            self.main_order[instrument] = \
                self.broker.BuyOrderBracket(
                    instrument=instrument,
                    order_code= BrokerOrderCodeConstants.INTRADAY,
                    order_variety= BrokerOrderVarietyConstants.LIMIT,
                    quantity=qty,
                    price=ltp,
                    stoploss_trigger=ltp - (ltp * self.stoploss),
                    target_trigger=ltp + (ltp * self.target),
                    trailing_stoploss_trigger=ltp * self.trailing_stoploss)
        elif sideband_info['action'] == 'SELL':
            qty = self.number_of_lots * instrument.lot_size
            ltp = self.broker.get_ltp(instrument)
            self.main_order[instrument] = \
                self.broker.SellOrderBracket(
                    instrument=instrument,
                    order_code=BrokerOrderCodeConstants.INTRADAY,
                    order_variety=BrokerOrderVarietyConstants.LIMIT,
                    quantity=qty,
                    price=ltp,
                    stoploss_trigger=ltp + (ltp * self.stoploss),
                    target_trigger=ltp - (ltp * self.target),
                    trailing_stoploss_trigger=ltp * self.trailing_stoploss)
        else:
            raise SystemExit(f'Got invalid sideband_info value: 
                             {sideband_info}')
        return self.main_order[instrument]

工作原理…

在这个示例中,我们继续编写StrategyMACDBracketOrder类的代码。我们为这个类定义一个新方法,strategy_enter_position(),如下所述:

  • 此方法除了self之外,还需要三个参数:

  • candle: 一个CandleTime类型的对象,包含当前蜡烛的时间戳。

  • instrument: 一个Instrument类型的对象,表示一个金融工具。

  • sideband_info: 一个字典对象,保存了instrument属性的交易信息。该对象的格式如下:{'action': [action_value]},其中[action_value]可以是'BUY''SELL'

  • 您通过将self.number_of_lots乘以instrument.lot_size来计算要下单的数量,并将其分配给一个新属性 qty。self.number_of_lots属性保存了要交易的手数信息,您可以在执行此策略时传递。instrument.lot_size属性保存了instrumentlot_size,它是一个正整数。例如,如果手数数为 2,而 instrument 的手数为 10,则订单的数量将为 2 * 10 = 20。

  • 如果sideband_info{'action': 'BUY'},则通过创建self.broker.BuyOrderBracket类的实例(参考第七章的第一个示例,在交易所上放置 Bracket 和 Cover 订单)并将其赋值给self.main_order[instrument]来下一个Bracket买单。

  • 类似地,如果sideband_info{'action': 'SELL'},你通过创建self.broker.SellOrderBracket类的实例(参考第七章的第一个示例,在交易所上放置 Bracket 和 Cover 订单)并将其值赋给self.main_order[instrument]来放置一个BUY交易类型的Bracket订单。

在两种情况下,self.main_order字典对象都保存了instrumentorder实例作为键值对。这对于稍后(在MACD-Bracket-Order strategy – coding the strategy_exit_position method一章中)退出由该方法创建的头寸非常有用。

self.broker属性在 AlgoBulls 核心引擎运行时被适当的经纪人实例替换。因此,相同的代码可以在 AlgoBulls 平台支持的所有经纪人上工作。

MACD-Bracket-Order 策略 – 编写strategy_select_instruments_for_exit方法

在这个示例中,您将继续编写StrategyMACDBracketOrder类的代码。在这里,您将编写strategy_select_instruments_for_exit()方法,这是StrategyBase基类强制执行的一个必需方法。这个方法由 AlgoBulls 核心引擎在每个新蜡烛时调用,用于回测、纸张交易和真实交易服务。

请参考本章介绍中的流程图,了解 AlgoBulls 核心引擎在策略执行期间如何调用strategy_select_instruments_for_exit()方法。

准备就绪

在开始本示例之前,请确保您已经按照上一个示例的步骤进行了操作。

如何做…

继续编写StrategyMACDBracketOrder类。定义一个新方法,根据交叉值的计算从instruments_bucket中选择仪器以退出现有头寸:

class StrategyMACDBracketOrder(StrategyBase):
    # Note: Some methods are not shown here   
    def strategy_select_instruments_for_exit(self, candle, 
                                             instruments_bucket):
        selected_instruments_bucket = []
        sideband_info_bucket = []
        for instrument in instruments_bucket:
            if self.main_order.get(instrument) is not None:
                crossover_value = self.get_crossover_value(instrument)
                if crossover_value in [1, -1]:
                    selected_instruments_bucket.append(instrument)
                    sideband_info_bucket.append({'action': 'EXIT'})
        return selected_instruments_bucket, sideband_info_bucket    

工作原理…

在这个示例中,我们继续编写StrategyMACDBracketOrder类的代码。我们为这个类定义一个新的方法,strategy_select_instruments_for_exit(),描述如下:

  • 这个方法除了self之外还接受两个参数:

  • candleCandleTime类型的对象,包含当前蜡烛的时间戳。

  • instruments_bucketSetInstruments类型的对象。该对象保存了通过strategy_enter_position()方法早些时候输入头寸的金融工具。

  • 你创建了两个空列表,selected_instruments_bucketsideband_info_bucket

  • 你对instruments_bucket运行了一个for循环。对于每个仪器,你使用'if self.main_order.get(instrument) is not None:'这一行检查给定仪器是否已输入头寸。只有在已有头寸的情况下才继续进行。

  • 你调用了self.get_crossover_value()并将其值保存到一个新属性crossover_value中。基于crossover_value的值,你做出如下决定:

  • 如果crossover_value1-1,这意味着发生了交叉。你要做如下操作:

  • selected_instruments_bucket 中添加 instrument 属性。

  • sideband_info_bucket 属性中追加一个 {'action': 'EXIT'} 字典。

  • 如果 crossover_value 既不是 1 也不是 -1,这意味着策略没有发出信号。在这里您不需要做任何操作。

  • 最后,您将返回 selected_instruments_bucketsideband_info_bucket 两个属性。这些属性可能已经被填充,也可能保持为空列表。

请记住,每个蜡烛的 strategy_select_instruments_for_exit() 方法都会被调用,因此前面的步骤会针对每个新的蜡烛重复执行。在适当的蜡烛中,如果存在持仓,可能会得到一个 EXIT 信号,在其他情况下,将不会收到任何信号。根据信号,您可以通过下一步骤中讨论的适当订单退出持仓。

MACD-Bracket-Order 策略 - 编写 strategy_exit_position 方法。

在此示例中,您将继续编写 StrategyMACDBracketOrder 类的代码。在这里,您将编写 strategy_exit_position() 方法,这是 StrategyBase 基类强制实施的最后一个必需方法。每当 strategy_select_instruments_for_exit 方法返回非空数据时,AlgoBulls 核心引擎都会调用此方法。通过此示例结束时,您将完成 StrategyMACDBracketOrder 类的编写。

请参考本章介绍中的流程图,了解 AlgoBulls 核心引擎在执行策略期间如何调用 strategy_select_instruments_for_exit() 方法。

准备工作

在开始此步骤之前,请确保您已经按照上一步骤进行了操作。

如何做…

继续编写 StrategyMACDBracketOrder 类。定义一种方法,根据 sideband_info 退出给定仪器的持仓:

class StrategyMACDBracketOrder(StrategyBase):
    # Note: Some methods are not shown here   
    def strategy_exit_position(self, candle, instrument, 
                               sideband_info):
        if sideband_info['action'] == 'EXIT':
            self.main_order[instrument].exit_position()
            self.main_order[instrument] = None
            return True
        return False

工作原理…

在此示例中,我们继续编写 StrategyMACDBracketOrder 类。我们为此类定义了一个新方法,strategy_exit_position(),描述如下:

  • 此方法除了 self 外,还接受三个参数:

  • candle:包含当前蜡烛的时间戳的 CandleTime 类型的对象。

  • instrument:代表金融工具的 Instrument 类型的对象。

  • sideband_info:一个包含有关为 instrument 属性放置交易的信息的字典对象。此对象看起来像 {'action': EXIT}

  • 如果 sideband_info{'action': 'EXIT'},请执行以下操作:

  • 使用 self.main_order[instrument] 获取订单。(请记住,self.main_order 是一个字典,将仪器和相应的订单实例作为键值对存储。)

  • 通过调用其 exit_position() 方法来退出此订单的持仓。

由于这是一个Bracket订单策略,存在targetstoploss订单可能触发且头寸退出而我们的策略不知情的可能性。您仍然可以使用exit_position()方法来处理这些情况。exit_position()方法适用于以下两种退出情况:

  • 位置已开启,您希望自行退出。

  • 该头寸已由经纪人退出,原因是已完成了stoploss订单或target订单,并且没有任何操作需要执行。

  • 您将self.main_order中与键instrument对应的值重置为None。这表明与instrument对应的头寸已经没有了。

  • 您返回True,向 AlgoBulls 核心引擎发出信号,表明在此调用中已退出instrument的头寸。

  • 如果sideband_info不是{'action': 'EXIT'},则返回False,向 AlgoBulls 核心引擎发出信号,表明在此调用中没有退出instrument的头寸。

self.broker属性在运行时由 AlgoBulls 核心引擎替换为相应的经纪人实例。因此,相同的代码可以在 AlgoBulls 平台支持的所有经纪人中使用。

您现在已经完成了StrategyMACDBracketOrder类的编码。

MACD-Bracket-Order 策略 — 在 AlgoBulls 交易平台上上传策略

在此示例中,您将上传在前五个示例中创建的策略类StrategyMACDBracketOrder到 AlgoBulls 交易平台。一旦上传完成,您就可以在同一代码库上执行回测、模拟交易和实际交易。

准备工作

确保您已在 AlgoBulls 平台(algobulls.com)上设置了您的帐户以获取 API 令牌。设置帐户是免费的。根据您的使用情况,使用其服务可能会产生费用。您可以从该网站上的免费套餐开始。有关更多详情,请参阅附录 II

如何实现…

我们执行以下步骤来完成此示例:

  1. 导入必要的模块:
>>> import inspect
>>> from pyalgostrategypool.strategy_macd_bracket_order import StrategyMACDBracketOrder
>>> from pyalgotrading.algobulls import AlgoBullsConnection
  1. 创建一个新的 AlgoBulls 连接对象:
>>> algobulls_connection = AlgoBullsConnection()
  1. 获取授权 URL:
>>> algobulls_connection.get_authorization_url()

我们得到了以下输出:

Please login to this URL with your AlgoBulls credentials and get your developer access token: https://app.algobulls.com/user/login
'https://app.algobulls.com/user/login'
  1. 使用您的 AlgoBulls 凭据登录到上述链接,获取您的令牌,并在此处设置它(有关更多详情,请参阅附录 II):
>>> algobulls_connection.set_access_token('80b7a69b168c5b3f15d56688841a8f2da5e2ab2c')
  1. 在上传您的策略之前,您可以检查您的策略代码,以确保您正在上传正确的策略:
>>> print(inspect.getsource(StrategyMACDBracketOrder))

我们得到了以下输出:

class StrategyMACDBracketOrder(StrategyBase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fastMA_period = \
            self.strategy_parameters['fastma_period']
        self.slowMA_period = \
            self.strategy_parameters['slowma_period']
        self.signal_period = \
            self.strategy_parameters['signal_period']
        self.stoploss = 
            self.strategy_parameters['stoploss_trigger']
        self.target = 
            self.strategy_parameters['target_trigger']
        self.trailing_stoploss = 
            self.strategy_parameters['trailing_stoploss_trigger']

        self.main_order = None

    def initialize(self):
        self.main_order = {}

    @staticmethod
    def name():
        return 'MACD Bracket Order Strategy'
    ...
    def strategy_exit_position(self, candle, instrument, 
                               sideband_info):
        if sideband_info['action'] == 'EXIT':
            self.main_order[instrument].exit_position()
            self.main_order[instrument] = None
            return True
        return False

完整的输出在此未显示。请访问以下链接以阅读完整的输出:

github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/strategy_macd_bracket_order.py

  1. 上传StrategyMACDBracketOrder到 AlgoBulls 平台。这将为您的 AlgoBulls 帐户创建一个新的策略:
>>> algobulls_connection.create_strategy(StrategyMACDBracketOrder)

我们得到了以下输出(您的输出可能会有所不同):

Validating Strategy...
{'details': 'success', 'strategy_code': '4faf514fe096432b8e9f80f5951bd2ea'}

它的工作原理…

我们在步骤 1中导入所需的模块。在步骤 2中,创建了AlgoBullsConnection类的实例,命名为algobulls_connection。在步骤 3中,你可以使用algobulls_connection对象的get_authorization_url()方法获取授权 URL。你应该从浏览器中访问此 URL 以登录到 AlgoBulls 平台并获取您的开发者访问令牌。(你可以在附录 II中找到有关从 AlgoBulls 平台获取开发者访问令牌的更多详细信息和截图。)你复制访问令牌并在步骤 4中使用algobulls_connectionset_access_token()方法设置它。如果令牌被接受,则成功建立与 AlgoBulls 平台的连接。

我们在步骤 5中编写的StrategyMACDBracketOrder策略类也可以在pyalgostrategypool包中找到。我们在步骤 1中导入了这个类。或者,你也可以将你的策略类保存在一个单独的 Python 模块中,并在步骤 1中导入它,而不是从pyalgostrategypool中导入。

你可以使用algobulls_connectionupload_strategy()方法,通过将其作为参数传递来上传StrategyMACDBracketOrder策略类。如果上传成功,你将获得一个带有strategy_code的成功消息,该代码是唯一的字符串。strategy_code可以在以后的章节中用于执行与策略相关的所有操作,例如编辑策略、执行回测、执行模拟交易和执行真实交易。

还有更多内容...

如果在上传后对策略进行了更改,你可以使用algobulls_connectionupload_strategy()方法,将更新后的类和overwrite=True作为参数上传到 AlgoBulls 平台。如果更改成功上传,你将收到一个成功消息。

你可以如下修改已经上传的策略:

>>> algobulls_connection.create_strategy(StrategyMACDBracketOrder, 
                                         overwrite=True)

我们得到以下输出:

Validating Strategy…
{'details': 'success'}

在 AlgoBulls 平台上,不允许存在具有相同名称(通过name()方法返回的)的多个策略。overwrite=True参数用于更新已存在的同名策略。如果未将overwrite=True传递给create_strategy()方法,则默认值为False,这意味着它会尝试在 AlgoBulls 平台上创建一个新的策略。

第九章:算法交易 - 回测

在构建算法交易策略之后,正如我们在上一章中所做的那样,第一步是对给定的策略配置在给定的时间段内进行回测。

回测是通过在过去的数据上虚拟执行交易策略并分析其风险和回报指标来评估交易策略性能的方法。这里不使用真实资金。典型的回测指标包括利润和损失P&L),最大回撤,总交易数,盈利交易数,亏损交易数,多头交易和空头交易,每笔盈利和亏损交易的平均利润等。直到这些指标满足必要要求为止,整个过程应该重复进行,逐步对策略参数和/或策略实施进行更改。

如果一个策略在过去的数据上表现良好,那么它在实时数据上也很可能表现良好。同样,如果一个策略在过去的数据上表现不佳,那么它在实时数据上也很可能表现不佳。这是回测的基本前提。您可以不断更改策略配置或实现,直到回测产生预期的结果为止。

回测还有助于在我们将策略用于真实资金之前验证策略的行为。这意味着它有助于确保策略在过去的各种营销场景中表现如预期。

对于回测,需要策略配置。它由多个参数组成,其中一些如下:

  • 开始和结束时间戳:回测应运行的时间段。

  • 金融工具:应进行回测的一个或多个金融工具。

  • 蜡烛间隔:许多可能的蜡烛间隔之一;例如,1 分钟,15 分钟,1 小时或 1 天。

  • 特定策略参数:策略中定义的自定义参数的值。

  • 策略模式:日内交易或交割之一。日内交易策略在当天结束时打入日内订单,这些订单在当天结束时结清。交割策略打入交割订单。这些订单不会在当天结束时结清,而是转到下一个交易日。

需要一个回测引擎来执行给定策略的回测。在本章中,您将使用由 AlgoBulls 提供的回测引擎 (algobulls.com),这是一个通过其开发者选项提供其服务的算法交易平台。它提供了一个名为pyalgotrading的 Python 包 (github.com/algobulls/pyalgotrading) 来提供这些服务。

您在第八章中编码了两种算法交易策略,算法交易策略 - 逐步编码。回想一下,策略描述如下:

  • EMA-Regular-Order 策略:该策略基于技术指标 EMA 和常规订单。(第八章 的前六个配方,算法交易策略 – 逐步编码。)

  • MACD-Bracket-Order 策略:该策略基于技术指标 MACD 和括号订单。(第八章 的剩余六个配方,算法交易策略 – 逐步编码。)

这些策略也作为名为 pyalgostrategypool 的 Python 包的一部分可用。您可以使用 pip 安装它,使用命令 $ pip install pyalgostrategypool。您还可以在 GitHub 上查看它们(github.com/algobulls/pyalgostrategypool)。

在上一章中,您已将这两个策略上传到您的 AlgoBulls 账户。在本章中,您将从您的 AlgoBulls 账户获取这些策略,并对其进行回测。回测后,您将收集策略执行日志和各种报告,即 P&L 报告、统计报告和订单历史。这些日志和报告有助于验证策略的表现,并为下一阶段——模拟交易做准备,然后我们最终进行实际交易。通过使用 pyalgotrading,您确保专注于通过回测开发和验证策略,而无需担心策略执行所需的生态系统。

本章包括了前述策略的逐步配方,从与 AlgoBulls 平台建立连接、获取策略、运行回测任务,到获取执行日志和各种类型的报告。

在本章中,我们将涵盖以下配方:

  • EMA-Regular-Order 策略 – 获取策略

  • EMA-Regular-Order 策略 – 回测策略

  • EMA-Regular-Order 策略 – 实时获取回测日志

  • EMA-Regular-Order 策略 – 获取回测报告 – P&L 表

  • EMA-Regular-Order 策略 – 获取回测报告 – 统计表

  • EMA-Regular-Order 策略 – 获取回测报告 – 订单历史

  • MACD-Bracket-Order 策略 – 获取策略

  • MACD-Bracket-Order 策略 – 回测策略

  • MACD-Bracket-Order 策略 – 实时获取回测日志

  • MACD-Bracket-Order 策略 – 获取回测报告 – P&L 表

  • MACD-Bracket-Order 策略 – 获取回测报告 – 统计表

  • MACD-Bracket-Order 策略 – 获取回测报告 – 订单历史

让我们开始吧!

技术要求

您将需要以下内容才能成功执行本章的配方:

  • Python 3.7+

  • Python 包:

  • pyalgotrading$ pip install pyalgotrading

本章最新的 Jupyter 笔记本可以在 GitHub 上找到:github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/tree/master/Chapter09

EMA-Regular-Order 策略 - 获取策略

在这个配方中,您将从 AlgoBulls 平台上的您的账户中获取StrategyEMARegularOrder策略类,该策略类是您在第八章,算法交易策略 - 逐步编码中通过 EMA-Regular-Order 策略 - 上传策略 配方上传到 AlgoBulls 交易平台的。本配方从设置到 AlgoBulls 平台的连接开始,查询您账户中所有可用的策略,并获取所需策略类StrategyEMARegularOrder的详细信息。

确保您已经完成了前一章的前六个配方,以完全了解我们将要使用的策略类;即StrategyEMARegularOrder

如何操作…

我们执行了本配方的以下步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.algobulls import AlgoBullsConnection
  1. 创建一个新的 AlgoBulls 连接对象:
>>> algobulls_connection = AlgoBullsConnection()
  1. 获取授权 URL:
>>> algobulls_connection.get_authorization_url()

我们得到了以下输出:

Please login to this URL with your AlgoBulls credentials and get your developer access token: https://app.algobulls.com/user/login
'https://app.algobulls.com/user/login'
  1. 使用您的 AlgoBulls 凭据登录前面的链接,获取您的令牌,并在此处设置它(有关更多详细信息,请参阅附录 II):
>>> algobulls_connection.set_access_token('
                    80b7a69b168c5b3f15d56688841a8f2da5e2ab2c')
  1. 获取并显示到目前为止已创建和上传的所有策略:
>>> all_strategies = algobulls_connection.get_all_strategies()
>>> all_strategies

我们得到了以下输出。您的输出可能不同(确保您已经按照第八章,算法交易策略**- 逐步编码中的配方来获得类似的输出):

  1. 获取并显示第一个策略的策略代码:
>>> strategy_code1 = all_strategies.iloc[0]['strategyCode']
>>> strategy_code1

我们得到了以下输出(您的输出可能会有所不同):

'49287246f9704bbcbad76ade9e2091d9'
  1. 在对策略进行回测之前,您可以检查策略以确保您拥有正确的策略:
>>> strategy_details1 = \
        algobulls_connection.get_strategy_details(strategy_code1)
>>> print(strategy_details1)

我们得到了以下输出:

class StrategyEMARegularOrder(StrategyBase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.timeperiod1 = self.strategy_parameters['timeperiod1']
        self.timeperiod2 = self.strategy_parameters['timeperiod2']

        self.main_order = None

    def initialize(self):
        self.main_order = {}

    @staticmethod
    def name():
        return 'EMA Regular Order Strategy'
    ….
    def strategy_exit_position(self, candle, instrument, 
                                sideband_info):
        if sideband_info['action'] == 'EXIT':
            self.main_order[instrument].exit_position()
            self.main_order[instrument] = None
            return True

        return False

此处未显示完整的输出。请访问以下链接以阅读完整的输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/strategy_ema_regular_order.py

工作原理…

第 1 步中,你导入必要的模块。在第 2 步中,创建了AlgoBullsConnection类的一个实例,命名为algobulls_connection。在第 3 步中,使用algobulls_connection对象的get_authorization_url()方法获取授权 URL。这将打印出授权 URL。你应该从浏览器中访问此 URL 以登录 AlgoBulls 平台并获取您的开发者访问令牌。(您可以在附录 II中找到更多细节,以及关于从 AlgoBulls 平台获取开发者访问令牌的屏幕截图。)你复制访问令牌,并在第 4 步中使用algobulls_connectionset_access_token()方法设置它。如果令牌被接受,就会建立与 AlgoBulls 平台的成功连接。

第 5 步中,你获取到目前已经创建并上传到 AlgoBulls 平台的所有策略。你使用get_all_strategies()方法来执行此步骤,并将其赋值给一个新变量all_strategies。这个变量是一个pandas.DataFrame对象,具有strategyCodestrategyName列。这个表格包含了你之前上传的策略代码和策略名称的信息。如果你按照第八章中的EMA-Regular-Order 策略-上传策略到 AlgoBulls 交易平台的步骤,你会找到一个名为EMA-Regular-Order 策略的策略。在第 6 步中,你将策略EMA-Regular-Order 策略的策略代码赋值给一个新变量strategy_code1。这个策略代码在此步骤的输出中显示。这个策略代码对于 AlgoBulls 平台上的每个策略都是唯一的。

最后,在第 7 步中,确保被strategy_code1引用的策略确实是你之前上传的那个(在第八章的EMA-Regular-Order 策略-上传策略到 AlgoBulls 交易平台配方中)。你使用algobulls_connection对象的get_strategy_details()方法来检查策略。这个方法将策略代码作为参数。你在这里传递strategy_code1。这个方法将整个类代码作为一个字符串返回。你将其赋值给一个新变量strategy_details1,并显示它。

如果你想要更改strategy_code1引用的类代码,如第 7 步所示,请参考第八章的EMA-Regular-Order 策略-上传策略到 AlgoBulls 交易平台配方中的还有更多...部分。

EMA-Regular-Order 策略-回测策略

在此配方中,您将对EMA-Regular-Order 策略进行回测。您必须在上一配方中从 AlgoBulls 平台的帐户中获取此策略。您将为此配方利用pyalgotrading提供的回测功能,该功能反过来会在 AlgoBulls 平台上提交一个回测作业。

提交后,回测将由 AlgoBulls 回测引擎运行。您可以随时查询状态以查找回测作业的状态。作业按给定顺序经历以下状态:

  • STARTING(中间状态)

  • STARTED(稳定状态)

  • STOPPING(中间状态)

  • STOPPED(稳定状态)

提交作业后,它将从中间状态开始,即STARTING。在此状态下,AlgoBulls 回测引擎获取策略并准备执行环境,这可能需要几分钟。完成后,作业移至STARTED状态。策略回测发生在此阶段。在此期间,作业保持在这个状态直到回测完成。完成后,作业移至中间状态,即STOPPING。在此状态下,AlgoBulls 回测引擎清理已分配给此作业的资源,这通常需要不到一分钟。最后,作业移至STOPPED状态。

如果您已经提交了一个策略回测作业,则不能为相同的策略提交另一个作业,直到第一个作业完成。这意味着您必须等待第一个作业转移到STOPPED状态。如果第一个作业运行时间很长,并且您想立即停止它,您可以通过pyalgotrading提交停止作业请求。在提交请求之前,您需要确保作业处于STARTED状态。

以下状态机图示了回测作业在 AlgoBulls 平台上的生命周期中的各种状态和转换:

图片

提交回测作业后,您可以实时获取策略执行的日志和报告。日志和报告有助于验证策略的性能并调试任何潜在问题。

确保您已经阅读了上一章的前六个配方,以全面了解我们将使用的策略类;即StrategyEMARegularOrder

准备工作

确保algobulls_connectionstrategy_code1对象在您的 Python 命名空间中可用。请参阅上一配方以设置algobulls_connectionstrategy_code1对象。

如何做...

我们为此配方执行以下步骤:

  1. 导入必要的模块:
>>> from datetime import datetime as dt
>>> from pyalgotrading.constants import *
  1. 使用交易符号作为关键字搜索工具。将返回的对象分配给instruments
>>> instruments = algobulls_connection.search_instrument('SBIN')
>>> instruments

我们得到以下输出(您的输出可能会有所不同):

[{'id': 7, 'value': 'NSE:SBIN'}]
  1. instruments获取所选工具的value
>>> instrument = instruments[0]['value']
>>> instrument

我们得到以下输出:

'NSE:SBIN'
  1. strategy_code1提交一个回测任务:
>>> algobulls_connection.backtest(strategy_code=strategy_code1, 
        start_timestamp=dt(year=2020, month=7, day=1, hour=9, 
                            minute=15), 
        end_timestamp=dt(year=2020, month=7, day=7, hour=15, 
                            minute=30),
        instrument=instrument, 
        lots=1,
        strategy_parameters={
            'timeperiod1': 5,
            'timeperiod2': 12
        }, 
        candle_interval=CandleInterval.MINUTES_15)

我们得到以下输出:

Setting Strategy Config... Success.
Submitting BACKTESTING job... Success.
  1. 检查已提交的回测作业的状态:
>>> algobulls_connection.get_backtesting_job_status(strategy_code1)

我们得到以下输出:

{'data': 'STARTING'}
  1. 一段时间后,再次检查已提交的作业状态:
>>> algobulls_connection.get_backtesting_job_status(strategy_code1)

我们得到以下输出:

{'data': 'STARTED'}

工作原理如下…

步骤 1 中,你从 datetime 模块中导入 datetime 类和 pyalgotrading.constants 模块中的所有常量。在 步骤 2 中,你使用 algobulls_connection 对象的 search_instrument() 方法获取要为之进行策略回测的工具,EMA-Regular-Order 策略search_instrument() 方法接受一个搜索字符串作为参数,该参数应该是你感兴趣的工具的交易代码的一部分或完整的交易代码。你在这里传递'SBIN'。该函数返回一个包含与搜索字符串匹配的工具详情的列表。可能有多个工具的交易代码中都包含搜索字符串。在 步骤 3 中,你获取第一个匹配工具的值,并将其分配给一个新变量 instrument

步骤 4 中,你使用 algobulls_connection() 对象的 backtest() 方法提交了一个回测作业。它接受以下参数:

  • strategy_code: 要进行回测的策略的策略代码。这应该是一个字符串。你在这里传递 strategy_code1

  • start_timestamp: 从中开始回测的过去时间戳。这应该是一个 datetime.datetime 对象。在这里,你传递一个持有 2020 年 7 月 1 日 9:15 点的时间对象 - dt(year=2020, month=7, day=1, hour=9, minute=15)。有关创建 datetime 对象的详细信息,请参阅第一章中的 创建 datetime 对象 配方,处理和操作日期、时间和时间序列数据。.

  • end_timestamp: 回测应该执行的过去时间戳。这个对象应该持有一个比 start_timestamp 持有的时间戳值要晚的时间戳值。这应该是一个 datetime.datetime 实例。在这里,你传递一个持有 2020 年 7 月 7 日 15:30 点的时间对象 - dt(year=2020, month=7, day=7, hour=15, minute=30)

  • instrument: 应该为其运行回测的金融工具。将为此工具获取历史数据。这应该是一个字符串。你在这里传递 instrument

  • lots: 应进行回测的手数。这应该是一个整数。数量由策略计算得出,即 手数 × 金融工具的手数。 (请参阅上一章中的 EMA-Regular-Order 策略 - 编码 strategy_enter_position 方法)。你在这里传递1

  • strategy_parameters: 策略所需的参数名称和值。这应该是一个字典,其中parameter-nameparameter-value是键值对。你需要传递以下参数:

  • timeperiod1: 5

  • timeperiod2: 12 

(回想一下,EMA-Regular-Order 策略的参数是在其 __init__() 方法中定义的,如前一章的第一个示例所示)。

  • candle_interval:用于回测获取的历史数据的蜡烛间隔。这应该是 CandleInterval 类型的枚举。您在这里传递 CandleInterval.MINUTES_15。(CandleInterval 枚举提供了各种蜡烛间隔的枚举,其中一些是 MINUTE_1MINUTES_3MINUTES_5MINUTES_10MINUTES_15MINUTES_30HOURDAY)。

如果作业提交成功,您将会在 backtest() 函数中看到打印的 Success 消息。

一旦作业提交,启动需要一段时间。启动后,根据策略的复杂性和使用 start_timestampend_timestamp 参数指定的回测持续时间,完成可能需要一些时间。几天的回测可能在几秒钟内完成,而几个月的回测可能需要几分钟。

步骤 5 中,您使用 algobulls_connection 对象的 get_backtesting_job_status() 方法获取作业状态。您在这里将 strategy_code1 作为参数传递。此方法返回一个字典,其中包含一个键值对 - datajob 状态。如果在放置作业后立即查询状态,您将得到 'STARTING' 作为状态。在 步骤 6 中,您在一段时间后再次查询状态,如果作业已启动,则状态将为 'STARTED'

成功提交意味着已以所需格式传递了回测策略所需的最小输入。但是,这并不保证策略将无错误地运行。在回测期间,策略的执行可能仍然会遇到错误。要调试执行问题,您需要获取输出日志,这在 MACD-Bracket-Order 策略 - 实时获取回测日志 配方中有解释。错误的可能原因可能是策略类 Python 代码中的错误或将不完整的 strategy_parameters 字典传递给 backtest() 函数。

还有更多...

如果作业运行时间很长,您希望在其完成之前停止它,您可以使用 algobulls_connection 对象的 stop_backtesting_job() 方法。此方法接受策略代码作为参数。您在这里传递 strategy_code1。此方法向 AlgoBulls 回测引擎提交停止请求。如果请求被接受,您将在此处看到 Success 消息:

>>> algobulls_connection.stop_backtesting_job(strategy_code1)
 Stopping BACKTESTING job... Success.

如果在提交停止请求后查询状态,您将会得到状态为 'STOPPING'

>>> algobulls_connection.get_backtesting_job_status(strategy_code1)
{'data': 'STOPPING'} 

如果在一段时间后再次查询状态,并且作业已停止,则状态将为 'STOPPED'

>>> algobulls_connection.get_backtesting_job_status(strategy_code1)
{'data': 'STOPPED'}

EMA-Regular-Order 策略 - 实时获取回测日志

在 AlgoBulls 平台上提交回测作业后,AlgoBulls 回测引擎开始执行策略。在执行过程中,AlgoBulls 回测引擎发生的每个事件以及所采取的每个决策都以精确的时间戳记录为文本日志。记录的活动示例包括给定策略配置,定期生成的每个新蜡烛,您的策略打出的交易,由这些交易创建的头寸的进入和退出,等待新蜡烛等等。这些日志对于验证策略行为以及调试在开发策略时经常遇到的行为或性能问题至关重要。

在这个配方中,你将获取你的策略的回测日志。一旦您提交的回测作业达到'STARTED'状态(有关回测作业状态的更多信息,请参考上一节),日志就会开始出现。AlgoBulls 平台允许您实时获取日志,即使回测作业仍在进行中。您可以在不必等待回测作业完成的情况下获取对策略执行的见解,这在作业运行时间很长时非常有用。pyalgotrading包提供了一个简单的方法,可用于获取给定策略的执行日志。

确保您已经阅读了上一章的前六个配方,以完全了解我们将使用的策略类的情况;即,StrategyEMARegularOrder

准备就绪

确保您的 Python 命名空间中可用algobulls_connectionstrategy_code1对象。请参考本章的第一个配方设置algobulls_connectionstrategy_code1对象。

如何做…

我们对这个配方执行以下步骤:

  1. 获取strategy_code1的回测执行日志:
>>> logs = algobulls_connection.get_backtesting_logs(
                                                strategy_code1)
>>> print(logs)

我们得到以下输出(您的输出可能有所不同):

[2020-07-30 17:25:18] Logs not available yet. Please retry in sometime.
  1. 再过一段时间,再次获取strategy_code1的回测执行日志:
>>> logs = algobulls_connection.get_backtesting_logs(
                                                strategy_code1)
>>> print(logs)

我们得到以下输出(您的输出可能有所不同):

...
########################################
 INITIALIZING ALGOBULLS CORE (v3.2.0 SECURE MODE)... 
########################################
[2020-07-30 11:56:29] Welcome ALGOBULLS VIRTUAL USER!
[2020-07-30 11:56:29] Reading strategy…
...
 [BT] [2020-07-01 09:15:00] [INFO] [tls] STARTING ALGOBULLS CORE...
...
[BT] [2020-07-01 09:45:00] [CRITICAL] [order] [PLACING NEW ORDER] [2020-07-01 09:45:00] [2333198611b744aeb287300d371c8eb5] [BUY] [NSE:SBIN] [QTY:1] [QTY PENDING: 1] [ENTRY PRICE: 180.25] [PRICE:None] [TRIGGER PRICE:None] [ORDER_TYPE_REGULAR] [ORDER_CODE_INTRADAY] [ORDER_VARIETY_MARKET] [ORDER_POSITION_ENTER]
...
 [BT] [2020-07-07 15:30:00] [INFO] [clock] Candle generation has been stopped...
[BT] [2020-07-07 15:30:00] [INFO] [tls] Received event END OF MARKET. Stopping Trading Core Engine…
[BT] [2020-07-07 15:30:00] [INFO] [tls] Exiting all open positions with order code: ORDER_CODE_INTRADAY (if any)...
[BT] [2020-07-07 15:30:00] [CRITICAL] [tls] [User: ALGOBULLS VIRTUAL USER] Trading session completed
 ...

这里没有显示完整的输出。请访问以下链接以阅读完整的输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/sample/backtesting/strategy_ema_regular_order/logs.txt

工作原理…

步骤 1中,您使用algobulls_connection对象的get_backtesting_logs()方法实时获取策略回测日志。此方法接受策略代码作为参数。您在这里传递了strategy_code1。返回数据是一个字符串。如果您在提交作业后立即尝试此步骤,则会得到一个字符串,该字符串显示日志尚未准备好([2020-07-30 17:27:25] Logs not available yet. Please retry in sometime.)。如果回测作业处于'STARTING'状态,则会发生这种情况。

步骤 2 中,您再次在一段时间后获取日志。如果作业处于 'STARTING' 状态之外,您就开始获取您的策略执行日志。每次调用 get_backtesting_logs() 函数时,您都会获得完整的回测日志。

还有更多...

一旦回测作业转移到 'STOPPED' 状态,就不会生成新的日志。在您提交下一个相同策略的回测作业之前,您可以随时获取完整的日志。如果提交了新的回测作业(针对相同的策略),这些日志将无法通过 get_backtesting_logs() 方法再次访问。如果您希望以后参考,您可以将获取的日志保存到文件中。

EMA-Regular-Order 策略 – 获取一个回测报告 – 收益和损失表

在 AlgoBulls 平台提交了一个回测作业后,AlgoBulls 回测引擎开始执行策略。在执行过程中,除了日志之外,AlgoBulls 回测引擎还实时生成了一个 P&L 表格。这个表格包含了策略 Punch 进来的每一笔交易的信息。它还包含了入场和出场订单之间的映射细节、交易 P&L 以及累计 P&L,按时间顺序排序,最新的订单排在最前面。这张表格让我们通过个别和累计 P&L 数字来洞察整体策略的表现。入场-出场订单映射也有助于验证策略行为。

在这个教程中,您将获取您的策略的 P&L 表格报告。在您提交了回测作业后,只要您的策略 Punch 进了第一笔交易,这份报告就会立即可用。AlgoBulls 平台允许您实时获取 P&L 表格,即使回测作业仍在进行中。您可以在等待回测作业完成时获取策略表现的洞察,这在作业运行时间较长时非常有用。pyalgotrading 包提供了一个简单的方法,用于获取给定策略的 P&L 表格。

确保您已经阅读了前一章的前六个教程,以完全了解我们将要使用的策略类;即 StrategyEMARegularOrder

准备工作

确保 algobulls_connectionstrategy_code1 对象在您的 Python 命名空间中可用。请参考本章第一篇教程设置 algobulls_connectionstrategy_code1 对象。

如何操作…

获取 strategy_code1 的回测 P&L 报告:

>>> algobulls_connection.get_backtesting_report_pnl_table(strategy_code1)

我们得到了以下输出。您的输出可能会有所不同(请注意,以下输出已被分成多个表格以表示目的。您将在您的 Jupyter Notebook 中看到一个宽表):

图片

工作原理…

在本示例中,您使用algobulls_connection对象的get_backtesting_report_pnl_table()方法实时获取回测损益表。此方法接受策略代码作为参数。您在此处传递strategy_code1。返回的数据是一个pandas.DataFrame对象,具有以下多列,描述如下:

  • instrument: 进入交易的金融工具。

  • entry_timestamp: 进入订单放置的时间戳。(请注意,它可能会在进入'COMPLETE'状态之前保持'OPEN'状态一段时间。您可以使用订单历史表找到此状态转换的时间,如本章第六个示例所述。)

  • entry_transaction_type: 进入订单交易类型(BUYSELL)。

  • entry_quantity: 进入订单数量。

  • entry_price: 进入订单执行的价格,并进入'COMPLETE'状态。

  • exit_timestamp: 退出订单放置的时间戳。(请注意,它可能会在进入'COMPLETE'状态之前保持'OPEN'状态一段时间。)

  • exit_transaction_type: 退出订单交易类型(BUYSELL)。

  • exit_quantity: 退出订单数量。

  • exit_price: 退出订单执行的价格,并进入'COMPLETE'状态。

  • pnl_absolute: 退出订单执行价格与进入订单执行价格之间的差值。数学上,这是对于多头交易为(exit_price - entry_price)exit_quantity,对于空头交易为(entry_price* - exit_price)exit_quantity*。正值意味着交易盈利,负值意味着交易亏损。

  • pnl_percentage: 利润或亏损的百分比,相对于进入价格。数学上,这是pnl_absolute / entry_price / exit_quantity × 100

  • pnl_cumulative_absolute: 累积利润或亏损。数学上,这是以前交易的所有pnl_absolute值的总和。此数字直接反映了策略绩效与模拟时间的关系。

  • pnl_cumulative_percentage: 相对于进入价格的累积利润或亏损的百分比。数学上,这是pnl_cumulative / entry_price / exit quantity × 100

更多信息...

一旦回测作业转移到'STOPPED'状态,损益表报告将不再更新。您可以在提交下一个相同策略的回测作业之前随时获取完整的损益报告。如果提交了新的回测作业(针对相同策略),则无法再通过get_backtesting_report_pnl_table()方法访问此报告。如果您希望以后参考,可以将获取的报告保存为.csv文件。

EMA-Regular-Order 策略 — 获取回测报告 - 统计表

在 AlgoBulls 平台上提交回测任务后,AlgoBulls 回测引擎开始执行策略。在执行过程中,除了日志和盈亏表外,AlgoBulls 回测引擎还实时生成来自盈亏表的摘要。这个摘要是一个包含各种统计数字的统计表,例如净盈亏(绝对值和百分比)、最大回撤(绝对值和百分比)、总交易次数、盈利交易次数、亏损交易次数、多头交易和空头交易次数、最大收益和最小收益(或最大亏损),以及每笔盈利交易和亏损交易的平均利润。这个表提供了对整体策略表现的即时概览。

在这个配方中,您将获取您的策略的统计表报告。这份报告在您提交回测工作后,您的策略首次打入第一笔交易后即可获得。AlgoBulls 平台允许您实时获取统计表,即使回测工作仍在进行中。您可以在等待回测工作完成时就获得对策略表现的洞察,这对于长时间运行的工作非常有帮助。pyalgotrading包提供了一个简单的方法,用于获取给定策略的统计表。

确保您已经阅读了上一章的前六个配方,以完全了解我们将使用的策略类;即StrategyEMARegularOrder

准备工作

确保algobulls_connectionstrategy_code1对象在你的 Python 命名空间中可用。参考本章第一个配方设置algobulls_connectionstrategy_code1对象。

如何操作…

获取strategy_code1的回测统计报告:

>>> algobulls_connection.get_backtesting_report_statistics(strategy_code1)

我们得到了以下输出(您的输出可能不同):

如何运作…

在这个配方中,你使用algobulls_connection对象的get_backtesting_report_statistics()方法实时获取回测统计表。该方法接受策略代码作为参数。你在这里传递了strategy_code1。返回的数据是一个pandas.DataFrame对象,有两列 - highlight_typehighlight_value - 以及多行。行的描述如下:

  • 净盈亏:累积回测盈亏。这也是盈亏表中第一个条目的pnl_cumulative_absolute值。

  • 净盈亏 %:累积回测盈亏百分比。这也是盈亏表中第一个条目的pnl_cumulative_percentage值。

  • 最大回撤:盈亏表的pnl_cumulative列中的最低值。这表明您的策略在执行过程中遇到的最大损失。

  • 最大回撤 %:从数学上讲,这是(最大回撤) / *(相应的入场价格) / 出场数量 *× 100

  • 交易次数:会话期间的总交易次数(入场和出场计为一次)。

  • 获利次数:交易盈亏非负的交易次数。

  • 亏损次数:交易盈亏为负的交易次数。

  • 多头交易次数:输入交易类型为 'BUY' 的交易次数。

  • 空头交易次数:输入交易类型为 'SELL' 的交易次数。

  • 最大收益:所有交易中收益最大的交易的盈亏。

  • 最小收益:所有交易中收益最小的交易的盈亏。

  • 每次盈利交易的平均利润:在数学上,这是 (盈利交易的总盈亏) / (盈利交易的次数)

  • 每次亏损交易的平均利润:在数学上,这是 (亏损交易的总盈亏) / (亏损交易的次数)

还有更多...

如果在回测任务仍在运行时获取统计表,则上述数字将是中间数字,基于截至该时刻完成的交易。随着更多交易被输入,这些数字可能会更改,直到回测任务完成。

一旦回测任务转移到 'STOPPED' 状态,统计表将不再更改。您可以在提交下一个相同策略的回测任务之前的任何时间获取完整的统计表。如果提交了新的回测任务(相同的策略),则通过 get_backtesting_report_statistics() 方法将无法再访问此表。如果您希望以后参考,可以将获取的报告表保存到 .csv 文件中。

EMA-Regular-Order 策略 - 获取回测报告 - 订单历史

在 AlgoBulls 平台提交回测任务后,AlgoBulls 回测引擎开始执行策略。在执行过程中,除了日志外,AlgoBulls 回测引擎的盈亏表和统计表会实时生成订单历史记录。此日志包含每个订单的状态转换,以及每个订单状态的时间戳和附加信息(如果有)。订单历史记录对于理解交易从 'OPEN' 状态到 'COMPLETE''CANCELLED' 状态所需的时间至关重要。例如,MARKET 订单将立即从 'OPEN' 转换为 'COMPLETE',但是 LIMIT 订单可能需要一段时间,根据市场条件从 'OPEN' 转换到 'COMPLETE' - 甚至可能转为 'CANCELLED'。所有这些信息都包含在订单历史记录中。(有关订单状态转换的更多信息,请参见第六章的状态机图,在交易所上放置常规订单。)

在本配方中,你将获取你的策略的订单历史记录。只要你的策略首次进行交易,并提交了一个回测任务,这个日志就可以获取。AlgoBulls 平台允许你实时获取订单历史记录,即使回测任务仍在进行中也是如此。这帮助我们获取订单的最终状态的细节,而无需等待回测任务完成。pyalgotrading包提供了一个简单的方法,我们可以用它来获取给定策略的订单历史记录。

确保你已经阅读了上一章的前六个配方,以完整了解我们将使用的策略类;即StrategyEMARegularOrder

准备工作

确保 algobulls_connectionstrategy_code1 对象在你的 Python 命名空间中可用。参考本章第一个配方设置 algobulls_connectionstrategy_code1 对象。

如何做...

获取strategy_code1的回测订单历史报告:

>>> order_history = \
        algobulls_connection.get_backtesting_report_order_history(
                                                          strategy_code1)
>>> print(order_history)

我们得到了以下输出(你的输出可能不同):

+-------------+---------------------+----------------------------------+------+
| INST        | TIME                | ID                               | TT   |
|-------------+---------------------+----------------------------------+------|
| NSE_EQ:SBIN | 2020-07-01 09:45:00 | 2333198611b744aeb287300d371c8eb5 | BUY  |
+-------------+---------------------+----------------------------------+------+
+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-01 09:45:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-01 09:45:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-01 09:45:00 | OPEN PENDING           |       |
|  3 | 2020-07-01 09:45:00 | OPEN                   |       |
|  4 | 2020-07-01 09:45:00 | COMPLETE               |       |
+----+---------------------+------------------------+-------+
+-------------+---------------------+----------------------------------+------+
| INST        | TIME                | ID                               | TT   |
|-------------+---------------------+----------------------------------+------|
| NSE_EQ:SBIN | 2020-07-01 15:30:00 | 67f39f41885b476295e7e959b0470d49 | SELL |
+-------------+---------------------+----------------------------------+------+
+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-01 15:30:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-01 15:30:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-01 15:30:00 | OPEN PENDING           |       |
|  3 | 2020-07-01 15:30:00 | OPEN                   |       |
|  4 | 2020-07-01 15:30:00 | COMPLETE               |       |
+----+---------------------+------------------------+-------+
...

这里未显示完整的输出。请访问此链接阅读完整的输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/sample/backtesting/strategy_ema_regular_order/oms_order_history.log

工作原理...

在本配方中,你使用algobulls_connection对象的get_backtesting_report_order_history()方法实时获取订单历史记录。此方法接受策略代码作为参数。你在这里传递了strategy_code1。返回的数据是一个字符串,描述如下:

对于每个订单,日志包含以下信息:

  • 一个包含以下列的描述性订单表:

  • INST: 订单的金融工具

  • TIME: 下单时间

  • ID: 订单的唯一 ID

  • TT: 订单交易类型(BUYSELL

此表的示例如下:

+-------------+---------------------+----------------------------------+------+
| INST        | TIME                | ID                               | TT   |
|-------------+---------------------+----------------------------------+------|
| NSE_EQ:SBIN | 2020-07-01 09:45:00 | 2333198611b744aeb287300d371c8eb5 | BUY  |
+-------------+---------------------+----------------------------------+------+

这些信息将帮助你在策略执行日志中找到这个确切的订单。

  • 一个订单状态转换表,包括以下列:

  • TIME: 订单进入由'STATE'列表示的状态的时间戳。

  • STATE: 订单在'TIME'列中提到的时间戳进入此'STATE'

  • MSG: 来自订单管理系统OMS)的额外信息,用于任何意外状态转换。例如,进入REJECTED状态的订单有来自 OMS 的拒绝原因的消息。该列通常为空。

此表的示例如下:

+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-01 09:45:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-01 09:45:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-01 09:45:00 | OPEN PENDING           |       |
|  3 | 2020-07-01 09:45:00 | OPEN                   |       |
|  4 | 2020-07-01 09:45:00 | COMPLETE               |       |
+----+---------------------+------------------------+-------+

从这张表中,你可以看到,在上午 9:45 下单后,订单立即转移到'COMPLETE'状态。这是预期的,因为订单是常规市价订单。

还有更多...

一旦回测作业转移到'STOPPED'状态,就不会生成新的订单历史记录日志。您可以在提交下一个相同策略的回测作业之前的任何时间获取完整的订单历史记录日志。如果提交了新的回测作业(用于相同的策略),这些日志将不再通过get_backtesting_report_order_history()方法访问。如果您希望以后参考这些日志,可以将获取的日志保存到文件中。

MACD-Bracket-Order 策略 – 获取策略

在这个配方中,您将从您在 AlgoBulls 平台上的帐户中获取StrategyMACDBracketOrder策略类,这是您在上一章的最后一个配方中上传的。这个配方从建立到 AlgoBulls 平台的连接开始,查询您帐户中的所有可用策略,并获取所需策略类StrategyMACDBracketOrder的详细信息。

确保您已经阅读了上一章的最后六个配方,以完全了解我们将要使用的策略类,即StrategyMACDBracketOrder

如何实现…

我们为此配方执行以下步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.algobulls import AlgoBullsConnection
  1. 创建一个新的 AlgoBulls 连接对象:
>>> algobulls_connection = AlgoBullsConnection()
  1. 获取授权 URL:
>>> algobulls_connection.get_authorization_url()

我们得到以下输出:

Please login to this URL with your AlgoBulls credentials and get your developer access token: https://app.algobulls.com/user/login
'https://app.algobulls.com/user/login'
  1. 使用您的 AlgoBulls 凭据登录前面的链接,获取您的令牌,并在此设置它(有关更多详细信息,请参阅附录 II):
>>> algobulls_connection.set_access_token('
                    80b7a69b168c5b3f15d56688841a8f2da5e2ab2c')
  1. 获取并显示您迄今为止创建和上传的所有策略:
>>> all_strategies = algobulls_connection.get_all_strategies()
>>> all_strategies

我们得到以下输出。您的输出可能会有所不同(确保您已经按照上一章的配方来获得类似的输出):

  1. 获取并显示第二个策略 MACD-Bracket-Order 策略 的策略代码:
>>> strategy_code2 = all_strategies.iloc[1]['strategyCode']
>>> strategy_code2

我们得到以下输出(您的输出可能不同):

'49287246f9704bbcbad76ade9e2091d9'
  1. 在对您的策略进行回测之前,您可以检查您的策略以确保您拥有正确的策略:
>>> strategy_details2 = \
        algobulls_connection.get_strategy_details(strategy_code2)
>>> print(strategy_details2)

我们得到以下输出:

class StrategyMACDBracketOrder(StrategyBase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fastMA_period = \
            self.strategy_parameters['fastma_period']
        self.slowMA_period = \
            self.strategy_parameters['slowma_period']
        self.signal_period = \
            self.strategy_parameters['signal_period']
        self.stoploss = \
            self.strategy_parameters['stoploss_trigger']
        self.target = self.strategy_parameters['target_trigger']
        self.trailing_stoploss = \
            self.strategy_parameters['trailing_stoploss_trigger']

        self.main_order = None

    def initialize(self):
        self.main_order = {}

    @staticmethod
    def name():
        return 'MACD Bracket Order Strategy'
    ….
    def strategy_exit_position(self, candle, instrument, 
                               sideband_info):
        if sideband_info['action'] == 'EXIT':
            self.main_order[instrument].exit_position()
            self.main_order[instrument] = None
            return True

        return False

这里没有显示完整的输出。请访问以下链接以阅读完整的输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/strategy_macd_bracket_order.py

工作原理如下…

步骤 1 中,您导入必要的模块。在步骤 2 中,您创建了 AlgoBullsConnection 类的一个实例,命名为 algobulls_connection。在步骤 3 中,您使用 algobulls_connection 对象的 get_authorization_url() 方法获取授权 URL。这会打印授权 URL。您应该从您的网络浏览器访问此 URL 来登录 AlgoBulls 平台并获取您的开发者访问令牌。(您可以在附录 II中找到更多详细信息,以及从 AlgoBulls 平台获取开发者访问令牌的屏幕截图。)您复制访问令牌并在步骤 4 中使用 algobulls_connectionset_access_token() 方法设置它。如果令牌被接受,则会建立与 AlgoBulls 平台的成功连接。

步骤 5 中,您获取到目前在 AlgoBulls 平台上创建并上传的所有策略。您使用 get_all_strategies() 方法进行此步骤,并将其赋值给一个新变量 all_strategies。此变量是一个 pandas.DataFrame 对象,具有 strategyCodestrategyName 列。此表包含您之前上传的策略代码和策略名称的信息。如果您遵循了第八章中的 MACD-Bracket-Order 策略 - 在 AlgoBulls 交易平台上上传策略 配方,算法交易策略 - 逐步编码,您将会找到一个名为 MACD-Regular-Order 策略 的策略。在步骤 6 中,您将策略 MACD-Regular-Order 策略 的策略代码分配给一个名为 strategy_code2 的新变量。策略代码显示在此步骤的输出中。此策略代码对于 AlgoBulls 平台上的每个策略都是唯一的。

最后,在步骤 7 中,您确保 strategy_code2 所引用的策略确实是您之前上传的那一个(在上一章的最后一个配方中)。您使用 algobulls_connection 对象的 get_strategy_details() 方法来检查策略。这个方法以策略代码作为参数。您在这里传递 strategy_code2。此方法返回整个类代码作为字符串。您将其赋值给一个新变量 strategy_details2,并显示它。

如果您想要更改由 strategy_code2 引用的类代码,如 步骤 7 中所示,请参考第八章的上一个配方的 更多信息… 部分,算法交易策略 - 逐步编码

MACD-Bracket-Order 策略 - 回测策略

在这个配方中,您将对 MACD-Bracket-Order 策略 进行回测。您必须在本章的上一个配方中从 AlgoBulls 平台的您的账户中获取此策略。您将利用 pyalgotrading 为这个配方提供的回测功能,这将在 AlgoBulls 平台上提交一个回测任务。

一旦提交,回测将由 AlgoBulls 回测引擎运行。您可以随时查询状态以了解回测作业的状态。作业按以下顺序经历以下状态:

  • STARTING(中间状态)

  • STARTED(稳定状态)

  • STOPPING(中间状态)

  • STOPPED(稳定状态)

在提交作业时,它会从中间状态'STARTING'开始。在此状态下,AlgoBulls 回测引擎获取策略并准备执行环境,这可能需要几分钟。完成后,作业转移到'STARTED'状态。策略回测发生在此阶段。在此阶段,作业将停留,直到回测完成。完成后,作业将转移到中间状态'STOPPING'。在此状态下,AlgoBulls 回测引擎清理分配给此作业的资源,通常需要不到一分钟。最后,作业转移到'STOPPED'状态。

如果您已经为某个策略提交了回测作业,那么在第一个作业完成之前,您不能为同一策略提交另一个作业。这意味着您必须等待第一个作业进入'STOPPED'状态。如果第一个作业运行时间很长,并且您希望立即停止它,您可以通过pyalgotrading提交停止作业请求。您需要确保作业在提交请求前处于'STARTED'状态。

在提交回测作业后,您可以实时获取策略执行的日志和报告。日志和报告有助于验证策略的性能并调试任何潜在问题。

您可以参考本章的第二个配方,查看 AlgoBulls 平台上回测作业的状态机图。它展示了回测作业在其生命周期中的各种状态和转换。

确保您已经阅读了上一章的最后六个配方,以全面了解我们将使用的策略类,即StrategyMACDBracketOrder

准备就绪

确保algobulls_connectionstrategy_code2对象在您的 Python 命名空间中可用。请参考本章的上一配方来设置algobulls_connectionstrategy_code2对象。

如何操作...

我们执行以下步骤来完成此操作:

  1. 导入必要的模块:
>>> from datetime import datetime as dt
>>> from pyalgotrading.constants import *
  1. 使用其交易符号作为关键字搜索工具。将返回的对象分配给instruments
>>> instrument = algobulls_connection.search_instrument(
                                                'TATASTEEL')
>>> instrument

我们获得以下输出(您的输出可能不同):

[{'id': 1, 'value': 'NSE:TATASTEEL'}]
  1. instruments获取所选工具的value
>>> instrument = instrument[0]['value']
>>> instrument

我们获得以下输出:

'NSE:TATASTEEL'
  1. strategy_code2提交回测作业:
>>> algobulls_connection.backtest(
        strategy_code=strategy_code2, 
        start_timestamp=dt(year=2020, month=7, day=1, hour=9, 
                            minute=15),
        end_timestamp=dt(year=2020, month=7, day=7, hour=15, 
                            minute=30),
        instrument=instrument,
        lots=1,
        strategy_parameters={
            'fastma_period': 26, 
            'slowma_period': 6, 
            'signal_period': 9,
            'target_trigger': 0.01, 
            'stoploss_trigger': 0.01, 
            'trailing_stoploss_trigger': 1
        },
        candle_interval=CandleInterval.MINUTES_15)

我们获得以下输出:

Setting Strategy Config... Success.
Submitting BACKTESTING job... Success.
  1. 检查提交的回测作业的状态:
>>> algobulls_connection.get_backtesting_job_status(strategy_code2)
{'data': 'STARTING'}
  1. 过一段时间后再次检查提交的回测作业的状态:
>>> algobulls_connection.get_backtesting_job_status(strategy_code2)
{'data': 'STARTED'}

工作原理...

步骤 1中,您从datetime模块中导入datetime类,以及从pyalgotrading.constants模块中导入所有所需的常量。在步骤 2中,您使用algobulls_connection对象的search_instrument()方法获取您想要为其回测策略的工具,MACD-Bracket-Order 策略search_instrument()方法接受一个搜索字符串作为参数,该字符串应该是您感兴趣的工具的交易代码的部分或完整内容。这里您传递了'TATASTEEL'。该函数返回一个包含与搜索字符串匹配的工具详细信息的列表。可能会有多个工具的交易代码中含有搜索字符串。在步骤 3中,您获取第一个匹配工具的值,并将其赋值给一个新变量,instrument

步骤 4中,您使用algobulls_connection()对象的backtest()方法提交一个回测任务。它接受以下参数:

  • strategy_code: 要进行回测的策略的策略代码。这应该是一个字符串。在这里,您传递了strategy_code2

  • start_timestamp: 进行回测的起始时间戳。这应该是一个datetime.datetime对象。这里,您传递了一个包含 2020 年 7 月 1 日 9:15 小时的值的对象 - dt(year=2020, month=7, day=1, hour=9, minute=15)。有关创建datetime对象的详细信息,请参阅第一章的第一个示例,即处理和操作日期、时间和时间序列数据

  • end_timestamp: 进行回测的结束时间戳。此对象应持有比start_timestamp持有的时间戳值要晚的时间戳。这应该是一个datetime.datetime实例。在这里,您传递了一个包含 2020 年 7 月 7 日 15:30 小时的值的对象 - dt(year=2020, month=7, day=7, hour=15, minute=30)

  • instrument: 应该运行回测的金融工具。将为此工具获取历史数据。这应该是一个字符串。在这里,您传递了instrument

  • lots: 进行回测的手数。这应该是一个整数。该数量由策略计算为手数×金融工具的手数。 (请参阅第八章的MACD-Bracket-Order 策略 - 编写策略 _select_instruments_for_entry 方法算法交易策略 - 逐步编码。) 在这里,您传递了1

  • strategy_parameters: 策略所需的参数名称和值。这应该是一个字典,其中parameter-nameparameter-value是键值对。您在这里传递以下参数:

  • fastma_period: 26

  • slowma_period: 6

  • signal_period: 9

  • target_trigger: 0.01

  • stoploss_trigger: 0.01

  • trailing_stoploss_trigger: 1

(回想一下,MACD-Bracket-Order 策略的参数在前一章的__init__()方法中定义)。

  • candle_interval: 用于回测的历史数据获取的蜡烛图间隔。这应该是CandleInterval类型的枚举。在这里,你传递CandleInterval.MINUTES_15。(CandleInterval枚举提供各种蜡烛图间隔的枚举,其中一些是MINUTE_1MINUTES_3MINUTES_5MINUTES_10MINUTES_15MINUTES_30HOURDAY)。

如果作业提交成功,你会看到backtest()函数打印出Success消息。

一旦作业提交,启动需要一段时间。启动后,根据指定的策略复杂度和回测持续时间(使用start_timestampend_timestamp参数),完成可能需要一些时间。几天的回测可能在几秒内完成,而几个月的回测可能需要几分钟。

步骤 5中,你使用algobulls_connection对象的get_backtesting_job_status()方法获取作业状态。在这里,你将strategy_code2作为参数传递。该方法返回一个键值对字典 - 数据和作业状态。如果在放置作业后立即查询状态,你会得到'STARTING'作为状态。在步骤 6中,你再次查询状态,如果作业已经启动,你会得到'STARTED'的状态。

成功提交意味着以所需格式传递了回测策略的最小输入。然而,这并不保证策略会在回测期间无错误地运行。策略的执行仍然可能在回测过程中遇到错误。要调试执行问题,你需要获取输出日志,这将在下一个示例中解释。错误的可能原因包括策略类的 Python 代码中的错误或传递给backtest()函数的strategy_parameters字典不完整。

还有更多...

如果作业运行时间很长,你希望在完成之前停止它,你可以使用algobulls_connection对象的stop_backtesting_job()方法。此方法接受策略代码作为参数。在这里,你传递strategy_code2。该方法向 AlgoBulls 回测引擎提交停止请求。如果请求被接受,你会看到一个Success消息:

>>> algobulls_connection.stop_backtesting_job(strategy_code2)
 Stopping BACKTESTING job... Success.

如果在提交停止请求后查询状态,你会得到一个'STOPPING'的状态:

>>> algobulls_connection.get_backtesting_job_status(strategy_code2)
{'data': 'STOPPING'}

如果一段时间后再次查询状态,如果作业已经停止,你会得到'STOPPED'的状态:

>>> algobulls_connection.get_backtesting_job_status(strategy_code2)
{'data': 'STOPPED'} 

MACD-Bracket-Order 策略 - 实时获取回测日志

在 AlgoBulls 平台提交回测作业后,AlgoBulls 回测引擎开始执行策略。在执行过程中,AlgoBulls 回测引擎记录了发生的每个事件以及所做出的决策,以确切的时间戳形式记录在文本日志中。记录的活动示例包括给定策略配置、定期生成的每个新蜡烛、您的策略输入的交易、由这些交易创建的头寸的进入和退出、等待新蜡烛等等。这些日志对于验证策略行为以及调试在开发策略时经常遇到的行为或性能问题至关重要。

在这个配方中,您将获取您的策略的回测日志。日志会在您提交的回测作业达到'STARTED'状态时开始出现。AlgoBulls 平台允许您实时获取日志,即使回测作业仍在进行中。您可以在不等待回测作业完成的情况下深入了解策略执行情况,这在作业运行时间较长时非常有用。pyalgotrading包提供了一个简单的方法来获取给定策略的执行日志。

确保您已经浏览了上一章的最后六个配方,以完整了解我们将要使用的策略类别;也就是StrategyMACDBracketOrder

准备工作

确保algobulls_connectionstrategy_code2对象在您的 Python 命名空间中可用。请参考本章的MACD-Bracket-Order strategy – fetching the strategy配方来设置algobulls_connectionstrategy_code2对象。

如何操作...

我们对这个配方执行以下步骤:

  1. 获取strategy_code2的回测执行日志:
>>> logs = algobulls_connection.get_backtesting_logs(
                                              strategy_code2)
>>> print(logs)

我们得到了以下输出(您的输出可能不同):

[2020-07-30 17:27:25] Logs not available yet. Please retry in sometime.
  1. 一段时间后再次获取strategy_code2的回测执行日志:
>>> logs = algobulls_connection.get_backtesting_logs(
                                               strategy_code2)
>>> print(logs)

我们得到了以下输出(您的输出可能不同):

...
########################################
 INITIALIZING ALGOBULLS CORE (v3.2.0 SECURE MODE)... 
########################################
...
[BT] [2020-07-01 09:15:00] [INFO] [tls] STARTING ALGOBULLS CORE...
...
[BT] [2020-07-01 12:30:00] [CRITICAL] [order] [PLACING NEW ORDER] [2020-07-01 12:30:00] [1cbefcf395c344c88a228a1b01c32ef6] [BUY] [NSE:TATASTEEL] [QTY:1] [QTY PENDING: 1] [ENTRY PRICE: 322.6] [PRICE:322.6] [TRIGGER PRICE:None] [ORDER_TYPE_BRACKET] [ORDER_CODE_INTRADAY] [ORDER_VARIETY_LIMIT] [ORDER_POSITION_ENTER] [STOPLOSS TRIGGER:319.374] [TARGET TRIGGER:325.826] [TRAILING STOPLOSS TRIGGER:322.6]
...
[BT] [2020-07-07 15:30:00] [INFO] [clock] Candle generation has been stopped...
[BT] [2020-07-07 15:30:00] [INFO] [tls] Received event END OF MARKET. Stopping Trading Core Engine...
[BT] [2020-07-07 15:30:00] [CRITICAL] [tls] [User: ALGOBULLS VIRTUAL USER] Trading session completed
...

此处未显示完整输出。请访问以下链接阅读完整输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/sample/backtesting/strategy_macd_bracket_order/logs.txt

工作原理...

步骤 1中,您使用algobulls_connection对象的get_backtesting_logs()方法实时获取策略回测日志。此方法接受策略代码作为参数。在此处传递strategy_code2。返回的数据是一个字符串。如果在提交作业后立即尝试此步骤,您将获得一个字符串,指出日志尚未准备好([2020-07-30 17:27:25] 日志尚未准备好,请稍后重试。)。如果回测作业处于'STARTING'状态,则会发生这种情况。

第 2 步中,你在一段时间后再次获取日志。如果作业处于'STARTING'状态之外,你就开始获取你的策略执行日志。每次调用get_backtesting_logs()函数时,你都会得到整个回测日志。

还有更多...

一旦回测作业转移到'STOPPED'状态,就不会生成新的日志。你可以在提交下一个相同策略的回测作业之前的任何时候获取完整的日志。如果提交了一个新的回测作业(针对相同的策略),这些日志将不再通过get_backtesting_logs()方法访问。如果你希望以后参考,你可以将获取的日志保存到文件中。

MACD-Bracket-Order 策略 – 获取回测报告 – 盈亏表

在 AlgoBulls 平台上提交回测作业后,AlgoBulls 回测引擎开始执行策略。在其执行过程中,除了日志外,AlgoBulls 回测引擎还实时生成 P&L 表。该表包含了策略输入的每一笔交易的信息。它还包含了入口和出口订单之间的映射、交易 P&L 和累积 P&L 的详细信息,按时间顺序排序,最新的订单排在最前面。

这个表格通过个人和累积 P&L 数字帮助我们了解策略的整体表现。入口-出口订单映射也有助于验证策略的行为。

在这个食谱中,你将获取你的策略的 P&L 表报告。在提交回测作业后,只要你的策略打出第一笔交易,这个报告就可以获得。AlgoBulls 平台允许你实时获取 P&L 表,即使回测作业仍在进行中。在作业运行时间较长时,这对于获取策略表现的洞察力是有帮助的。pyalgotrading包提供了一个简单的方法,你可以用它来获取给定策略的 P&L 表。

确保你已经浏览了上一章的最后六个食谱,以完整地了解我们将使用的策略类的情况;也就是说,StrategyMACDBracketOrder

准备就绪

确保你的 Python 命名空间中有algobulls_connectionstrategy_code2对象可用。请参考本章的 MACD-Bracket-Order 策略 – 获取策略 食谱设置algobulls_connectionstrategy_code2对象。

如何操作...

获取strategy_code2的回测 P&L 报告:

>>> algobulls_connection.get_backtesting_report_pnl_table(strategy_code2)

我们得到了以下输出。你的输出可能会有所不同(注意以下输出已经分成多个表格以便表示。你在 Jupyter Notebook 中将看到一个宽表):

运作原理...

在本示例中,您可以使用algobulls_connection对象的get_backtesting_report_pnl_table()方法实时获取回测 P&L 表。 此方法接受策略代码作为参数。 在此处传递strategy_code2。 返回数据是一个带有多列的pandas.DataFrame对象,描述如下:

  • instrument:输入的金融工具。

  • entry_timestamp:放置进入订单的时间戳。(请注意,它可能在进入'COMPLETE'状态之前保持一段时间处于'OPEN'状态。可以通过订单历史表找到此状态转换的时间,如本章的最后一个示例所述。)

  • entry_transaction_type:进入订单交易类型(BUYSELL)。

  • entry_quantity:进入订单数量。

  • 进入价格:进入订单执行并进入'COMPLETE'状态的价格。

  • 退出时间戳:放置退出订单的时间戳。(请注意,它可能在进入'COMPLETE'状态之前保持一段时间处于'OPEN'状态。)

  • exit_transaction_type:退出订单交易类型(BUYSELL)。

  • exit_quantity:退出订单数量。

  • 退出价格:退出订单执行并进入'COMPLETE'状态的价格。

  • pnl_absolute:退出订单执行价格与进入订单执行价格之间的差值。 在数学上,这可以表示为(退出价格 - 进入价格退出数量(对于多头交易),以及(进入价格* - 退出价格退出数量*(对于空头交易)。 正值意味着交易是盈利交易。 负值意味着交易是亏损交易。

  • pnl_percentage:相对于进入价格的利润或损失的百分比。 在数学上,这是 pnl_absolute / *entry_price / exit_quantity *× 100

  • pnl_cumulative_absolute:累积利润或损失。 在数学上,这是以前交易的所有pnl_absolute值的总和。 这个数字直接反映了策略相对于模拟时间的表现。

  • pnl_cumulative_percentage:相对于进入价格的累积利润或损失的百分比。 在数学上,这是 pnl_cumulative / entry_price / exit_quantity ×* 100*。

还有更多...

一旦回测作业转移到'STOPPED'状态,P&L 表报告将不再更新。 您可以在提交下一个相同策略的回测作业之前随时获取完整的 P&L 报告。 如果提交了新的回测作业(针对相同的策略),则将无法通过get_backtesting_report_pnl_table()方法再访问此报告。 如果您希望稍后参考,可以将获取的报告保存为.csv文件。

MACD-Bracket-Order 策略 - 获取回测报告 - 统计表

在 AlgoBulls 平台上提交回测任务后,AlgoBulls 回测引擎开始执行策略。在执行过程中,除了日志和 P&L 表之外,AlgoBulls 回测引擎还实时从 P&L 表生成摘要。这个摘要是一个包含各种统计数字的统计表,比如净利润(绝对值和百分比)、最大回撤(绝对值和百分比)、总交易次数、盈利交易、亏损交易、多头交易和空头交易的计数、最大盈利和最小盈利(或最大亏损)、以及每笔盈利和亏损交易的平均利润。这张表可以让我们快速了解策略的整体表现。

在这个配方中,您将为您的策略获取统计表报告。此报告在您提交回测任务后,您的策略打入第一笔交易后即可获取。AlgoBulls 平台允许您实时获取统计表,即使回测任务仍在进行中。您可以在等待回测任务完成时获得对策略性能的见解,这在任务运行时间较长时非常有用。pyalgotrading 包提供了一个简单的方法,我们可以使用它来获取给定策略的统计表。

确保您已经阅读了上一章的最后六个配方,以完全了解我们将使用的策略类别;即StrategyMACDBracketOrder

准备工作

确保您的 Python 命名空间中有 algobulls_connectionstrategy_code2 对象可用。请参考本章的 MACD-Bracket-Order 策略 - 获取策略 配方来设置 algobulls_connectionstrategy_code2 对象。

如何操作...

获取 strategy_code2 的回测统计报告:

>>> algobulls_connection.get_backtesting_report_statistics(strategy_code2)

我们得到以下输出(您的输出可能会有所不同):

工作原理...

在这个配方中,您可以使用 algobulls_connection 对象的 get_backtesting_report_statistics() 方法实时获取回测统计表。此方法接受策略代码作为参数。您在这里传递了 strategy_code2。返回的数据是一个 pandas.DataFrame 对象,有两列 - HighlightValue - 以及多行。行的描述如下:

  • 净利润:累积回测净利润。这也是 P&L 表中第一个条目的 pnl_cumulative_absolute 值。

  • 净利润 %:累积回测利润百分比。这也是 P&L 表中第一个条目的 pnl_cumulative_percentage 值。

  • 最大回撤:P&L 表中 pnl_cumulative 列中的最低值。这表示您的策略在执行过程中遇到的最大损失。

  • 最大回撤 %:在数学上,这是 (最大回撤) / (相应的 entry_price) / exit_quantity × 100

  • 交易次数:会话期间的总交易次数(入场和出场计算为一次)。

  • 盈利次数:交易 P&L 为非负数的交易计数。

  • 亏损次数:交易 P&L 为负数的交易计数。

  • 买入交易数:以 'BUY' 为入场交易类型的交易计数。

  • 卖出交易数:以 'SELL' 为入场交易类型的交易计数。

  • 最大利润:所有交易中 P&L 值最大的交易的 P&L。

  • 最小利润:所有交易中 P&L 值最小的交易的 P&L。

  • 每次盈利交易的平均利润:从数学上讲,这是 (盈利交易的总 P&L) / (盈利交易的计数)

  • 每次亏损交易的平均利润:从数学上讲,这是 (亏损交易的总 P&L) / (亏损交易的计数)

还有更多...

如果在回测作业仍在运行时获取统计表,则上述数字将是中间数字,基于直到该时刻已完成的交易。随着更多交易的输入,这些数字可能会发生变化,直到回测作业完成。

一旦回测作业转移到 'STOPPED' 状态,统计表将不再更改。您可以在提交下一个相同策略的回测作业之前的任何时间获取完整的统计表。如果提交了新的回测作业(针对相同策略),则无法通过 get_backtesting_report_statistics() 方法访问此表。如果您希望稍后参考,可以将获取的报告保存为 .csv 文件。

MACD-Bracket-Order 策略 - 获取回测报告 - 订单历史

在 AlgoBulls 平台上提交回测作业后,AlgoBulls 回测引擎开始执行策略。在执行过程中,除了日志、P&L 表和统计表之外,AlgoBulls 回测引擎还实时生成订单历史记录。此日志包含每个订单的状态转换,以及每个订单状态的时间戳和附加信息(如果有)。订单历史记录对于了解交易从 'OPEN''COMPLETE''CANCELLED' 状态所需的时间非常重要。例如,MARKET 订单将立即从 'OPEN' 转移到 'COMPLETE',但是 LIMIT 订单可能需要一段时间,根据市场条件,从 'OPEN' 转移到 'COMPLETE' - 甚至可能会被 'CANCELLED'。所有这些信息都在订单历史记录中提供。(有关订单状态转换的更多信息,请参阅第六章的状态机图,在交易所上下单。)

在这个配方中,您将获取您的策略的订单历史记录。只要您提交了回测作业后,您的策略就 punched in 第一笔交易,这个日志就可用了。AlgoBulls 平台允许您实时获取订单历史记录,即使回测作业仍在进行中。这样我们就可以获得处于最终状态的订单的详细信息,而不必等待回测作业完成。pyalgotrading软件包提供了一种简单的方法,我们可以用它来获取给定策略的订单历史记录。

确保您已经阅读了前一章节的最后几个配方,以完全了解我们将使用的策略类StrategyMACDBracketOrder

准备就绪

确保algobulls_connectionstrategy_code2对象在您的 Python 命名空间中可用。请参考本章的MACD-Bracket-Order strategy – fetching the strategy配方设置algobulls_connectionstrategy_code2对象。

怎么做…

获取strategy_code2的回测订单历史报告:

>>> order_history = algobulls_connection.get_backtesting_report_order_history(strategy_code2)
>>> print(order_history)

我们得到以下输出(您的输出可能不同):

...
+------------------+---------------------+----------------------------------+------+
| INST             | TIME                | ID                               | TT   |
|------------------+---------------------+----------------------------------+------|
| NSE_EQ:TATASTEEL | 2020-07-03 10:00:00 | 03436b72ad8a47a8b29bb727876b0b95 | BUY  |
+------------------+---------------------+----------------------------------+------+
+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-03 10:00:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-03 10:00:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-03 10:00:00 | OPEN PENDING           |       |
|  3 | 2020-07-03 10:00:00 | TRIGGER PENDING        |       |
|  4 | 2020-07-03 12:30:00 | OPEN                   |       |
|  5 | 2020-07-03 12:30:00 | COMPLETE               |       |
+----+---------------------+------------------------+-------+
+------------------+---------------------+----------------------------------+------+
| INST             | TIME                | ID                               | TT   |
|------------------+---------------------+----------------------------------+------|
| NSE_EQ:TATASTEEL | 2020-07-03 10:00:00 | 62458cf47d5f4a12b6c31c490451fdb0 | BUY  |
+------------------+---------------------+----------------------------------+-----
+
+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-03 10:00:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-03 10:00:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-03 10:00:00 | OPEN PENDING           |       |
|  3 | 2020-07-03 10:00:00 | OPEN                   |       |
|  4 | 2020-07-03 12:30:00 | CANCEL PENDING         |       |
|  5 | 2020-07-03 12:30:00 | CANCELLED              |       |
+----+---------------------+------------------------+-------+
...

这里没有显示完整的输出。请访问以下链接以阅读完整的输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/sample/backtesting/strategy_macd_bracket_order/oms_order_history.log

工作原理…

在这个配方中,您使用algobulls_connection对象的get_backtesting_report_order_history()方法来实时获取订单历史记录。此方法接受策略代码作为参数。您在这里传递了strategy_code2。返回的数据是一个字符串,描述如下:

对于每个订单,日志包含以下信息:

  • 订单的描述性表,包括以下列:

  • INST:订单的金融工具

  • TIME:订单被放置的时间

  • ID:订单的唯一标识符

  • TT:订单交易类型(BUYSELL

表的示例如下所示:

+------------------+---------------------+----------------------------------+------+
| INST             | TIME                | ID                               | TT   |
|------------------+---------------------+----------------------------------+------|
| NSE_EQ:TATASTEEL | 2020-07-03 10:00:00 | 03436b72ad8a47a8b29bb727876b0b95 | BUY  |
+------------------+---------------------+----------------------------------+------+

这些信息将帮助您在策略执行日志中找到这个确切的订单。

  • 一个订单状态转换表,具有以下列:

  • TIME:订单进入由'STATE'列表示的状态的时间戳。

  • STATE:订单在'STATE'列中的状态进入时间戳所述的时间。

  • MSG:OMS 对于任何意外状态转换的额外消息;例如,进入REJECTED状态的订单具有来自 OMS 的说明其被拒绝原因的消息。此列通常为空。

表的示例如下所示:

+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-03 10:00:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-03 10:00:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-03 10:00:00 | OPEN PENDING           |       |
|  3 | 2020-07-03 10:00:00 | TRIGGER PENDING        |       |
|  4 | 2020-07-03 12:30:00 | OPEN                   |       |
|  5 | 2020-07-03 12:30:00 | COMPLETE               |       |
+----+---------------------+------------------------+-------+

从这个表格可以看出,在上午 10:00 下单后,订单转移到'OPEN PENDING'状态。在停留了 2.5 小时后,订单转移到'COMPLETE'状态。这是预期的,因为该订单是一个区间限价订单。

还有更多...

一旦回测任务转移到'STOPPED'状态,将不再生成新的订单历史日志。在提交下一个相同策略的回测任务之前,您可以随时获取完整的订单历史日志。如果提交了新的回测任务(针对相同的策略),则无法再通过get_backtesting_report_order_history()方法访问这些日志。如果您希望在以后参考这些日志,可以将获取的日志保存到文件中。

第十章:算法交易 - 模拟交易

在第八章 算法交易策略 - 编码 - 逐步 中构建算法交易策略,并在上一章成功进行了令人满意的回测后,下一步是在实时市场中模拟交易这些策略。

模拟交易 是在实时市场小时内执行交易策略的方法,仅通过实时记录来自策略执行的交易。这些交易不通过经纪人使用真实资金执行。之前,这些交易记录是在纸上完成的,因此称为 纸上交易。这些虚拟交易可用于分析风险和回报指标。典型的模拟交易指标包括 盈亏P&L)、最大回撤、总交易次数、盈利交易、亏损交易、多头交易和空头交易、每个盈利和亏损交易的平均利润等。应至少进行几个交易日的模拟交易,并在这些指标满足必要要求之前,整个过程应重复进行,包括更新策略参数和/或策略实施,然后进行回测和模拟交易。

模拟交易背后的基本思想是,交易策略可以在实时市场中执行,几乎与实际交易相似,但不冒真实资金的风险。模拟交易有助于确保过去的市场情景,用于回测的情景仍然有效。如果过去的市场情景当前不再存在,即使回测结果盈利,模拟交易结果可能会相反。这表明在将策略用于真实资金之前,策略参数和/或策略实施需要更多工作。

对于模拟交易,需要一个策略配置。其中包含多个参数,其中一些如下:

  • 开始和结束时间:在当前一天内进行模拟交易的时间段。

  • 金融工具:应进行模拟交易的一个或多个金融工具。

  • 蜡烛间隔:各种可能的蜡烛间隔之一 - 例如,1 分钟15 分钟小时

  • 策略特定参数:策略中定义的自定义参数的值。

  • 策略模式:即日内或交割。日内策略进行日内订单,这些订单在一天结束时平仓。交割策略进行交割订单,这些订单不会在一天结束时平仓,并会延续到下一个交易日。

执行模拟交易需要一个模拟交易引擎。在本章中,您将使用 AlgoBulls 提供的模拟交易引擎(algobulls.com),这是一个通过其开发者选项提供服务的算法交易平台。它提供了一个名为 pyalgotrading 的 Python 包(github.com/algobulls/pyalgotrading)来使用这些服务。

您已经在第八章,算法交易策略 - 逐步编码,中编写了两种算法交易策略。请回想策略描述如下:

  • EMA-Regular-Order 策略:基于 EMA 技术指标和常规订单的策略。(第八章的前六个配方,算法交易策略 - 逐步编码。)

  • MACD-Bracket-Order 策略:基于 MACD 技术指标和括号订单的策略。(第八章的后六个配方,算法交易策略 - 逐步编码。)

这些策略也作为 Python 包的一部分提供,pyalgostrategypool。您可以使用 pip 安装它,如下所示:$ pip install pyalgostrategypool

您还可以在 GitHub 上查看它们(github.com/algobulls/pyalgostrategypool)。

由于您已经按照第八章,算法交易策略 - 逐步编码,将这两种策略上传到您的 AlgoBulls 账户。在本章中,您将从您的 AlgoBulls 账户中获取这些策略并对其进行模拟交易。在模拟交易后,您将获得策略执行日志和各种报告 - 即损益报告、统计报告和订单历史。这些日志和报告有助于验证策略的性能,并为实际交易做准备。通过使用 pyalgotrading,您可以确保在模拟交易中专注于开发和验证策略,而不必担心策略执行所需的生态系统。

本章包括了逐步配方,涵盖了之前提到的两种策略,从与 AlgoBulls 平台建立连接、获取策略、运行模拟交易任务到获取执行日志和各种类型的报告。

本章将涵盖以下内容:

  • EMA-Regular-Order 策略 – 获取该策略

  • EMA-Regular-Order 策略 – 模拟交易该策略

  • EMA-Regular-Order 策略 – 实时获取模拟交易日志

  • EMA-Regular-Order 策略 – 获取模拟交易报告 – 损益表

  • EMA-Regular-Order 策略 – 获取模拟交易报告 – 统计表

  • EMA-Regular-Order 策略 – 获取模拟交易报告 – 订单历史

  • MACD-Bracket-Order 策略 – 获取策略

  • MACD-Bracket-Order 策略 – 模拟交易策略

  • MACD-Bracket-Order 策略 – 获取实时模拟交易日志

  • MACD-Bracket-Order 策略 – 获取模拟交易报告 – P&L 表

  • MACD-Bracket-Order 策略  – 获取模拟交易报告 – 统计表

  • MACD-Bracket-Order 策略 – 获取模拟交易报告 - 订单历史

只有在实时市场小时内运行的模拟交易才有意义,与可以在任何时间运行的回测不同。请确保您在实时市场小时内尝试本章的配方。

技术要求

要成功执行本章的配方,您需要以下内容:

  • Python 3.7+

  • Python 软件包:

  • pyalgotrading$ pip install pyalgotrading

本章的最新 Jupyter 笔记本可在 GitHub 上找到,网址为github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/tree/master/Chapter10

EMA-Regular-Order 策略 – 获取策略

在这个步骤中,您将从您在 AlgoBulls 平台上的帐户中获取策略类StrategyEMARegularOrder,这是您在第八章中通过EMA-Regular-Order 策略 – 在 AlgoBulls 交易平台上上传策略步骤中上传的。这个步骤首先建立到 AlgoBulls 平台的连接,查询您帐户中所有可用的策略,并获取所需策略类StrategyEMARegularOrder的详细信息。

确保您已经完成第八章中的前六个配方,Algorithmic Trading Strategies – Coding Step by Step,以获取使用的策略类StrategyEMARegularOrder的完整图片。

如何操作…

我们为本配方执行以下步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.algobulls import AlgoBullsConnection
  1. 创建一个新的 AlgoBulls 连接对象:
>>> algobulls_connection = AlgoBullsConnection()
  1. 获取授权 URL:
>>> algobulls_connection.get_authorization_url()

我们得到以下输出:

Please login to this URL with your AlgoBulls credentials and get your developer access token: https://app.algobulls.com/user/login
'https://app.algobulls.com/user/login'
  1. 使用您的 AlgoBulls 凭据登录到上述链接,获取您的令牌,并在此处设置它(有关更多详情,请参阅附录 II):
>>> algobulls_connection.set_access_token(
                    '80b7a69b168c5b3f15d56688841a8f2da5e2ab2c')
  1. 获取并显示到目前为止您创建和上传的所有策略:
>>> all_strategies = algobulls_connection.get_all_strategies()
>>> all_strategies

我们得到以下输出。您的输出可能不同(确保您已经按照第八章中的配方,Algorithmic Trading Strategies – Coding Step by Step,来获得类似的输出):

  1. 获取并显示第一个策略的策略代码:
>>> strategy_code1 = all_strategies.iloc[0]['strategyCode']
>>> strategy_code1

我们得到以下输出(您的输出可能不同):

'49287246f9704bbcbad76ade9e2091d9'
  1. 在模拟交易您的策略之前,您可以检查它以确保您有正确的策略:
>>> strategy_details1 = \
        algobulls_connection.get_strategy_details(strategy_code1)
>>> print(strategy_details1)

我们得到以下输出:

class StrategyEMARegularOrder(StrategyBase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.timeperiod1 = self.strategy_parameters['timeperiod1']
        self.timeperiod2 = self.strategy_parameters['timeperiod2']

        self.main_order = None

    def initialize(self):
        self.main_order = {}

    @staticmethod
    def name():
        return 'EMA Regular Order Strategy'
    ….
    def strategy_exit_position(self, candle, instrument, 
                               sideband_info):
        if sideband_info['action'] == 'EXIT':
            self.main_order[instrument].exit_position()
            self.main_order[instrument] = None
            return True

        return False

这里没有显示完整的输出。请访问以下链接以阅读完整的输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/strategy_ema_regular_order.py

如何运作…

您在第 1 步中导入了必要的模块。在第 2 步中,创建了 AlgoBullsConnection 类的一个实例,命名为 algobulls_connection。在第 3 步中,您使用 algobulls_connection 对象的 get_authorization_url() 方法获取授权 URL。这将打印授权 URL。您应该从您的 Web 浏览器中访问此 URL,以登录 AlgoBulls 平台并获取您的开发者访问令牌。(您可以在 附录 II 中找到有关从 AlgoBulls 平台获取开发者访问令牌的更多详细信息和截图。)您复制访问令牌,并在第 4 步中使用 algobulls_connectionset_access_token() 方法设置它。如果令牌被接受,则会建立与 AlgoBulls 平台的成功连接。

第 5 步,您获取到目前已经在 AlgoBulls 平台上创建并上传的所有策略。您可以使用 get_all_strategies() 方法来完成这一步,并将其赋值给一个新变量 all_strategies。这个变量是一个 pandas.DataFrame 对象,具有 strategyCodestrategyName 两列。该表格保存了您之前上传的策略代码和策略名称的信息。如果您按照 第八章 中的 EMA-Regular-Order 策略——将策略上传至 AlgoBulls 交易平台 这一步骤,您会找到一个名为 EMA-Regular-Order 策略 的策略。在第 6 步,您将 EMA-Regular-Order 策略 策略的策略代码赋值给一个新变量 strategy_code1。该策略代码将显示在此步骤的输出中。这个策略代码对于 AlgoBulls 平台上的每个策略都是唯一的。

最后,在第 7 步,您确保由 strategy_code1 引用的策略确实是您之前上传的策略(在 第八章 中的 EMA-Regular-Order 策略——将策略上传至 AlgoBulls 交易平台 步骤中,在 Algorithmic Trading Strategies – Coding Step by Step)。您使用 algobulls_connection 对象的 get_strategy_details() 方法来检查策略。此方法以策略代码作为参数。您在这里传递 strategy_code1。此方法将整个类代码作为字符串返回。您将其分配给一个新变量 strategy_details1,并显示它。

如果您想要更改strategy_code1所引用的类代码,如步骤 7所示,请参考第八章中EMA-Regular-Order 策略 - 在 AlgoBulls 交易平台上上传策略配方的There's more…部分,算法交易策略 - 逐步编码

EMA-Regular-Order 策略 - 对策略进行纸张交易

在这个配方中,您将在EMA-Regular-Order策略上进行纸张交易。您必须在上一个配方中从 AlgoBulls 平台上的账户中获取此策略。您将利用pyalgotrading提供的纸张交易功能进行此配方,该功能反过来在 AlgoBulls 平台上提交了一份纸张交易作业。

提交后,AlgoBulls 纸张交易引擎将运行纸张交易。您可以随时查询状态,以了解纸张交易作业的状态。作业按照以下给定顺序经历以下状态:

  • 'STARTING'(中间状态)

  • 'STARTED'(稳定状态)

  • 'STOPPING'(中间状态)

  • 'STOPPED'(稳定状态)

提交作业后,它将以中间状态'STARTING'开始。在此状态下,AlgoBulls 纸张交易引擎将获取策略并准备执行环境,这可能需要几分钟的时间。一旦完成,作业将转移到'STARTED'状态。纸张交易策略在此阶段发生。在此阶段,它保持不变,直到纸张交易完成。完成后,作业将移动到中间状态'STOPPING'。在此状态下,AlgoBulls 纸张交易引擎将清理为此作业分配的资源,这通常需要不到一分钟的时间。最后,作业将移动到'STOPPED'状态。

如果您已经提交了策略纸张交易作业,则在第一个作业完成之前,不能提交另一个相同策略的作业。这意味着您必须等待第一个作业进入'STOPPED'状态。如果第一个作业运行时间较长,并且您希望立即停止它,则可以通过pyalgotrading提交停止作业请求。在提交请求之前,您需要确保作业处于'STARTED'状态。

以下状态机图演示了在 AlgoBulls 平台上进行纸张交易作业期间的各种状态和转换:

提交纸张交易作业后,您可以实时获取策略执行的日志和报告。日志和报告有助于验证策略的性能并调试任何潜在问题。

确保您已经阅读了第八章的前六个配方,算法交易策略 - 逐步编码,以完全了解所使用的策略类StrategyEMARegularORder的完整情况。

准备就绪

确保algobulls_connectionstrategy_code1对象在您的 Python 命名空间中可用。请参阅本章第一个配方以设置algobulls_connectionstrategy_code1对象。

如何做…

对于这个配方,我们执行以下步骤:

  1. 导入必要的模块:
>>> from datetime import time
>>> from pyalgotrading.constants import *
  1. 使用其交易符号作为关键字搜索工具。将返回的对象赋值给instruments
>>> instruments = algobulls_connection.search_instrument('SBIN')
>>> instruments

我们得到以下输出(你的输出可能不同):

[{'id': 7, 'value': 'NSE:SBIN'}]
  1. instruments中获取所选工具的value
>>> instrument = instruments[0]['value']
>>> instrument

我们得到以下输出:

'NSE:SBIN'
  1. 提交一个strategy_code1的纸上交易任务:
>>> algobulls_connection.papertrade(strategy_code=strategy_code1, 
        start_time=time(hour=9, minute=15), 
        end_time=time(hour=15, minute=30),
        instrument=instrument, 
        lots=1,
        strategy_parameters={
            'timeperiod1': 5,
            'timeperiod2': 12
        }, 
        candle_interval=CandleInterval.MINUTES_15)

我们得到以下输出:

Setting Strategy Config... Success.
Submitting PAPERTRADING job... Success.
  1. 检查已提交的纸上交易任务的状态:
>>> algobulls_connection.get_papertrading_job_status(
                                                strategy_code1)

我们得到以下输出:

{'data': 'STARTING'}
  1. 再过一段时间后再次检查提交的任务的状态:
>>> algobulls_connection.get_papertrading_job_status(
                                                strategy_code1)

我们得到以下输出:

{'data': 'STARTED'}

工作原理…

步骤 1中,从datetime模块中导入time类以及从pyalgotrading.constants模块中导入所有常量。在步骤 2中,使用algobulls_connection对象的search_instrument()方法获取您想要纸上交易策略的工具,EMA-Regular-Order strategysearch_instrument()方法接受一个搜索字符串作为参数,该参数应该是您感兴趣的工具的交易符号的一部分或全部。这里你传递了'SBIN'。该函数返回一个包含与搜索字符串匹配的工具详细信息的列表。可能有多个工具的交易符号中可能有搜索字符串。在步骤 3中,您获取第一个匹配工具的值并将其分配给一个新变量instrument

步骤 4中,使用algobulls_connection()对象的papertrade()方法提交一个纸上交易任务。它接受以下参数:

  • strategy_code: 要执行纸上交易的策略的策略代码。这应该是一个字符串。你在这里传递了strategy_code1

  • start_time: 今天开始纸上交易的时间。应该是一个datetime.time对象。在这里,你传递了一个包含值为 9 小时 15 分钟的对象 – time(hour=9, minute=15)。请参阅本书第一个配方以了解如何创建time对象的详细信息。

  • end_time: 今天的时间,纸上交易应该执行到什么时候。此对象应该包含一个比start_time所持有的值提前的时间值。应该是一个datetime.time实例。在这里,你传递一个包含值为 15 小时 30 分钟的对象 – time(hour=15, minute=30)

  • instrument: 应该运行纸上交易的金融工具。将为该工具获取历史数据。应该是一个字符串。你在这里传递了instrument

  • lots: 纸上交易应该执行的手数。这应该是一个整数。数量由策略计算为手数×金融工具的手数。你在这里传递了1

  • strategy_parameters: 策略期望的参数名称和值。这应该是一个字典,其中parameter-nameparameter-value作为键值对。你在这里传递以下参数:

  • timeperiod1: 5

  • timeperiod2: 12 

(回想一下,EMA-Regular-Order 策略的参数已经在其__init__()方法中定义,就像第八章的第一个示例中展示的那样,按步骤编写算法交易策略。)

  • candle_interval: 获取用于模拟交易的历史数据的蜡烛图间隔。这应该是一个CandleInterval类型的枚举。你在这里传递CandleInterval.MINUTES_15。(CandleInterval枚举提供各种蜡烛图间隔的枚举,其中一些是MINUTE_1MINUTES_3MINUTES_5MINUTES_10MINUTES_15MINUTES_30HOURDAY。)

如果任务提交成功,你将会看到papertrade()函数打印的Success消息。

一旦提交了任务,它就需要一段时间才能启动。启动后,根据使用start_timeend_time参数指定的模拟交易持续时间,可能需要一些时间才能完成。通常,模拟交易在整个交易日运行,这意味着任务将运行 6-8 小时。

步骤 5 中,你使用algobulls_connection对象的get_papertrading_job_status()方法获取任务状态。你在这里传递strategy_code1作为参数。该方法返回一个带有单个键值对的字典,即datajob状态。如果在放置任务后立即查询状态,你会得到'STARTING'作为状态。在步骤 6 中,你再次查询状态,如果任务已经启动,你会得到状态为'STARTED'

成功的提交意味着已经以所需格式传递了模拟交易策略的最小输入。然而,这并不保证策略会在模拟交易过程中无错误地运行。策略执行仍可能在模拟交易期间遇到错误。要调试执行问题,你需要获取输出日志,这将在下一个示例中解释。导致错误的可能原因可能是策略类 Python 代码中的错误或者传递给papertrade()函数的strategy_parameters字典不完整。

还有更多…

如果一个任务运行时间很长,而你希望在其完成之前停止它,你可以使用algobulls_connection对象的stop_papertrading_job()方法。此方法接受策略代码作为参数。你在这里传递strategy_code1。该方法向 AlgoBulls 模拟交易引擎提交停止请求。如果请求被接受,你会看到一个Success消息:

>>> algobulls_connection.stop_papertrading_job(strategy_code1)
 Stopping PAPERTRADING job... Success.

如果在提交停止请求后再次查询状态,你会得到状态为'STOPPING'

>>> algobulls_connection.get_papertrading_job_status(strategy_code1)
{'data': 'STOPPING'}

如果一段时间后再次查询状态,如果任务已经停止,你会得到状态为'STOPPED'

>>> algobulls_connection.get_papertrading_job_status(strategy_code1)
{'data': 'STOPPED'} 

EMA-Regular-Order 策略 - 实时获取模拟交易日志

在 AlgoBulls 平台上提交模拟交易作业后,AlgoBulls 模拟交易引擎开始执行策略。在执行过程中,AlgoBulls 模拟交易引擎记录发生的每一个事件和所采取的决策,并以文本日志的形式记录具有精确时间戳。一些记录的活动示例包括给定策略配置、定期间隔生成的每个新蜡烛、您的策略打出的交易、这些交易创建的持仓的进入和退出、等待新蜡烛等。这些日志在验证策略行为和调试在开发策略时经常遇到的行为或性能问题方面至关重要。

在这个配方中,你将获取你的策略的模拟交易日志。这些日志会在你提交的模拟交易作业达到'STARTED'状态时立即出现(有关模拟交易作业状态的更多信息,请参阅前面的配方)。AlgoBulls 平台允许你实时获取日志,即使模拟交易作业仍在进行中。你可以在无需等待模拟交易作业完成的情况下了解策略执行情况。这对于模拟交易作业通常是长时间运行的情况非常有帮助。pyalgotrading包提供了一个简单的方法来获取给定策略的执行日志。

确保你已经阅读了第八章的前六个配方,算法交易策略 - 逐步编码,以获取使用的完整策略类StrategyEMARegularOrder的完整图片。

准备就绪

确保algobulls_connectionstrategy_code1对象在你的 Python 命名空间中可用。请参阅本章的第一个配方,设置algobulls_connectionstrategy_code1对象。

如何实现…

执行以下步骤以完成此配方:

  1. 获取strategy_code1的模拟交易执行日志:
>>> logs = algobulls_connection.get_papertrading_logs(
                                              strategy_code1)
>>> print(logs)

我们获得以下输出(您的输出可能不同):

[2020-07-09 09:12:18] Logs not available yet. Please retry in sometime.
  1. 过一段时间后,再次获取strategy_code1的模拟交易执行日志:
>>> logs = algobulls_connection.get_papertrading_logs(
                                               strategy_code1)
>>> print(logs)

我们获得以下输出(您的输出可能不同):

...
########################################
 INITIALIZING ALGOBULLS CORE (v3.2.0 SECURE MODE)... 
########################################
[2020-07-09 09:12:31] Welcome ALGOBULLS VIRTUAL USER!
[2020-07-09 09:12:31] Reading strategy…
…
[PT] [2020-07-09 09:15:00] [INFO] [tls] STARTING ALGOBULLS CORE…
...
[PT] [2020-07-09 10:30:00] [CRITICAL] [order] [PLACING NEW ORDER] [2020-07-09 10:30:00] [96c24ca4b3e448f381fc5c2bc52f7a29] [BUY] [NSE:SBIN] [QTY:1] [QTY PENDING: 1] [ENTRY PRICE: 194.7] [PRICE:None] [TRIGGER PRICE:None] [ORDER_TYPE_REGULAR] [ORDER_CODE_INTRADAY] [ORDER_VARIETY_MARKET] [ORDER_POSITION_ENTER]
…
[PT] [2020-07-09 15:30:00] [INFO] [clock] Candle generation has been stopped...
[PT] [2020-07-09 15:30:00] [INFO] [tls] Received event END OF MARKET. Stopping Trading Core Engine...
[PT] [2020-07-09 15:30:00] [INFO] [tls] Exiting all open positions with order code: ORDER_CODE_INTRADAY (if any)...
[PT] [2020-07-09 15:30:00] [CRITICAL] [tls] [User: ALGOBULLS VIRTUAL USER] Trading session completed
...

这里没有显示完整的输出。请访问以下链接阅读完整的输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/sample/papertrading/strategy_ema_regular_order/logs.txt

工作原理…

第 1 步,你使用 algobulls_connection 对象的 get_papertrading_logs() 方法实时获取策略 paper trading 日志。此方法接受策略代码作为参数。你在这里传递了 strategy_code1。返回的数据是一个字符串。如果在提交作业后立即尝试此步骤,你会得到一个字符串,表示日志尚未准备好([2020-07-09 09:14:18] Logs not available yet. Please retry in sometime.)。如果 paper trading 作业处于 'STARTING' 状态,则会发生这种情况。

第 2 步,你会在一段时间后再次获取日志。如果作业不再处于 'STARTING' 状态,你就开始获取策略执行日志。每次调用 get_papertrading_logs() 函数时,你都会获取整个 paper trading 日志。

还有更多...

一旦 paper trading 作业转移到 'STOPPED' 状态,就不会生成新的日志。你可以在提交下一个相同策略的 paper trading 作业之前的任何时候获取完整的日志。如果提交了新的 paper trading 作业(针对相同的策略),则无法再通过 get_papertrading_logs() 方法访问这些日志。如果你想在以后参考它,可以将获取的日志保存到文件中。

EMA-Regular-Order 策略 – 获取 paper trading 报告 – 盈亏表

在 AlgoBulls 平台提交 paper trading 作业后,AlgoBulls paper trading 引擎开始执行策略。在执行过程中,除了日志之外,AlgoBulls paper trading 引擎还实时生成 P&L 表。这张表记录了策略打出的每一笔交易信息。它还有关于入场和出场订单以及交易 P&L 和累计 P&L 之间的映射的详细信息,按时间顺序排序,最新的订单排在最前面。这张表通过个别和累计 P&L 数字提供了对整体策略表现的洞察。入场-出场订单映射也有助于验证策略行为。

在这个示例中,你将获取你的策略的 P&L 表报告。只要提交 paper trading 作业后,你的策略打出第一笔交易,这份报告就会可用。AlgoBulls 平台允许你实时获取 P&L 表,即使 paper trading 作业仍在进行中。这样可以让你在 paper trading 作业完成之前就了解策略的表现。这对于 paper trading 作业通常需要很长时间的情况很有帮助。pyalgotrading 包提供了一种简单的方法来获取给定策略的 P&L 表。

确保你已经通过了 第八章 的前六个示例,算法交易策略 – 逐步编码,以完全了解所使用的策略类 StrategyEMARegularOrder

准备工作

确保 algobulls_connectionstrategy_code1 对象在你的 Python 命名空间中可用。参考本章的第一个示例设置 algobulls_connectionstrategy_code1 对象。

如何实现...

获取strategy_code1的模拟交易 P&L 报告:

>>> algobulls_connection.get_papertrading_report_pnl_table(strategy_code1)

我们获得以下输出。你的输出可能会有所不同(请注意以下输出已分成多个表格以表示,你在 Jupyter 笔记本中将看到单个宽表):

工作原理...

在本示例中,你使用 algobulls_connection 对象的 get_papertrading_report_pnl_table() 方法实时获取模拟交易 P&L 表。此方法接受策略代码作为参数。你在这里传递了 strategy_code1。返回数据是一个 pandas.DataFrame 对象,具有多列,描述如下:

  • instrument: 进行交易的金融工具。

  • entry_timestamp: 下达进入订单的时间戳。(注意它可能在进入 'COMPLETE' 状态之前保持 'OPEN' 状态一段时间。可以使用订单历史表(本章第六个示例中解释)找到此状态转换的时间。)

  • entry_transaction_type: 进入订单的交易类型(可以是 BUYSELL)。

  • entry_quantity: 进入订单数量。

  • entry_price: 进入订单执行并进入 'COMPLETE' 状态的价格。

  • exit_timestamp: 下达退出订单的时间戳。(注意它可能在进入 'COMPLETE' 状态之前保持 'OPEN' 状态一段时间。)

  • exit_transaction_type: 退出订单的交易类型(可以是 BUYSELL)。

  • exit_quantity: 退出订单数量。

  • exit_price: 退出订单执行并进入 'COMPLETE' 状态的价格。

  • pnl_absolute: 退出订单执行价格与进入订单执行价格之间的差异。数学上,这是(exit_price - entry_priceexit_quantity* (长买)或(entry_price - exit_priceexit_quantity*(短卖)。正值意味着交易盈利,负值意味着交易亏损。

  • pnl_percentage: 相对于进入价格的利润或损失的百分比。数学上,这是 pnl_absolute / entry_price / exit_quantity x 100

  • pnl_cumulative_absolute: 累计利润或损失。数学上,这是前期交易的所有 pnl_absolute 值的总和。此数字直接反映了策略相对于模拟时间的表现。

  • pnl_cumulative_percentage: 相对于进入价格的累计利润或损失的百分比。数学上,这是 pnl_cumulative / entry_price / exit_quantity x 100

还有更多...

一旦模拟交易任务转移到“停止”状态,P&L 表报告将不再更新。您可以在提交下一个相同策略的模拟交易任务之前随时获取完整的 P&L 报告。如果提交了新的模拟交易任务(针对相同策略),则将无法通过get_papertrading_report_pnl_table()方法访问此报告。如果您想以后参考它,可以将获取的报告保存为.csv文件。

EMA-Regular-Order 策略 – 获取模拟交易报告 – 统计表

在 AlgoBulls 平台提交模拟交易任务后,AlgoBulls 模拟交易引擎开始执行策略。在执行过程中,除了日志和 P&L 表外,AlgoBulls 模拟交易引擎还实时从 P&L 表生成摘要。此摘要是包含各种统计数字的统计表,例如净 P&L(绝对值和百分比)、最大回撤(绝对值和百分比)、总交易次数、盈利交易次数、亏损交易次数、多头交易次数和空头交易次数、最大盈利和最小盈利(或最大亏损),以及每次盈利和亏损交易的平均利润。此表提供了对整体策略绩效的即时概览。

在本示例中,您将获取策略的统计表报告。在提交模拟交易任务后,您的策略打出第一笔交易后,此报告即可获得。AlgoBulls 平台允许您实时获取统计表,即使模拟交易任务仍在进行中。您可以在等待模拟交易任务完成之前了解策略绩效。这对于长时间运行的模拟交易任务非常有帮助。pyalgotrading包提供了一种简单的方法来获取给定策略的统计表。

确保您已经阅读了第八章中的前六个示例,算法交易策略 – 逐步编码,以完整了解使用的策略类StrategyEMARegularOrder

准备工作

确保algobulls_connectionstrategy_code1对象在您的 Python 命名空间中可用。请参考本章的第一个示例设置algobulls_connectionstrategy_code1对象。

如何做…

获取strategy_code1的模拟交易统计报告:

>>> algobulls_connection.get_papertrading_report_statistics(strategy_code1)

我们得到以下输出(您的输出可能不同):

工作原理…

在本示例中,您将使用algobulls_connection对象的get_papertrading_report_statistics()方法实时获取模拟交易统计表。此方法接受策略代码作为参数。在这里,您传递strategy_code1。返回数据是一个pandas.DataFrame对象,具有两列—highlight_typehighlight_value—以及多行。行描述如下:

  • 净盈亏:累积纸交易盈亏。这也是 P&L 表中第一个条目的pnl_cumulative_absolute值。

  • 净盈亏百分比:累积纸交易盈亏百分比。这也是 P&L 表中第一个条目的pnl_cumulative_percentage值。

  • 最大回撤:P&L 表中pnl_cumulative列中的最低值。这表示您的策略在执行过程中遇到的最大损失。

  • 最大回撤百分比:在数学上,这是(最大回撤) / (相应的入场价格/退出数量 x 100)

  • 交易次数:会话期间的总交易数(入场和退出计为一次)。

  • 盈利次数:交易盈亏为非负数的次数。

  • 亏损次数:交易中交易盈亏为负的次数。

  • 多头交易次数:入场交易类型为'BUY'的交易次数。

  • 空头交易次数:入场交易类型为'SELL'的交易次数。

  • 最大获利:所有交易中 P&L 值最大的交易的 P&L。

  • 最小获利:所有交易中 P&L 值最小的交易的 P&L。

  • 每笔盈利交易的平均利润:在数学上,这是(盈利交易的总 P&L) / (盈利交易的数量)

  • 每笔亏损交易的平均利润:在数学上,这是(亏损交易的总 P&L) / (亏损交易的数量)

还有更多...

如果在纸交易工作仍在运行时获取统计表,则上述数字将是中间数字,基于到达该时间的交易完成情况。随着更多交易被打入,这些数字可能会发生变化,直到纸交易工作完成。

一旦纸张交易工作进入'STOPPED'状态,统计表将不再更改。您可以在提交下一个相同策略的纸交易工作之前的任何时间获取完整的统计表。如果提交了新的纸交易工作(用于相同策略),则将无法通过get_papertrading_report_statistics()方法访问此表。如果您希望稍后参考它,可以将获取的报告表保存到.csv文件中。

EMA-Regular-Order 策略 – 获取纸交易报告 – 订单历史

在 AlgoBulls 平台上提交了一个模拟交易任务后,AlgoBulls 模拟交易引擎开始执行该策略。在执行过程中,除了日志、损益表和统计表之外,AlgoBulls 模拟交易引擎还会实时生成一个订单历史记录。该记录包含每个订单的状态转换,以及每个订单状态的时间戳和附加信息(如果有)。订单历史记录对于理解一个交易从'OPEN''COMPLETE''CANCELLED'状态所需的时间非常重要。例如,MARKET订单会立即从'OPEN'状态转移到'COMPLETE'状态,但LIMIT订单可能需要一段时间,根据市场条件,才能从'OPEN'状态转移到'COMPLETE'状态,或者甚至可能转移到'CANCELLED'状态。所有这些信息都包含在订单历史记录中。(有关订单状态转换的更多信息,请参考第六章的状态机图。)

在这个示例中,你将获取你的策略的订单历史记录。这个记录在你提交一个模拟交易任务后,你的策略进行了第一笔交易后就可以获取。AlgoBulls 平台允许你实时获取订单历史记录,即使模拟交易任务仍在进行中。这有助于我们在等待模拟交易任务完成之前获取订单的最终状态的详细信息。pyalgotrading包提供了一种简单的方法来获取给定策略的订单历史记录。

确保你已经阅读了第八章的前六个示例,算法交易策略 - 逐步编码,以获得所使用的策略类StrategyEMARegularOrder的完整图片。

准备工作

确保你的 Python 命名空间中有algobulls_connectionstrategy_code1对象。参考本章的第一个示例来设置algobulls_connectionstrategy_code1对象。

如何做…

获取strategy_code1的模拟交易订单历史报告:

>>> order_history = \
        algobulls_connection.get_papertrading_report_order_history(
                                                           strategy_code1)

我们获得以下输出。你的输出可能会有所不同:

+-------------+---------------------+----------------------------------+------+
| INST        | TIME                | ID                               | TT   |
|-------------+---------------------+----------------------------------+------|
| NSE_EQ:SBIN | 2020-07-09 10:30:00 | 96c24ca4b3e448f381fc5c2bc52f7a29 | BUY  |
+-------------+---------------------+----------------------------------+------+
+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-09 10:30:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-09 10:30:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-09 10:30:00 | OPEN PENDING           |       |
|  3 | 2020-07-09 10:30:00 | OPEN                   |       |
|  4 | 2020-07-09 10:30:00 | COMPLETE               |       |
+----+---------------------+------------------------+-------+
+-------------+---------------------+----------------------------------+------+
| INST        | TIME                | ID                               | TT   |
|-------------+---------------------+----------------------------------+------|
| NSE_EQ:SBIN | 2020-07-09 10:45:00 | 3bbd433edd004630b122de07873864d7 | SELL |
+-------------+---------------------+----------------------------------+------+
+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-09 10:45:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-09 10:45:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-09 10:45:00 | OPEN PENDING           |       |
|  3 | 2020-07-09 10:45:00 | OPEN                   |       |
|  4 | 2020-07-09 10:45:00 | COMPLETE               |       |
+----+---------------------+------------------------+-------+
...

完整输出未在此处显示。请访问以下链接以阅读完整输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/sample/papertrading/strategy_ema_regular_order/oms_order_history.log

工作原理…

在这个示例中,你可以使用algobulls_connection对象的get_papertrading_report_order_history()方法来实时获取订单历史记录。这个方法接受策略代码作为参数。这里传递了strategy_code1。返回的数据是一个字符串,描述如下:

对于每个订单,记录中都包含以下信息:

  • 一个关于订单的描述性表格,具有以下列:

  • INST:订单的金融工具

  • TIME:订单下单的时间

  • ID:订单的唯一 ID

  • TT:订单交易类型(BUYSELL

表格的一个示例如下所示:

+-------------+---------------------+----------------------------------+------+
| INST        | TIME                | ID                               | TT   |
|-------------+---------------------+----------------------------------+------|
| NSE_EQ:SBIN | 2020-07-09 10:30:00 | 96c24ca4b3e448f381fc5c2bc52f7a29 | BUY  |
+-------------+---------------------+----------------------------------+------+

此信息将帮助您在策略执行日志中找到该订单。

  • 一个订单状态转换表,具有以下列:

  • TIME:订单在 'STATE' 列所表示的状态中出现的时间。

  • STATE:订单在 'TIME' 列中提到的时间进入此状态。

  • MSG:来自 OMS 的任何意外状态转换的额外消息。例如,进入 REJECTED 状态的订单会有来自 OMS 的说明其被拒绝的原因的消息。此列通常为空。

表格的一个示例如下所示:

+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-09 10:30:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-09 10:30:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-09 10:30:00 | OPEN PENDING           |       |
|  3 | 2020-07-09 10:30:00 | OPEN                   |       |
|  4 | 2020-07-09 10:30:00 | COMPLETE               |       |
+----+---------------------+------------------------+-------+

从这个表格中,您可以看到在上午 10:30 下单后,订单立即转移到 'COMPLETE' 状态。这是预期的,因为订单是常规市价订单。(有关常规市价订单的更多详细信息,请参阅第六章的第一个配方,在交易所上下单。)

还有更多内容……

一旦 paper trading 任务转移到 'STOPPED' 状态,就不会生成新的订单历史记录日志。您可以在提交下一个相同策略的 paper trading 任务之前随时获取完整的订单历史记录日志。如果提交了新的 paper trading 任务(用于相同的策略),则无法通过 get_papertrading_report_order_history() 方法访问这些日志。如果您希望稍后参考,可以将获取的日志保存到文件中。

MACD-Bracket-Order 策略 - 获取策略

在此配方中,您将从 AlgoBulls 平台上的帐户中获取策略类 StrategyMACDBracketOrder,您必须在学习第八章的最后一个配方时上传该策略类。该配方从建立到 AlgoBulls 平台的连接开始,查询您帐户中所有可用的策略,然后获取所需的策略类 StrategyMACDBracketOrder 的详细信息。

确保您已经学习了第八章的最后六个配方,算法交易策略 - 逐步编码,以完全了解所使用的策略类 StrategyMACDBracketOrder

怎么做……

我们为这个配方执行以下步骤:

  1. 导入必要的模块:
>>> from pyalgotrading.algobulls import AlgoBullsConnection
  1. 创建一个新的 AlgoBulls 连接对象:
>>> algobulls_connection = AlgoBullsConnection()
  1. 获取授权 URL:
>>> algobulls_connection.get_authorization_url()

我们获得以下输出:

Please login to this URL with your AlgoBulls credentials and get your developer access token: https://app.algobulls.com/user/login
'https://app.algobulls.com/user/login'
  1. 使用您的 AlgoBulls 凭据登录到上述链接,获取您的令牌,并在此处设置它(有关更多详细信息,请参阅《附录 II》):
>>> algobulls_connection.set_access_token(
                    '80b7a69b168c5b3f15d56688841a8f2da5e2ab2c')
  1. 检索并显示你目前已经创建并上传的所有策略:
>>> all_strategies = algobulls_connection.get_all_strategies()
>>> all_strategies

我们得到以下输出。你的输出可能会有所不同(确保您已经遵循了《第八章》中的配方,算法交易策略-逐步编码,以获得类似的输出):

  1. 检索并显示第二个策略 MACD-Bracket-Order 策略的策略代码:
>>> strategy_code2 = all_strategies.iloc[1]['strategyCode']
>>> strategy_code2

我们得到以下输出(你的输出可能会有所不同):

'49287246f9704bbcbad76ade9e2091d9'
  1. 在模拟交易您的策略之前,您可以检查您的策略以确保您选择了正确的策略:
>>> strategy_details2 = \
        algobulls_connection.get_strategy_details(strategy_code2)
>>> print(strategy_details2)

我们得到以下输出:

class StrategyMACDBracketOrder(StrategyBase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fastMA_period = \
            self.strategy_parameters['fastma_period']
        self.slowMA_period = \
            self.strategy_parameters['slowma_period']
        self.signal_period = \
            self.strategy_parameters['signal_period']
        self.stoploss = \
            self.strategy_parameters['stoploss_trigger']
        self.target = self.strategy_parameters['target_trigger']
        self.trailing_stoploss = \
            self.strategy_parameters['trailing_stoploss_trigger']

        self.main_order = None

    def initialize(self):
        self.main_order = {}

    @staticmethod
    def name():
        return 'MACD Bracket Order Strategy'
    ….
    def strategy_exit_position(self, candle, instrument, 
                               sideband_info):
        if sideband_info['action'] == 'EXIT':
            self.main_order[instrument].exit_position()
            self.main_order[instrument] = None
            return True

        return False

此处未显示完整输出。请访问以下链接以阅读完整输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/strategy_macd_bracket_order.py

工作原理…

第 1 步中,您导入所需的模块。在第 2 步中,您创建了AlgoBullsConnection类的一个实例,命名为algobulls_connection。在第 3 步中,您使用algobulls_connection对象的get_authorization_url()方法获取授权 URL。这将打印授权 URL。您应该从您的网络浏览器访问此 URL 以登录到 AlgoBulls 平台并获取您的开发者访问令牌。 (您可以在《附录 II》中的屏幕截图中找到有关从 AlgoBulls 平台获取开发者访问令牌的更多详细信息。)您复制访问令牌,并在第 4 步中使用algobulls_connectionset_access_token()方法设置它。如果令牌被接受,则与 AlgoBulls 平台建立了成功的连接。

第五步中,您将检索到目前在 AlgoBulls 平台上创建并上传的所有策略。您可以使用get_all_strategies()方法来执行此步骤,并将其分配给一个新变量all_strategies。此变量是一个pandas.DataFrame对象,具有strategyCodestrategyName两列。此表格包含了您之前上传的策略代码和策略名称的信息。如果您遵循了来自第八章《算法交易策略-逐步编码》的《MACD-Bracket-Order 策略 - 将策略上传到 AlgoBulls 交易平台》的配方,您会发现一个名为MACD-Regular-Order 策略的策略。在第 6 步中,您将MACD-Regular-Order 策略策略的策略代码分配给一个新变量strategy_code2。此步骤的输出显示了策略代码。此策略代码对于 AlgoBulls 平台上的每个策略都是唯一的。

最后,在步骤 7 中,您确保 strategy_code2 引用的策略确实是您之前上传的策略(在第八章的最后一个配方中,算法交易策略 - 逐步编码)。您使用 algobulls_connection 对象的 get_strategy_details() 方法来检查策略。该方法以策略代码作为参数。您在此处传递 strategy_code2。该方法将整个类代码作为字符串返回。您将其赋值给一个新变量 strategy_details2,并显示它。

如果您想更改由 strategy_code2 引用的类代码,就像步骤 7 中所示,请参考第八章的最后一个配方的更多内容部分,算法交易策略 - 逐步编码

MACD-Bracket-Order 策略 - 模拟交易策略

在本配方中,您将对 MACD-Bracket-Order strategy 策略进行模拟交易。您必须在本章的前一个配方中从 AlgoBulls 平台的您的帐户中获取此策略。您将利用 pyalgotrading 提供的模拟交易功能来执行此配方,该功能进而在 AlgoBulls 平台上提交一个模拟交易任务。

一旦提交,模拟交易将由 AlgoBulls 模拟交易引擎运行。您可以随时查询状态以了解模拟交易任务的状态。该任务按以下给定顺序经历以下状态:

  • 'STARTING'(中间状态)

  • 'STARTED'(稳定状态)

  • 'STOPPING'(中间状态)

  • 'STOPPED'(稳定状态)

在提交任务时,它从一个中间状态 'STARTING' 开始。在这种状态下,AlgoBulls 模拟交易引擎将获取策略并准备执行环境,这可能需要几分钟。完成后,任务转移到 'STARTED' 状态。模拟交易策略在此阶段发生。在此期间,它会保持不变,直到模拟交易完成。完成后,任务转移到一个中间状态 'STOPPING'。在这种状态下,AlgoBulls 模拟交易引擎清理为此任务分配的资源,通常需要不到一分钟。最后,任务移动到 'STOPPED' 状态。

如果您已经为一种策略提交了一个模拟交易任务,您就不能再为同一策略提交另一个任务,直到第一个任务完成为止。这意味着您必须等待第一个任务转移到 'STOPPED' 状态。如果第一个任务运行时间较长,而您希望立即停止它,您可以通过 pyalgotrading 提交一个停止任务请求。在提交请求之前,您需要确保任务处于 'STARTED' 状态。

在提交模拟交易任务后,您可以实时获取有关策略执行的日志和报告。这些日志和报告有助于验证策略的性能并调试任何潜在问题。

你可以参考本章的第二个配方,了解 AlgoBulls 平台上一份模拟交易任务的状态机图。它展示了一份模拟交易任务在其生命周期中的各种状态和转换。

确保你已经阅读完第八章的最后六个配方,逐步编码算法交易策略,以全面了解使用的策略类StrategyMACDBracketOrder

准备就绪

确保algobulls_connectionstrategy_code2 对象在你的 Python 命名空间中可用。请参考本章的MACD-Bracket-Order 策略 - 获取策略配方来设置 algobulls_connectionstrategy_code2 对象。

如何操作…

我们执行以下步骤来完成此配方:

  1. 导入必要的模块:
>>> from datetime import time
>>> from pyalgotrading.constants import *
  1. 使用其交易代码作为关键字搜索仪器。将返回的对象分配给 instruments
>>> instrument = algobulls_connection.search_instrument(
                                                    'TATASTEEL')
>>> instrument

我们得到以下输出(你的输出可能不同):

[{'id': 1, 'value': 'NSE:TATASTEEL'}]
  1. instruments 获取选择仪器的value
>>> instrument = instrument[0]['value']
>>> instrument

我们得到以下输出:

'NSE:TATASTEEL'
  1. 提交一个 strategy_code2 的模拟交易任务:
>>> algobulls_connection.papertrade(
        strategy_code=strategy_code2, 
        start_time=time(hour=9, minute=15),
        end_time=time(hour=15, minute=30),
        instrument=instrument,
        lots=1,
        strategy_parameters={
            'fastma_period': 26, 
            'slowma_period': 6, 
            'signal_period': 9,
            'target_trigger': 0.01, 
            'stoploss_trigger': 0.01, 
            'trailing_stoploss_trigger': 1
        },
        candle_interval=CandleInterval.MINUTES_15)

我们得到以下输出:

Setting Strategy Config... Success.
Submitting PAPERTRADING job... Success.
  1. 检查已提交的模拟交易任务的状态:
>>> algobulls_connection.get_papertrading_job_status(
                                                strategy_code2)
{'data': 'STARTING'}
  1. 再过一段时间后再次检查提交的模拟交易任务的状态:
>>> algobulls_connection.get_papertrading_job_status(
                                                strategy_code2)
{'data': 'STARTED'} 

工作原理如下…

步骤 1 中,你从 datetime 模块导入 time 类和pyalgotrading.constants 模块中的所有常量。在 步骤 2 中,你使用 algobulls_connection 对象的 search_instrument() 方法获取你想要进行策略模拟交易的仪器,即 MACD-Bracket-Order strategysearch_instrument() 方法接受一个搜索字符串作为参数,这个字符串应该是你感兴趣的仪器的交易代码的部分或完整代码。在这里你传递了 'TATASTEEL'。这个函数返回一个包含与搜索字符串匹配的仪器详情的列表。可能有多个仪器的交易代码中包含搜索字符串。在 步骤 3 中,你获取第一个匹配仪器的值,并将其分配给一个新变量,instrument

步骤 4 中,你使用 algobulls_connection() 对象的 papertrade() 方法提交了一个模拟交易任务。它接受以下参数:

  • strategy_code:要执行模拟交易的策略的策略代码。应该是一个字符串。你在这里传递了 strategy_code2

  • start_time:应该开始模拟交易的今天时间。应该是一个datetime.time对象。在这里,你传递一个持有 9 小时 15 分钟值的对象 - time(hour=9, minute=15)。有关创建time对象的详细信息,请参考本书的第一个配方。

  • end_time:应该执行模拟交易的今天时间。这个对象应该是 start_time 持有的值之后的时间值。应该是一个 datetime.time 实例。在这里,你传递一个持有 15:30 小时值的对象 - time(hour=15, minute=30)

  • instrument: 应运行纸张交易的金融工具。将获取此工具的历史数据。应为字符串。你在此处传递instrument

  • lots: 应执行纸张交易的批次数。应为整数。数量由策略计算为批次数 × 金融工具的批次大小。你在此处传递1

  • strategy_parameters: 策略期望的参数名称和值。应为字典,带有parameter-nameparameter-value作为键值对。你在此处传递以下参数:

  • fastma_period: 26

  • slowma_period: 6

  • signal_period: 9

  • target_trigger: 0.01

  • stoploss_trigger: 0.01

  • trailing_stoploss_trigger: 1

(回想一下 MACD-Bracket-Order 策略的参数已在其__init__()方法中定义,如第八章的第一个示例所示,算法交易策略 - 逐步编码)。

  • candle_interval: 用于获取用于纸张交易的历史数据的蜡烛图间隔。应为CandleInterval类型的枚举。你在此处传递CandleInterval.MINUTES_15。(CandleInterval枚举提供各种蜡烛图间隔的枚举,其中一些是MINUTE_1MINUTES_3MINUTES_5MINUTES_10MINUTES_15MINUTES_30HOURDAY)。

如果作业提交成功,则papertrade()函数将打印Success消息。

提交作业后,启动需要一段时间。启动后,根据使用start_timeend_time参数指定的纸张交易持续时间的长短,完成可能需要一些时间。通常,纸张交易运行整个交易日,这意味着作业将运行 6-8 小时。

步骤 5中,您使用algobulls_connection对象的get_papertrading_job_status()方法获取作业状态。你在此处传递strategy_code2作为参数。此方法返回一个带有单个键值对的字典,数据作业状态。如果在放置作业后立即查询状态,则会得到'STARTING'作为状态。在步骤 6中,您再次查询状态,如果作业已经启动,则会得到状态为'STARTED'

成功提交意味着已以所需格式传递了用于纸张交易策略的最小输入。但是,这并不保证策略在纸张交易期间不会出现错误。策略执行在纸张交易期间仍可能遇到错误。要调试执行问题,您需要获取输出日志,下一节将对此进行解释。错误的可能原因可能是策略类 Python 代码中的错误或传递给papertrade()函数的strategy_parameters字典不完整。

还有更多...

如果作业运行时间很长,并且您希望在完成之前停止它,您可以使用algobulls_connection对象的stop_papertrading_job()方法。此方法接受策略代码作为参数。这里传递了strategy_code2。此方法向 AlgoBulls 模拟交易引擎提交停止请求。如果请求被接受,您将在此处看到一个Success消息:

>>> algobulls_connection.stop_papertrading_job(strategy_code2)
 Stopping PAPERTRADING job... Success.

如果在提交停止请求后查询状态,则状态为'STOPPING'

>>> algobulls_connection.get_papertrading_job_status(strategy_code2)
{'data': 'STOPPING'}

如果过一段时间后再次查询状态,如果作业已停止,则状态为'STOPPED'

>>> algobulls_connection.get_papertrading_job_status(strategy_code2)
{'data': 'STOPPED'} 

MACD-Bracket-Order 策略 – 实时获取模拟交易日志

在 AlgoBulls 平台上提交模拟交易作业后,AlgoBulls 模拟交易引擎开始执行策略。在执行过程中,AlgoBulls 模拟交易引擎记录了发生的每个事件以及采取的决策,以确切的时间戳形式记录在文本日志中。记录的活动示例包括给定策略配置,定期生成的每个新蜡烛,您的策略打出的交易,这些交易创建的持仓的进入和退出,等待新蜡烛等。这些日志在验证策略行为和调试开发策略时经常遇到的行为或性能问题方面至关重要。

在本配方中,您将为您的策略获取模拟交易日志。一旦您提交的模拟交易作业达到'STARTED'状态(有关模拟交易作业状态的更多信息,请参考前面的配方),日志就会开始出现。AlgoBulls 平台允许您实时获取日志,即使模拟交易作业仍在进行中。您可以在不等待模拟交易作业完成的情况下了解策略执行情况,这在作业运行时间较长时非常有用。pyalgotrading包提供了一个简单的方法来获取给定策略的执行日志。

确保您已经阅读了第八章的最后六个配方,算法交易策略 – 逐步编码,以获得使用的完整策略类StrategyMACDBracketOrder的完整图片。

准备工作

确保algobulls_connectionstrategy_code2对象在你的 Python 命名空间中可用。参考本章的 MACD-Bracket-Order 策略 – 获取策略 配方来设置algobulls_connectionstrategy_code2对象。

如何做…

我们执行以下步骤来完成此配方:

  1. 获取strategy_code2的模拟交易执行日志:
>>> logs = algobulls_connection.get_papertrading_logs(
                                                strategy_code2)
>>> print(logs)

我们得到以下输出(你的输出可能会有所不同):

[2020-07-09 09:14:12] Logs not available yet. Please retry in sometime.
  1. 过一段时间后再次获取strategy_code2的模拟交易执行日志:
>>> logs = algobulls_connection.get_papertrading_logs(
                                                strategy_code2)
>>> print(logs)

我们得到以下输出(你的输出可能会有所不同):

...
########################################
 INITIALIZING ALGOBULLS CORE (v3.2.0)... 
########################################
…
[PT] [2020-07-09 09:15:00] [INFO] [tls] STARTING ALGOBULLS CORE…
…
[PT] [2020-07-09 09:45:00] [CRITICAL] [order] [PLACING NEW ORDER] [2020-07-09 09:45:00] [a310755e3d8b4a1ab4667882bf25751d] [BUY] [NSE:TATASTEEL] [QTY:1] [QTY PENDING: 1] [ENTRY PRICE: 345.0] [PRICE:345.0] [TRIGGER PRICE:None] [ORDER_TYPE_BRACKET] [ORDER_CODE_INTRADAY] [ORDER_VARIETY_LIMIT] [ORDER_POSITION_ENTER] [STOPLOSS TRIGGER:341.55] [TARGET TRIGGER:348.45] [TRAILING STOPLOSS TRIGGER:345.0]
...
[PT] [2020-07-09 15:30:00] [INFO] [clock] Candle generation has been stopped...
[PT] [2020-07-09 15:30:00] [INFO] [tls] Received event END OF MARKET. Stopping Trading Core Engine...
[PT] [2020-07-09 15:30:00] [INFO] [tls] Exiting all open positions with order code: ORDER_CODE_INTRADAY (if any)...
[PT] [2020-07-09 15:30:00] [CRITICAL] [tls] [User: ALGOBULLS VIRTUAL USER] Trading session completed
…

此处未显示完整输出。请访问以下链接阅读完整输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/sample/papertrading/strategy_macd_bracket_order/logs.txt

如何运作…

第一步中,您使用algobulls_connection对象的get_papertrading_logs()方法实时获取策略的模拟交易日志。此方法接受策略代码作为参数。您在这里传递了strategy_code2。返回的数据是一个字符串。如果在提交作业后立即尝试此步骤,您将获得一个字符串,该字符串表示日志尚未准备好([2020-07-09 09:14:12] 日志尚未准备好,请稍后重试。)。如果模拟交易任务处于'STARTING'状态,就会发生这种情况。

第二步中,您再次经过一段时间后重新获取日志。如果任务处于'STARTING'状态之外,您将开始获取您的策略执行日志。每次调用get_papertrading_logs()函数时,您都会获得整个模拟交易日志。

更多内容…

一旦模拟交易任务转移到'STOPPED'状态,就不会生成新的日志。您可以在提交下一个相同策略的模拟交易任务之前的任何时候获取完整的日志。如果提交了新的模拟交易任务(针对相同的策略),则无法通过get_papertrading_logs()方法再访问这些日志。如果您希望稍后参考它,可以将获取的日志保存到文件中。

MACD-Bracket-Order 策略 - 获取模拟交易报告 - 盈亏表

在 AlgoBulls 平台上提交模拟交易任务后,AlgoBulls 模拟交易引擎开始执行策略。在执行过程中,随着日志一起,AlgoBulls 模拟交易引擎还实时生成了一张 P&L 表。该表包含策略打出的每笔交易的信息。它还包括了入场和出场订单以及交易 P&L 和累计 P&L 之间的映射细节,按时间顺序排列,最新订单排在最前面。该表通过个人和累计 P&L 数字提供了对整体策略表现的洞察。入场-出场订单映射还有助于验证策略行为。

在此示例中,您将获取策略的 P&L 表报告。只要提交模拟交易任务后您的策略打出了第一笔交易,此报告就可用。AlgoBulls 平台允许您实时获取 P&L 表,即使模拟交易任务仍在进行中。您无需等待模拟交易任务完成即可了解策略表现。这对于模拟交易任务通常需要长时间运行很有帮助。pyalgotrading包提供了一个简单的方法来获取给定策略的 P&L 表。

确保你已经阅读了第八章的最后六个示例,算法交易策略-逐步编码,以完整了解使用的策略类,StrategyMACDBracketOrder

准备工作

确保algobulls_connectionstrategy_code2对象在你的 Python 命名空间中可用。参考本章的MACD-Bracket-Order 策略-获取策略示例来设置algobulls_connectionstrategy_code2对象。

如何操作...

获取strategy_code2的模拟交易盈亏报告:

>>> algobulls_connection.get_papertrading_report_pnl_table(strategy_code2)

我们得到以下输出。你的输出可能不同(请注意,以下输出已被拆分为多个表以进行表示。你将在你的 Jupyter 笔记本中看到一个单独的宽表):

工作原理...

在这个示例中,你将使用algobulls_connection对象的get_papertrading_report_pnl_table()方法实时获取模拟交易的盈亏表。该方法接受策略代码作为参数。你在这里传递了strategy_code2。返回的数据是一个pandas.DataFrame对象,具有多列,描述如下:

  • instrument: 进行交易的金融工具。

  • entry_timestamp: 进入订单放置的时间戳。(请注意,它可能在进入'COMPLETE'状态之前保持'OPEN'状态一段时间。此状态转换的时间可以在本章的EMA-Regular-Order 策略-获取模拟交易报告-订单历史示例中的订单历史表中找到。)

  • entry_transaction_type: 进场订单交易类型(BUYSELL)。

  • entry_quantity: 进场订单数量。

  • entry_price: 进入订单执行的价格并进入'COMPLETE'状态。

  • exit_timestamp: 退出订单放置的时间戳。(请注意,它可能在进入'COMPLETE'状态之前保持'OPEN'状态一段时间。)

  • exit_transaction_type: 退出订单交易类型(BUYSELL)。

  • exit_quantity: 退出订单数量。

  • exit_price: 退出订单执行的价格并进入'COMPLETE'状态。

  • pnl_absolute: 退出订单执行价格与进入订单执行价格之间的差异。在数学上,这是(exit_price - entry_priceexit_quantity* (对于多头交易)和(entry_price - exit_priceexit_quantity* (对于空头交易)。正值意味着交易盈利,负值意味着交易亏损。

  • pnl_percentage: 相对于进场价格的利润或亏损百分比。在数学上,这是pnl_absolute / entry_price / exit_quantity x 100

  • pnl_cumulative_absolute:累积利润或损失。在数学上,这是前期交易的所有pnl_absolute值的总和。这个数字直接反映了策略在模拟时间上的表现。

  • pnl_cumulative_percentage:累积利润或损失的百分比,与进场价格相比。在数学上,这是 pnl_cumulative / entry_price / exit_quantity x 100

还有更多...

一旦模拟交易任务进入'STOPPED'状态,损益表报告将不再更新。在提交下一个相同策略的模拟交易任务之前,您可以随时获取完整的损益报告。如果提交了新的模拟交易任务(相同策略),则无法再通过get_papertrading_report_pnl_table()方法访问此报告。如果您希望在以后参考该报告,可以将获取的报告保存到.csv文件中。

MACD-Bracket-Order 策略 - 获取模拟交易报告 - 统计表

在 AlgoBulls 平台上提交模拟交易任务后,AlgoBulls 模拟交易引擎开始执行策略。在执行过程中,除了日志和损益表之外,AlgoBulls 模拟交易引擎还实时从损益表生成摘要。这个摘要是一个包含各种统计数字的统计表,如净盈亏(绝对值和百分比)、最大回撤(绝对值和百分比)、总交易次数、盈利交易次数、亏损交易次数、多头交易次数和空头交易次数、最大收益和最小收益(或最大损失),以及盈利和亏损交易的平均利润。这张表可以立即了解整体策略的表现。

在本示例中,您将获取策略的统计表报告。在您提交模拟交易任务后,只要您的策略 punched 第一笔交易,此报告即可获得。AlgoBulls 平台允许您实时获取统计表,即使模拟交易任务仍在进行中。您可以在等待模拟交易任务完成之前即可获得策略表现的见解。这对于模拟交易任务通常持续很长时间来说是有帮助的。pyalgotrading包提供了一个简单的方法来获取给定策略的统计表。

确保你已经阅读了第八章的最后六个示例,算法交易策略 - 逐步编码,以完全了解所使用的策略类StrategyMACDBracketOrder的全貌。

准备工作

确保algobulls_connectionstrategy_code2对象在您的 Python 命名空间中可用。参考本章的MACD-Bracket-Order 策略 - 获取策略示例设置algobulls_connectionstrategy_code2对象。

如何做...

获取strategy_code2的模拟交易统计报告:

>>> algobulls_connection.get_papertrading_report_statistics(strategy_code2)

我们得到以下输出(你的输出可能有所不同):

工作原理...

在此示例中,您使用algobulls_connection对象的get_papertradig_report_statistics()方法实时获取模拟交易统计表。此方法接受策略代码作为参数。您在此处传递了strategy_code2。返回的数据是一个pandas.DataFrame对象,有两列—highlight_typehighlight_value—以及多行。行描述如下:

  • 净盈亏:累计模拟交易盈亏。这也是盈亏表中第一项的pnl_cumulative_absolute值。

  • 净盈亏百分比:累计模拟交易盈亏百分比。这也是盈亏表中第一项的pnl_cumulative_percentage值。

  • 最大回撤:盈亏表中pnl_cumulative列中的最低值。这表示您的策略在执行过程中遇到的最大损失。

  • 最大回撤百分比:数学上来说,这是(最大回撤) / (相应的入场价格 / 出场数量) × 100

  • 交易数目:会话期间的总交易数(入场和出场计为一次)。

  • 获利次数:交易盈亏为非负数的交易计数。

  • 亏损次数:交易盈亏为负数的交易计数。

  • 多头交易数目:入场交易类型为'BUY'的交易计数。

  • 空头交易数目:入场交易类型为'SELL'的交易计数。

  • 最大盈利:所有交易中盈利最高的交易的盈亏(P&L)。

  • 最小盈利:所有交易中盈利最低的交易的盈亏(P&L)。

  • 平均每次盈利交易盈利:数学上来说,这是(盈利交易的总盈亏) / (盈利交易的计数)

  • 平均每次亏损交易盈利:数学上来说,这是(亏损交易的总盈亏) / (亏损交易的计数)

更多内容...

如果在模拟交易作业仍在运行时获取统计表,则先前提到的数字将是中间数字,基于直到那时完成的交易。随着更多交易完成直到模拟交易作业完成,这些数字可能会更改。

一旦模拟交易作业转移到'STOPPED'状态,统计表将不再更改。在提交下一个相同策略的模拟交易作业之前,您可以随时获取完整的统计表。如果提交了新的模拟交易作业(针对相同策略),则无法通过get_papertrading_report_statistics()方法再次访问此表。如果您希望稍后参考,可以将获取的报告保存到.csv文件中。

MACD-Bracket-Order 策略 – 获取模拟交易报告 – 订单历史

在 AlgoBulls 平台上提交了一个模拟交易作业后,AlgoBulls 模拟交易引擎开始执行策略。在执行过程中,除了日志、盈亏表和统计表之外,AlgoBulls 模拟交易引擎还实时生成订单历史记录。此日志包含每个订单的状态转换,以及每个订单状态的时间戳和其他信息(如果有的话)。订单历史记录对于理解交易从 'OPEN' 状态到 'COMPLETE' 或 'CANCELLED' 状态所需的时间至关重要。例如,MARKET 订单将立即从 'OPEN' 转换到 'COMPLETE' 状态,但是 LIMIT 订单可能需要一段时间,根据市场情况,从 'OPEN' 转换到 'COMPLETE' 状态,或者甚至转换到 'CANCELLED' 状态。所有这些信息都包含在订单历史记录中。(有关订单状态转换的更多信息,请参阅 第六章 中的状态机图,在交易所上放置常规订单。)

在这个示例中,您将获取您策略的订单历史记录。此日志在您提交模拟交易作业后,您的策略第一笔交易打入后即可使用。即使模拟交易作业仍在进行中,AlgoBulls 平台也允许您实时获取订单历史记录。这有助于我们获取最终状态订单的详细信息,而无需等待模拟交易作业完成。pyalgotrading 包提供了一个简单的方法来获取给定策略的订单历史记录。

确保您已经阅读了 第八章 的最后六个示例,算法交易策略 - 逐步编码,以获取使用的策略类 StrategyMACDBracketOrder 的完整图景。

准备工作

确保您的 Python 命名空间中可用 algobulls_connectionstrategy_code2 对象。请参阅本章 MACD-Bracket-Order 策略 - 获取策略 的示例设置 algobulls_connectionstrategy_code2 对象。

如何做...

获取 strategy_code2 的模拟交易订单历史报告:

>>> order_history = \
        algobulls_connection.get_papertrading_report_order_history(
                                                         strategy_code2)
>>> print(order_history)

我们得到以下输出(您的输出可能有所不同):

…
+------------------+---------------------+----------------------------------+------+
| INST             | TIME                | ID                               | TT   |
|------------------+---------------------+----------------------------------+------|
| NSE_EQ:TATASTEEL | 2020-07-09 10:00:00 | 56970bffe8be4650a71857bc4472e6c8 | SELL |
+------------------+---------------------+----------------------------------+------+
+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-09 10:00:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-09 10:00:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-09 10:00:00 | OPEN PENDING           |       |
|  3 | 2020-07-09 10:00:00 | OPEN                   |       |
|  4 | 2020-07-09 10:15:00 | COMPLETE               |       |
+----+---------------------+------------------------+-------+
+------------------+---------------------+----------------------------------+------+
| INST             | TIME                | ID                               | TT   |
|------------------+---------------------+----------------------------------+------|
| NSE_EQ:TATASTEEL | 2020-07-09 10:00:00 | 0a06e41aac0744adb45bb4d3d2e19728 | SELL |
+------------------+---------------------+----------------------------------+------+
+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-09 10:00:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-09 10:00:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-09 10:00:00 | OPEN PENDING           |       |
|  3 | 2020-07-09 10:00:00 | TRIGGER PENDING        |       |
|  4 | 2020-07-09 10:15:00 | CANCEL PENDING         |       |
|  5 | 2020-07-09 10:15:00 | CANCELLED              |       |
+----+---------------------+------------------------+-------+
...

此处未显示完整输出。请访问以下链接阅读完整输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/sample/papertrading/strategy_macd_bracket_order/oms_order_history.log

工作原理...

在这个示例中,您使用 algobulls_connection 对象的 get_papertrading_report_order_history() 方法实时获取订单历史记录。该方法接受策略代码作为参数。您在此处传递 strategy_code2。返回的数据是一个字符串,描述如下:

对于每个订单,日志都包含以下信息:

  • 一个关于订单的描述性表,列出了以下列:

  • INST:订单的财务工具

  • TIME:订单下达的时间

  • ID:订单的唯一 ID

  • TT:订单交易类型(买入卖出

表格的示例如下所示:

+------------------+---------------------+----------------------------------+------+
| INST             | TIME                | ID                               | TT   |
|------------------+---------------------+----------------------------------+------|
| NSE_EQ:TATASTEEL | 2020-07-09 10:00:00 | 0a06e41aac0744adb45bb4d3d2e19728 | SELL |
+------------------+---------------------+----------------------------------+------+

这些信息将帮助您在策略执行日志中找到这个确切的订单。

  • 一个订单状态转换表,列出了以下列:

  • TIME:订单进入由 STATE 列表示的状态的时间戳。

  • STATE:订单在 TIME 列中提到的时间进入此状态。

  • MSG:来自 OMS 的关于任何意外状态转换的附加消息。例如,进入 REJECTED 状态的订单会有来自 OMS 的消息说明其被拒绝的原因。此列通常为空。

表格的示例如下所示:

+----+---------------------+------------------------+-------+
|    | TIME                | STATE                  | MSG   |
|----+---------------------+------------------------+-------|
|  0 | 2020-07-09 10:00:00 | PUT ORDER REQ RECEIVED |       |
|  1 | 2020-07-09 10:00:00 | VALIDATION PENDING     |       |
|  2 | 2020-07-09 10:00:00 | OPEN PENDING           |       |
|  3 | 2020-07-09 10:00:00 | TRIGGER PENDING        |       |
|  4 | 2020-07-09 10:15:00 | CANCEL PENDING         |       |
|  5 | 2020-07-09 10:15:00 | CANCELLED              |       |
+----+---------------------+------------------------+-------+

从这个表中,你可以看到在上午 10:00 下达订单后,它转换到 TRIGGER PENDING 状态。它在那里停留了 15 分钟,然后转换到 CANCELLED 状态。这是符合预期的,因为订单是一个限价范围订单。

这还不是全部...

一旦模拟交易作业进入 STOPPED 状态,就不会生成新的订单历史记录日志。您可以在提交下一个相同策略的模拟交易作业之前随时获取完整的订单历史记录日志。如果提交了新的模拟交易作业(对于相同的策略),则无法通过 get_papertrading_report_order_history() 方法访问这些日志。如果您希望稍后参考这些日志,可以将获取的日志保存到文件中。

第十一章:算法交易 - 实际交易

现在我们已经建立了各种算法交易策略,并成功地进行了令人满意的回测,并在实时市场中进行了纸上交易,现在终于到了进行实际交易的时候了。

实际交易是指我们在真实市场小时内用真钱执行交易策略。如果您的策略在回测和纸上交易中表现良好,您可以期待在真实市场中获得类似的结果。请注意,尽管您的策略在回测和纸上交易中表现良好,但在真实市场中可能表现不如预期。盈利的回测和纸上交易结果是盈利的实际交易经验的前提,但不足以保证每个交易会盈利。

对于实际交易,需要策略配置。它包含多个参数,其中一些如下:

  • 开始和结束时间:在当前一天内进行纸上交易的时间段。

  • 金融工具:进行纸上交易的一个或多个金融工具。

  • 蜡烛间隔:各种可能的蜡烛间隔之一;例如,1 分钟15 分钟小时

  • 策略特定参数:在策略中定义的自定义参数的值。

  • 策略模式:其中之一是日内或交割。日内策略 punch 日内订单,这些订单在当天结束时平仓。交割策略 punch 交割订单,这些订单不会在当天结束时平仓,而会被延续到下一个交易日。

实际交易引擎需要根据给定的策略执行实际交易。在本章中,您将使用由 AlgoBulls 提供的实际交易引擎(algobulls.com),这是一个通过其开发者选项提供其服务的算法交易平台。它提供了一个名为 pyalgotrading 的 Python 包(github.com/algobulls/pyalgotrading)来提供这些服务。

您在 第八章 中编写了两个算法交易策略,逐步编码算法交易策略。请回忆一下策略描述如下:

  • EMA-Regular-Order 策略:基于技术指标 EMA 和常规订单的策略。(第七章的前六个配方,在交易所上放置挂单和止损单

  • MACD-Bracket-Order 策略:基于技术指标 MACD 和挂单的策略。(第七章的其余六个配方,在交易所上放置挂单和止损单

这些策略也作为 Python 包的一部分提供,即 pyalgostrategypool。你可以使用 $ pip install pyalgostrategypool 命令安装它。你也可以在 GitHub 上查看它们 (github.com/algobulls/pyalgostrategypool)。

当你在 第八章 中跟随 算法交易策略 – 逐步编码,你将这两个策略上传到你的 AlgoBulls 账户。在本章中,你将从你的 AlgoBulls 账户获取这些策略,并对它们进行真实交易。真实交易完全自动化,不需要你的任何干预,而交易会话正在进行时。进行真实交易后,你将收集策略执行日志和各种报告 – 即盈亏报告和统计报告。通过使用 pyalgotrading,你确保专注于开发和执行真实交易策略,而不必担心策略执行所需的生态系统。

本章包括了先前提及的策略的逐步示例,从建立到 AlgoBulls 平台的连接,获取策略,运行真实交易作业到获取执行日志和各种类型报告。

在本章中,你将学习以下内容:

  • EMA-Regular-Order 策略 – 获取该策略

  • EMA-Regular-Order 策略 – 实际交易该策略

  • EMA-Regular-Order 策略 – 实时获取实际交易日志

  • EMA-Regular-Order 策略 – 获取实际交易报告 – P&L 表

  • EMA-Regular-Order 策略 – 获取实际交易报告 – 统计表

  • MACD-Bracket-Order 策略 – 获取该策略

  • MACD-Bracket-Order 策略 – 实际交易该策略

  • MACD-Bracket-Order 策略 – 实时获取实际交易日志

  • MACD-Bracket-Order 策略 – 获取实际交易报告 – P&L 表

  • MACD-Bracket-Order 策略 – 获取实际交易报告 – 统计表

只有在实时市场小时运行的真实交易才有意义,而不像回测可以在任何时间运行。请确保你在实时市场小时尝试本章的示例。

技术要求

为了成功执行本章的示例,你将需要以下内容:

  • Python 3.7+

  • Python 包:

  • pyalgotrading$ pip install pyalgotrading

确保你已在 algobulls.com 上添加并绑定了你的经纪详情。有关更多细节,请参考 附录 II。你可以在此章节中使用任何 AlgoBulls 平台支持的经纪商。

本章的最新 Jupyter 笔记本可以在 GitHub 上找到 github.com/PacktPublishing/Python-Algorithmic-Trading-Cookbook/tree/master/Chapter11

EMA-Regular-Order 策略 – 获取该策略

在这个配方中,你将从 AlgoBulls 平台上的你的账户中获取 StrategyEMARegularOrder 策略类。这个配方从建立到 AlgoBulls 平台的连接开始,查询你的账户中的所有可用策略,并获取所需策略类 StrategyEMARegularOrder 的详细信息。

确保你已经学习了第八章中的前六个配方,算法交易策略-逐步编码,以完全了解我们将使用的策略类;即 StrategyEMARegularOrder

如何操作…

我们对此配方执行以下步骤:

  1. 导入所需模块:
>>> from pyalgotrading.algobulls import AlgoBullsConnection
  1. 创建一个新的 AlgoBulls 连接对象:
>>> algobulls_connection = AlgoBullsConnection()
  1. 获取授权 URL:
>>> algobulls_connection.get_authorization_url()

我们得到了以下输出:

Please login to this URL with your AlgoBulls credentials and get your developer access token: https://app.algobulls.com/user/login
'https://app.algobulls.com/user/login'
  1. 使用你的 AlgoBulls 凭证登录上述链接,获取你的令牌,并在此处设置它(有关更多详细信息,请参阅 附录 II):
>>> algobulls_connection.set_access_token(
                    '80b7a69b168c5b3f15d56688841a8f2da5e2ab2c')
  1. 获取并显示到目前为止创建和上传的所有策略:
>>> all_strategies = algobulls_connection.get_all_strategies()
>>> all_strategies

我们得到了以下输出。你的输出可能会有所不同(确保你已经按照第八章中的配方操作,以获得类似的输出):

  1. 获取并显示第一个策略的策略代码:
>>> strategy_code1 = all_strategies.iloc[0]['strategyCode']
>>> strategy_code1

我们得到了以下输出(你的输出可能会有所不同):

'49287246f9704bbcbad76ade9e2091d9'
  1. 在实际交易策略之前,你可以检查你的策略,以确保你有正确的策略:
>>> strategy_details1 = \
         algobulls_connection.get_strategy_details(strategy_code1)
>>> print(strategy_details1)

我们得到了以下输出:

class StrategyEMARegularOrder(StrategyBase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.timeperiod1 = self.strategy_parameters['timeperiod1']
        self.timeperiod2 = self.strategy_parameters['timeperiod2']

        self.main_order = None

    def initialize(self):
        self.main_order = {}

    @staticmethod
    def name():
        return 'EMA Regular Order Strategy'
    ....
    def strategy_exit_position(self, candle, instrument, 
                               sideband_info):
        if sideband_info['action'] == 'EXIT':
            self.main_order[instrument].exit_position()
            self.main_order[instrument] = None
            return True

        return False

这里未显示完整的输出。请访问以下链接以阅读完整的输出,网址为github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/strategy_ema_regular_order.py

工作原理…

你在 第 1 步 中导入了所需的模块。在 第 2 步 中,创建了一个名为 algobulls_connectionAlgoBullsConnection 类的实例。在 第 3 步 中,使用 algobulls_connection 对象的 get_authorization_url() 方法获取授权 URL。这将打印授权 URL。你应该从 web 浏览器访问此 URL,以登录 AlgoBulls 平台并获取你的开发者访问令牌。(你可以在 附录 II 中找到有关从 AlgoBulls 平台获取开发者访问令牌的更多详细信息和截图。)在 第 4 步 中,使用 algobulls_connectionset_access_token() 方法设置访问令牌。如果令牌被接受,则与 AlgoBulls 平台建立成功的连接。

第 5 步中,您获取到目前在 AlgoBulls 平台上创建并上传的所有策略。您使用 get_all_strategies() 方法进行此步骤,并将其赋值给一个新变量 all_strategies。这个变量是一个 pandas.DataFrame 对象,具有 strategyCodestrategyName 列。此表包含您先前上传的策略代码和策略名称的信息。如果您遵循了 第八章 的 EMA-Regular-Order strategy – uploading the strategy on the AlgoBulls trading platform 配方,Algorithmic Trading Strategies – Coding Step by Step,您将会找到一种名为 EMA regular order strategy 的策略。在第 6 步中,您将 EMA regular order strategy 策略的策略代码分配给一个新变量 strategy_code1。此步骤的输出中显示策略代码。这个策略代码对于 AlgoBulls 平台上的每个策略都是唯一的。

最后,在第 7 步中,您确保被 strategy_code1 引用的策略确实是您之前上传的那个(在 EMA-Regular-Order strategy – uploading the strategy on the AlgoBulls trading platform 配方中,在 第八章,Algorithmic Trading Strategies – Coding Step by Step)。您使用 algobulls_connection 对象的 get_strategy_details() 方法来检查策略。此方法将策略代码作为参数。您在这里传递 strategy_code1。此方法将整个类代码作为字符串返回。您将其分配给一个新变量 strategy_details1 并显示它。

如果您想更改由 strategy_code1 引用的类代码,如第 7 步所示,请参考 第八章 的 EMA-Regular-Order strategy – uploading the strategy on the AlgoBulls trading platform 配方中的 There's more… 部分,Algorithmic Trading Strategies – Coding Step by Step

EMA–Regular–Order strategy – real trading the strategy

在这个配方中,您将在EMA-Regular-Order strategy上执行真实交易。您必须在本章的前一步骤中从 AlgoBulls 平台上的您的账户中提取此策略。您将在此配方中利用 pyalgotrading 提供的实际交易功能,这将在 AlgoBulls 平台上提交一个真实交易任务。

一旦提交,AlgoBulls 实际交易引擎将运行真实交易。您可以随时查询其状态以了解真实交易工作的状态。该任务按给定的顺序经历以下状态:

  • STARTING(中间状态)

  • STARTED(稳定状态)

  • STOPPING(中间状态)

  • STOPPED(稳定状态)

提交作业后,它会以中间状态 STARTING 开始。在此状态下,AlgoBulls 真实交易引擎获取策略并准备执行环境,这可能需要几分钟。一旦完成,作业就会转移到 STARTED 状态。真实交易策略是在此阶段实施的。在此阶段,它会停留,直到真实交易完成。完成后,作业会转移到中间状态 STOPPING。在此状态下,AlgoBulls 真实交易引擎会清理为此作业分配的资源,这通常需要不到一分钟。最后,作业转移到 STOPPED 状态。

如果您已经提交了策略真实交易作业,就不能再为同一策略提交另一个作业,直到第一个作业完成。这意味着您必须等待第一个作业转移到 STOPPED 状态。如果第一个作业运行时间很长,而您希望立即停止它,可以通过 pyalgotrading 提交停止作业请求。在提交请求之前,您需要确保作业处于 STARTED 状态。

下面的状态机图演示了 AlgoBulls 平台上一项真实交易工作在其生命周期内的各种状态和转换:

提交真实交易工作后,您可以实时获取策略执行的日志和报告。这些日志和报告有助于验证策略的性能并调试任何潜在问题。

确保您已经完成了第八章的前六个食谱,即 第八章 算法交易策略 - 逐步编码,以全面了解我们将使用的策略类 StrategyEMARegularORder

准备工作

确保 algobulls_connectionstrategy_code1 对象在您的 Python 命名空间中可用。请参阅前面的食谱设置 algobulls_connectionstrategy_code1 对象。

如何操作...

我们对此食谱执行以下步骤:

  1. 导入必要的模块:
>>> from datetime import time
>>> from pyalgotrading.constants import *
  1. 使用其交易符号作为关键字搜索工具。将返回的对象分配给 instruments
>>> instruments = algobulls_connection.search_instrument('SBIN')
>>> instruments

我们得到了以下输出(您的输出可能会有所不同):

[{'id': 7, 'value': 'NSE:SBIN'}]
  1. instruments 获取所选工具的 value
>>> instrument = instruments[0]['value']
>>> instrument

我们得到了以下输出:

'NSE:SBIN'
  1. 提交 strategy_code1 的真实交易工作:
>>> algobulls_connection.realtrade(
        strategy_code=strategy_code1, 
        start_time=time(hour=9, minute=15), 
        end_time=time(hour=15, minute=30),
        instrument=instrument, 
        lots=1,
        strategy_parameters={
            'timeperiod1': 5,
            'timeperiod2': 12
        }, 
        candle_interval=CandleInterval.MINUTES_15)

我们得到了以下输出:

Setting Strategy Config... Success.
Submitting REALTRADING job... Success.
  1. 检查已提交真实交易工作的状态:
>>> algobulls_connection.get_realtrading_job_status(strategy_code1)

我们得到了以下输出:

{'data': 'STARTING'}
  1. 一段时间后再次检查已提交作业的状态:
>>> algobulls_connection.get_realtrading_job_status(strategy_code1)

我们得到了以下输出:

{'data': 'STARTED'}

工作原理...

第 1 步中,你从datetime模块中导入time类,并从pyalgotrading.constants模块导入所有常量。在第 2 步中,你使用algobulls_connection对象的search_instrument()方法获取你想要实时交易策略的工具,EMA-Regular-Order 策略search_instrument()方法接受一个搜索字符串作为参数,该字符串应为您感兴趣的工具的交易符号的一部分或全部。你在这里传递了'SBIN'。该函数返回一个包含与搜索字符串匹配的工具详情的列表。可能有多个工具的交易符号中包含搜索字符串。在第 3 步中,你获取第一个匹配工具的值,并将其赋值给一个新变量instrument

第 4 步中,你使用algobulls_connection()对象的realtrade()方法提交一个实时交易作业。它接受以下参数:

  • strategy_code: 要执行实时交易的策略的策略代码。这应该是一个字符串。你在这里传递了strategy_code1

  • start_time: 实时交易应该开始的今天时间。这应该是一个datetime.time对象。在这里,你传递了一个持有 9:15 小时值的对象 - time(hour=9, minute=15)。有关创建time对象的详细信息,请参阅本书的第一篇配方。

  • end_time: 实时交易应该执行的今天时间。这个对象应该包含一个比start_time持有的值提前的时间值。这应该是一个datetime.time实例。在这里,你传递了一个持有 15:30 小时值的对象 - time(hour=15, minute=30)

  • instrument: 应该进行实时交易的金融工具。将为此工具获取历史数据。这应该是一个字符串。你在这里传递了instrument

  • lots: 应该进行实时交易的手数。这应该是一个整数。数量由策略计算为手数×金融工具的手数。你在这里传递了1

  • strategy_parameters: 策略期望的参数名称和值。这应该是一个字典,带有parameter-nameparameter-value作为键值对。你在这里传递了以下参数:

  • timeperiod1: 5

  • timeperiod2: 12 

(回想一下,EMA-Regular-Order 策略的参数已经在其__init__()方法中定义,如第八章的第一篇配方所示,逐步编码的算法交易策略)。

  • candle_interval: 用于实时交易获取历史数据的蜡烛间隔。这应该是CandleInterval类型的枚举。你在这里传递了CandleInterval.MINUTES_15。(CandleInterval枚举提供各种蜡烛间隔的枚举,其中一些是MINUTE_1MINUTES_3MINUTES_5MINUTES_10MINUTES_15MINUTES_30HOURDAY。)

如果作业提交成功,您将看到由realtrade()函数打印的Success消息。

一旦作业被提交,启动需要一段时间。启动后,完成可能需要一些时间,这取决于实际交易的持续时间,该持续时间使用start_timeend_time参数指定。通常,实际交易运行整个交易日,这意味着作业将运行 6-8 小时。

步骤 5中,您使用algobulls_connection对象的get_realtrading_job_status()方法获取作业的状态。在这里将strategy_code1作为参数传递。此方法返回一个具有单个键值对 - datajob 状态的字典。如果在放置作业后立即查询状态,则会得到'STARTING'作为状态。在步骤 6中,您再次查询状态,如果作业启动,您将获得'STARTED'的状态。

成功提交意味着已经以所需格式传递了实际交易策略所需的最小输入。但是,这并不保证策略在实际交易中不会出现错误。策略的执行仍可能在实际交易过程中遇到错误。要调试执行问题,您需要获取输出日志,这将在下一个示例中进行说明。错误可能的原因可能是策略类 Python 代码中的错误或未完整传递到 realtrade() 函数的 strategy_parameters 字典。

还有更多内容…

如果作业运行时间很长,您想在其完成之前停止它,您可以使用algobulls_connection对象的stop_realtrading_job()方法。此方法接受策略代码作为参数。在这里传递strategy_code1。此方法向 AlgoBulls 实时交易引擎提交停止请求。如果请求被接受,您将在此处看到一个Success消息:

>>> algobulls_connection.stop_realtrading_job(strategy_code1)
 Stopping REALTRADING job... Success.

如果在提交停止请求后查询状态,您将得到'STOPPING'作为状态:

>>> algobulls_connection.get_realtrading_job_status(strategy_code1)
{'data': 'STOPPING'}

如果一段时间后再次查询状态,并且作业已停止,您将得到'STOPPED'作为状态:

>>> algobulls_connection.get_realtrading_job_status(strategy_code1)
{'data': 'STOPPED'} 

EMA–Regular–Order 策略 – 实时获取真实交易日志

在 AlgoBulls 平台上提交实际交易任务后,AlgoBulls 实时交易引擎开始执行策略。在执行过程中,AlgoBulls 实时交易引擎发生的每个事件和所做的决定都以精确的时间戳形式记录在文本日志中。

记录活动的示例包括给定的策略配置,定期生成的每个新蜡烛,您的策略执行的交易,由这些交易创建的仓位的进入和退出,等待新蜡烛等等。在验证策略并调试在开发策略时经常遇到的行为或性能问题时,这些日志是必不可少的。

在这个配方中,您将为您的策略获取真实的交易日志。日志会在您提交的真实交易作业达到 'STARTED' 状态后立即开始出现(有关真实交易作业状态的更多信息,请参阅上一篇配方)。AlgoBulls 平台允许您实时获取日志,即使真实交易作业仍在进行中。您可以在无需等待真实交易作业完成的情况下深入了解策略的执行情况。这对于真实交易作业通常是长时间运行的情况非常有帮助。pyalgotrading 包提供了一个简单的方法,我们可以用来获取给定策略的执行日志。

确保您已经熟悉了 第八章 的前六个配方,算法交易策略 - 逐步编码,以全面了解我们将使用的策略类;即,StrategyEMARegularOrder

准备就绪

确保您的 Python 命名空间中有 algobulls_connectionstrategy_code1 对象。参考本章的第一个配方设置 algobulls_connectionstrategy_code1 对象。

如何操作…

执行以下步骤完成此配方:

  1. 获取 strategy_code1 的真实交易执行日志:
>>> logs = algobulls_connection.get_realtrading_logs(
                                                strategy_code1)
>>> print(logs)

我们得到了以下输出(您的输出可能有所不同):

[2020-07-09 09:12:25] Logs not available yet. Please retry in sometime.
  1. 一段时间后再次获取 strategy_code1 的真实交易执行日志:
>>> logs = algobulls_connection.get_realtrading_logs(
                                                strategy_code1)
>>> print(logs)

我们得到了以下输出(您的输出可能有所不同):

…
########################################
 INITIALIZING ALGOBULLS CORE (v3.2.0)... 
########################################
[2020-07-09 09:13:05] Welcome PUSHPAK MAHAVIR DAGADE!
[2020-07-09 09:13:05] Reading strategy...
[2020-07-09 09:13:05] STARTING ALGOBULLS CORE...
...
[2020-07-09 10:30:00] [CRITICAL] [order] [PLACING NEW ORDER] [2020-07-09 10:30:00] [2b079bc873f64f53a33f91b6ceec707b] [BUY] [NSE:SBIN] [QTY:1] [QTY PENDING: 1] [ENTRY PRICE: 194.7] [PRICE:None] [TRIGGER PRICE:None] [ORDER_TYPE_REGULAR] [ORDER_CODE_INTRADAY] [ORDER_VARIETY_MARKET] [ORDER_POSITION_ENTER]
...
[2020-07-09 15:30:00] [INFO] [clock] Candle generation has been stopped...
[2020-07-09 15:30:00] [INFO] [tls] Received event END OF MARKET. Stopping Trading Core Engine...
[2020-07-09 15:30:00] [INFO] [tls] Exiting all open positions with order code: ORDER_CODE_INTRADAY (if any)...
[2020-07-09 15:30:00] [CRITICAL] [tls] [User: PUSHPAK MAHAVIR DAGADE] Trading session completed
...

这里未显示完整输出。请访问以下链接以阅读完整输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/sample/realtrading/strategy_ema_regular_order/logs.txt

工作原理…

第一步 中,您使用 algobulls_connection 对象的 get_realtrading_logs() 方法实时获取策略的真实交易日志。此方法接受策略代码作为参数。在这里传递了 strategy_code1。返回的数据是一个字符串。如果您在提交作业后立即尝试此步骤,您会得到一个字符串,其中说日志尚未准备好([2020-07-09 09:12:25] Logs not available yet. Please retry in sometime.)。如果真实交易作业处于 'STARTING' 状态,就会发生这种情况。

第二步 中,您再次在一段时间后获取日志。如果作业不在 'STARTING' 状态,则开始获取您的策略执行日志。每次调用 get_realtrading_logs() 函数时,您都会获得所有真实交易日志。

还有更多…

一旦真实交易任务转移到 'STOPPED' 状态,就不会生成新的日志。在提交下一个相同策略的真实交易任务之前,你可以随时获取完整的日志。如果提交了新的真实交易任务(相同的策略),则无法通过 get_realtrading_logs() 方法再次访问这些日志。如果需要,你可以将获取的日志保存到文件中,以便日后参考。

EMA–Regular–Order 策略 – 获取真实交易报告 – 损益表

在 AlgoBulls 平台上提交真实交易任务后,AlgoBulls 实时交易引擎开始执行策略。在执行过程中,除了日志外,AlgoBulls 实时交易引擎还实时生成了一张损益表。该表包含了策略输入的每一笔交易的信息。它还包含了入场和出场订单之间的映射细节、交易损益以及累积损益,按时间顺序排序,最新的订单排在前面。通过这张表,我们可以通过单个和累积损益数字了解策略的整体表现。入场-出场订单映射也有助于验证策略的行为。

在本示例中,你将获取策略的损益表报告。在提交真实交易任务后,只要你的策略打入了第一笔交易,这份报告就可以获得。AlgoBulls 平台允许你实时获取损益表,即使真实交易任务仍在进行中。你可以在不必等待真实交易任务完成的情况下,了解策略的表现。这对于长时间运行的真实交易任务非常有帮助。pyalgotrading 包提供了一个简单的方法,我们可以用它来获取给定策略的损益表。

确保你已经完成了 第八章 的前六个示例,算法交易策略 – 逐步编码,以完整了解我们将使用的策略类;即 StrategyEMARegularOrder

准备工作

确保 algobulls_connectionstrategy_code1 对象在你的 Python 命名空间中可用。请参阅本章第一个示例来设置 algobulls_connectionstrategy_code1 对象。

操作步骤…

获取 strategy_code1 的真实交易损益报告:

>>> algobulls_connection.get_realtrading_report_pnl_table(strategy_code1)

我们得到以下输出。你的输出可能有所不同(注意以下输出已分成多个表格以供表示目的。在 Jupyter Notebook 中,你会看到一个宽表):

工作原理…

在本示例中,你可以使用 algobulls_connection 对象的 get_realtrading_report_pnl_table() 方法实时获取真实交易损益表。该方法接受策略代码作为参数。你在这里传递了 strategy_code1。返回的数据是一个 pandas.DataFrame 对象,具有多个列,描述如下:

  • instrument: 进行交易的金融工具。

  • entry_timestamp: 下单进入的时间戳。(注意,在进入 'COMPLETE' 状态之前,可能会保持 'OPEN' 状态一段时间。)

  • entry_transaction_type: 进入订单的交易类型(可以是 BUYSELL)。

  • entry_quantity: 进入订单数量。

  • entry_price: 进入订单执行并进入 'COMPLETE' 状态的价格。

  • exit_timestamp: 下单退出的时间戳。(注意,在进入 'COMPLETE' 状态之前,可能会保持 'OPEN' 状态一段时间。)

  • exit_transaction_type: 退出订单的交易类型(可以是 BUYSELL)。

  • exit_quantity: 退出订单数量。

  • exit_price: 退出订单执行并进入 'COMPLETE' 状态的价格。

  • pnl_absolute: 退出订单执行价格与进入订单执行价格之间的差异。在数学上,对于多头交易,这是 (exit_price - entry_price)exit_quantity,而对于空头交易,这是 (entry_price* - exit_price)exit_quantity*。正值意味着交易是盈利的,负值意味着交易是亏损的。

  • pnl_percentage: 相对于进入价格的利润或损失百分比。在数学上,这是 pnl_absolute / entry_price / exit_quantity × 100

  • pnl_cumulative_absolute: 累积利润或损失。在数学上,这是之前交易的所有 pnl_absolute 值的总和。此数字直接反映了策略相对于模拟时间的表现。

  • pnl_cumulative_percentage: 相对于进入价格的累积利润或损失的百分比。在数学上,这是 pnl_cumulative / entry_price / exit_quantity × 100

还有更多...

一旦真实交易作业进入 'STOPPED' 状态,损益表报告将不再更新。您可以在提交同一策略的下一个真实交易作业之前随时获取完整的损益报告。如果提交了新的真实交易作业(对于相同的策略),则无法通过 get_realtrading_report_pnl_table() 方法再访问此报告。如果您希望以后参考,可以将获取的报告保存为 .csv 文件。

EMA–Regular–Order 策略 – 获取真实交易报告 – 统计表

在 AlgoBulls 平台上提交了一个实际交易任务后,AlgoBulls 实际交易引擎开始执行该策略。在执行过程中,除了日志和 P&L 表之外,AlgoBulls 实际交易引擎还实时从 P&L 表中生成一个摘要。此摘要是一个包含各种统计数字的统计表,例如 净盈亏(绝对值和百分比)、最大回撤(绝对值和百分比)、总交易次数、盈利交易次数、亏损交易次数、多头交易和空头交易次数、最大利润和最小利润(或最大损失),以及每次盈利和亏损交易的平均利润。该表提供了策略整体表现的即时概述。

在这个示例中,你将获取你的策略的统计表报告。这份报告在你提交实际交易任务后,你的策略第一笔交易被打入后即可获得。AlgoBulls 平台允许你实时获取统计表,即使实际交易任务仍在进行中。你可以在不必等待实际交易任务完成的情况下,了解策略的表现。这对于实际交易任务通常是长时间运行的情况很有帮助。pyalgotrading 包提供了一个简单的方法,我们可以用来获取给定策略的统计表。

确保你已经阅读了《第八章》的前六个配方,即《算法交易策略 - 逐步编码》,以全面了解我们将要使用的策略类别,即 StrategyEMARegularOrder

准备工作

确保 algobulls_connectionstrategy_code1 对象在你的 Python 命名空间中可用。参考本章的第一个配方来设置 algobulls_connectionstrategy_code1 对象。

如何实现…

获取 strategy_code1 的实际交易统计报告:

>>> algobulls_connection.get_realtrading_report_statistics(strategy_code1)

我们得到了以下输出(你的输出可能不同):

工作原理…

在这个示例中,你可以使用 algobulls_connection 对象的 get_realtrading_report_statistics() 方法实时获取实际交易统计表。此方法接受策略代码作为参数。在这里你传递了 strategy_code1。返回的数据是一个 pandas.DataFrame 对象,包含两列 - highlight_typehighlight_value - 以及多行。行描述如下:

  • Net PnL: 累积实际交易盈亏。这也是 P&L 表中第一条目的 pnl_cumulative_absolute 值。

  • Net PnL %: 累积实际交易盈亏百分比。这也是 P&L 表中第一条目的 pnl_cumulative_percentage 值。

  • 最大回撤: P&L 表中 pnl_cumulative 列中的最低值。这表示你的策略在执行过程中遭遇的最大损失。

  • 最大回撤 %: 数学上来说,这是 (最大回撤) / (相应的进入价格) / 退出数量 × 100

  • 交易数量:会话期间的总交易次数(入场和出场计为一次)。

  • 盈利交易次数:交易收益为非负数的交易次数。

  • 亏损交易次数:交易收益为负数的交易次数。

  • 多头交易数量:入场交易类型为'BUY'的交易数量。

  • 空头交易数量:入场交易类型为'SELL'的交易数量。

  • 最大收益:所有交易中收益最大的交易的收益和损失。

  • 最小收益:所有交易中收益最小的交易的收益和损失。

  • 平均每次盈利交易的利润:从数学上讲,这是(盈利交易的总收益) / (盈利交易的数量)

  • 平均每次亏损交易的利润:从数学上讲,这是(亏损交易的总收益) / (亏损交易的数量)

更多内容...

如果在真实交易作业仍在运行时获取统计表,则上述数字将是中间数字,基于截至那时完成的交易。随着更多交易被输入,数字可能会发生变化,直到真实交易作业完成。

一旦真实交易作业转移到'STOPPED'状态,统计表将不再更改。您可以在提交下一个相同策略的真实交易作业之前随时获取完整的统计表。如果提交了新的真实交易作业(相同策略),则无法通过get_realtrading_report_statistics()方法访问此表。如果您希望日后参考,可以将获取的报表保存为.csv文件。

MACD-Bracket-Order 策略 - 提取策略

在本文中,您将从 AlgoBulls 平台上的帐户中提取StrategyMACDBracketOrder策略类,您必须在上一篇第八章的最后一篇配方中上传。该配方开始于建立与 AlgoBulls 平台的连接,查询您帐户中所有可用的策略,并获取所需策略类(即StrategyMACDBracketOrder)的详细信息。

确保您已经阅读了第八章中的最后六个配方,逐步了解我们将使用的策略类的完整情况,即StrategyMACDBracketOrder

操作步骤如下:

执行以下步骤完成此配方:

  1. 导入必要的模块:
>>> from pyalgotrading.algobulls import AlgoBullsConnection
  1. 创建一个新的 AlgoBulls 连接对象:
>>> algobulls_connection = AlgoBullsConnection()
  1. 获取授权 URL:
>>> algobulls_connection.get_authorization_url()

我们获得了以下输出:

Please login to this URL with your AlgoBulls credentials and get your developer access token: https://app.algobulls.com/user/login
'https://app.algobulls.com/user/login'
  1. 使用您的 AlgoBulls 凭据登录上述链接,获取您的令牌,并将其设置在此处(有关更多详细信息,请参阅附录 II):
>>> algobulls_connection.set_access_token('
                        80b7a69b168c5b3f15d56688841a8f2da5e2ab2c')
  1. 提取并显示您迄今为止创建和上传的所有策略。
>>> all_strategies = algobulls_connection.get_all_strategies()
>>> all_strategies

我们得到了以下输出。你的输出可能不同(确保你已经按照第八章中的配方,逐步编码算法交易策略,来获得类似的输出):

  1. 获取并显示第二个策略的策略代码;也就是 MACD-Bracket-Order 策略:
>>> strategy_code2 = all_strategies.iloc[1]['strategyCode']
>>> strategy_code2

我们得到了以下输出(你的输出可能不同):

'49287246f9704bbcbad76ade9e2091d9'
  1. 在实际交易你的策略之前,你可以检查你的策略以确保你拥有正确的策略:
>>> strategy_details2 = \
        algobulls_connection.get_strategy_details(strategy_code2)
>>> print(strategy_details2)

我们得到了以下输出:

class StrategyMACDBracketOrder(StrategyBase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fastMA_period = \
            self.strategy_parameters['fastma_period']
        self.slowMA_period = \
            self.strategy_parameters['slowma_period']
        self.signal_period = \
            self.strategy_parameters['signal_period']
        self.stoploss = \
            self.strategy_parameters['stoploss_trigger']
        self.target = self.strategy_parameters['target_trigger']
        self.trailing_stoploss = \
            self.strategy_parameters['trailing_stoploss_trigger']

        self.main_order = None

    def initialize(self):
        self.main_order = {}

    @staticmethod
    def name():
        return 'MACD Bracket Order Strategy'
    ....
    def strategy_exit_position(self, candle, instrument, \
                               sideband_info):
        if sideband_info['action'] == 'EXIT':
            self.main_order[instrument].exit_position()
            self.main_order[instrument] = None
            return True

        return False

完整的输出在此处未显示。请访问以下链接以阅读完整的输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/strategy_macd_bracket_order.py

工作原理是…

第 1 步 中,你导入了必要的模块。在 第 2 步 中,你创建了AlgoBullsConnection类的一个实例,命名为algobulls_connection。在 第 3 步 中,你使用algobulls_connection对象的get_authorization_url()方法获取了授权 URL。这会打印授权 URL。你应该从你的网络浏览器中访问这个 URL,以登录 AlgoBulls 平台并获取你的开发者访问令牌。(你可以在 附录 II 中找到更多详细信息和屏幕截图,关于如何从 AlgoBulls 平台获取开发者访问令牌。)你复制访问令牌并在 第 4 步 中使用algobulls_connectionset_access_token()方法设置它。如果令牌被接受,就会建立与 AlgoBulls 平台的成功连接。

第 5 步 中,你获取了到目前为止在 AlgoBulls 平台上创建并上传的所有策略。你使用get_all_strategies()方法来执行此步骤,并将其分配给一个新变量,all_strategies。这个变量是一个pandas.DataFrame对象,带有strategyCodestrategyName列。这张表保存了你之前上传的策略代码和策略名称的信息。

如果你遵循了第八章中的 MACD-Bracket-Order Strategy – 将策略上传到 AlgoBulls 交易平台 配方,逐步编码算法交易策略,你将找到一个名为 MACD-Regular-Order strategy 的策略。在 第 6 步 中,你将 MACD-Regular-Order 策略的策略代码分配给一个名为 strategy_code2 的新变量。这个策略代码显示在这一步的输出中。这个策略代码对于 AlgoBulls 平台上的每个策略都是唯一的。

最后,在 步骤 7 中,您确保被 strategy_code2 引用的策略确实是您之前上传的那个(在第八章的最后一个配方中,《算法交易策略-逐步编码》)。您使用 algobulls_connection 对象的 get_strategy_details() 方法来检查策略。该方法以策略代码作为参数。在这里传递 strategy_code2。该方法返回整个类代码作为字符串。您将其分配给一个新变量 strategy_details2 并显示它。

如果您想要更改被 strategy_code2 引用的类代码,如 步骤 7 所示,请参考 第八章 的最后一个配方中的还有更多……部分,《算法交易策略-逐步编码》。

MACD–Bracket–Order 策略-实际交易该策略

在本配方中,您将在 MACD-Bracket-Order 策略上执行实际交易。您必须在本章的前一个配方中从 AlgoBulls 平台上的您的帐户中获取此策略。您将利用由 pyalgotrading 提供的实际交易功能来执行此配方,该功能又会在 AlgoBulls 平台上提交一个实际交易作业。

提交后,实际交易将由 AlgoBulls 实时交易引擎运行。您可以随时查询状态以了解实际交易作业的状态。作业按给定顺序经历以下状态:

  • STARTING(中间状态)

  • STARTED(稳定状态)

  • STOPPING(中间状态)

  • STOPPED(稳定状态)

在提交作业时,它以一个中间状态 'STARTING' 开始。在这种状态下,AlgoBulls 实时交易引擎获取策略并准备执行环境,这可能需要几分钟时间。完成后,作业将移至 'STARTED' 状态。策略实时交易发生在此阶段。在此阶段,它会停留直到实际交易完成。完成后,作业将移至一个中间状态 'STOPPING'。在此状态下,AlgoBulls 实时交易引擎清理为此作业分配的资源,通常需要不到一分钟的时间。最后,作业移至 'STOPPED' 状态。

如果您已经为某个策略提交了实际交易作业,则在第一个作业完成之前,不能为相同的策略提交另一个作业。这意味着您必须等待第一个作业移至 'STOPPED' 状态。如果第一个作业运行时间很长,并且您想立即停止它,您可以通过 pyalgotrading 提交一个停止作业请求。在提交请求之前,您需要确保作业处于 'STARTED' 状态。

在提交实际交易作业后,您可以实时获取策略执行的日志和报告。这些日志和报告有助于验证策略的性能并调试任何潜在问题。

您可以参考本章的第二个示例,了解 AlgoBulls 平台上实际交易工作的状态机图。它展示了实际交易工作在其整个生命周期中的各种状态和转换。

确保您已经阅读了本书的第八章的最后六个示例,逐步了解我们将使用的策略类的完整情况;也就是StrategyMACDBracketOrder

准备就绪

确保algobulls_connectionstrategy_code2对象在您的 Python 命名空间中可用。参考本章的前一节设置algobulls_connectionstrategy_code2对象。

如何做…

执行以下步骤以完成此示例:

  1. 导入必要的模块:
>>> from datetime import time
>>> from pyalgotrading.constants import *
  1. 搜索一个工具并使用其交易符号作为关键字。将返回的对象分配给instruments
>>> instrument = algobulls_connection.search_instrument('
                                                    TATASTEEL')
>>> instrument

我们得到了以下输出(您的输出可能有所不同):

[{'id': 1, 'value': 'NSE:TATASTEEL'}]
  1. instruments中获取选择的工具的value
>>> instrument = instrument[0]['value']
>>> instrument

我们得到了以下输出:

'NSE:TATASTEEL'
  1. 提交一个用于strategy_code2的实际交易工作:
>>> algobulls_connection.realtrade(
        strategy_code=strategy_code2,     
        start_time=time(hour=9, minute=15),
        end_time=time(hour=15, minute=30),
        instrument=instrument,
        lots=1,
        strategy_parameters={
            'fastma_period': 26, 
            'slowma_period': 6, 
            'signal_period': 9,
            'target_trigger': 0.01, 
            'stoploss_trigger': 0.01, 
            'trailing_stoploss_trigger': 1
        },
        candle_interval=CandleInterval.MINUTES_15)

我们得到了以下输出:

Setting Strategy Config... Success.
Submitting REALTRADING job... Success.
  1. 检查已提交的实际交易工作的状态:
>>> algobulls_connection.get_realtrading_job_status(strategy_code2)
{'data': 'STARTING'} 
  1. 过一段时间后再次检查已提交的实际交易工作的状态:
>>> algobulls_connection.get_realtrading_job_status(strategy_code2)
{'data': 'STARTED'}

它是如何工作的…

步骤 1中,您从datetime模块中导入了time类和pyalgotrading.constants模块中的所有常量。在步骤 2中,您使用algobulls_connection对象的search_instrument()方法获取您想要为其进行实际交易的工具,MACD-Bracket-Order 策略search_instrument()方法接受一个搜索字符串作为参数,该参数应该是您感兴趣的工具的交易符号的一部分或完整内容。您在这里传递了'TATASTEEL'。此函数返回一个包含与搜索字符串匹配的工具详情的列表。可能有多个工具的交易符号中包含搜索字符串。在步骤 3中,您获取第一个匹配工具的值,并将其分配给一个新变量,instrument

步骤 4中,您使用algobulls_connection()对象的realtrade()方法提交了一个实际交易工作。它接受以下参数:

  • strategy_code:要执行实际交易的策略的策略代码。这应该是一个字符串。您在这里传递strategy_code2

  • start_time:应该开始实际交易的今天的时间。这应该是一个datetime.time对象。在这里,您传递了一个保存了 9:15 小时值的对象 - time(hour=9, minute=15)。有关创建datetime对象的详细信息,请参阅本书的第一节。

  • end_time:应该在今天进行实际交易的时间。此对象应该保存一个在start_time保存的值之前的时间值。这应该是一个datetime.time实例。在这里,您传递了一个保存了 15:30 小时的值的对象 - time(hour=15, minute=30)

  • instrument: 应该运行实际交易的金融工具。将为此工具获取历史数据。这应该是一个字符串。在这里传递instrument

  • lots: 应该进行实际交易的手数。这应该是一个整数。数量由策略计算为手数×金融工具的手数。在这里传递1

  • strategy_parameters: 策略期望的参数名称和值。这应该是一个字典,其中parameter-nameparameter-value是键值对。在这里传递以下参数:

  • fastma_period: 26

  • slowma_period: 6 

  • signal_period: 9

  • target_trigger: 0.01

  • stoploss_trigger: 0.01

  • trailing_stoploss_trigger: 1

(回想一下,MACD-Bracket-Order 策略的参数已经在其__init__()方法中定义,如第八章的第一个示例中所示,算法交易策略 - 逐步编码)。

  • candle_interval: 用于实际交易获取的历史数据的蜡烛间隔。这应该是CandleInterval类型的枚举。在这里传递CandleInterval.MINUTES_15。(CandleInterval枚举提供各种蜡烛间隔的枚举,其中一些是MINUTE_1MINUTES_3MINUTES_5MINUTES_10MINUTES_15MINUTES_30HOURDAY。)

如果作业提交成功,您将看到由realtrade()函数打印的Success消息。

一旦任务提交,启动需要一段时间。启动后,根据使用start_timeend_time参数指定的实际交易持续时间,可能需要一段时间才能完成。通常,实际交易会持续整个交易日,这意味着任务将运行 6-8 小时。

步骤 5中,使用algobulls_connection对象的get_realtrading_job_status()方法获取作业的状态。在这里传递strategy_code2作为参数。此方法返回一个具有单个键值对 - 数据作业状态的字典。如果在放置作业后立即查询状态,则会将'STARTING'作为状态返回。在步骤 6中,经过一段时间后再次查询状态,如果作业已启动,则会返回状态'STARTED'

成功提交意味着已按所需格式传递了实际交易策略的最小输入。但是,这并不保证策略在实际交易中运行时不会出现错误。在实际交易期间,策略的执行仍可能遇到错误。要调试执行问题,您需要获取输出日志,下一个示例将对此进行说明。出错的可能原因可能是策略类 Python 代码中的错误或已将不完整的strategy_parameters字典传递给realtrade()函数。

还有更多...

如果一个任务运行了很长时间,并且你想在完成之前停止它,你可以使用 algobulls_connection 对象的 stop_realtrading_job() 方法。这个方法接受策略代码作为参数。在这里你传递 strategy_code2。这个方法提交一个停止请求给 AlgoBulls 真实交易引擎。如果请求被接受,你将会看到一个 Success 消息:

>>> algobulls_connection.stop_realtrading_job(strategy_code2)
 Stopping REALTRADING job... Success.

如果你在提交停止请求后查询状态,你会得到 'STOPPING' 作为状态:

>>> algobulls_connection.get_realtrading_job_status(strategy_code2)
{'data': 'STOPPING'}

如果你一段时间后再次查询状态,且任务已经停止,你将会得到 'STOPPED' 作为状态:

>>> algobulls_connection.get_realtrading_job_status(strategy_code2)
{'data': 'STOPPED'}

MACD–Bracket–Order 策略 – 实时获取真实交易日志

在 AlgoBulls 平台提交真实交易任务后,AlgoBulls 真实交易引擎开始执行策略。在执行过程中,AlgoBulls 真实交易引擎记录下每个事件和每个决策,并以文本日志的形式精确记录时间戳。

记录的活动示例包括给定策略配置、定期生成的每个新蜡烛、策略输入的交易、这些交易创建的头寸的进入和退出、等待新蜡烛等。这些日志对于验证策略和调试经常在开发策略时遇到的行为或性能问题至关重要。

在这个配方中,你将获取你的策略的真实交易日志。一旦你提交的真实交易任务达到 'STARTED' 状态,日志就会开始出现(有关真实交易任务状态的更多信息,请参考上一个配方)。AlgoBulls 平台允许你实时获取日志,即使真实交易任务仍在进行中。你可以在不等待真实交易任务完成的情况下了解策略的执行情况。这对于真实交易任务通常是长时间运行的情况很有帮助。pyalgotrading 包提供了一个简单的方法来获取给定策略的执行日志。

确保你已经学习了第八章的最后六个配方,算法交易策略 – 逐步编码,以获得我们将使用的策略类 StrategyMACDBracketOrder 的完整图片。

准备就绪

确保 algobulls_connectionstrategy_code2 对象在你的 Python 命名空间中可用。参考本章的MACD-Bracket-Order 策略 – 获取策略配方来设置 algobulls_connectionstrategy_code2 对象。

如何实现…

执行以下步骤来完成这个配方:

  1. 获取 strategy_code2 的真实交易执行日志:
>>> logs = algobulls_connection.get_realtrading_logs(
                                                strategy_code2)
>>> print(logs)

我们得到了以下输出(你的输出可能不同):

[2020-07-09 09:13:45] Logs not available yet. Please retry in sometime.
  1. 一段时间后,再次获取 strategy_code2 的真实交易执行日志:
>>> logs = algobulls_connection.get_realtrading_logs(
                                                strategy_code2)
>>> print(logs)

我们得到了以下输出(你的输出可能不同):

...
########################################
 INITIALIZING ALGOBULLS CORE (v3.2.0)... 
########################################
[2020-07-09 09:14:09] Welcome PUSHPAK MAHAVIR DAGADE!
[2020-07-09 09:14:09] Reading strategy...
[2020-07-09 09:14:09] STARTING ALGOBULLS CORE...
...
[2020-07-09 09:45:00] [CRITICAL] [order] [PLACING NEW ORDER] [2020-07-09 09:45:00][577e6b4cb646463282ae98ec1c0e6c25] [BUY] [NSE:TATASTEEL] [QTY:1] [QTY PENDING: 1] [ENTRY PRICE: 345.0] [PRICE:345.0] [TRIGGER PRICE:None] [ORDER_TYPE_BRACKET] [ORDER_CODE_INTRADAY] [ORDER_VARIETY_LIMIT] [ORDER_POSITION_ENTER] [STOPLOSS TRIGGER:341.55] [TARGET TRIGGER:348.45] [TRAILING STOPLOSS TRIGGER:345.0]
...
[2020-07-09 15:30:00] [INFO] [clock] Candle generation has been stopped...
[2020-07-09 15:30:00] [INFO] [tls] Received event END OF MARKET. Stopping Trading Core Engine...
[2020-07-09 15:30:00] [INFO] [tls] Exiting all open positions with order code: ORDER_CODE_INTRADAY (if any)...
[2020-07-09 15:30:00] [CRITICAL] [tls] [User: PUSHPAK MAHAVIR DAGADE] Trading session completed
...

此处未显示完整输出。请访问以下链接以阅读完整输出:github.com/algobulls/pyalgostrategypool/blob/master/pyalgostrategypool/sample/realtrading/strategy_macd_bracket_order/logs.txt

工作原理如下…

步骤 1中,您使用algobulls_connection对象的get_realtrading_logs()方法实时获取策略的真实交易日志。此方法接受策略代码作为参数。在此处传递strategy_code2。返回的数据是一个字符串。如果在提交任务后立即尝试此步骤,您会得到一个字符串,其中说日志尚未准备好([2020-07-09 09:13:45] Logs not available yet. Please retry in sometime.)。如果真实交易任务处于'STARTING'状态,则会出现此情况。

步骤 2中,您会在一段时间后再次获取日志。如果任务不再处于'STARTING'状态,则开始获取您的策略执行日志。每次调用get_realtrading_logs()函数时,都会获取所有真实交易日志。

还有更多…

当真实交易任务进入'STOPPED'状态后,将不再生成新的日志。您可以在提交下一个相同策略的真实交易任务之前的任何时间获取完整的日志。如果提交了新的真实交易任务(针对相同的策略),则无法再通过get_realtrading_logs()方法访问这些日志。如果您希望以后参考这些日志,可以将获取的日志保存到文件中。

MACD–Bracket–Order 策略 – 获取真实交易报告 – 利润和损失表

在 AlgoBulls 平台上提交真实交易任务后,AlgoBulls 真实交易引擎开始执行该策略。在执行过程中,除了日志外,AlgoBulls 真实交易引擎还实时生成 P&L 表。此表包含策略打入的每笔交易的信息。它还包含有关入场和出场订单之间的映射、交易 P&L 和累积 P&L 的详细信息,按时间顺序排序,最新的订单排在最前面。这张表通过个人和累积 P&L 数字为我们提供了有关策略整体表现的见解。入场-出场订单映射还有助于验证策略的行为。

在本示例中,您将获取您策略的 P&L 表报告。在提交真实交易任务后,您的策略第一笔交易打入后,此报告即可获得。AlgoBulls 平台允许您实时获取 P&L 表,即使真实交易任务仍在进行中。这样做有助于了解策略的表现,而无需等待真实交易任务完成。这对于长时间运行的真实交易任务非常有帮助。pyalgotrading包提供了一个简单的方法,我们可以用来获取给定策略的 P&L 表。

确保你已经熟悉了 第八章 的最后六个示例,逐步编码算法交易策略,以完整了解我们将使用的策略类;即 StrategyMACDBracketOrder

准备工作

确保 algobulls_connectionstrategy_code2 对象在你的 Python 命名空间中可用。参考本章的 MACD-Bracket-Order 策略 – 获取策略 示例设置 algobulls_connectionstrategy_code2 对象。

如何做…

获取 strategy_code2 的真实交易盈亏报告:

>>> algobulls_connection.get_realtrading_report_pnl_table(strategy_code2)

我们得到了以下输出。你的输出可能会有所不同(注意以下输出已分成多个表格以表示目的。在你的 Jupyter Notebook 中,你将看到一个宽表格):

工作原理…

在这个示例中,你将使用 algobulls_connection 对象的 get_realtrading_report_pnl_table() 方法实时获取真实交易的盈亏表。该方法接受策略代码作为参数。你在这里传递了 strategy_code2。返回的数据是一个 pandas.DataFrame 对象,具有多列,描述如下:

  • instrument: 进行交易的金融工具。

  • entry_timestamp: 放置进入订单的时间戳。(注意,它可能在进入 'OPEN' 状态一段时间后才转入 'COMPLETE' 状态。)

  • entry_transaction_type: 进入订单交易类型(BUYSELL)。

  • entry_quantity: 进入订单数量。

  • entry_price: 进入订单执行并进入 'COMPLETE' 状态的价格。

  • exit_timestamp: 放置退出订单的时间戳。(注意,它可能在进入 'OPEN' 状态一段时间后才转入 'COMPLETE' 状态。)

  • exit_transaction_type: 退出订单交易类型(BUYSELL)。

  • exit_quantity: 退出订单数量。

  • exit_price: 退出订单执行并进入 'COMPLETE' 状态的价格。

  • pnl_absolute: 退出订单执行价格与进入订单执行价格的差额。从数学上讲,这是长期交易的 (退出价格 - 进入价格)退出数量,短期交易的 (进入价格* - 退出价格)退出数量*。正值意味着该交易是盈利交易。负值意味着该交易是亏损交易。

  • pnl_percentage: 相对于进入价格的利润或损失百分比。从数学上讲,这是 pnl_absolute / entry_price / exit_quantity x 100

  • pnl_cumulative_absolute: 累积盈亏。从数学上讲,这是之前所有交易的 pnl_absolute 值的总和。这个数字直接反映了策略在模拟时间内的表现。

  • pnl_cumulative_percentage: 相对于入场价格的累计利润或损失的百分比。从数学上讲,这是 pnl_cumulative / entry_price / exit_quantity x 100

更多内容...

一旦真实交易任务转移到 'STOPPED' 状态,P&L 表报告将不再更新。您可以在提交下一个相同策略的真实交易任务之前的任何时候获取完整的 P&L 报告。如果提交了新的真实交易任务(相同的策略),则无法再通过 get_realtrading_report_pnl_table() 方法访问此报告。如果您希望以后参考,可以将获取的报告保存为 .csv 文件。

MACD-Bracket-Order 策略 - 获取真实交易报告 - 统计表

在 AlgoBulls 平台上提交了一个真实交易任务后,AlgoBulls 真实交易引擎开始执行策略。在其执行过程中,除了日志和 P&L 表之外,AlgoBulls 真实交易引擎还实时生成来自 P&L 表的摘要。此摘要是包含各种统计数字的统计表,如 净利润(绝对值和百分比)、最大回撤(绝对值和百分比)、总交易次数、盈利交易次数、亏损交易次数、多头交易和空头交易次数、最大盈利和最小盈利(或最大亏损),以及每个盈利和亏损交易的平均利润。此表提供了策略整体表现的即时概览。

在这个示例中,您将获取您的策略的统计表报告。此报告在您提交了一个真实的交易任务后,只要您的策略首次进行了交易,就可以获得。AlgoBulls 平台允许您在真实交易任务仍在进行时实时获取统计表。您可以在等待真实交易任务完成之前了解策略的表现。这对于真实交易任务通常是长时间运行的情况很有帮助。pyalgotrading 包提供了一个简单的方法,我们可以用来获取给定策略的统计表。

确保您已经阅读了第八章的最后六个示例,算法交易策略 - 逐步编码,以完整了解我们将要使用的策略类别; 即 StrategyMACDBracketOrder

准备工作

确保您的 Python 命名空间中有 algobulls_connectionstrategy_code2 对象可用。参考本章的 MACD-Bracket-Order 策略 - 获取策略 示例来设置 algobulls_connectionstrategy_code2 对象。

如何做…

获取strategy_code2的真实交易统计报告:

>>> algobulls_connection.get_realtrading_report_statistics(strategy_code2)

我们得到了以下输出(您的输出可能会有所不同):

工作原理…

在本示例中,您使用algobulls_connection对象的get_realtrading_report_statistics()方法实时获取真实交易统计表。此方法接受策略代码作为参数。您在此处传递了strategy_code2。返回数据是一个pandas.DataFrame对象,具有两列 - highlight_typehighlight_value - 和多行。行的描述如下:

  • Net PnL: 累计真实交易盈亏。这也是盈亏表中第一项的pnl_cumulative_absolute值。

  • Net PnL %: 累计真实交易盈亏百分比。这也是盈亏表中第一项的pnl_cumulative_percentage值。

  • Max Drawdown: 盈亏表中pnl_cumulative列的最低值。这表示您的策略在执行过程中遇到的最大损失。

  • Max Drawdown %: 在数学上,这是 (最大回撤) / (对应的入场价格) / 退出数量 x 100。

  • Number of Trades: 会话期间的总交易数(入场和出场计为一次)。

  • Number of Wins: 交易盈亏为非负数的交易数量。

  • Number of Losses: 交易盈亏为负数的交易数量。

  • Number of Long Trades: 入场交易类型为'BUY'的交易数量。

  • Number of Short Trades: 入场交易类型为'SELL'的交易数量。

  • Max Gain: 所有交易中盈利最高的交易的盈亏。

  • Min Gain: 所有交易中盈利最低的交易的盈亏。

  • Avg. Profit per winning trade: 在数学上,这是 (盈利交易的总盈亏) / (盈利交易的数量)

  • Avg. Profit per losing trade: 在数学上,这是 (亏损交易的总盈亏) / (亏损交易的数量)

还有更多...

如果在真实交易作业仍在运行时获取统计表,则上述数字将是中间数字,基于截至该时间的已完成交易。随着更多交易的录入,这些数字可能会发生变化,直到真实交易作业完成。

一旦真实交易作业转移到'STOPPED'状态,统计表将不再更改。您可以在提交下一个相同策略的真实交易作业之前随时获取完整的统计表。如果提交了新的真实交易作业(相同的策略),则无法再通过get_realtrading_report_statistics()方法访问此表。如果您希望以后参考,可以将获取的报告保存为.csv文件。

附录 I:设置您的 Zerodha 账户

本附录将帮助您设置与 Zerodha 的证券账户(zerodha.com)。

本附录将解释以下部分:

  • 在线开设 Zerodha 账户

  • 登录到 Zerodha 交易平台网站

  • 设置您的 Zerodha 开发者选项账户

  • 登录到 Zerodha 开发者选项网站

  • 购买并启用 Zerodha 开发者选项 API

  • 测试 API 密钥并通过发出您的第一个 API 调用授权应用程序

在线开设 Zerodha 账户

您可以按照此 YouTube 视频中解释的步骤在线开设 Zerodha 账户:www.youtube.com/watch?v=dcOIc8YZ9pc

如果您有视频中提到的必要文件,账户设置可以在不到 30 分钟内完成。 完成后,您的账户开户流程已启动,您必须等待 Zerodha 的账户开户团队的响应。 Zerodha 通常会在一周内回复您的账户凭证。

如果您在开设账户方面需要任何帮助,也可以访问 Zerodha 支持链接(support.zerodha.com/category/account-opening/online-account-opening/articles/how-do-i-open-an-account-online)。

登录到 Zerodha 交易平台网站

成功开设 Zerodha 账户后,您可以使用您收到的凭证登录到他们的交易平台,称为 Kite。 访问 kite.zerodha.com

访问网站后,您可以通过以下五个步骤登录到网站:

  1. 输入您的用户 ID。

  2. 输入您的密码。

  3. 单击登录按钮:

  1. 输入您的 PIN:

  1. 点击继续按钮。

成功登录后,您应该会看到以下截图中显示的仪表板:

设置您的 Zerodha 开发者选项账户

一旦您获得了 Zerodha 证券账户凭据,我们现在可以继续设置 Zerodha 的开发者选项账户。

请前往 kite.trade 并点击注册

您可以通过以下八个步骤注册新账户:

  1. 输入您的电子邮件地址。

  2. 输入您的名字

  3. 输入您的密码。

  4. 验证您的密码。

  5. 输入您的电话号码。

  6. 选择您的居住州。

  7. 阅读并检查我同意上述条款复选框。

  8. 单击注册按钮:

登录到 Zerodha 开发者选项网站

成功注册 Kite Connect 后,您可以使用您设置的凭据登录。 访问 kite.trade

你可以通过三个步骤登录到网站:

  1. 输入你注册的电子邮箱 ID。

  2. 输入你的密码(用于注册的密码)。

  3. 点击登录按钮:

第一次登录后,你应该会登陆到这样的页面:

购买并启用 Zerodha 开发者选项 API

有两种类型的 API 可用:

  • Connect API:此 API 允许通过 API 下订单,获取持仓/持仓/订单,获取配置文件等。这个 API 对于实际交易是必需的,但对于回测或模拟交易是不必要的。

  • 历史数据 API:此 API 允许获取历史数据。如果您正在实施自己的算法交易系统,则可能需要此 API。您还可以从其他供应商获取历史数据。

这两个 API 的文档可以在此处找到:kite.trade/docs/connect

如果你打算在实际交易中使用 AlgoBulls 交易平台(algobulls.com),那么你只需要为你的经纪账户购买和启用 Connect API。你不需要为回测或模拟交易购买任何 API。AlgoBulls 平台提供所有服务的历史数据。

登录到kite.trade后,你可以通过以下两个步骤购买所需的 API 积分:

  1. 点击顶部菜单中的计费。这将加载一个新页面:

  1. 输入所需金额,然后点击添加积分按钮。通过弹出的支付网关窗口完成支付:

如果你想要启用这两个 API,所需金额应为4000。如果你只想启用 Connect API,则所需金额应为2000。登录到developers.kite.trade/create了解购买 API 所需确切金额的更多信息。

接下来,为了启用所需的 API,应该从首页执行以下步骤:

  1. 点击创建新应用程序。这将加载一个新页面:

  1. 确保选择了类型Connect

  2. 输入你的应用程序名称。

  3. 输入你的 Zerodha 客户 ID。

  4. http://127.0.0.1作为你的重定向 URL。(如果你在本地端口80上托管服务器,你可以输入一个不同端口的 URL,该端口未被使用,比如http://127.0.0.1:8000)。

  5. 为你的应用程序输入一个描述。

  6. 点击创建按钮:

  1. 阅读确认消息并输入I UNDERSTAND

  2. 点击OK按钮:

这完成了你的第一个应用程序的创建。现在你应该在首页看到你的应用程序:

单击该应用程序以检查您的详细信息并获取 API 密钥和 API 密钥。 可选地,如果您想激活历史数据 API,请在附加组件部分单击订阅,并确认订阅。 这将花费 2,000 积分:

如下屏幕截图所示,可以获取密钥(API 密钥API 密钥)。 单击显示 API 密钥按钮以显示它:

通过发出您的第一个 API 调用来测试 API 密钥并授权该应用程序。

您可以使用一个简单的 Python 程序测试您的 API 密钥。 执行以下步骤:

  1. pyalgotrading中导入 Zerodha 经纪人连接类:
>>> from pyalgotrading.broker.broker_connection_zerodha import BrokerConnectionZerodha
  1. 使用您的 API 密钥和 API 密钥创建经纪人连接:
>>> api_key = "<your-api-key>"
>>> api_secret = "<your-api-secret>"
>>> broker_connection = BrokerConnectionZerodha(api_key, api_secret)

您将获得以下结果:

https://kite.trade/connect/login?api_key=

您需要通过单击生成的链接并按照以下步骤进行 Zerodha 登录:

  1. 输入您的用户 ID。

  2. 输入您的密码(用于交易平台)。

  3. 单击登录按钮:

  1. 输入您的 PIN。

  2. 单击继续按钮:

  1. 单击授权按钮:

此步骤仅在新的 Zerodha Developer Options 应用程序中发生一次。

  1. 如果您的凭据正确,您将被重定向到127.0.0.1。 忽略可能显示的任何错误消息。 只需从您的标签页的地址栏中复制令牌。 令牌是在request_token=&字符之间的字符串,两者都不包括。 每次按照这些步骤操作时都会生成一个新的随机令牌。 例如,如果重定向 URL 为https://127.0.0.1/?request_token=iHCKrv8oAM9X2oPRURMNRdZdG4uxhfJq&action=login&status=success,则令牌为iHCKrv8oAM9X2oPRURMNRdZdG4uxhfJq

  1. 设置通过上一输出中给出的 URL 接收的令牌:
>>> request_token = "<your-request-token>"
>>> broker_connection.set_access_token(request_token)
  1. 您可以通过获取您的个人资料详细信息来检查连接是否成功:
>>> broker_connection.get_profile()

这将产生以下输出:

{'user_id': <your-user-id>,
 'user_type': 'individual',
 'email': '<your-email-id>',
 'user_name': <your-user-name>',
 'user_shortname': <your-user-shortname>',
 'broker': 'ZERODHA',
 'exchanges': ['CDS', 'MF', 'NFO', 'NSE', 'BSE'],
 'products': ['CNC', 'NRML', 'MIS', 'BO', 'CO'],
 'order_types': ['MARKET', 'LIMIT', 'SL', 'SL-M'],
 'avatar_url': '',
 'meta': {'demat_consent': 'physical'}}

这成功验证了 API 密钥是否有效。

附录 II:设置您的 AlgoBulls 账户

本附录将帮助您在 AlgoBulls(algobulls.com)上设置您的账户。

本附录解释了以下部分:

  • 在 AlgoBulls 平台上注册

  • 登录到 AlgoBulls 网站

  • 获取您的 AlgoBulls 开发者选项令牌

  • 为实际交易设置您的经纪账户

在 AlgoBulls 平台上注册

您可以通过转到官方网站algobulls.com,并点击 SIGNUP 注册 AlgoBulls 平台,如下面的截图所示:

您可以在九个步骤中注册该网站,如下所示:

  1. 输入您的名字。

  2. 输入您的电子邮件 ID。

  3. 输入您的电话号码。确保此电话号码可用于接收一次性密码OTP)。

  4. 点击“I'm not a robot”复选框。

  5. 点击获取 OTP 按钮,如下面的截图所示:

  1. 等待直到您收到手机上的 OTP。收到后输入 OTP。

  2. 输入您的密码。

  3. 确认您的密码。

  4. 点击注册按钮,如下面的截图所示:

您现在已在该网站上注册。点击 Go to Login 按钮登录到该网站,如下面的截图所示:

登录到 AlgoBulls 网站

您可以通过转到官方网站algobulls.com并点击 LOGIN 登录到 AlgoBulls 平台,如下面的截图所示:

您可以在三个步骤中登录到网站,如下:

  1. 输入您注册的电话号码。

  2. 输入您的密码。

  3. 点击登录按钮,如下面的截图所示:

获取您的 AlgoBulls 开发者选项令牌

登录后,您可以通过从侧边栏导航到开发者选项页面—Settings | General | Developer Options | API Token 来获取您的开发者选项令牌。您可以点击查看按钮(右侧的眼睛图标)来查看和复制应用程序编程接口API)令牌,如下面的截图所示:

例如,前面截图中显示的 API 令牌为b042fdd8b8cc996abe6773edb4c8ead28acc1c47

设置您的 AlgoBulls 账户

您需要执行以下操作才能从 AlgoBulls 平台获得算法交易服务:

  • 订阅计划:服务 - 回测、模拟交易和实盘交易 - 是收费的。您需要在使用这些服务之前订阅一个计划。有免费计划可供您在有限的时间内使用这些服务。您可以订阅这些计划来测试本章中的所有策略。稍后,您可以订阅付费计划,以月付的方式使用服务。

  • 对于真实交易(第十一章,算法交易 - 真实交易),您需要额外设置以下内容:

  • 风险管理设置:这些设置帮助您在处理真实资金时限制损失。

  • 添加经纪人:您需要将您的经纪账户与 AlgoBulls 连接起来。您可以选择 AlgoBulls 支持的任何经纪人。

你可以在help.algobulls.com/developers/找到设置这些细节的逐步信息和截图。任何可能随时间而来的额外设置说明都将在该链接上提供。你也可以通过 developers@algobulls.com 联系我们以获取任何额外需求。

附录 III:开发和改进策略

在这个附录中,我们将涵盖一些关于执行算法交易策略时应考虑的关键要点。

策略的盈利性受季节影响

策略可能不会全年都有良好的回报。它们可能是季节性的,这意味着它们在一年中的某些时候表现良好,而在其他时候表现不佳。因此,识别策略的正确时间或正确季节,并仅在那些时间使用它是至关重要的。

策略的盈利性受其参数值影响

策略依赖于各种参数。相同的策略可能对不同的工具和技术指标的不同值表现不同。例如,具有参数(时间段)4 和 9 的指数移动平均线EMA)策略可能对股票 X 表现良好,但具有不同参数值(例如 5 和 13)的相同策略可能不适用于股票 X,甚至相同参数值的相同策略可能不适用于股票 Y。因此,找到正确的工具和参数可以使您的策略成功。

您可以使用优化算法找到使您的策略盈利的正确参数组合。成本函数可以是您的回测收益和损失(要最大化)以及回撤(要最小化)。变量可以是工具和策略参数值。

仅仅通过回测不能保证策略的盈利性

一个盈利的回测报告是盈利交易的先决条件之一,但并非唯一的先决条件。这增加了策略在实际交易中表现良好的机会,但并不保证。除了历史结果外,还有许多其他因素可能影响实际策略的表现。风险管理条件应该在你的策略中得到很好的安置,以减少在任何这种不可预见情况下的不利影响。确保这一点的一种方法是通过使用括号盖单,在任何时候都放置强制性的止损。

经纪人的限制

并非所有经纪人都提供用于算法交易的 API。此外,即使提供了 API,经纪人也可能不支持所有类型的订单,例如内置风险管理的括号或盖单。在利用某个特定经纪人的服务之前,请检查并验证其提供的所有支持和服务。选择正确的经纪人可能会最大程度地减少你的策略编码工作。

与社区保持联系

你可以通过在论坛上向社区提问来获得编写自己策略的支持。你也可以从技术分析和其他算法交易论坛的书籍中获取见解和指引。此外,你可以关注提供免费策略及其 Python 代码的 GitHub 仓库(例如,github.com/algobulls/pyalgostrategypool)。

准备好在实际交易中遇到技术故障

无论你的策略有多么强大,实际交易中的策略执行可能不会按计划进行。这可能出现各种原因:

  • 经纪商 API 可能由于服务器过载而出现超时故障。这种情况经常发生在市场开盘时间,那时大量交易者几乎同时下单以抓住市场开盘机会。

  • 经纪商技术栈可能依赖于多个供应商,除了其自有的专有技术之外,这意味着即使其中一个失败,你也可能成为受害者,因为你的订单可能无法通过。

  • 如果你使用的是算法交易平台,由于上述第一点提到的相同原因,它可能会失败。

  • 你的策略可能会失败,因为它可能遇到了测试中未涵盖的新条件。例如,如果在实际交易中下达涉及非常大数量的订单,订单可能会分成多个较小的订单分别执行。如果你的策略没有考虑到这一点,它可能会失败。此外,这样的情况在回测过程中无法发现,因为这是虚拟交易,订单从不分裂,因此提供解决方案可能会比较棘手。

  • 历史数据源可能会消失。可能是过时数据,也可能是没有数据,这两种情况都可能导致根据你的策略做出错误决策。

posted @ 2024-05-09 17:01  绝不原创的飞龙  阅读(10)  评论(0编辑  收藏  举报