与纽约证券交易所等传统股票交易所的固定交易时间不同,加密货币是 24/7 全天候交易的,这使得任何人都不可能独自监控市场。
过去,我经常需要处理与我的加密货币交易相关的以下问题:
- 隔夜发生了什么?
- 为什么没有日志条目?
- 为什么下了这个订单?
- 为什么没有下订单?
通常的解决方案是使用加密货币交易机器人,在您做其他事情时(例如睡觉、陪伴家人或享受空闲时间)为您下单。有很多商业解决方案可用,但我想要一个开源选项,所以我创建了加密货币交易机器人 Pythonic。正如 我去年在一篇介绍性文章中写道,“Pythonic 是一款图形化编程工具,使用户能够轻松地使用现成的功能模块创建 Python 应用程序。” 它起源于加密货币机器人,并具有广泛的日志引擎和经过良好测试的可重用部件,例如调度器和计时器。
入门
这篇实践<0xC2><0xA0>教程教您如何开始使用 Pythonic 进行自动化交易。它使用在 Binance 交易平台上交易 Tron 对 Bitcoin 的示例。我选择这些币是因为它们彼此之间的波动性,而不是任何个人偏好。
机器人将根据指数移动平均线 (EMA) 做出决策。

TRX/BTC 1 小时蜡烛图
EMA 指标通常是一个加权移动平均线,它赋予最近的价格数据更多的权重。尽管移动平均线可能是一个简单的指标,但我使用它有很好的经验。
上图中的紫色线显示了 EMA-25 指标(意味着考虑了最近 25 个值)。
机器人监控当前 EMA-25 值 (t0) 和前一个 EMA-25 值 (t-1) 之间的倾斜度。如果倾斜度超过某个值,则表示价格上涨,机器人将下达买入订单。如果倾斜度低于某个值,机器人将下达卖出订单。
倾斜度将是制定交易决策的主要指标。在本教程中,它将被称为交易因子。
工具链
本教程中使用了以下工具:
- Binance 专业交易视图(可视化<0xC2><0xA0>数据已经由许多其他人完成,因此没有必要通过自己做来重新发明轮子)
- Jupyter Notebook 用于<0xC2><0xA0>数据科学任务
- Pythonic,它是<0xC2><0xA0>总体框架
- PythonicDaemon 作为<0xC2><0xA0>纯运行时(仅限控制台和 Linux)
数据挖掘
为了让加密货币交易机器人做出好的决策,以可靠的方式获取资产的开盘价-最高价-最低价-收盘价 (OHLC) 数据至关重要。您可以使用 Pythonic 的内置元素并使用自己的逻辑扩展它们。
一般工作流程是:
- 与 Binance 时间同步
- 下载 OHLC 数据
- 将现有的 OHLC 数据从文件加载到内存中
- 比较两个数据集,并用较新的行扩展现有数据集
此工作流程可能有点过分,但它使此解决方案非常强大,可以抵抗停机和断开连接。
要开始,您需要 Binance OHLC Query 元素和一个 Basic Operation 元素来执行您自己的代码。

数据挖掘工作流程
OHLC 查询设置为以一小时的间隔查询资产对 TRXBTC (Tron/Bitcoin)。

配置 OHLC 查询元素
此元素的输出是一个 Pandas DataFrame。您可以使用 Basic Operation 元素中的 input 变量访问 DataFrame。在这里,Basic Operation 元素设置为使用 Vim 作为默认代码编辑器。

Basic Operation 元素设置为使用 Vim
以下是代码的样子:
import pickle, pathlib, os
import pandas as pd
outout = None
if isinstance(input, pd.DataFrame):
file_name = 'TRXBTC_1h.bin'
home_path = str(pathlib.Path.home())
data_path = os.path.join(home_path, file_name)
try:
df = pickle.load(open(data_path, 'rb'))
n_row_cnt = df.shape[0]
df = pd.concat([df,input], ignore_index=True).drop_duplicates(['close_time'])
df.reset_index(drop=True, inplace=True)
n_new_rows = df.shape[0] - n_row_cnt
log_txt = '{}: {} new rows written'.format(file_name, n_new_rows)
except:
log_txt = 'File error - writing new one: {}'.format(e)
df = input
pickle.dump(df, open(data_path, "wb" ))
output = df
首先,检查输入是否为 DataFrame 类型。然后在用户的主目录 (~/) 中查找名为 TRXBTC_1h.bin 的文件。如果存在,则打开它,连接新行(try 部分中的代码),并删除重叠的重复项。如果该文件不存在,则触发一个 exception 并执行 except 部分中的代码,创建一个新文件。
只要启用复选框 log output,您就可以使用命令行工具 tail 跟踪日志记录。
$ tail -f ~/Pythonic_2020/Feb/log_2020_02_19.txt
出于开发目的,现在跳过与 Binance 时间的同步和定期调度。这将在下面实现。
数据准备
下一步是在单独的网格中处理评估逻辑;因此,您必须借助 Return element 将 DataFrame 从网格 1 传递到网格 2 的第一个元素。
在网格 2 中,通过 Basic Technical Analysis 元素传递 DataFrame,从而通过包含 EMA 值的列来扩展 DataFrame。

配置技术分析元素以计算 25 个周期内的 EMA。

配置<0xC2><0xA0>技术分析元素
当您运行整个设置并激活 Technical Analysis 元素的调试输出时,您将意识到 EMA-25 列的值看起来都相同。

这是因为调试输出中的 EMA-25 值仅包含六位小数,即使输出保留了 8 字节浮点值的完整精度。
为了进一步处理,添加一个 Basic Operation 元素

网格 2 中的工作流程
使用 Basic Operation 元素,转储带有附加 EMA-25 列的 DataFrame,以便可以将其加载到 Jupyter Notebook 中;

将扩展的 DataFrame 转储到文件
评估逻辑
在 Juypter Notebook 中开发评估逻辑使您能够以更直接的方式访问代码。要加载 DataFrame,您需要以下行:

用所有小数位表示
您可以使用 iloc 和列名访问最新的 EMA-25 值。这保留了所有的小数位。
您已经知道如何获取最新值。上面的示例的最后一行仅显示该值。要将该值复制到单独的变量,您必须使用 .at 方法访问它,如下所示。
您还可以直接计算交易因子,这将在下一步中需要。

确定交易因子
正如您在上面的代码中看到的,我选择了 0.009 作为交易因子。但我怎么知道 0.009 是否是用于决策的良好交易因子呢?实际上,这个因子真的很糟糕,所以相反,您可以暴力破解表现最佳的交易因子。
假设您将根据收盘价买入或卖出。

在本例中,buy_factor 和 sell_factor 是预定义的。因此,扩展逻辑以暴力破解表现最佳的值。

用于确定买入和卖出因子的嵌套 for 循环
这有 81 个循环要处理 (9x9),在我的机器(Core i7 267QM)上需要几分钟。

暴力破解时的系统利用率
在每个循环之后,它将 buy_factor、sell_factor 和结果 profit 的元组附加到 trading_factors 列表中。按利润降序排列列表。

按利润降序排列与相关交易因子的利润
当您打印列表时,您可以看到 0.002 是最有希望的因子。

当我在 2020 年 3 月写这篇文章时,价格波动不足以呈现更有希望的结果。我在 2 月份获得了更好的结果,但即使在那时,表现最佳的交易因子也约为 0.002。
拆分执行路径
现在启动一个新的网格以保持清晰度。使用 Return 元素将带有 EMA-25 列的 DataFrame 从网格 2 传递到网格 3 的元素 0A。
在网格 3 中,添加一个 Basic Operation 元素来执行评估逻辑。以下是该元素的代码:

如果应该买入,则元素输出 1;如果应该卖出,则输出 -1。输出 0 表示现在没事可做。使用 Branch 元素来控制执行路径。

由于 0 和 -1 都以相同的方式处理,因此您需要在最右侧的执行路径上添加一个额外的 Branch 元素,以决定是否应该卖出。

网格 3 现在应该看起来像这样:

执行订单
由于您不能买入两次,因此您必须在周期之间保持一个持久变量,该变量指示您是否已经买入。
您可以使用 Stack element 来执行此操作。顾名思义,Stack 元素是基于文件的堆栈的表示,可以填充任何 Python 数据类型。
您需要定义堆栈仅包含一个布尔元素,该元素确定您是否已买入 (True) 或未买入 (False)。因此,您必须使用一个 False 预设堆栈。例如,您可以在网格 4 中通过简单地将 False 传递到堆栈来设置它。

分支树之后的 Stack 实例可以配置如下:

配置 Stack 元素
在 Stack 元素配置中,将 Do this with input 设置为 Nothing。否则,布尔值将被 1 或 0 覆盖。
此配置确保堆栈中只保存一个值(True 或 False),并且只能读取一个值(为了清晰起见)。
在 Stack 元素之后,您需要一个额外的 Branch 元素来评估堆栈值,然后再放置 Binance Order 元素。

评估<0xC2><0xA0>来自堆栈的变量
将 Binance Order 元素附加到 Branch 元素的 True 路径。网格 3 上的工作流程现在应该看起来像这样:

Binance Order 元素配置如下:

配置<0xC2><0xA0>Binance Order 元素
您可以在 Binance 网站上的帐户设置下生成 API 和 Secret 密钥。

在 Binance 帐户设置中创建 API 密钥
在本教程中,每笔交易都作为市价单执行,成交量为 10,000 TRX(2020 年 3 月约为 150 美元)。(为了本教程的目的,我正在演示使用市价单的整个过程。因此,我建议至少使用限价单。)
如果订单未正确执行(例如,连接问题、资金不足或货币对不正确),则不会触发后续元素。因此,您可以假设如果后续元素被触发,则订单已下达。
以下是来自 XMRBTC 成功卖出订单的输出示例:

成功的卖出订单
此行为使后续步骤更加方便:您可以始终假设,只要输出正确,订单就已下达。因此,您可以附加一个 Basic Operation 元素,该元素只需将输出写入 True,并将此值写入堆栈,以指示订单是否已下达。
如果出现问题,您可以在日志消息中找到详细信息(如果启用了日志记录)。

来自<0xC2><0xA0>Binance Order 元素的日志输出
计划和同步
对于定期调度和同步,请在网格 1 中的整个工作流程前面加上 Binance Scheduler 元素。

网格 1 中位置 1A 的 Binance Scheduler
Binance Scheduler 元素仅执行一次,因此在网格 1 的末尾拆分执行路径,并通过将输出传递回 Binance Scheduler 元素来强制其重新同步自身。

元素 5A 指向网格 2 的元素 1A,元素 5B 指向网格 1 的元素 1A (Binance Scheduler)。
部署
您可以在本地机器上 24/7 全天候运行整个设置,或者您可以将其完全托管在廉价的云系统上。例如,您可以使用 Linux/FreeBSD 云系统,每月约 5 美元,但它们通常不提供窗口系统。如果您想利用这些低成本云,可以使用 PythonicDaemon,它完全在终端内运行。

PythonicDaemon 控制台
PythonicDaemon 是基本安装的一部分。要使用它,请保存您的完整工作流程,将其传输到远程运行系统(例如,通过安全复制 [SCP]),并使用工作流程文件作为参数启动 PythonicDaemon
$ PythonicDaemon trading_bot_one
要在系统启动时自动启动 PythonicDaemon,您可以向 crontab 添加一个条目
# crontab -e

下一步
正如我在开头写的那样,本教程只是自动化交易的起点。编程交易机器人大约 10% 是编程,90% 是测试。当涉及到让您的机器人用您的钱进行交易时,您肯定会对您编程的代码三思而后行。因此,我建议您尽可能保持代码简单易懂。
如果您想继续自行开发您的交易机器人,接下来要设置的是:
- 自动利润计算(希望只有正数!)
- 您想要买入的价格的计算
- 与您的订单簿进行比较(即,订单是否已完全成交?)
您可以在 GitHub 上下载整个示例。
4 条评论