我们正在使用 Tinkoff Invest API 开发微服务,以自动处理经纪报告和计算佣金。

Программирование

Tinkoff Investments统计服务开发背后的启发者是:

将讨论什么?

  • 关于开发的只有应用部分。
  • 真正的知识和经验,这在使用金融工具时非常重要。
  • 要处理的问题概述

所以,我想计算贸易统计数据并以方便的方式进行。 

逐步开发统计服务: 

  1. 连接到 Tinkoff Invest API
  2. 在浏览器中从 Tinkoff Invest API 绘制数据
  3. 接收经纪报告和交易
  4. 计算和输出感兴趣的信息
  5. 结论和未来计划

连接到 Tinkoff Invest API

要连接到 API,您可以从文档https://github.com/Tinkoff/investAPI#sdk中获取任何 sdk 。或者 npm 包` tinkoff-sdk-grpc-js`。开发人员将软件包更新到最新版本很重要。 安装

npm i tinkoff-sdk-grpc-js

检查

const { createSdk } = require(‘tinkoff-sdk-grpc-js’);   // 可以这样  获取的token const TOKEN = ‘YOURAPI’;   // 可以在 TCS 日志中找到您的应用程序的名称。 const appName = ‘tcsstat’;   const sdk = createSdk(TOKEN, appName); (async () => {     console.log(await sdk.users.getAccounts()); })();

结果:您的帐户列表将显示在控制台中。例如,让我们 分析一下细微差别:我们正在使用 Tinkoff Invest API 开发微服务,以自动处理经纪报告和计算佣金。

  • 在帐户列表中有一个“投资银行”,您无法使用 API 与之合作
  • 请注意,这些字段采用驼峰式命名,而在文档中,这些字段以 under_score 形式显示。 
  • 到处都是这样,所以你不能只是从文档中复制一个字段。

有用:

  • 您可以在项目分支中找到此代码

https://github.com/pskucherov/tcsstat/tree/step1 https://github.com/pskucherov/tcsstat/compare/step1   

在浏览器中从 Tinkoff Invest API 绘制数据

我使用了 next.js 和 socket.io。这不是强烈推荐,请自行选择。 

npx create-next-app@latest npm i socket.io socket.io-客户端

我们立即进行友谊步骤 next+socket+investapi,并查看此步骤的有用部分以了解所有详细信息。  我将描述细节: 

  • 在 nodejs(服务器)端,有一个 pages/api/investapi.js 文件。这是我们创建 socket.io 服务器并连接到 investapi 的地方。
  • 在浏览器(客户端)端,我们通过套接字连接到服务器并从经纪人请求帐户数据。 
  • 我们从服务器上的代理接收数据,然后将其发送给客户端。当客户端接收到它们时,它们将显示在浏览器中。 

结果: 在浏览器控制台中我们可以看到有关帐户的信息。 也就是说,在上一步中,我们在服务器控制台(nodejs)中看到了有关帐户的信息,在当前步骤中,我们将这些信息传输到客户端(浏览器)。

我们正在使用 Tinkoff Invest API 开发微服务,以自动处理经纪报告和计算佣金。

现在让我们这样做,以便您可以从浏览器中选择一个帐户,如果没有令牌,则会将错误发送到控制台。工作很简单,没有什么新东西,所以我只给出提交的链接

  1. https://github.com/pskucherov/tcsstat/commit/7e1ac57061e5e971588479015b06d8814d6609a9
  2. https://github.com/pskucherov/tcsstat/commit/b28ac973a57494f5232589b4cb6b9fb13b8af759 

有用:

  • 接下来和socket如何交友这里有详细介绍。 
  • 友情码next+socket+investapi:

https://github.com/pskucherov/tcsstat/commit/a443a4ac1bb4f0aa898f638128755fe7391ee381 以上对谁来说有难度,那么我们就停留在这个阶段,处理代码。如果你有问题 – 问。 https://github.com/pskucherov/tcsstat/tree/step2 https://github.com/pskucherov/tcsstat/compare/step1…step2

接收经纪报告和交易

接收经纪报告和交易的三种方法

  1. 获取经纪人报告
  2. 获取股息外国发行人
  3. GetOperationsByCursor

从一开始就知道: 

  • 经纪报告以T-3模式生成,即 交易在实际执行后显示在那里。 
  • 因此,如果您在过去两天请求这份报告,它会在三天内准备好。 
  • 获取最近几天的交易,我们使用的是接收交易的方法,但是要记住它们的id和内容可能会在券商报告生成后发生变化。

获取经纪人报告

获取经纪报告,需要取账户id,报告开始日期和结束日期,但不超过31天。我们在generate _broker_report_request中向 API 发送一个生成报告的请求,得到一个 taskId 作为响应。之后,使用这个 taskId,我们从get_broker_report_response中获取数据[剧透标题=“因此文档说,实际上存在细微差别。注意你的手:”]

  • 您需要为这些日期准​​确地永远保存 TaskID。 
  • 因为如果你丢失了它,那么对于请求的日期,报告将首先响应生成请求, 
  • 然后它根本不会来。

[/剧透]下面开始写代码

获取日期的方法,考虑到从当前日期减去

const getDateSubDay = (subDay = 5, start = true) => {     const date = new Date();     date.setUTCDate(date.getUTCDate() – subDay);       如果(开始){         date.setUTCHours(0, 0, 0, 0);     } else {         date.setUTCHours(23, 59, 59, 999);     返回       日期; };

报告生成请求 

const brokerReport = await (sdk.operations.getBrokerReport)({         generateBrokerReportRequest: {             accountId,             from,             to,         }, });

结果:

  • 作为第一次执行命令的结果,我们得到了 taskId。 
  • 报告开始在经纪人端生成。何时准备就绪未知,我们等待并定期拉取 taskId 以期待报告。
  • 为什么?因为如果报告没有准备好,它会抛出一个错误。如果经纪人方面的报告还没有准备好,那么这是您的代码中的一个错误。请处理:30058|INVALID_ARGUMENT|任务尚未完成,请稍后重试

等待和接收报告的代码看起来像这样。

const timer = async time => {     return new Promise(resolve => setTimeout(resolve, time)); }   const getBrokerResponseByTaskId = async (taskId, page = 0) => {     try {         return await (sdk.operations.getBrokerReport)({             getBrokerReportRequest: {                 taskId,                 page,             },         });     } catch (e) {         console.log(‘wait’, e);         等待定时器(10000);         返回等待 getBrokerResponseByTaskId(taskId,页面);     } };

然后同样的魔法发生了。我们停止我们的脚本,重新启动它,我们没有 taskId。我们执行带有 taskId 请求的代码,但是我们不再获取 taskId,而是立即得到报告。魔法!如果一直这样,一切都会好起来的。但是一个月后将完全没有数据。 有用的

  • 这里这里概述了一些理论
  • 将代码放在一起,草稿看起来像这样。

https://github.com/pskucherov/tcsstat/tree/step3.1 https://github.com/pskucherov/tcsstat/compare/step3.1

  • 如果有人遇到这个,那么欢迎来到这个问题。他们修复这个魔法后,它会失去力量,并且会有所不同。但目前 (03/21/2023) 它就是这样工作的。

获取股息外国发行人

有人可能会认为该方法与前一个方法类似,您可以使用一个方法,只更改操作的名称。但他们没有猜到!  那里的命名在方法和返回的信息上都有很大的不同。页数从 0 开始,然后从 1 开始。为了不混淆这一切,编写两个不同的方法会更容易。这很奇怪,因为 工作的逻辑是一样的。 当我尝试制作一个方法并且代码更少时,我吐了很长时间。这里就不举例了。

GetOperationsByCursor

我最喜欢的三个。虽然不是最准确的,但却是最充足的。我们从创建帐户开始到最长可能日期(关闭帐户或当前帐户)提出请求。我们得到答案,只要有数据,就拿光标重新请求。  而且代码比上面的例子更简洁。

const timer = async time => {     return new Promise(resolve => setTimeout(resolve, time)); }   const getOperationsByCursor = async (sdk, accountId, from, to, cursor = ”) => {     尝试 {         const reqData = {             accountId,             from,             to,             limit: 1000,             state: sdk.OperationState.OPERATION_STATE_EXECUTED,             withoutCommissions: false,             withoutTrades: false,             withoutOvernights: false,             cursor,         };           返回等待 sdk.operations.getOperationsByCursor(reqData);     } catch (e) {         await timer(60000);         返回等待 getOperationsByCursor(sdk, accountId, from, to, cursor = ”);     } };

要运行的草稿在这里: https: //github.com/pskucherov/tcsstat/tree/step3.3 https://github.com/pskucherov/tcsstat/compare/step3.3 现在我们准备添加接收操作到我们的应用程序。如果操作正确,那么您需要获取整个账户存在的经纪报告。对于丢失的数据,那些相同的 T-3,从操作中重新加载。但这可以分开成一篇单独的文章。 您将遇到的主要细微差别是粘合操作和经纪报告。

  •  如果今天您收到了所需日期的经纪报告和交易,将其全部放入数据库中,那么就没有问题。 
  • 当您从报告和操作中收到下一部分数据并决定将它们与现有数据库同步时,明天您将遇到问题。 
  • 关于处理后不匹配或更改 ID 的许多细微差别
  • 然后对于 OTC 市场,id 根本不匹配。
  •  以及同步工具的细微差别,由于 API 的特殊性,它们又不一致。不过那是另一回事了。

让我们将获取有关操作的信息添加到我们的应用程序中。 主要问题是数据将在何处处理和存储。

  •  如果您自己做,您将使用来自不同设备的相同数据。然后你需要在服务器上处理和存储数据。
  • 如果你有许多不同用户使用的大量不同数据,那么你需要决定哪个更重要:用户的速度或你这边的铁节省。谁能负担得起无限量的硬件,他的服务器上的所有东西都算在内,并为用户提供超快的速度,从而节省用户资源,例如电池和流量,这在手机上非常重要。

反过来,在浏览器中计数原则上并不是最优解。因此,不贵的东西,我们考虑放在我们的服务器上。我们把剩下的留给客户。 我真的很想在服务器上收取并计算佣金。但是这里出现了称为“交互性”的细微差别。假设您有数千个操作,需要五分钟才能接收到它们。 这个时候用户会有什么?旋转器?进步?Infa大约上传了多少? 当进程中的用户已经可以看到某些东西时,使用“主动等待”是理想的选择。 这是 结果:我们正在使用 Tinkoff Invest API 开发微服务,以自动处理经纪报告和计算佣金。

  • 页面加载
  • 要求所有发票
  • 之后,所有账户都需要执行交易的佣金交易。收到数据后,它会在浏览器中呈现。

为了不每次都过滤事件中的数据,我们为每个账户拉取自己的事件。像这样:

socket.emit(‘sdk:getOperationsCommissionResult_’ + accountId, {                 items: data?.items,                 inProgress: Boolean(nextCursor), });

要发布的草案在这里: https://github.com/pskucherov/tcsstat/tree/step3 https://github.com/pskucherov/tcsstat/compare/step2…step3 继续。很高兴您阅读了这一行! 

计算和输出感兴趣的信息

取决于谁需要什么信息。因此,我立即告诉您您将遇到的主要细微差别。

处理价格 

每个从事金融工作的人都知道货币交易只能用整数进行。 由于小数点后数值不准确,加上大量运算的累积误差。这就是为什么所有价格都以以下MoneyValue格式显示的原因我们正在使用 Tinkoff Invest API 开发微服务,以自动处理经纪报告和计算佣金。

场地类型描述
货币细绳字符串 ISO 货币代码
单位整数64和的整数部分,可以是负数
纳米整数 32金额的小数部分,可以是负数

我们分别处理它们,然后将它们带入价格值:

quotation.units + quotation.nano / 1e9

期货合约的成本

期货的价格以点数表示,当您有货币期货时,您需要知道汇率。当然还有以点为单位的价格和价格步长。 当您计算交易利润时,这可以射击,因为。如果您通过将价格乘以数量来计算总金额。在这里你需要小心。现在,我们将看看进展如何。这适用于货币期货,在其他地方一切都可以。我们正在使用 Tinkoff Invest API 开发微服务,以自动处理经纪报告和计算佣金。我们正在使用 Tinkoff Invest API 开发微服务,以自动处理经纪报告和计算佣金。

场外交易市场

这个行情有很多特殊性,我们单独研究一下对它的操作, 当你开始同步操作的时候,会发现你需要把figi/ticker带到同一个表格,才能正确匹配工具。 当您开始将其与经纪报告同步时,会发现同一笔交易的 tradeID 在交易的开头有字母,而它们不在经纪报告中。所以,他们不能相提并论……咳咳……相提并论!我匹配了交易时间、代码并匹配了一个 tradeId 包含在另一个中。对,我不知道。谁遇到这个,谁关心它,来解决这个问题或开始一个新的问题。我们正在使用 Tinkoff Invest API 开发微服务,以自动处理经纪报告和计算佣金。

工具的数学运算

不看就不可能对整个列表进行数学运算。为了不让温暖变得柔和,我们总是检查货币,只有在我们确定货币匹配时才进行处理,并将积分转换为所需的货币。 了解有关使用银行号码的知识后,我们将计算花费在每个帐户上的佣金。像这样:https: //github.com/pskucherov/tcsstat/tree/step4 https://github.com/pskucherov/tcsstat/compare/step3…step4我们正在使用 Tinkoff Invest API 开发微服务,以自动处理经纪报告和计算佣金。   

微服务已准备就绪!

https://github.com/pskucherov/tcsstat 作为一项家庭作业,您可以检查该服务是否在低速连接下工作、连接何时断开、Internet 断开连接、代理出现错误或限制是否过期。 

结论和未来计划

  • 了解基本操作和使用 Invest API
  • 花费的时间 ~ 10 小时
  • 难度级别 ~ 初级+ / 中低 

如果你继续完善微服务,你可能会得到这样的结果

https://opexbot.info

  这是我的开发,懒得看的,自己跑,自己算。我计划应用户的要求在那里添加分析。 如果您喜欢这篇文章,请订阅我的电报频道 我们正在使用 Tinkoff Invest API 开发微服务,以自动处理经纪报告和计算佣金。

Pavel
Rate author
Add a comment

  1. Isakiiev

    Полезная статья. Не могу представить, сколько усилий автора потребовалось, чтобы все описать. Благодарю.

    回复