Vi utvikler en mikrotjeneste som bruker Tinkoff Invest API for å automatisere arbeidet med meglerrapporter og beregning av provisjoner.

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

Inspiratorene bak utviklingen av statistikktjenesten til Tinkoff Investments var:

Hva vil bli diskutert?

  • Bare den anvendte delen om utvikling.
  • Reell kunnskap og erfaring, som er svært viktig i arbeid med finansielle instrumenter.
  • Oversikt over problemstillinger å jobbe med

jeg vil beregne handelsstatistikk og gjøre det på en praktisk måte. 

Utvikle en statistikktjeneste trinn for trinn: 

  1. Tilkobling til Tinkoff Invest API
  2. Tegning av data fra Tinkoff Invest API i en nettleser
  3. Motta meglerrapporter og transaksjoner
  4. Beregning og produksjon av informasjon av interesse
  5. Konklusjoner og planer for fremtiden

Tilkobling til Tinkoff Invest API

For å koble til API, kan du ta hvilken som helst sdk fra dokumentasjonen https://github.com/Tinkoff/investAPI#sdk . Eller npm-pakken ` tinkoff-sdk-grpc-js `. Det er viktig at pakken oppdateres til siste versjon av utviklerne. Installere

npm i tinkoff-sdk-grpc-js

Sjekker

const { createSdk } = require(‘tinkoff-sdk-grpc-js’);   // Token som kan fås på denne måten  const TOKEN = ‘YOURAPI’;   // Navnet på applikasjonen som du kan finne i TCS-loggene. const appName = ‘tcsstat’;   const sdk = createSdk(TOKEN, appnavn); (async () => {     console.log(avventer sdk.users.getAccounts()); })();

Resultat: en liste over kontoene dine vises i konsollen. La oss for eksempel analysere nyansene:Vi utvikler en mikrotjeneste som bruker Tinkoff Invest API for å automatisere arbeidet med meglerrapporter og beregning av provisjoner.

  • I listen over kontoer er det en «Investeringsbank», som du ikke kan jobbe med ved å bruke API
  • Vær oppmerksom på at feltene kommer i camelCase, mens i dokumentasjonen er disse feltene presentert i under_score. 
  • Det vil være slik overalt, så du kan ikke bare ta og kopiere et felt fra dokumentasjonen.

Nyttig:

  • Du finner denne koden i prosjektgrenen

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

Tegning av data fra Tinkoff Invest API i en nettleser

Jeg tok next.js og socket.io. Dette er ikke en sterk anbefaling, velg etter eget skjønn. 

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

Vi fortsetter umiddelbart til vennskapstrinnet neste+socket+investapi, og se Nyttig- delen av dette trinnet for alle detaljer.  Jeg skal beskrive detaljene: 

  • På nodejs (server) siden er det en pages/api/investapi.js fil. Det er her vi oppretter socket.io-serveren og kobler til investapi.
  • På nettlesersiden (klient) kobler vi til serveren via en socket og ber om kontodata fra megleren. 
  • Vi mottar data fra megleren på serveren, og sender dem deretter til klienten. Når de mottas på klienten, vises de i nettleseren. 

Resultat:  i nettleserkonsollen kan vi se informasjon om kontoer. Det vil si at i det siste trinnet så vi informasjon om kontoer i serverkonsollen (nodejs), i det nåværende trinnet overførte vi denne informasjonen til klienten (nettleseren).

Vi utvikler en mikrotjeneste som bruker Tinkoff Invest API for å automatisere arbeidet med meglerrapporter og beregning av provisjoner.

La oss nå gjøre det slik at du kan velge en konto fra nettleseren, og hvis det ikke er noe token, sendes en feilmelding til konsollen. Arbeidet er enkelt og ikke noe nytt, så jeg gir kun lenker til forpliktelser

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

Nyttig:

  • Hvordan få venner neste og socket er beskrevet i detalj her
  • Vennekode neste+socket+investapi:

https://github.com/pskucherov/tcsstat/commit/a443a4ac1bb4f0aa898f638128755fe7391ee381 For hvem det ovenfor er vanskelig, forblir vi på dette stadiet og håndterer koden. Hvis du har spørsmål – spør. https://github.com/pskucherov/tcsstat/tree/step2 https://github.com/pskucherov/tcsstat/compare/step1…step2

Motta meglerrapporter og transaksjoner

Det er tre metoder for å motta meglerrapporter og transaksjoner

  1. GetBrokerReport
  2. GetDividends Utenlandsk utsteder
  3. GetOperationsByCursor

Helt fra begynnelsen er det viktig å vite: 

  • Meglerrapporten genereres i T-3-modus, dvs. handler vises der etter deres faktiske utførelse. 
  • Følgelig, hvis du ber om denne rapporten for de siste to dagene, vil den være klar om tre dager. 
  • For å få transaksjoner for de siste dagene bruker vi metoden for å motta transaksjoner, men husk at deres id og innhold kan endres etter at meglerrapporten er generert.

GetBrokerReport

For å få en meglerrapport må du ta konto-ID, startdato og sluttdato for rapporten, men ikke mer enn 31 dager. Vi sender en forespørsel om å generere en rapport til API-en i generer _broker_report_request , får en oppgave-ID som svar. Etter det, ved å bruke denne oppgave-IDen, får vi data fra get _broker_report_response.

Så dokumentasjonen sier, i virkeligheten er det nyanser. Pass på hendene dine:
  • Du må lagre TaskID for alltid nøyaktig for disse datoene. 
  • Siden hvis du mister den, for de forespurte datoene vil rapporten først komme som svar på generasjonsforespørselen, 
  • Og da kommer den ikke i det hele tatt.
La oss begynne å skrive kode

Metode for å få datoen, tar hensyn til subtraksjon fra gjeldende dato

const getDateSubDay = (subDay = 5, start = true) => {     const date = new Date();     date.setUTCDate(date.getUTCDate() – underdag);       if (start) {         date.setUTCHours(0, 0, 0, 0);     } annet {         date.setUTCHours(23, 59, 59, 999);     }       returdato; };

Rapportgenereringsforespørsel 

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

Resultat:

  • Som et resultat av den første utførelsen av kommandoen får vi taskId. 
  • Rapporten begynner å bli generert på meglersiden. Når den er klar er ukjent, venter vi og trekker med jevne mellomrom taskId i påvente av rapporten.
  • Hvorfor? For hvis rapporten ikke er klar, gir den en feil. Hvis rapporten ikke er klar på meglersiden, så er dette en feil i koden din. Behandle: 30058|INVALID_ARGUMENT|oppgaven er ikke fullført ennå, prøv igjen senere

Koden for å vente og motta en rapport ser omtrent slik ut.

const timer = asynkron tid => {     returner nytt løfte(resolve => setTimeout(resolve, time)); }   const getBrokerResponseByTaskId = async (taskId, page = 0) => {     try {         return await (sdk.operations.getBrokerReport)({             getBrokerReportRequest: {                 taskId,                 page,             },         });     } catch (e) {         console.log(‘vent’, e);         vent timer(10000);         return await getBrokerResponseByTaskId(taskId, side);     } };

Så skjer den samme magien. Vi stopper skriptet vårt, starter det på nytt, vi har ingen oppgave-ID. Vi utfører koden med taskId-forespørselen, men vi får ikke lenger taskId, men umiddelbart rapporten. Magi! Og alt ville vært bra hvis det alltid var slik. Men om en måned vil det ikke være noen data i det hele tatt. Nyttig :

  • Litt teori er skissert her og her .
  • Setter du koden sammen, vil utkastet se omtrent slik ut.

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

  • Hvis noen kommer over dette, velkommen til problemet . Etter at de har reparert denne magien, vil den miste kraften og være annerledes. Men for øyeblikket (21.03.2023) fungerer det akkurat slik.

GetDividends Utenlandsk utsteder

Noen tror kanskje at metoden ligner den forrige, og du kan bruke en enkelt metode der du bare endrer navnet på operasjonene. Men de gjettet ikke!  Navngivningen der er svært forskjellig både i metodene og i den returnerte informasjonen. Og sidetallet starter fra 0, deretter fra 1. For ikke å bli forvirret i alt dette, er det lettere å skrive to forskjellige metoder. Noe som er rart, fordi logikken i arbeidet er den samme. Jeg spyttet lenge da jeg prøvde å lage en metode og det var mindre kode. Det vil ikke være noen eksempler her.

GetOperationsByCursor

Min favoritt av de tre. Selv om ikke den mest nøyaktige, men den mest dekkende. Vi sender en forespørsel fra begynnelsen av opprettelsen av en konto til den maksimalt mulige datoen (stenge en konto eller den nåværende). Vi får svaret, tar markøren og ber om på nytt så lenge det er data.  Og koden er mer kortfattet enn i eksemplene ovenfor.

const timer = asynkron tid => {     returner nytt løfte(resolve => setTimeout(resolve, time)); }   const getOperationsByCursor = async (sdk, accountId, from, to, cursor = ») => {     prøv {         const reqData = {             accountId,             from,             to,             limit: 1000,             state: 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 = »);     } };

Utkastet som skal kjøres er her: https://github.com/pskucherov/tcsstat/tree/step3.3 https://github.com/pskucherov/tcsstat/compare/step3.3 Nå er vi klare til å legge til mottaksoperasjoner til vår søknad. Hvis det gjøres riktig, må du få meglerrapporter for hele eksistensen av kontoen. Og for de manglende dataene, de samme T-3-ene, lastes inn på nytt fra operasjoner. Men dette kan deles inn i en egen artikkel. Av de viktigste nyansene du vil møte er å lime operasjoner og en meglerrapport.

  •  Hvis du i dag mottok en meglerrapport og transaksjoner for de nødvendige datoene, legg alt inn i databasen, så er det ingen problemer. 
  • Du vil få problemer i morgen når du mottar neste del av data fra rapporten og operasjonene og bestemmer deg for å synkronisere dem med den eksisterende databasen. 
  • Mange nyanser om mismatch eller endring av id etter behandling
  • Så for OTC-markedet samsvarer ikke ID-ene i det hele tatt.
  •  Samt nyansene til synkroniseringsinstrumenter, som igjen ikke sammenfaller, på grunn av særegenhetene til API. Men det er en annen historie.

La oss legge til informasjon om operasjoner i applikasjonen vår. Hovedspørsmålet vil være hvor dataene skal behandles og lagres.

  •  Hvis du gjør det for deg selv, vil du konsumere de samme dataene fra forskjellige enheter. Deretter må du behandle og lagre data på serveren.
  • Hvis du har mye forskjellig data som forbrukes av mange forskjellige brukere, må du bestemme hva som er viktigere: hastigheten til brukerne eller sparingen av jern på din side. Den som har råd til en uendelig mengde maskinvare, teller alt på serveren sin og gjør det superraskt for brukerne, og sparer brukerressurser, som batteri og trafikk, noe som er veldig viktig på telefoner.

I sin tur er telling i nettleseren i utgangspunktet ikke den mest optimale løsningen. Derfor, det som ikke er dyrt, vurderer vi det på serveren vår. Resten overlater vi til kunden. Jeg vil virkelig ta og beregne provisjonen på serveren. Men her kommer nyansen kalt «interaktivitet». La oss si at du har tusenvis av operasjoner og at det tar fem minutter å motta dem. Hva vil brukeren ha på dette tidspunktet? Spinner? Framgang? Info om hvor mye som ble lastet opp? Det er ideelt å bruke «aktiv venting» når brukeren i prosessen allerede kunne se noe. Her er resultatet:Vi utvikler en mikrotjeneste som bruker Tinkoff Invest API for å automatisere arbeidet med meglerrapporter og beregning av provisjoner.

  • Side laster inn
  • Alle fakturaer er forespurt
  • Etter det blir alle transaksjoner med provisjoner for utførte transaksjoner forespurt for alle kontoer. Etter hvert som data mottas, gjengis de i nettleseren.

For ikke å filtrere dataene i hendelsene hver gang, trekker vi vår egen hendelse for hver konto. Som dette:

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

Utkastet som skal lanseres er her: https://github.com/pskucherov/tcsstat/tree/step3 https://github.com/pskucherov/tcsstat/compare/step2…step3 Gå videre. Det er flott at du har lest denne linjen! 

Beregning og produksjon av informasjon av interesse

Kommer an på hvem som trenger hvilken informasjon. Derfor forteller jeg deg umiddelbart de viktigste nyansene du vil møte.

Jobber med priser 

Alle som jobber med økonomi vet at pengetransaksjoner kun skal utføres med hele tall. På grunn av unøyaktigheten av verdier etter desimaltegnet og den kumulative feilen med et stort antall operasjoner. Det er derfor alle priser presenteres i følgende MoneyValue- formatVi utvikler en mikrotjeneste som bruker Tinkoff Invest API for å automatisere arbeidet med meglerrapporter og beregning av provisjoner.

felttypeBeskrivelse
valutastrengStreng ISO-valutakode
enheterint64Heltallsdel av summen kan være et negativt tall
nanoint32Brøkdel av mengden, kan være et negativt tall

Vi behandler dem separat, og bringer dem deretter til prisverdien:

quotation.units + quotation.nano / 1e9

Kostnaden for futureskontrakter

Prisen på futures presenteres i poeng, når du har en valutafuture, må du vite kursen. Og selvfølgelig prisen i poeng og pristrinnet. Når du beregner overskuddet fra transaksjoner, kan dette skyte, fordi. hvis du beregner totalbeløpet ved å multiplisere prisen med kvantumet. Her må du være forsiktig. Foreløpig får vi se hvordan det går. Dette gjelder valutaterminer, andre steder er alt ok med dette.Vi utvikler en mikrotjeneste som bruker Tinkoff Invest API for å automatisere arbeidet med meglerrapporter og beregning av provisjoner.Vi utvikler en mikrotjeneste som bruker Tinkoff Invest API for å automatisere arbeidet med meglerrapporter og beregning av provisjoner.

OTC-markedet

Dette markedet har mange særegenheter, så la oss studere operasjoner på det separat. Når du begynner å synkronisere operasjoner, vil det vise seg at du må bringe figi / ticker til samme form for å matche instrumentet korrekt. Når du begynner å synkronisere dette med meglerrapporten vil det vise seg at tradeID til samme transaksjon har bokstaver i begynnelsen i transaksjonene og de er ikke i meglerrapporten. Derfor kan de ikke sammenlignes … ahem-ahem … til sammenligning! Jeg matchet byttetiden, ticker og matching at en tradeId er inneholdt i en annen. Greit, jeg vet ikke. Den som møter dette og som bryr seg om det, kom til saken eller start en ny.Vi utvikler en mikrotjeneste som bruker Tinkoff Invest API for å automatisere arbeidet med meglerrapporter og beregning av provisjoner.

Matematiske operasjoner på verktøy

Det er umulig, uten å se, å utføre matematiske operasjoner med hele listen. For ikke å legge varm til myk, sjekker vi alltid valutaen og behandler kun hvis vi er sikre på at valutaen stemmer, og poengene konverteres til ønsket valuta. Bevæpnet med kunnskap om arbeid med banknummer, vil vi beregne provisjonen brukt på hver av kontoene. Slik: https://github.com/pskucherov/tcsstat/tree/step4 https://github.com/pskucherov/tcsstat/compare/step3…step4Vi utvikler en mikrotjeneste som bruker Tinkoff Invest API for å automatisere arbeidet med meglerrapporter og beregning av provisjoner.   

Microservice er klar!

https://github.com/pskucherov/tcsstat Som hjemmelekse kan du sjekke om tjenesten fungerer med en treg tilkobling, når tilkoblinger er brutt, når Internett er frakoblet, når feil eller utløpt grenser fra meglerens side. 

Konklusjoner og planer for fremtiden

  • Lært om grunnleggende operasjoner og arbeid med Invest API
  • Tid brukt ~ 10 timer
  • Vanskelighetsgrad ~ junior+ / lav middels 

Hvis du fortsetter å avgrense mikrotjenesten, kan du ende opp med noe sånt som dette

https://opexbot.info

  Dette er min utvikling, for de som er for late til å forstå, løpe og regne på egenhånd. Jeg planlegger å legge til analyser der på forespørsel fra brukere. Hvis du likte artikkelen, abonner på telegramkanalen min . Vi utvikler en mikrotjeneste som bruker Tinkoff Invest API for å automatisere arbeidet med meglerrapporter og beregning av provisjoner.

Pavel
Rate author
Add a comment

  1. Isakiiev

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

    Svar