În fintech, adesea trebuie să gestionăm volume destul de mari de date privind cursurile de schimb valutar. Obținem date din diverse surse, fiecare având propria reprezentare despre cum să extrapoleze valorile cursurilor pentru mâine, poimâine, luna următoare și chiar următorii trei ani. Dacă cineva ar putea prezice cursurile, săar fi momentul să închidem afacerea și pur și simplu să schimbăm bani dintr-o parte în alta. Unele surse sunt mai de încredere, altele oferă doar informații inutile, cu câteva valori aproape corecte, dar pentru perechi exotice. Munca noastră constă în a analiza zecile de mii de valori pe secundă și a determina ce să arătăm clienților. Trebuie să filtrăm valoarea corectă dintr-o tonă de mizerie și nămol, așa cum o fac flamingo la masă.

O caracteristică distinctivă a flamingo-ului este ciocul masiv, curbat în jos, cu ajutorul căruia își filtrează hrana din apă sau nămol.
—
Așa a luat naștere biblioteca , care stochează cache-ul stării pentru mai multe valori în intervale de timp date. Sub capotă, aceasta filtrează în timp real datele proaste și învechite, oferind acces la ultimele N valori validate pentru fiecare cheie (perechi valutare, în cazul nostru).
Să presupunem că strângem cursurile pentru trei perechi valutare. O definiție simplă Vela pentru stocarea stării actuale ar arăta cam așa:
defmodule Pairs do
use Vela,
eurusd: [sorter: &Kernel. 0
endActualizarea valorilor
funcția va face următoarele:
- va apela
validatorulpe valoare, dacă este definit (vezi capitolul Validare de mai jos); - va adăuga valoarea fie în rândul valorilor bune, dacă validarea a avut succes, fie într-un rând de erori
:__errors__în caz contrar; - va apela sortarea dacă
sorter-uleste definit pentru această cheie, sau pur și simplu va adăuga valoarea la începutul listei (LIFO, vezi capitolul Sortare de mai jos); - îi va tăia rândul conform parametrului
:limittransmis la crearea sa; - va returna structura actualizată
Vela.
iex|1 > pairs = %Pairs{}
iex|2 > Vela.put(pairs, :eurcad, 1.0)
#⇒ %Pairs{..., eurcad: [1.0], ...}
iex|3 > Vela.put(pairs, :eurcad, -1.0)
#⇒ %Pairs{__errors__: [eurcad: -1.0], ...}
iex|4 > pairs |> Vela.put(:eurusd, 2.0) |> Vela.put(:eurusd, 1.0)
#⇒ %Pairs{... eurusd: [1.0, 2.0]}De asemenea, Vela implementează , astfel încât puteți utiliza oricare din funcțiile standard pentru actualizarea profundă a structurilor disponibile : , , , , și .
Validare
Validatorul poate fi definit ca:
- o funcție externă cu un singur argument (
&MyMod.my_fun/1), care va primi doar valoarea pentru validare; - o funcție externă cu două argumente,
&MyMod.my_fun/2, care va primi o perecheserie, valuepentru validare; - un modul care implementează ;
- parametru de configurare
threshold, și - opțional -compare_by, vezi capitolul Comparare mai jos.
Dacă validarea a fost efectuată cu succes, valoarea este adăugată în listă sub cheia corespunzătoare, iar în caz contrar tupla {serie, value} se trimite în :__errors_.
Comparare
Valorile stocate în aceste linii pot fi orice. Pentru a învăța Vela să le compare, este necesar să transmiteți compare_by parametrul în definiția liniei (cu excepția cazului în care valorile nu pot fi comparate standard Kernel.<\/2); acest parametru ar trebui să aibă tipul (Vela.value() -> number()). Implicit, acesta este pur și simplu & &1.
De asemenea, în definiția liniei se poate transmite parametrul comparator pentru calcularea valorilor de delta (min/max); de exemplu, prin transmiterea Date.diff/2 ca comparator, se pot obține delta corecte pentru date.
O altă metodă convenabilă de operare este transmiterea parametrului threshold, care definește raportul maxim permis al noii valori la {min, max} interval. Deoarece este specificat în procente, verificarea nu utilizează comparator, dar tot utilizează compare_by. De exemplu, pentru a specifica un prag pentru timestamp-uri, este necesar să specificați compare_by: &DateTime.to_unix/1 (pentru a obține o valoare întreagă) și threshold: 1, astfel încât noile valori să fie permise doar dacă se află în ±band intervalul valorilor curente.
În cele din urmă, se poate folosi Vela.equal?/2 compare/2 , dacă aceste funcții definesc sau equal?/2, atunci aceste funcții vor fi folosite pentru comparare, în caz contrar, utilizăm pur și simplu ==/2.
Obținerea valorilor
Procesarea stării curente începe de obicei cu apelul Vela.purge/1, care elimină valorile depășite (dacă validatorul este legat de timestamps). Apoi se poate apela Vela.slice/1, care va returna keyword cu numele rândurilor ca chei și prima, valorile actuale.
De asemenea, se poate utiliza get_in/2/pop_in/2 pentru acces de nivel inferior la valorile din fiecare rând.
Aplicație
Vela poate fi extrem de util ca un cache pentru serii temporale în starea unui proces de tip GenServer/Agent. Nu dorim niciodată să folosim valori învechite ale cursurilor, iar pentru asta pur și simplu menținem un proces cu starea gestionată Vela, cu un validator prezentat mai jos.
@impl Vela.Validator
def valid?(_cheie, %Rate{} = rate),
do: Rate.age(rate) < @death_ageși Vela.purge/1 șterge în mod liniștit toate valorile învechite de fiecare dată când avem nevoie de date. Pentru a accesa valorile actuale, pur și simplu apelăm Vela.slice/1, iar când avem nevoie de o scurtă istorie a cursului (întreaga serie), pur și simplu o returnăm - deja sortată - cu valorile validate.
Succes la caching-ul seriilor temporale!
Sursa: habr.com
