metaseller/tinkoff-robot-buyer

一个基于PHP的控制台应用程序,演示了如何使用Tinkoff Invest Api 2,同时实现了ETF简单购买机器人的演示策略。

0.2.2 2022-05-20 06:49 UTC

README

该项目是为了展示与Tinkoff Invest Api 2(https://www.tinkoff.ru/invest/open-api/)的基本操作能力而制作的,通过PHP实现,同时也是为了参加Tinkoff Invest Robot Contest(https://meetup.tinkoff.ru/event/tinkoff-invest-robot-contest/https://t.me/tinkoff_invest_robot_contest)而设计的。

简介

Tinkoff Invest API的第二个版本目前定位为与Tinkoff Investment交易平台的gRPC接口。

目前,已提供适用于Python(https://github.com/Tinkoff/invest-python)、Java(https://github.com/Tinkoff/invest-api-java-sdk)、C#(https://github.com/Tinkoff/invest-api-csharp-sdk)等流行编程语言的官方SDK。

为了简化操作,我决定制作一个小型的非官方SDK,用于PHP7(https://github.com/metaseller/tinkoff-invest-api-v2-php)以及用于流行框架Yii 2 Framework的包装器(https://github.com/metaseller/tinkoff-invest-api-v2-yii2)。

计划通过实现类似InstrumentsProviderhttps://github.com/metaseller/tinkoff-invest-api-v2-php/blob/main/src/providers/InstrumentsProvider.php)的数据提供程序来进一步改进SDK的功能。如果有人愿意加入这个过程,我将非常高兴 :)

最初的想法是降低门槛,让PHP开发者更容易使用。虽然PHP不是开发交易系统的最佳选择,但许多开发者可能会希望在PHP产品/网站上使用Tinkoff Invest Api 2的功能。

SDK以库的形式提供,可以通过composer轻松安装。

基于此SDK,决定在Yii2 Framework上创建一个简单的项目,以参加Tinkoff Invest Robot Contest。

该项目作为一个控制台应用程序运行,旨在

  1. 展示SDK的使用方法,降低门槛,让使用Tinkoff Invest Api 2的PHP开发者更容易使用。
  2. 展示使用Tinkoff Invest Api 2的基本功能(如何连接到API,如何请求账户和投资组合标识符,如何获取投资组合信息,如何获取投资组合充值历史,如何订阅K线流)。
  3. 实现一个简单的ETF购买机器人的逻辑,通过配置文件配置,作为控制台应用程序运行,可以通过cron调用。

购买机器人的工作策略

实现的机器人不是一个赚取买卖差价的 scalp robot。这是一个购买ETF的机器人(在微小修改后也可以购买其他工具,但这只是作业!) :)

一种流行的投资策略是定期购买并持有你相信的东西。定期意味着在上涨和下跌时都购买,而不是试图捕捉底部或趋势。均匀性可以消除所有这些。而且,位置组合越均匀,越好!我们的机器人会将这个想法推向极致 :)

为了示例实现,机器人将定期购买配置中指定的ETF,使用自由实现的跟踪买入算法(重新表述的跟踪止损),随时准备建仓,并在上涨或回调到局部最低点的一定范围内购买。

选择了简单易懂、门槛低的简单策略实现,甚至不需要连接数据库。机器人也不使用流数据,只需要按照cron规则定期执行。

机器人是如何工作的

首先,机器人通过文件./config/tinkoff-buy-strategy.php以下参数进行配置

[
    'ETF' => [
        'TMOS' => [
            'ACTIVE' => true,
            'INCREMENT_VALUE' => 1, // На сколько мы увеличиваем накопленное количество лотов позиций к покупке через каждый период
            'INCREMENT_PERIOD' => 10, // Период в минутах, через который мы инкрементируем количество лотов позиций к покупке
            'BUY_CHECK_PERIOD' => 1, // Период в минутах, через который мы проверяем возможность покупки накопленного количества лотов позиций
            'BUY_LOTS_BOTTOM_LIMIT' => 5, // Не пытаемся совершить покупку, пока не достигнут указанный накопленный лимит лотов к покупке
            'BUY_TRAILING_PERCENTAGE' => 0.09, //Величина в процентах, на которую текущая цена должна превысить трейлинг цену для совершения покупки
        ],
    ],
];

机器人定期按以下逻辑购买ETF #TMOS(https://www.tinkoff.ru/invest/etfs/TMOS/

  1. 有一个名为 "待购买累计手数" 的数值。在每次交易开始时,它等于0。然后,每10分钟(INCREMENT_PERIOD)从交易开始(只在交易期间)增加1手(INCREMENT_VALUE)。这是通过一个单独的控制台命令执行的,由cron调用。
  2. 同时,将 "最后价格"(最佳ASK价格)的值保存在缓存中。一旦 "待购买累计手数" 大于或等于 BUY_LOTS_BOTTOM_LIMIT,则在此脚本中停止更改 "最后价格"。
  3. 同时,每分钟(BUY_CHECK_PERIOD)通过单独的控制台命令调用一个脚本,该脚本如果 "待购买累计手数" >= BUY_LOTS_BOTTOM_LIMIT,则将 "最后价格" 与当前最佳ASK价格进行比较。如果最佳ASK价格小于 "最后价格"(价格下跌),则将保存的 "最后价格" 更新为较小的值(跟随价格下跌,变为 "跟踪价格"),然后机器人等待价格进一步变动。
  4. 一旦最佳ASK价格在价格牌中变为大于或等于 "跟踪价格" 的 BUY_TRAILING_PERCENTAGE 百分比(价格反弹或持续上涨),则提交一个购买 "待购买累计手数" 的限价订单,价格为最佳ASK价格。然后将 "待购买累计手数" 设置为0。不跟踪订单执行成功的事实。
  5. 在交易结束前的一小段时间内(值在代码中硬编码),如果当前 "待购买累计手数" > BUY_LOTS_BOTTOM_LIMIT / 2,则强制提交一个限价订单购买 "待购买累计手数",价格为 "最佳ASK价格"。也就是说,"待购买累计手数" 不会转移到第二天。

这就是这样一个简单的定期购买策略。限价订单和 "小的" 测试量几乎总是能保证购买订单的执行。

Покупка ETF TMOS

第1-2点由actionIncrementEtfTrailinghttps://github.com/metaseller/tinkoff-robot-buyer/blob/main/commands/TinkoffInvestController.php#L175)控制台控制器 Yii2 TinkoffInvestController 处理。

第3-5点由actionBuyEtfTrailinghttps://github.com/metaseller/tinkoff-robot-buyer/blob/main/commands/TinkoffInvestController.php#L268)控制台控制器 Yii2 TinkoffInvestController 处理。

为了实现,选择了一个流行的框架Yii2。所有机器人的操作、通过cron调用的后台任务、API请求错误都记录在Yii的标准文件夹(./runtime/logs)中相应的日志目标文件中,同时也输出到stdout。

机器人没有UI,您可以通过相应的日志文件(例如使用命令tail -f tinkoff_invest_strategy.log打开)跟踪机器人的工作进度,或者重写错误处理程序逻辑,自行添加通过email/telegram或其它方式发送事件或错误通知到自己的功能。

机器人提交申请时不需要监控资金。当前机器人正在一个已关闭杠杆交易的账户上操作(与它未进行测试),如果资金不足,机器人将记录相应的错误信息,购买操作将不会进行。

项目启动的技术要求

项目目前部署在Timeweb VPS主机上(非广告!)。如果您想亲自尝试,您需要以下内容:

开始工作之前,我们需要以下内容

  • PHP 7.4或更高版本(我已在php 7.4 / Ubuntu 18.04.5上进行开发和测试)
  • PECL,Composer版本2+。

如果还没有Composer,请按照以下链接进行安装和配置:[Composer安装](https://getcomposer.org.cn/doc/00-intro.md#installation-linux-unix-macos)。最好进行“全局安装”。

项目依赖于以下项目(https://github.com/metaseller/tinkoff-invest-api-v2-yii2https://github.com/metaseller/tinkoff-invest-api-v2-php)。

说明:我使用的非官方SDK tinkoff-invest-api-v2-php 已经包含了从proto文件生成的模型。仅供参考,如果您想从官方仓库(https://github.com/Tinkoff/investAPI/)构建项目,则需要:

  1. 安装protoc
  2. 构建grpc_php_plugin插件(参见https://grpc.org.cn/docs/languages/php/basics/#setup
  3. 执行类似以下命令
sudo protoc --proto_path=~/contracts_dir/ --php_out=~/models_dic/ --grpc_out=~/models_dir/ --plugin=protoc-gen-grpc=./grpc_php_plugin ~/contracts_dir/*

替换为您的相应目录(启动项目时不需要这些)。

然后我们需要PHP的grps.so扩展(https://cloud.google.com/php/grpc)。

sudo pecl install grpc

之后,不要忘记在php.ini中添加(最好同时添加到普通和cli中)

extension=grpc.so

如果您需要记录执行日志,也可以在php.ini中添加

grpc.grpc_verbosity=debug
grpc.grpc_trace=all,-polling,-polling_api,-pollable_refcount,-timer,-timer_check
grpc.log_filename=/var/log/grpc.log

当然,别忘了

sudo touch /var/log/grpc.log
sudo chmod 666 /var/log/grpc.log

我作为缓存服务使用了Redis,并已按照以下配置使其可用

[
    'hostname' => 'localhost',
    'port' => 6379,
    'database' => 0,
]

如果您不想配置Redis,可以在项目配置中(参见https://github.com/metaseller/tinkoff-robot-buyer/blob/main/config/console.php#L46https://github.com/metaseller/tinkoff-robot-buyer/blob/main/config/web.php#L51)将缓存服务替换为yii\caching\FileCachehttps://yiiframework.cn/doc/api/2.0/yii-caching-filecache),并从配置中移除Redis(https://github.com/metaseller/tinkoff-robot-buyer/blob/main/config/web.php#L40)。

PS:总体来说,迟早需要了解GRPC的文档。

  1. 使用PHP快速入门 -> https://grpc.org.cn/docs/languages/php/quickstart/
  2. 基础教程 -> https://grpc.org.cn/docs/languages/php/basics/

PSS:以下是可以用于启动项目的依赖项列表:[依赖项列表](https://packagist.org.cn/packages/metaseller/tinkoff-robot-buyer)

项目的安装和启动步骤

  1. 我们将使用Composer进行安装。

在准备好的服务器上,切换到所需的文件夹,例如

cd /var/www/

然后执行以下命令

composer create-project --prefer-dist --stability=dev metaseller/tinkoff-robot-buyer contest.metaseller.local

contest.metaseller.local文件夹中,将下载/安装项目并自动下载所有依赖项。

进入这个文件夹

cd contest.metaseller.local

然后执行以下命令

./init

如果不起作用,则执行以下操作

sudo chmod 755 init

然后启动。此命令初始化环境(选择0 - dev)

Yii Application Initialization Tool v1.0

Which environment do you want the application to be initialized in?

  [0] dev
  [1] prod

  Your choice [0-1, or "q" to quit] 0

然后,为了保险起见,检查/设置logs文件夹的权限

cd /var/www/contest.metaseller.local/runtime
mkdir logs
sudo chmod -R 777 logs

然后进入config目录并配置您的环境

vim credentials.php 

(被vim吓到的人可以使用nano :))输入您的令牌(secret_key)和账户/投资组合标识符(account_id

<?php

return [
    'tinkoff_invest' => [
        'secret_key' => '<ВАШ API ТОКЕН>',
        'account_id' => '<ВАШ ИДЕНТИФИКАТОР ПОРТФЕЛЯ>',
    ],
];

如何获取令牌请参阅此处:https://tinkoff.github.io/investAPI/token/ 如果您不知道账户标识符,则只需输入令牌,然后执行控制台命令

cd /var/www/contest.metaseller.local
php yii tinkoff-invest/accounts

(如果一切设置正确并启动,并且令牌有效)您将在 stdout 中看到您的投资组合标识符列表

root@server:/var/www/contest.metaseller.local# php yii tinkoff-invest/accounts

Портфель 1 => 206*******
ИИС => 205*******
Инвесткопилка => 203*******

接下来需要配置 cron

sudo su
crontab -e

在 crontab 中添加一行

* * * * * cd /var/www/contest.metaseller.local && sudo -u www-data php yii app-schedule/run --scheduleFile=@app/config/schedule.php 1>>runtime/logs/scheduler.log 2>&1

(作为按计划运行的过程的管理员,使用的是 https://github.com/omnilight/yii2-scheduling 库,在项目中它配置在文件 https://github.com/metaseller/tinkoff-robot-buyer/blob/main/config/schedule.php 中)

最后,我们需要配置我们的机器人购买者的策略参数(别忘了在 config/credentials.php 文件中写入获取到的 account_id)

cd /var/www/contest.metaseller.local/config/
vim tinkoff-buy-strategy.php

内容

<?php

return [
    'ETF' => [
        'TMOS' => [
            'ACTIVE' => true,
            'INCREMENT_VALUE' => 1, // На сколько мы увеличиваем накопленное количество лотов позиций к покупке через каждый период
            'INCREMENT_PERIOD' => 10, // Период в минутах, через который мы инкрементируем количество лотов позиций к покупке
            'BUY_CHECK_PERIOD' => 1, // Период в минутах, через который мы проверяем возможность покупки накопленного количества лотов позиций
            'BUY_LOTS_BOTTOM_LIMIT' => 5, // Не пытаемся совершить покупку, пока не достигнут указанный накопленный лимит лотов к покупке
            'BUY_TRAILING_PERCENTAGE' => 0.09, //Величина в процентах, на которую текущая цена должна превысить трейлинг цену для совершения покупки
        ],
    ],
];

这些控制参数的语义在上面的“策略工作逻辑”部分中已说明。

顺便说一下,您有权限更改请求的 appname(请参阅 https://tinkoff.github.io/investAPI/grpc/#appname

cd /var/www/contest.metaseller.local/config/
vim tinkoff-invest.php

内容

<?php

$credentials = require __DIR__ . '/credentials.php';

return [
    'secret_key' => $credentials['tinkoff_invest']['secret_key'] ?? '',
    'account_id' => $credentials['tinkoff_invest']['account_id'] ?? '',
    'app_name' => 'metaseller.tinkoff-robot-buyer',
];

Tinkoff Invest Api 2 功能演示

正如我在引言中所写的那样,这个项目是为了演示如何在 PHP7 上使用 Tinkoff Invest 2 API 的基本功能而制作的。该项目是一个控制台应用程序,所有主要业务逻辑都描述在控制台控制器 https://github.com/metaseller/tinkoff-robot-buyer/blob/main/commands/TinkoffInvestController.php

除了 ETF 机器人购买者的功能外,您还可以尝试以下控制台命令

  1. 在 STDOUT 中获取您的配置文件信息
cd /var/www/contest.metaseller.local
php yii tinkoff-invest/user-info

结果

array(1) {
  ["user_info"]=>
  array(3) {
    ["prem_status"]=>
    bool(false)
    ["qual_status"]=>
    bool(false)
    ["qualified_for_work_with"]=>
    array(4) {
      [0]=>
      string(4) "bond"
      [1]=>
      string(11) "foreign_etf"
      [2]=>
      string(14) "foreign_shares"
      [3]=>
      string(14) "russian_shares"
    }
  }
}

此命令由 TinkoffInvestController::actionUserInfo() 方法处理(请参阅 https://github.com/metaseller/tinkoff-robot-buyer/blob/main/commands/TinkoffInvestController.php#L76

  1. 在 STDOUT 中获取您的投资组合标识符列表
cd /var/www/contest.metaseller.local
php yii tinkoff-invest/accounts

结果

Портфель 1 => 206*******
ИИС => 205*******
Инвесткопилка => 203*******

此命令由 TinkoffInvestController::actionAccounts() 方法处理(请参阅 https://github.com/metaseller/tinkoff-robot-buyer/blob/main/commands/TinkoffInvestController.php#L137

  1. 在 STDOUT 中获取指定账户标识符(投资组合)的配置文件信息
cd /var/www/contest.metaseller.local
php yii tinkoff-invest/portfolio 206*******

此命令从命令行接收账户标识符,并由 TinkoffInvestController::actionPortfolio(string $account_id) 方法处理(请参阅 https://github.com/metaseller/tinkoff-robot-buyer/blob/main/commands/TinkoffInvestController.php#L193

  1. 在 STDOUT 中获取投资组合的总充值信息,按时间段划分(这个功能是我偶然发现的,当时需要控制 IIS 按月(每月投入多少)和按年(每年投入多少)的总充值金额)
cd /var/www/contest.metaseller.local
php yii tinkoff-invest/funding 206******* 2021

结果

Запрашиваем информацию о пополнениях счета 206*******
[2021 год] => 168535.52 руб.
[2022 год] => 43639.99 руб.

[Всего] => 212175.51 руб.

Разбивка по месяцам текущего года:
January -> 1989.5 руб.
February -> 37649.68 руб.
March -> 0.81 руб.
April -> 3000 руб.
May -> 1000 руб.

此命令从命令行接收账户标识符和起始年,并由 TinkoffInvestController::actionFunding(string $account_id, int $from_year = 2021) 方法处理(请参阅 https://github.com/metaseller/tinkoff-robot-buyer/blob/main/commands/TinkoffInvestController.php#L554

  1. 订阅蜡烛流并显示来自流的信息,输出到 STDOUT(无限循环,按 Ctrl-C / Cmd-C 中断)
cd /var/www/contest.metaseller.local
php yii tinkoff-invest/candles-stream SBER

此命令从命令行接收工具的股票代码。由 TinkoffInvestController::actionCandlesStream(string $ticker) 方法处理(请参阅 https://github.com/metaseller/tinkoff-robot-buyer/blob/main/commands/TinkoffInvestController.php#L554

PS: 此外,请注意,我的SDK Tinkoff Invest 2 for PHP中也有一些简单的示例,您可以使用composer将https://github.com/metaseller/tinkoff-invest-api-v2-php库连接到您的PHP项目,并无需使用Yii2框架就可以运行示例https://github.com/metaseller/tinkoff-invest-api-v2-php/tree/main/examples

有用链接

  1. 此演示项目: https://github.com/metaseller/tinkoff-robot-buyer (https://packagist.org.cn/packages/metaseller/tinkoff-robot-buyer)
  2. PHP版的非官方Tinkoff Invest Api v2 SDK: https://github.com/metaseller/tinkoff-invest-api-v2-php (https://packagist.org.cn/packages/metaseller/tinkoff-invest-api-v2-php)
  3. PHP版的非官方Tinkoff Invest Api v2 SDK的Yii2封装: https://github.com/metaseller/tinkoff-invest-api-v2-yii2 (https://packagist.org.cn/packages/metaseller/tinkoff-invest-api-v2-yii2)
  4. Tinkoff Invest Api开发者文档链接: https://tinkoff.github.io/investAPI/
  5. Telegram开发者社区: https://t.me/joinchat/VaW05CDzcSdsPULM
  6. 算法交易开发者社区: https://t.me/tradinggroupTinkoff
  7. Tinkoff Invest机器人竞赛社区: https://t.me/tinkoff_invest_robot_contest