Els inspiradors darrere del desenvolupament del servei d’estadístiques de Tinkoff Investments van ser:
- article sobre Habré “El que no diu Tinkoff Investments”
- anàlisi dels desitjos dels usuaris de la plataforma
- un article sobre el càlcul de comissions .
- Què es parlarà?
- Desenvolupament d’un servei d’estadístiques pas a pas:
- Connexió a l’API de Tinkoff Invest
- Dibuixant dades de l’API Tinkoff Invest en un navegador
- Recepció d’informes i transaccions d’intermediació
- GetBrokerReport
- Mètode per obtenir la data, tenint en compte la resta de la data actual
- Sol·licitud de generació d’informes
- Resultat:
- GetDividendsForeignIssuer
- GetOperationsByCursor
- Càlcul i sortida d’informació d’interès
- Treballant amb preus
- El cost dels contractes de futurs
- Mercat OTC
- Operacions matemàtiques sobre eines
- El microservei està preparat!
- Conclusions i plans de futur
- https://opexbot.info
Què es parlarà?
- Només la part aplicada sobre desenvolupament.
- Coneixements i experiència reals, molt importants en el treball amb instruments financers.
- Visió general dels temes a treballar
Per tant, vull calcular les estadístiques comercials i fer-ho d’una manera convenient.
Desenvolupament d’un servei d’estadístiques pas a pas:
- Connexió a l’API de Tinkoff Invest
- Dibuixant dades de l’API Tinkoff Invest en un navegador
- Recepció d’informes i transaccions d’intermediació
- Càlcul i sortida d’informació d’interès
- Conclusions i plans de futur
Connexió a l’API de Tinkoff Invest
Per connectar-vos a l’API, podeu agafar qualsevol sdk de la documentació https://github.com/Tinkoff/investAPI#sdk . O el paquet npm ` tinkoff-sdk-grpc-js `. És important que els desenvolupadors actualitzin el paquet a la darrera versió. Instal·lar
npm i tinkoff-sdk-grpc-js
Comprovació
const { createSdk } = require(‘tinkoff-sdk-grpc-js’); // Token que es pot obtenir així const TOKEN = ‘YOURAPI’; // El nom de l’aplicació amb la qual es pot trobar als registres del TCS. const appName = ‘tcsstat’; const sdk = createSdk(TOKEN, appName); (async () => { console.log(espera sdk.users.getAccounts()); })();
Resultat: es mostrarà una llista dels vostres comptes a la consola. Per exemple, analitzem els matisos:
- A la llista de comptes hi ha un “Banc d’inversió”, amb el qual no podeu treballar mitjançant l’API
- Tingueu en compte que els camps vénen en camelCase, mentre que a la documentació aquests camps es presenten en under_score.
- Serà així a tot arreu, de manera que no podeu agafar i copiar un camp de la documentació.
Útil:
- Podeu trobar aquest codi a la branca del projecte
https://github.com/pskucherov/tcsstat/tree/step1 https://github.com/pskucherov/tcsstat/compare/step1
Dibuixant dades de l’API Tinkoff Invest en un navegador
Vaig agafar next.js i socket.io. Aquesta no és una recomanació contundent, trieu a la vostra discreció.
npx create-next-app@latest npm i socket.io socket.io-client
Passem immediatament al pas d’amistat següent+socket+investapi i vegeu la secció Útil d’aquest pas per a tots els detalls. Descriuré els detalls:
- Al costat de nodejs (servidor), hi ha un fitxer pages/api/investapi.js. Aquí és on creem el servidor socket.io i ens connectem a investapi.
- Al costat del navegador (client), ens connectem al servidor mitjançant un sòcol i sol·licitem dades del compte al corredor.
- Rebem dades del corredor al servidor i després les enviem al client. Quan es reben al client, es mostren al navegador.
Resultat: a la consola del navegador podem veure informació sobre els comptes. És a dir, en l’últim pas, vam veure informació sobre els comptes a la consola del servidor (nodejs), en el pas actual, vam transferir aquesta informació al client (navegador).
Ara fem-ho perquè pugueu seleccionar un compte des del navegador i, si no hi ha cap testimoni, s’envia un error a la consola. El treball és senzill i res nou, així que només dono enllaços a commits
- https://github.com/pskucherov/tcsstat/commit/7e1ac57061e5e971588479015b06d8814d6609a9
- https://github.com/pskucherov/tcsstat/commit/b28ac973a57494f5232589b4cb6b9fb13b8af759
Útil:
- Aquí es descriu detalladament com fer amics i el sòcol .
- Codi d’amistat next+socket+investapi:
https://github.com/pskucherov/tcsstat/commit/a443a4ac1bb4f0aa898f638128755fe7391ee381 Per a qui l’anterior és difícil, romandrem en aquesta etapa i tractarem el codi. Si teniu preguntes, pregunteu. https://github.com/pskucherov/tcsstat/tree/step2 https://github.com/pskucherov/tcsstat/compare/step1…step2
Recepció d’informes i transaccions d’intermediació
Hi ha tres mètodes per rebre informes i transaccions d’intermediació
Des del primer moment és important saber:
- L’informe d’intermediació es genera en la modalitat T-3, és a dir. les operacions es mostren allà després de la seva execució real.
- En conseqüència, si sol·liciteu aquest informe durant els dos darrers dies, estarà llest en tres dies.
- Per obtenir transaccions dels darrers dies, utilitzem el mètode per rebre transaccions, però recordeu que el seu identificador i contingut poden canviar després de generar l’informe d’intermediació.
GetBrokerReport
Per obtenir un informe d’intermediació, cal que introduïu l’identificador del compte, la data d’inici i la data de finalització de l’informe, però no més de 31 dies. Enviem una sol·licitud per generar un informe a l’API a generate _broker_report_request , obtenim un taskId com a resposta. Després d’això, utilitzant aquest taskId, obtenim dades de get _broker_report_response.
- Heu de desar el TaskID per sempre exactament per a aquestes dates.
- Atès que si el perdeu, per a les dates sol·licitades, primer arribarà l’informe en resposta a la sol·licitud de generació,
- I aleshores no arribarà gens.
Mètode per obtenir la data, tenint en compte la resta de la data actual
const getDateSubDay = (subDia = 5, inici = true) => { const data = data nova (); date.setUTCDate(date.getUTCDate() – subDia); if (inici) { date.setUTCHours (0, 0, 0, 0); } else { date.setUTCHours(23, 59, 59, 999); } data de retorn; };
Sol·licitud de generació d’informes
const brokerReport = await (sdk.operations.getBrokerReport)({ generateBrokerReportRequest: { accountId, from, to, }, });
Resultat:
- Com a resultat de la primera execució de l’ordre, obtenim el taskId.
- L’informe es comença a generar per part del corredor. Quan estigui llest no se sap, esperem i tirem periòdicament el taskId en previsió de l’informe.
- Per què? Perquè si l’informe no està preparat, genera un error. Si l’informe no està preparat per part del corredor, es tracta d’un error al vostre codi. Si us plau, processeu: 30058|INVALID_ARGUMENT|la tasca encara no s’ha completat; torneu-ho a provar més tard
El codi per esperar i rebre un informe té un aspecte semblant a aquest.
const timer = temps asincrònic => { retorna nova promesa (resolució => setTimeout (resolució, temps)); } const getBrokerResponseByTaskId = async (taskId, pàgina = 0) => { try { return await (sdk.operations.getBrokerReport)({ getBrokerReportRequest: { taskId, page, }, }); } catch (e) { console.log(‘espera’, e); temporitzador d’espera (10000); return await getBrokerResponseByTaskId(taskId, pàgina); } };
Aleshores passa la mateixa màgia. Aturem el nostre script, el tornem a començar, no tenim un taskId. Executem el codi amb la sol·licitud taskId, però ja no obtenim el taskId, sinó immediatament l’informe. Màgia! I tot aniria bé si sempre fos així. Però d’aquí a un mes no hi haurà dades. Útil :
https://github.com/pskucherov/tcsstat/tree/step3.1 https://github.com/pskucherov/tcsstat/compare/step3.1
- Si algú es troba amb això, benvingut al problema . Després de reparar aquesta màgia, perdrà el seu poder i serà d’alguna manera diferent. Però en el moment actual (21/03/2023) funciona així.
GetDividendsForeignIssuer
Algú podria pensar que el mètode és semblant a l’anterior i podeu utilitzar un únic mètode en el qual només canvieu el nom de les operacions. Però no ho van endevinar! La denominació és molt diferent tant en els mètodes com en la informació retornada. I el recompte de pàgines comença des de 0, després des d’1. Per no confondre’s en tot això, és més fàcil escriure dos mètodes diferents. La qual cosa és estrany, perquè la lògica del treball és la mateixa. Vaig escopir durant molt de temps quan vaig intentar fer un mètode i hi havia menys codi. Aquí no hi haurà exemples.
GetOperationsByCursor
El meu preferit dels tres. Encara que no és el més precís, però sí el més adequat. Realitzem una sol·licitud des de l’inici de la creació d’un compte fins a la data màxima possible (tancar un compte o l’actual). Obtenim la resposta, agafem el cursor i tornem a sol·licitar sempre que hi hagi dades. I el codi és més concís que en els exemples anteriors.
const timer = temps asincrònic => { retorna nova promesa (resolució => setTimeout (resolució, temps)); } const getOperationsByCursor = async (sdk, accountId, from, to, cursor = ”) => { try { const reqData = { accountId, from, to, limit: 1000, state: sdk.OperationState.OPERATION_STATE_EXECUTED, withoutCommissions: false, sense Operacions: fals, senseOvernights: fals, cursor, }; return await sdk.operations.getOperationsByCursor(reqData); } catch (e) { await timer (60000); return await getOperationsByCursor(sdk, accountId, from, to, cursor = ”); } };
L’esborrany per executar és aquí: https://github.com/pskucherov/tcsstat/tree/step3.3 https://github.com/pskucherov/tcsstat/compare/step3.3 Ara estem preparats per afegir operacions de recepció a la nostra aplicació. Si es fa correctament, haureu d’obtenir informes d’intermediació durant tota l’existència del compte. I per a les dades que falten, aquests mateixos T-3, es recarreguen des de les operacions. Però això es pot separar en un article separat. Dels principals matisos que trobareu és enganxar les operacions i un informe d’intermediació.
- Si avui heu rebut un informe d’intermediació i transaccions per a les dates requerides, poseu-ho tot a la base de dades, no hi ha problemes.
- Demà tindreu problemes quan rebeu la següent part de dades de l’informe i les operacions i decidiu sincronitzar-les amb la base de dades existent.
- Molts matisos sobre l’identificador no coincident o el canvi després del processament
- Aleshores, per al mercat OTC, els identificadors no coincideixen gens.
- Així com els matisos dels instruments de sincronització, que de nou no coincideixen, a causa de les peculiaritats de l’API. Però això és una altra història.
Afegim obtenir informació sobre les operacions a la nostra aplicació. La pregunta principal serà on es processaran i emmagatzemaran les dades.
- Si ho fas per tu mateix, consumiràs les mateixes dades de diferents dispositius. Aleshores, heu de processar i emmagatzemar dades al servidor.
- Si teniu moltes dades diferents consumides per molts usuaris diferents, heu de decidir què és més important: la velocitat dels usuaris o l’estalvi de ferro al vostre costat. Qui es pot permetre una quantitat infinita de maquinari compta tot el que hi ha al seu servidor i ho fa molt ràpid per als usuaris, estalviant-li recursos, com ara bateria i trànsit, que és molt important als telèfons.
Al seu torn, comptar al navegador no és la solució més òptima en principi. Per tant, el que no és car, ho considerem al nostre servidor. La resta la deixem al client. Tinc moltes ganes de prendre i calcular la comissió al servidor. Però aquí ve el matís anomenat “interactivitat”. Suposem que tens milers d’operacions i trigues cinc minuts a rebre-les. Què tindrà l’usuari en aquest moment? Spinner? Progrés, progressar? Infa sobre quant s’ha penjat? És ideal utilitzar l'”espera activa” quan l’usuari en procés ja pot veure alguna cosa. Aquí teniu el resultat:
- S’està carregant la pàgina
- Es demanen totes les factures
- Després d’això, es demanen totes les transaccions amb comissions per a les transaccions executades per a tots els comptes. A mesura que es reben les dades, es mostren al navegador.
Per no filtrar les dades dels esdeveniments cada vegada, fem el nostre propi esdeveniment per a cada compte. Com això:
socket.emit(‘sdk:getOperationsCommissionResult_’ + accountId, { elements: data?.items, inProgress: Boolean(nextCursor), });
L’esborrany per llançar és aquí: https://github.com/pskucherov/tcsstat/tree/step3 https://github.com/pskucherov/tcsstat/compare/step2…step3 Seguint endavant. És genial que hagis llegit aquesta línia!
Càlcul i sortida d’informació d’interès
Depèn de qui necessiti quina informació. Per tant, de seguida us explico els principals matisos que trobareu.
Treballant amb preus
Tothom que treballa amb finances sap que les transaccions de diners només s’han de fer amb números sencers. A causa de la imprecisió dels valors després del punt decimal i l’error acumulat amb un gran nombre d’operacions. És per això que tots els preus es presenten en el següent format MoneyValue
camp | tipus | Descripció |
---|---|---|
moneda | corda | Cadena de codi de moneda ISO |
unitats | int64 | Part entera de la suma, pot ser un nombre negatiu |
nano | int32 | Una fracció de la quantitat, pot ser un nombre negatiu |
Els processem per separat i després els portem al valor del preu:
quota.unitats + quota.nano / 1e9
El cost dels contractes de futurs
El preu dels futurs es presenta en punts, quan tens un futur de divises, has de conèixer la taxa. I per descomptat el preu en punts i el pas de preu. Quan calculeu el benefici de les transaccions, això pot disparar, perquè. si calculeu l’import total multiplicant el preu per la quantitat. Aquí cal anar amb compte. De moment, veurem com va. Això s’aplica als futurs de divises, en altres llocs tot està bé amb això.
Mercat OTC
Aquest mercat té moltes peculiaritats, així que estudiem-ne les operacions per separat, quan comenci a sincronitzar les operacions, resultarà que cal portar figi/ticker a la mateixa forma per tal de fer coincidir correctament l’instrument. Quan comenceu a sincronitzar-ho amb l’informe d’intermediació, resultarà que el tradeID de la mateixa transacció té lletres al principi de les transaccions i no es troben a l’informe d’intermediació. Per tant, no es poden comparar… ahem-ahem… per comparació! Vaig fer coincidir l’hora de comerç, el ticker i la coincidència que un tradeId es troba en un altre. D’acord, no ho sé. Qui es trobi amb això i a qui li importa, vingui al tema o en comenci un de nou.
Operacions matemàtiques sobre eines
És impossible, sense mirar, realitzar operacions matemàtiques amb tota la llista. Per no afegir calent a suau, sempre comprovem la moneda i el processem només si estem segurs que la moneda coincideix i els punts es converteixen a la moneda desitjada. Armats amb coneixements sobre el treball amb números bancaris, calcularem la comissió gastada en cadascun dels comptes. Així: https://github.com/pskucherov/tcsstat/tree/step4 https://github.com/pskucherov/tcsstat/compare/step3…step4
El microservei està preparat!
https://github.com/pskucherov/tcsstat Com a deures, podeu comprovar si el servei funciona amb una connexió lenta, quan les connexions estan trencades, quan es desconnecta Internet, quan hi ha errors o límits vençuts per part del corredor.
Conclusions i plans de futur
- Aprendre sobre les operacions bàsiques i el treball amb l’API d’Invest
- Temps dedicat ~ 10 hores
- Nivell de dificultat ~ júnior+ / mitjà baix
Si continueu perfeccionant el microservei, és possible que acabeu amb alguna cosa com això
https://opexbot.info
Aquest és el meu desenvolupament, per a aquells que són massa mandrós per entendre, córrer i comptar pel seu compte. Penso afegir-hi analítiques a petició dels usuaris. Si t’ha agradat l’article, subscriu-te al meu canal de Telegram .
Полезная статья. Не могу представить, сколько усилий автора потребовалось, чтобы все описать. Благодарю.