Inspiratorami rozwoju serwisu statystycznego dla Tinkoff Investments byli:
- artykuł o Habré „Czego nie mówią Tinkoff Investments”
- analiza życzeń użytkowników platformy
- artykuł o naliczaniu prowizji .
- Co zostanie omówione?
- Tworzenie serwisu statystycznego krok po kroku:
- Połączenie z API Tinkoff Invest
- Rysowanie danych z Tinkoff Invest API w przeglądarce
- Przyjmowanie raportów i transakcji maklerskich
- GetBrokerReport
- Metoda uzyskiwania daty z uwzględnieniem odejmowania od daty bieżącej
- Prośba o wygenerowanie raportu
- Wynik:
- GetDividendsForeignIssuer
- GetOperationsByCursor
- Obliczanie i wyprowadzanie interesujących informacji
- Praca z cenami
- Koszt kontraktów futures
- rynek OTC
- Operacje matematyczne na narzędziach
- Mikroserwis gotowy!
- Wnioski i plany na przyszłość
- https://opexbot.info
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:
- Połączenie z API Tinkoff Invest
- Rysowanie danych z Tinkoff Invest API w przeglądarce
- Przyjmowanie raportów i transakcji maklerskich
- Obliczanie i wyprowadzanie interesujących informacji
- 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:
- 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).
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
- https://github.com/pskucherov/tcsstat/commit/7e1ac57061e5e971588479015b06d8814d6609a9
- 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
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.
- 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.
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 :
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:
- Ł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 MoneyValue
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.
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ą.
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…step4
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 .
Полезная статья. Не могу представить, сколько усилий автора потребовалось, чтобы все описать. Благодарю.