Rozwijamy mikrousługę wykorzystującą Tinkoff Invest API do automatyzacji pracy z raportami maklerskimi i naliczania prowizji.

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

Inspiratorami rozwoju serwisu statystycznego dla Tinkoff Investments byli:

Co zostanie omówione?

  • Tylko zastosowana część dotycząca rozwoju.
  • Prawdziwa wiedza i doświadczenie, które są bardzo ważne w pracy z instrumentami finansowymi.
  • Przegląd zagadnień do pracy

Chcę  więc obliczyć statystyki handlu i zrobić to w wygodny sposób.

Tworzenie serwisu statystycznego krok po kroku: 

  1. Połączenie z API Tinkoff Invest
  2. Rysowanie danych z Tinkoff Invest API w przeglądarce
  3. Przyjmowanie raportów i transakcji maklerskich
  4. Obliczanie i wyprowadzanie interesujących informacji
  5. Wnioski i plany na przyszłość

Połączenie z API Tinkoff Invest

Aby połączyć się z API, możesz pobrać dowolny sdk z dokumentacji https://github.com/Tinkoff/investAPI#sdk . Lub pakiet npm ` tinkoff-sdk-grpc-js `. Ważne jest, aby pakiet był aktualizowany do najnowszej wersji przez programistów. zainstalować

npm i tinkoff-sdk-grpc-js

Kontrola

const { createSdk } = require(’tinkoff-sdk-grpc-js’);   // Token, który można uzyskać w ten sposób  const TOKEN = 'YOURAPI’;   // Nazwa aplikacji, po której można Cię znaleźć w logach TCS. const nazwa_aplikacji = 'tcsstat’;   const sdk = createSdk(TOKEN, nazwaaplikacji); (async () => {     console.log (oczekiwanie na sdk.users.getAccounts()); })();

Wynik: w konsoli zostanie wyświetlona lista Twoich kont. Na przykład przeanalizujmy niuanse:Rozwijamy mikrousługę wykorzystującą Tinkoff Invest API do automatyzacji pracy z raportami maklerskimi i naliczania prowizji.

  • Na liście kont znajduje się „Bank inwestycyjny”, z którym nie można pracować za pomocą interfejsu API
  • Zwróć uwagę, że pola występują w camelCase, podczas gdy w dokumentacji pola te są prezentowane w under_score. 
  • Tak będzie wszędzie, więc nie możesz tak po prostu wziąć i skopiować pola z dokumentacji.

Użyteczne:

  • Możesz znaleźć ten kod w gałęzi projektu

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

Rysowanie danych z Tinkoff Invest API w przeglądarce

Wziąłem next.js i socket.io. To nie jest silna rekomendacja, wybierz według własnego uznania. 

npx create-next-app@latest npm i socket.io socket.io-client

Natychmiast przechodzimy do kroku przyjaźni next+socket+investapi i zapoznajemy się ze wszystkimi szczegółami w  sekcji Przydatne tego kroku. Opiszę szczegóły: 

  • Po stronie nodejs (serwera) znajduje się plik pages/api/investapi.js. Tutaj tworzymy serwer socket.io i łączymy się z Investapi.
  • Po stronie przeglądarki (klienta) łączymy się z serwerem przez gniazdo i żądamy danych konta od brokera. 
  • Otrzymujemy dane od brokera na serwerze, następnie wysyłamy je do klienta. Gdy zostaną odebrane na kliencie, zostaną wyświetlone w przeglądarce. 

Efekt:  w konsoli przeglądarki widzimy informacje o rachunkach. Czyli w ostatnim kroku widzieliśmy informacje o kontach w konsoli serwera (nodejs), w bieżącym kroku przenieśliśmy te informacje do klienta (przeglądarki).

Rozwijamy mikrousługę wykorzystującą Tinkoff Invest API do automatyzacji pracy z raportami maklerskimi i naliczania prowizji.

Teraz zróbmy tak, abyś mógł wybrać konto z przeglądarki, a jeśli nie ma tokena, to do konsoli wysyłany jest błąd. Praca jest prosta i nic nowego, więc podaję tylko linki do commitów

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

Użyteczne:

  • Jak zaprzyjaźnić się z następnym i gniazdem jest szczegółowo opisane tutaj
  • Kod przyjaźni next+socket+investapi:

https://github.com/pskucherov/tcsstat/commit/a443a4ac1bb4f0aa898f638128755fe7391ee381 Dla kogo powyższe jest trudne, to zostajemy na tym etapie i zajmujemy się kodem. Masz pytania – pytaj. https://github.com/pskucherov/tcsstat/tree/step2 https://github.com/pskucherov/tcsstat/compare/step1…step2

Przyjmowanie raportów i transakcji maklerskich

Istnieją trzy metody otrzymywania raportów maklerskich i transakcji

  1. GetBrokerReport
  2. GetDividendsForeignIssuer
  3. GetOperationsByCursor

Od samego początku ważne jest, aby wiedzieć: 

  • Raport maklerski generowany jest w trybie T-3, tj. transakcje są tam wyświetlane po ich faktycznej realizacji. 
  • W związku z tym, jeśli poprosisz o ten raport za ostatnie dwa dni, będzie on gotowy za trzy dni. 
  • Do pozyskiwania transakcji za dni ostatnie stosujemy metodę przyjmowania transakcji, należy jednak pamiętać, że ich identyfikator oraz treść mogą ulec zmianie po wygenerowaniu raportu maklerskiego.

GetBrokerReport

Aby otrzymać raport maklerski należy podać identyfikator rachunku, datę początkową i końcową raportu, ale nie więcej niż 31 dni. Wysyłamy żądanie wygenerowania raportu do API w generate _broker_report_request , w odpowiedzi otrzymujemy taskId. Następnie za pomocą tego taskId otrzymujemy dane z get _broker_report_response.

Więc dokumentacja mówi, że w rzeczywistości są niuanse. Uważaj na ręce:

  • Musisz zapisać identyfikator zadania na zawsze dokładnie dla tych dat. 
  • Ponieważ jeśli go zgubisz, to dla żądanych dat raport nadejdzie najpierw w odpowiedzi na żądanie generacji, 
  • A potem wcale nie przyjdzie.

Zacznijmy pisać kod

Metoda uzyskiwania daty z uwzględnieniem odejmowania od daty bieżącej

const getDateSubDay = (subDay = 5, start = true) => {     const date = new Date();     date.setUTCDate(date.getUTCDate() – podDzień);       if (start) {         date.setUTCHours(0, 0, 0, 0);     } else {         date.setUTCHours(23, 59, 59, 999);     }       data powrotu; };

Prośba o wygenerowanie raportu 

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

Wynik:

  • W wyniku pierwszego wykonania polecenia otrzymujemy taskId. 
  • Raport zaczyna być generowany po stronie brokera. Kiedy jest gotowy, nie wiadomo, czekamy i okresowo pobieramy identyfikator zadania w oczekiwaniu na raport.
  • Dlaczego? Ponieważ jeśli raport nie jest gotowy, zgłasza błąd. Jeśli raport nie jest gotowy po stronie brokera, oznacza to błąd w twoim kodzie. Proszę przetworzyć: 30058|INVALID_ARGUMENT|zadanie nie zostało jeszcze zakończone, spróbuj ponownie później

Kod oczekiwania i odbioru raportu wygląda mniej więcej tak.

const timer = czas asynchroniczny => {     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(’czekaj’, e);         licznik czasu oczekiwania (10000);         return await getBrokerResponseByTaskId(taskId, page);     } };

Wtedy dzieje się ta sama magia. Zatrzymujemy nasz skrypt, uruchamiamy go ponownie, nie mamy identyfikatora zadania. Wykonujemy kod z żądaniem taskId, ale nie otrzymujemy już taskId, ale od razu raport. Magia! I wszystko byłoby dobrze, gdyby zawsze tak było. Ale za miesiąc nie będzie żadnych danych. Przydatne :

  • Trochę teorii jest opisane tutaj i tutaj .
  • Po złożeniu kodu szkic będzie wyglądał mniej więcej tak.

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

  • Jeśli ktoś się z tym spotka, to witamy w temacie . Po naprawieniu tej magii straci ona swoją moc i będzie jakoś inna. Ale w tej chwili (21.03.2023) tak właśnie działa.

GetDividendsForeignIssuer

Ktoś może pomyśleć, że metoda jest podobna do poprzedniej i można użyć jednej metody, w której zmienia się tylko nazwę operacji. Ale nie zgadli!  Nazewnictwo jest bardzo różne zarówno w metodach, jak iw zwracanych informacjach. A liczenie stron zaczyna się od 0, a potem od 1. Aby się w tym wszystkim nie pogubić, łatwiej napisać dwie różne metody. Co jest dziwne, bo logika pracy jest taka sama. Długo plułem, gdy próbowałem zrobić jedną metodę i było mniej kodu. Nie będzie tutaj żadnych przykładów.

GetOperationsByCursor

Mój faworyt z całej trójki. Chociaż nie najdokładniejszy, ale najbardziej odpowiedni. Żądanie składamy od początku założenia konta do maksymalnie możliwego terminu (zamknięcia konta lub bieżącego). Otrzymujemy odpowiedź, bierzemy kursor i ponawiamy prośbę, o ile są dane.  A kod jest bardziej zwięzły niż w powyższych przykładach.

const timer = czas asynchroniczny => {     return new Promise(resolve => setTimeout(resolve, time)); }   const getOperationsByCursor = async (sdk, accountId, from, to, kursor = ”) => {     try {         const reqData = {             accountId,             from,             to,             limit: 1000,             state: sdk.OperationState.OPERATION_STATE_EXECUTED,             withoutCommissions: false,             bezTrades: fałsz,             bezOvernights: fałsz,             kursor,         };           return await sdk.operations.getOperationsByCursor(reqData);     } catch (e) {         timer oczekiwania (60000);         return await getOperationsByCursor(sdk, accountId, from, to, kursor = ”);     } };

Szkic do uruchomienia jest tutaj: https://github.com/pskucherov/tcsstat/tree/step3.3 https://github.com/pskucherov/tcsstat/compare/step3.3 Teraz jesteśmy gotowi, aby dodać operacje odbiorcze do nasza aplikacja. Jeśli zrobiono to poprawnie, musisz uzyskać raporty maklerskie za cały okres istnienia konta. A jeśli chodzi o brakujące dane, te same T-3, przeładuj z operacji. Ale to można podzielić na osobny artykuł. Z głównych niuansów, które napotkasz, jest sklejenie operacji i raportu maklerskiego.

  •  Jeśli dzisiaj otrzymałeś raport maklerski i transakcje na wymagane terminy, umieść to wszystko w bazie danych, wtedy nie ma problemów. 
  • Problemy będziesz mieć jutro, gdy otrzymasz kolejną porcję danych z raportu i operacji i zdecydujesz się je zsynchronizować z istniejącą bazą danych. 
  • Wiele niuansów dotyczących niedopasowania lub zmiany identyfikatora po przetworzeniu
  • Następnie na rynku OTC identyfikatory w ogóle się nie zgadzają.
  •  Jak również niuanse synchronizacji instrumentów, które ponownie nie pokrywają się ze względu na specyfikę API. Ale to już inna historia.

Dodajmy do naszej aplikacji pobieranie informacji o operacjach. Głównym pytaniem będzie, gdzie dane będą przetwarzane i przechowywane.

  •  Jeśli zrobisz to dla siebie, będziesz konsumować te same dane z różnych urządzeń. Następnie musisz przetwarzać i przechowywać dane na serwerze.
  • Jeśli masz wiele różnych danych zużywanych przez wielu różnych użytkowników, musisz zdecydować, co jest ważniejsze: szybkość użytkowników czy oszczędność żelaza po twojej stronie. Kto może sobie pozwolić na nieskończoną ilość sprzętu, liczy wszystko na swoim serwerze i sprawia, że ​​jest on super szybki dla użytkowników, oszczędzając zasoby użytkowników, takie jak bateria i ruch, co jest bardzo ważne w telefonach.

Z kolei liczenie w przeglądarce nie jest w zasadzie rozwiązaniem najbardziej optymalnym. Dlatego, co nie jest drogie, rozważamy to na naszym serwerze. Resztę pozostawiamy klientowi. Naprawdę chcę wziąć i obliczyć prowizję na serwerze. Ale tutaj pojawia się niuans zwany „interaktywnością”. Załóżmy, że masz tysiące operacji i otrzymanie ich zajmuje pięć minut. Co użytkownik będzie miał w tym czasie? Prządka? Postęp? Infa o tym, ile zostało przesłane? Idealne jest użycie „aktywnego oczekiwania”, gdy użytkownik w procesie mógł już coś zobaczyć. Oto wynik:Rozwijamy mikrousługę wykorzystującą Tinkoff Invest API do automatyzacji pracy z raportami maklerskimi i naliczania prowizji.

  • Ładowanie strony
  • Wszystkie faktury są wymagane
  • Następnie wszystkie transakcje z prowizjami za zrealizowane transakcje są wymagane dla wszystkich rachunków. Po otrzymaniu danych są one renderowane w przeglądarce.

Aby nie filtrować danych w zdarzeniach za każdym razem, dla każdego konta pobieramy własne zdarzenie. Lubię to:

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

Szkic do uruchomienia jest tutaj: https://github.com/pskucherov/tcsstat/tree/step3 https://github.com/pskucherov/tcsstat/compare/step2…step3 Idąc dalej. To wspaniale, że przeczytałeś ten wiersz! 

Obliczanie i wyprowadzanie interesujących informacji

Zależy kto potrzebuje jakich informacji. Dlatego od razu powiem ci o głównych niuansach, które napotkasz.

Praca z cenami 

Każdy, kto pracuje z finansami, wie, że transakcje pieniężne powinny być przeprowadzane wyłącznie za pomocą liczb całkowitych. Ze względu na niedokładność wartości po przecinku i skumulowany błąd przy dużej liczbie operacji. Dlatego wszystkie ceny są prezentowane w następującym formacie MoneyValueRozwijamy mikrousługę wykorzystującą Tinkoff Invest API do automatyzacji pracy z raportami maklerskimi i naliczania prowizji.

pole typ Opis
waluta strunowy Kod waluty w formacie ISO
jednostki int64 Część całkowita sumy może być liczbą ujemną
nano int32 Ułamkowa część kwoty, może być liczbą ujemną

Przetwarzamy je osobno, a następnie doprowadzamy do wartości ceny:

cytat.jednostki + cytat.nano / 1e9

Koszt kontraktów futures

Cena kontraktów terminowych podawana jest w punktach, gdy masz kontrakt terminowy na walutę, musisz znać kurs. I oczywiście cena w punktach i krok cenowy. Kiedy obliczasz zysk z transakcji, może to strzelać, ponieważ. jeśli obliczysz całkowitą kwotę, mnożąc cenę przez ilość. Tutaj trzeba uważać. Na razie zobaczymy jak to będzie. Dotyczy to kontraktów terminowych na waluty, w innych miejscach wszystko jest z tym w porządku.Rozwijamy mikrousługę wykorzystującą Tinkoff Invest API do automatyzacji pracy z raportami maklerskimi i naliczania prowizji. Rozwijamy mikrousługę wykorzystującą Tinkoff Invest API do automatyzacji pracy z raportami maklerskimi i naliczania prowizji.

rynek OTC

Ten rynek ma wiele osobliwości, więc osobno przestudiujmy operacje na nim.Gdy zaczniesz synchronizować operacje, okaże się, że musisz doprowadzić figi / ticker do tej samej formy, aby poprawnie dopasować instrument. Kiedy zaczniesz synchronizować to z raportem maklerskim okaże się, że tradeID tej samej transakcji ma na początku litery w transakcjach i nie ma ich w raporcie maklerskim. Dlatego nie można ich porównywać… ahem-ahem… przez porównanie! Dopasowałem czas handlu, pasek i dopasowanie, że jeden tradeId jest zawarty w innym. Nie wiem. Ktokolwiek się z tym zetknie i komu na tym zależy, niech podejdzie do sprawy lub rozpocznie nową.Rozwijamy mikrousługę wykorzystującą Tinkoff Invest API do automatyzacji pracy z raportami maklerskimi i naliczania prowizji.

Operacje matematyczne na narzędziach

Nie da się bez patrzenia wykonać operacji matematycznych na całej liście. Aby nie dodawać ciepłego do miękkiego, zawsze sprawdzamy walutę i przetwarzamy tylko wtedy, gdy mamy pewność, że waluta się zgadza, a punkty są przeliczane na żądaną walutę. Uzbrojeni w wiedzę na temat pracy z numerami bankowymi, obliczymy prowizję wydaną na każdym z rachunków. W ten sposób: https://github.com/pskucherov/tcsstat/tree/step4 https://github.com/pskucherov/tcsstat/compare/step3…step4Rozwijamy mikrousługę wykorzystującą Tinkoff Invest API do automatyzacji pracy z raportami maklerskimi i naliczania prowizji.    

Mikroserwis gotowy!

https://github.com/pskucherov/tcsstat W ramach pracy domowej możesz sprawdzić, czy usługa działa przy wolnym łączu, kiedy połączenia są zrywane, kiedy Internet jest rozłączony, kiedy występują błędy lub wygasły limity po stronie brokera. 

Wnioski i plany na przyszłość

  • Dowiedz się o podstawowych operacjach i pracy z Invest API
  • Spędzony czas ~10 godzin
  • Poziom trudności ~ junior+ / niski średni 

Jeśli będziesz nadal udoskonalać mikroserwis, możesz skończyć z czymś takim

https://opexbot.info

  To jest mój rozwój, dla tych, którzy są zbyt leniwi, by zrozumieć, biegać i liczyć na własną rękę. Planuję dodać tam analitykę na prośbę użytkowników. Jeśli podobał Ci się ten artykuł, zasubskrybuj mój kanał telegramu . Rozwijamy mikrousługę wykorzystującą Tinkoff Invest API do automatyzacji pracy z raportami maklerskimi i naliczania prowizji.

Pavel
Rate author

  1. Isakiiev

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

    Odpowiedz