Gli ispiratori dietro lo sviluppo del servizio statistico per Tinkoff Investments sono stati:
- articolo su Habré “Quello che Tinkoff Investments non dice”
- analisi dei desideri degli utenti della piattaforma
- un articolo sul calcolo delle provvigioni .
- Di cosa si discuterà?
- Sviluppare un servizio di statistica passo dopo passo:
- Connessione all’API di Tinkoff Invest
- Disegno di dati dall’API Tinkoff Invest in un browser
- Ricevere rapporti e transazioni di intermediazione
- GetBrokerReport
- Metodo per ottenere la data, tenendo conto della sottrazione dalla data corrente
- Richiesta generazione report
- Risultato:
- GetDividendsForeignIssuer
- GetOperationsByCursor
- Calcolo e produzione di informazioni di interesse
- Lavorare con i prezzi
- Il costo dei contratti futures
- mercato OTC
- Operazioni matematiche sugli utensili
- Il microservizio è pronto!
- Conclusioni e progetti per il futuro
- https://opexbot.info
Di cosa si discuterà?
- Solo la parte applicata sullo sviluppo.
- Conoscenza ed esperienza reali, che sono molto importanti quando si lavora con strumenti finanziari.
- Panoramica dei problemi su cui lavorare
Quindi, voglio calcolare le statistiche commerciali e farlo in modo conveniente.
Sviluppare un servizio di statistica passo dopo passo:
- Connessione all’API di Tinkoff Invest
- Disegno di dati dall’API Tinkoff Invest in un browser
- Ricevere rapporti e transazioni di intermediazione
- Calcolo e produzione di informazioni di interesse
- Conclusioni e progetti per il futuro
Connessione all’API di Tinkoff Invest
Per connetterti all’API, puoi prendere qualsiasi sdk dalla documentazione https://github.com/Tinkoff/investAPI#sdk . Oppure il pacchetto npm ` tinkoff-sdk-grpc-js` . È importante che il pacchetto sia aggiornato all’ultima versione dagli sviluppatori. Installare
npm io tinkoff-sdk-grpc-js
Controllo
const { createSdk } = require(‘tinkoff-sdk-grpc-js’); // Token che può essere ottenuto in questo modo const TOKEN = ‘YOURAPI’; // Il nome dell’applicazione con cui puoi essere trovato nei log di TCS. const appName = ‘tcsstat’; const sdk = createSdk(TOKEN, appName); (async () => { console.log(attendere sdk.users.getAccounts()); })();
Risultato: nella console verrà visualizzato un elenco dei tuoi account. Ad esempio, analizziamo le sfumature:
- Nell’elenco dei conti è presente una “Banca d’investimento”, con la quale non è possibile lavorare utilizzando l’API
- Si noti che i campi sono in camelCase, mentre nella documentazione questi campi sono presentati in under_score.
- Sarà così ovunque, quindi non puoi semplicemente prendere e copiare un campo dalla documentazione.
Utile:
- Puoi trovare questo codice nel ramo del progetto
https://github.com/pskucherov/tcsstat/tree/step1 https://github.com/pskucherov/tcsstat/compare/step1
Disegno di dati dall’API Tinkoff Invest in un browser
Ho preso next.js e socket.io. Questa non è una raccomandazione forte, scegli a tua discrezione.
npx create-next-app@latest npm i socket.io socket.io-client
Procediamo immediatamente al passo dell’amicizia next+socket+investapi, e vediamo la sezione Utile di questo passo per tutti i dettagli. Descrivo i dettagli:
- Sul lato nodejs (server), c’è un file pages/api/investapi.js. Qui è dove creiamo il server socket.io e ci connettiamo a investapi.
- Sul lato browser (client), ci colleghiamo al server tramite un socket e richiediamo i dati dell’account al broker.
- Riceviamo i dati dal broker sul server, quindi li inviamo al client. Quando vengono ricevuti sul client, vengono visualizzati nel browser.
Risultato: nella console del browser possiamo vedere le informazioni sugli account. Cioè, nell’ultimo passaggio, abbiamo visto le informazioni sugli account nella console del server (nodejs), nel passaggio corrente, abbiamo trasferito queste informazioni al client (browser).
Ora facciamo in modo che tu possa selezionare un account dal browser e, se non è presente alcun token, viene inviato un errore alla console. Il lavoro è semplice e niente di nuovo, quindi fornisco solo collegamenti ai commit
- https://github.com/pskucherov/tcsstat/commit/7e1ac57061e5e971588479015b06d8814d6609a9
- https://github.com/pskucherov/tcsstat/commit/b28ac973a57494f5232589b4cb6b9fb13b8af759
Utile:
- Come fare amicizia dopo e presa è descritto in dettaglio qui .
- Codice amicizia next+socket+investapi:
https://github.com/pskucherov/tcsstat/commit/a443a4ac1bb4f0aa898f638128755fe7391ee381 Per chi quanto sopra è difficile, rimaniamo in questa fase e ci occupiamo del codice. Se hai domande, chiedi. https://github.com/pskucherov/tcsstat/tree/step2 https://github.com/pskucherov/tcsstat/compare/step1…step2
Ricevere rapporti e transazioni di intermediazione
Esistono tre metodi per ricevere rapporti e transazioni di intermediazione
Fin dall’inizio è importante sapere:
- Il report di intermediazione viene generato in modalità T-3, ovvero le negoziazioni vengono visualizzate lì dopo la loro effettiva esecuzione.
- Di conseguenza, se richiedi questo rapporto per gli ultimi due giorni, sarà pronto in tre giorni.
- Per ottenere le transazioni degli ultimi giorni, utilizziamo il metodo per la ricezione delle transazioni, ma ricorda che il loro ID e contenuto possono cambiare dopo la generazione del rapporto di intermediazione.
GetBrokerReport
Per ottenere un rapporto di intermediazione, è necessario prendere l’ID dell’account, la data di inizio e la data di fine del rapporto, ma non più di 31 giorni. Inviamo una richiesta per generare un report all’API in generate _broker_report_request , otteniamo un taskId in risposta. Successivamente, utilizzando questo taskId, otteniamo i dati da get _broker_report_response.
- Devi salvare il TaskID per sempre esattamente per queste date.
- Poiché se lo perdi, per le date richieste il rapporto arriverà prima in risposta alla richiesta di generazione,
- E poi non verrà affatto.
Metodo per ottenere la data, tenendo conto della sottrazione dalla data corrente
const getDateSubDay = (subDay = 5, start = true) => { const date = new Date(); date.setUTCDate(date.getUTCDate() – subDay); if (start) { date.setUTCHours(0, 0, 0, 0); } else { date.setUTCHours(23, 59, 59, 999); } data di ritorno; };
Richiesta generazione report
const brokerReport = wait (sdk.operations.getBrokerReport)({ generateBrokerReportRequest: { accountId, from, to, }, });
Risultato:
- Come risultato della prima esecuzione del comando, otteniamo il taskId.
- Il rapporto inizia a essere generato dal lato del broker. Quando è pronto non è noto, attendiamo ed estraiamo periodicamente il taskId in previsione del report.
- Perché? Perché se il rapporto non è pronto, genera un errore. Se il report non è pronto da parte del broker, si tratta di un errore nel codice. Elabora: 30058|INVALID_ARGUMENT|attività non ancora completata, riprova più tardi
Il codice per l’attesa e la ricezione di un rapporto è simile a questo.
const timer = tempo asincrono => { return new Promise(resolve => setTimeout(resolve, time)); } const getBrokerResponseByTaskId = async (taskId, page = 0) => { try { return wait (sdk.operations.getBrokerReport)({ getBrokerReportRequest: { taskId, page, }, }); } catch (e) { console.log(‘wait’, e); attesa timer(10000); return wait getBrokerResponseByTaskId(taskId, page); } };
Poi accade la stessa magia. Fermiamo il nostro script, lo ricominciamo, non abbiamo un taskId. Eseguiamo il codice con la richiesta taskId, ma non otteniamo più il taskId, ma subito il report. Magia! E andrebbe tutto bene se fosse sempre così. Ma tra un mese non ci saranno più dati. Utile :
https://github.com/pskucherov/tcsstat/tree/step3.1 https://github.com/pskucherov/tcsstat/compare/step3.1
- Se qualcuno si imbatte in questo, allora benvenuto al problema . Dopo aver riparato questa magia, perderà il suo potere e sarà in qualche modo diversa. Ma al momento attuale (21/03/2023) funziona proprio così.
GetDividendsForeignIssuer
Qualcuno potrebbe pensare che il metodo sia simile al precedente e che tu possa utilizzare un unico metodo in cui cambi solo il nome delle operazioni. Ma non hanno indovinato! La denominazione è molto diversa sia nei metodi che nelle informazioni restituite. E il conteggio delle pagine parte da 0, poi da 1. Per non confondersi in tutto questo, è più facile scrivere due metodi diversi. Il che è strano, perché la logica del lavoro è la stessa. Ho sputato a lungo quando ho provato a creare un metodo e c’era meno codice. Non ci saranno esempi qui.
GetOperationsByCursor
Il mio preferito dei tre. Sebbene non sia il più preciso, ma il più adeguato. Facciamo una richiesta dall’inizio della creazione di un account fino alla data massima possibile (chiusura di un account o quella corrente). Otteniamo la risposta, prendiamo il cursore e richiediamo finché ci sono dati. E il codice è più conciso rispetto agli esempi precedenti.
const timer = tempo asincrono => { return new Promise(resolve => setTimeout(resolve, time)); } const getOperationsByCursor = async (sdk, accountId, from, to, cursor = ”) => { try { const reqData = { accountId, from, to, limit: 1000, state: sdk.OperationState.OPERATION_STATE_EXECUTED, withoutCommissions: false, withoutTrades: false, withoutOvernights: false, cursor, }; return wait sdk.operations.getOperationsByCursor(reqData); } catch (e) { wait timer(60000); return wait getOperationsByCursor(sdk, accountId, from, to, cursor = ”); } };
La bozza da eseguire è qui: https://github.com/pskucherov/tcsstat/tree/step3.3 https://github.com/pskucherov/tcsstat/compare/step3.3 Ora siamo pronti per aggiungere le operazioni di ricezione a la nostra applicazione. Se fatto correttamente, è necessario ottenere rapporti di intermediazione per l’intera esistenza dell’account. E per i dati mancanti, quegli stessi T-3, ricaricati dalle operazioni. Ma questo può essere separato in un articolo separato. Delle principali sfumature che incontrerai è incollare le operazioni e un rapporto di intermediazione.
- Se oggi hai ricevuto un rapporto di intermediazione e transazioni per le date richieste, inserisci tutto nel database, quindi non ci sono problemi.
- Domani avrai problemi quando riceverai la porzione successiva di dati dal rapporto e dalle operazioni e deciderai di sincronizzarli con il database esistente.
- Molte sfumature sull’ID non corrispondente o sulla modifica dopo l’elaborazione
- Quindi per il mercato OTC, gli ID non corrispondono affatto.
- Così come le sfumature degli strumenti di sincronizzazione, che ancora una volta non coincidono, a causa delle peculiarità dell’API. Ma questa è un’altra storia.
Aggiungiamo ottenere informazioni sulle operazioni alla nostra applicazione. La domanda principale sarà dove i dati verranno elaborati e archiviati.
- Se lo fai per te stesso, consumerai gli stessi dati da dispositivi diversi. Quindi è necessario elaborare e archiviare i dati sul server.
- Se hai molti dati diversi consumati da molti utenti diversi, allora devi decidere cosa è più importante: la velocità degli utenti o il risparmio di ferro dalla tua parte. Chi può permettersi una quantità infinita di hardware conta tutto sul suo server e lo rende super veloce per gli utenti, risparmiando le risorse dell’utente, come la batteria e il traffico, che è molto importante sui telefoni.
A sua volta, il conteggio nel browser non è in linea di principio la soluzione ottimale. Pertanto, ciò che non è costoso, lo consideriamo sul nostro server. Il resto lo lasciamo al cliente. Voglio davvero prendere e calcolare la commissione sul server. Ma ecco che arriva la sfumatura chiamata “interattività”. Diciamo che hai migliaia di operazioni e ci vogliono cinque minuti per riceverle. Cosa avrà l’utente in questo momento? Filatore? Progresso? Informazioni su quanto è stato caricato? È ideale utilizzare “l’attesa attiva” quando l’utente nel processo potrebbe già vedere qualcosa. Ecco il risultato:
- Caricamento della pagina
- Tutte le fatture sono richieste
- Successivamente, tutte le transazioni con commissioni per le transazioni eseguite vengono richieste per tutti i conti. Man mano che i dati vengono ricevuti, vengono visualizzati nel browser.
Per non filtrare ogni volta i dati negli eventi, estraiamo il nostro evento per ciascun account. Come questo:
socket.emit(‘sdk:getOperationsCommissionResult_’ + accountId, { items: data?.items, inProgress: Boolean(nextCursor), });
La bozza da lanciare è qui: https://github.com/pskucherov/tcsstat/tree/step3 https://github.com/pskucherov/tcsstat/compare/step2…step3 Andiamo avanti. È fantastico che tu abbia letto questa riga!
Calcolo e produzione di informazioni di interesse
Dipende da chi ha bisogno di quali informazioni. Pertanto, ti dico subito le principali sfumature che incontrerai.
Lavorare con i prezzi
Tutti coloro che lavorano con la finanza sanno che le transazioni monetarie dovrebbero essere eseguite solo con numeri interi. A causa dell’imprecisione dei valori dopo il punto decimale e dell’errore cumulativo con un numero elevato di operazioni. Ecco perché tutti i prezzi sono presentati nel seguente formato MoneyValue
campo | tipo | Descrizione |
---|---|---|
valuta | corda | Stringa codice valuta ISO |
unità | int64 | Parte intera della somma, può essere un numero negativo |
nano | int32 | La parte frazionaria dell’importo può essere un numero negativo |
Li elaboriamo separatamente, quindi li portiamo al valore del prezzo:
quotazione.unità + quotazione.nano / 1e9
Il costo dei contratti futures
Il prezzo dei futures è presentato in punti, quando hai un future in valuta, devi conoscere il tasso. E ovviamente il prezzo in punti e il gradino di prezzo. Quando calcoli il profitto dalle transazioni, questo può sparare, perché. se calcoli l’importo totale moltiplicando il prezzo per la quantità. Qui devi stare attento. Per ora vedremo come va. Questo vale per i futures su valute, in altri posti va tutto bene.
mercato OTC
Questo mercato ha molte peculiarità, quindi studiamo le operazioni su di esso separatamente: quando inizi a sincronizzare le operazioni, risulterà che devi portare figi / ticker nella stessa forma per abbinare correttamente lo strumento. Quando inizi a sincronizzare questo con il rapporto di intermediazione, risulterà che il tradeID della stessa transazione ha lettere all’inizio delle transazioni e non sono nel rapporto di intermediazione. Pertanto, non possono essere confrontati … ehm-ehm … per confronto! Ho abbinato il tempo di scambio, il ticker e ho abbinato che un tradeId è contenuto in un altro. Esatto, non lo so. Chiunque incontri questo e chi se ne frega, vieni al problema o iniziane uno nuovo.
Operazioni matematiche sugli utensili
È impossibile, senza guardare, eseguire operazioni matematiche con l’intero elenco. Per non aggiungere warm a soft, controlliamo sempre la valuta ed elaboriamo solo se siamo sicuri che la valuta corrisponda e che i punti siano convertiti nella valuta desiderata. Armati della conoscenza del lavoro con i numeri bancari, calcoleremo la commissione spesa su ciascuno dei conti. In questo modo: https://github.com/pskucherov/tcsstat/tree/step4 https://github.com/pskucherov/tcsstat/compare/step3…step4
Il microservizio è pronto!
https://github.com/pskucherov/tcsstat Come compito a casa, puoi verificare se il servizio funziona con una connessione lenta, quando le connessioni sono interrotte, quando Internet è disconnesso, quando errori o limiti scaduti da parte del broker.
Conclusioni e progetti per il futuro
- Imparato le operazioni di base e l’utilizzo dell’API Invest
- Tempo trascorso ~ 10 ore
- Livello di difficoltà ~ junior+ / medio basso
Se continui a perfezionare il microservizio, potresti ritrovarti con qualcosa di simile
https://opexbot.info
Questo è il mio sviluppo, per chi è troppo pigro per capire, correre e contare da solo. Ho intenzione di aggiungere analisi lì su richiesta degli utenti. Se ti è piaciuto l’articolo, iscriviti al mio canale Telegram .
Полезная статья. Не могу представить, сколько усилий автора потребовалось, чтобы все описать. Благодарю.