Ние разработваме микроуслуга, използвайки Tinkoff Invest API за автоматизиране на брокерски отчети и изчисляване на комисионни.

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

Вдъхновителите зад развитието на статистическата услуга за Tinkoff Investments бяха:

Какво ще се обсъжда?

  • Само приложната част за разработката.
  • Реални знания и опит, които са много важни при работа с финансови инструменти.
  • Преглед на проблемите, по които да работите

И така, искам да изчисля търговски статистики и да го направя по удобен начин. 

Разработване на статистическа услуга стъпка по стъпка: 

  1. Връзка с Tinkoff Invest API
  2. Чертене на данни от Tinkoff Invest API в браузър
  3. Получаване на брокерски отчети и транзакции
  4. Изчисляване и извеждане на информация от интерес
  5. Изводи и планове за бъдещето

Връзка с Tinkoff Invest API

За да се свържете с API, можете да вземете всеки sdk от документацията https://github.com/Tinkoff/investAPI#sdk . Или npm пакет „ tinkoff-sdk-grpc-js “. Важно е пакетът да бъде актуализиран до най-новата версия от разработчиците. Инсталирай

npm и tinkoff-sdk-grpc-js

Проверка

const { createSdk } = require(‘tinkoff-sdk-grpc-js’);   // Токен, който може да бъде получен по този начин  const TOKEN = ‘YOURAPI’;   // Името на приложението, с което можете да бъдете намерени в регистрационните файлове на TCS. const appName = ‘tcsstat’;   const sdk = createSdk(TOKEN, име на приложение); (async () => {     console.log(await sdk.users.getAccounts()); })();

Резултат: в конзолата ще се покаже списък с вашите акаунти. Например, нека анализираме нюансите:Ние разработваме микроуслуга, използвайки Tinkoff Invest API за автоматизиране на брокерски отчети и изчисляване на комисионни.

  • В списъка със сметки има „Инвестиционна банка“, с която не можете да работите с API
  • Моля, обърнете внимание, че полетата са в camelCase, докато в документацията тези полета са представени в 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 

Полезен:

  • Как да се сприятеляваме next и 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. GetBrokerReport
  2. GetDividendsForeignIssuer
  3. GetOperationsByCursor

От самото начало е важно да знаете: 

  • Брокерският отчет се генерира в режим Т-3, т.е. сделките се показват там след действителното им изпълнение. 
  • Съответно, ако поискате този отчет за последните два дни, той ще бъде готов след три дни. 
  • За извършване на сделки за последните дни използваме метода за получаване на операции, но не забравяйте, че техният идентификатор и съдържание може да се променят след формирането на брокерския отчет.

GetBrokerReport

За да получите брокерски отчет, трябва да вземете идентификатора на акаунта, началната и крайната дата на отчета, но не повече от 31 дни. Изпращаме заявка за генериране на отчет към API в генериране на _broker_report_request , получаваме taskId в отговор. След това, използвайки този taskId, получаваме данни от get _broker_report_response.

Така че в документацията се казва, че в действителност има нюанси. Внимавайте с ръцете си:
  • Трябва да запазите TaskID завинаги точно за тези дати. 
  • Тъй като ако го загубите, тогава за заявените дати отчетът първо ще дойде в отговор на заявката за генериране, 
  • И тогава изобщо няма да дойде.
Нека започнем да пишем код

Метод за получаване на датата, като се вземе предвид изваждането от текущата дата

const getDateSubDay = (subDay = 5, start = true) => {     const date = new Date();     date.setUTCDate(date.getUTCDate() – subDay);       if (начало) {         date.setUTCHours(0, 0, 0, 0);     } else {         date.setUTCHours(23, 59, 59, 999);     }       дата на връщане; };

Заявка за генериране на отчет 

const brokerReport = await (sdk.operations.getBrokerReport)({         genereBrokerReportRequest: {             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);         return await getBrokerResponseByTaskId(taskId, страница);     } };

Тогава се случва същата магия. Спираме нашия скрипт, стартираме го отново, нямаме taskId. Изпълняваме кода с taskId заявката, но вече не получаваме taskId, а веднага отчета. Магия! И всичко щеше да е наред, ако винаги беше така. Но след месец няма да има никакви данни. полезно :

  • Малко теория е описана тук и тук .
  • Сглобявайки кода, черновата ще изглежда по следния начин.

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

  • Ако някой попадне на това, добре дошъл в темата . След като поправят тази магия, тя ще загуби силата си и ще бъде някак различна. Но в настоящия момент (21.03.2023 г.) работи точно така.

GetDividendsForeignIssuer

Някой може да си помисли, че методът е подобен на предишния и можете да използвате един метод, в който променяте само името на операциите. Но не са познали!  Именуването там е много различно както в методите, така и в върнатата информация. И броят на страниците започва от 0, след това от 1. За да не се объркате във всичко това, по-лесно е да напишете два различни метода. Което е странно, защото логиката на работа е същата. Плюех се дълго време, когато се опитах да направя един метод и имаше по-малко код. Тук няма да има примери.

GetOperationsByCursor

Моят любим от трите. Макар и не най-точният, но най-адекватният. Правим заявка от началото на създаване на акаунт до максимално възможната дата (закриване на акаунт или текущ). Получаваме отговора, вземаме курсора и повторно заявка, стига да има данни.  И кодът е по-сбит, отколкото в примерите по-горе.

const timer = async time => {     return new Promise(resolve => setTimeout(resolve, time)); }   const getOperationsByCursor = async (sdk, accountId, from, to, cursor = ”) => {     try {         const reqData = {             accountId,             from,             to,             limit: 1000,             състояние: sdk.OperationState.OPERATION_STATE_EXECUTED,             withoutCommissions: false,             withoutTrades: false,             withoutOvernights: false,             cursor,         };           return await sdk.operations.getOperationsByCursor(reqData);     } catch (e) {         await timer(60000);         return await getOperationsByCursor(sdk, accountId, from, to, cursor = ”);     } };

Черновата за изпълнение е тук: https://github.com/pskucherov/tcsstat/tree/step3.3 https://github.com/pskucherov/tcsstat/compare/step3.3 Сега сме готови да добавим операции за получаване към нашето приложение. Ако е направено правилно, тогава трябва да получите брокерски отчети за цялото съществуване на акаунта. И за липсващите данни, същите тези Т-3, презаредете от операции. Но това може да се отдели в отделна статия. От основните нюанси, с които ще се сблъскате, е да свържете операции и брокерски отчет.

  •  Ако днес сте получили брокерски отчет и транзакции за необходимите дати, поставете всичко в базата данни, тогава няма проблеми. 
  • Ще имате проблеми утре, когато получите следващата порция данни от отчета и операциите и решите да ги синхронизирате със съществуващата база данни. 
  • Много нюанси за несъответствие или промяна на идентификатора след обработка
  • Тогава за извънборсовия пазар идентификаторите изобщо не съвпадат.
  •  Както и нюансите на синхронизиране на инструменти, които отново не съвпадат, поради особеностите на API. Но това е друга история.

Нека добавим получаване на информация за операциите към нашето приложение. Основният въпрос ще бъде къде ще се обработват и съхраняват данните.

  •  Ако го направите за себе си, ще консумирате едни и същи данни от различни устройства. След това трябва да обработите и съхраните данните на сървъра.
  • Ако имате много различни данни, консумирани от много различни потребители, тогава трябва да решите кое е по-важно: скоростта на потребителите или спестяването на желязо от ваша страна. Всеки, който може да си позволи безкрайно количество хардуер, отчита всичко на своя сървър и го прави супер бърз за потребителите, спестявайки потребителски ресурси, като батерия и трафик, което е много важно за телефоните.

От своя страна преброяването в браузъра по принцип не е най-оптималното решение. Следователно, това, което не е скъпо, го считаме на нашия сървър. Останалото оставяме на клиента. Много искам да взема и изчислявам комисионната на сървъра. Но тук идва нюансът, наречен „интерактивност“. Да приемем, че имате хиляди операции и получаването им отнема пет минути. Какво ще има потребителят в този момент? Спинър? Напредък? Информация за това колко е качено? Идеално е да използвате „активно изчакване“, когато потребителят в процеса вече може да види нещо. Ето и резултата:Ние разработваме микроуслуга, използвайки 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 код на валута
единициint64Цялата част от сумата може да бъде отрицателно число
наноint32Дробна част от сумата, може да бъде отрицателно число

Ние ги обработваме отделно, след което ги привеждаме в ценовата стойност:

quotation.units + quotation.nano / 1e9

Цената на фючърсните договори

Цената на фючърсите е представена в точки, когато имате валутен фючърс, трябва да знаете курса. И разбира се цената в точки и ценовата стъпка. Когато изчислявате печалбата от транзакции, това може да стреля, защото. ако изчислите общата сума, като умножите цената по количеството. Тук трябва да внимавате. Засега ще видим как ще е. Това важи за валутните фючърси, на други места всичко е наред с това.Ние разработваме микроуслуга, използвайки Tinkoff Invest API за автоматизиране на брокерски отчети и изчисляване на комисионни.Ние разработваме микроуслуга, използвайки Tinkoff Invest API за автоматизиране на брокерски отчети и изчисляване на комисионни.

Извънборсов пазар

Този пазар има много особености, така че нека да проучим операциите върху него отделно.Когато започнете да синхронизирате операциите, ще се окаже, че трябва да приведете фигура / тикер в една и съща форма, за да съпоставите правилно инструмента. Когато започнете да синхронизирате това с брокерския отчет, ще се окаже, че 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 Като домашна работа можете да проверите дали услугата работи при бавна връзка, при прекъсване на връзките, при прекъсване на интернет връзката, при грешки или изтекли лимити от страна на брокера. 

Изводи и планове за бъдещето

  • Научих за основните операции и работата с Invest API
  • Прекарано време ~ 10 часа
  • Ниво на трудност ~ junior+ / ниско средно 

Ако продължите да усъвършенствате микроуслугата, може да се окажете с нещо подобно

https://opexbot.info

  Това е моята разработка, за тези, които ги мързи да разберат, бягат и разчитат сами. Смятам да добавя анализи там по искане на потребителите. Ако сте харесали статията, тогава се абонирайте за моя телеграм канал . Ние разработваме микроуслуга, използвайки Tinkoff Invest API за автоматизиране на брокерски отчети и изчисляване на комисионни.

Pavel
Rate author
Add a comment

  1. Isakiiev

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

    Reply