In fintech, we often have to process quite large volumes of currency exchange rate data. We obtain data from various sources, each of which has its own way of extrapolating values for tomorrow, the day after tomorrow, next month, and even the next three years. If someone could predict rates correctly, it would be time to close the business and simply exchange money back and forth. correctly, it would be time to close the business and just trade money back and forth. Some sources are more trusted, while others provide mostly junk, with rare instances of almost correct values, especially for exotic pairs. Our job is to sift through these tens of thousands of values per second and determine what to show our clients. We need to filter out the single correct value from a ton of dirt and mud, just like flamingos do during lunch.

A distinguishing feature of flamingos is their massive downturned beak, which they use to filter food from water or mud.
 âÂ
Thus, the library was born , which holds a cache of states for multiple values over specified time intervals. Under the hood, it automatically filters out bad and outdated data while providing access to the latest N validated values for each key (currency pairs, in our case).
Let's say we are collecting rates for three currency pairs. The simplest definition Vela for storing the current state would look something like this:
defmodule Pairs do
use Vela,
eurusd: [sorter: &Kernel. 0
endUpdating values
the function will sequentially perform the following:
- call
validatoron the value if it is defined (see chapter Validation below); - add the value to either the list of good values if validation was successful, or to the error queue
:__errors__otherwise; - call sorting if the
sorteris defined for this key, or simply place the value at the head of the list (LIFO, see chapter Sortera below); - trim the list according to the parameter
:limitpassed during creation; - return the updated structure
Vela.
iex|1 > par = %Pairs{}
iex|2 > Vela.put(par, :eurcad, 1.0)
#â %Pairs{..., eurcad: [1.0], ...}
iex|3 > Vela.put(par, :eurcad, -1.0)
#â %Pairs{__errors__: [eurcad: -1.0], ...}
iex|4 > par |> Vela.put(:eurusd, 2.0) |> Vela.put(:eurusd, 1.0)
#â %Pairs{... eurusd: [1.0, 2.0]}Ăven Vela implementerar , sĂ„ att du kan anvĂ€nda nĂ„gon av de standardfunktioner för djupuppdatering av strukturer frĂ„n arsenalen : , , , , och .
Validation
Validatorn kan definieras som:
- en extern funktion med ett argument (
&MyMod.my_fun/1), den kommer endast att fÄ vÀrdet för validering; - en extern funktion med tvÄ argument,
&MyMod.my_fun/2, den kommer att fÄ paretserie, vÀrdeför validering; - modul som implementerar ;
- konfigurationsparameter
tröskelvĂ€rde, och â valfritt âjĂ€mför_efter, see chapter JĂ€mförelse under.
Om valideringen lyckas lÀggs vÀrdet till i listan under motsvarande nyckel, i motsatt fall skickas en tuple {serie, vÀrde} till :__errors_.
JÀmförelse
VÀrdena som lagras i dessa rader kan vara vilka som helst. För att lÀra Vela dem att jÀmföra mÄste du skicka jÀmför_efter parametern i radens definition (om inte vÀrdena kan jÀmföras med standard Kernel.</2); denna parameter ska ha typ (Vela.value() -> number()). Som standard Àr detta bara & &1.
Dessutom kan man i radens definition skicka parameter komparator för att berÀkna vÀrdena för deltal.min/max); till exempel kan man genom att skicka Date.diff/2 som komparator fÄ rÀtt deltal för datum.
Ett annat bekvÀmt sÀtt att arbeta Àr att skicka parameter tröskelvÀrde, som definierar det maximalt tillÄtna förhÄllandet av det nya vÀrdet till {min, max} intervallet. Eftersom det anges i procent anvÀnds inte komparator, men anvÀnds fortfarande jÀmför_efter. Till exempel, för att ange ett tröskelvÀrde för tidsstÀmplar mÄste du ange jÀmför_efter: &DateTime.to_unix/1 (för att fÄ ett heltal) och tröskelvÀrde: 1, vilket gör att nya vÀrden endast tillÄts om de ligger inom ±band intervallet frÄn nuvarande vÀrden.
Slutligen kan du anvÀnda Vela.equal?/2 jÀmför/2 , om vÀrden definierar funktionen eller equal?/2jÀmför/2 ==/2.
, sÄ kommer dessa funktioner att anvÀndas för jÀmförelse, annars anvÀnder vi slaviskt
HÀmtning av vÀrden Bearbetning av det aktuella tillstÄndet börjar vanligtvis med att kalla pÄVela.purge/1 validator , som tar bort förÄldrade vÀrden (om kopplade till timestamps). DÀrför kan man anropa Vela.slice/1, som returnerar keyword med radnamn som nycklar och de aktuella vÀrdena som första vÀrde.
Man kan ocksÄ anvÀnda get_in/2/pop_in/2 för lÄg nivÄ Ätkomst till vÀrden i varje rad.
Applikation
Vela kan vara extremt anvÀndbart som en cache för tidsserier i tillstÄndet för en process av typen GenServer/Agent. Vi vill aldrig anvÀnda förÄldrade vÀrden av kurser, och för detta hÄller vi bara en process med tillstÄnd behandlat Vela, med en validator som visas nedan.
@impl Vela.Validator
def valid?(_key, %Rate{} = rate),
do: Rate.age(rate) < @death_ageoch Bearbetning av det aktuella tillstĂ„ndet börjar vanligtvis med att kalla pĂ„ raderar lugnt alla förĂ„ldrade vĂ€rden varje gĂ„ng vi behöver data. För att fĂ„ tillgĂ„ng till aktuella vĂ€rden anropar vi bara Vela.slice/1, och nĂ€r vi behöver en liten historik över kursen (hela raden) returnerar vi helt enkelt den â redan sorterad â med validerade vĂ€rden.
Lycka till med cachandet av tidsserier!
KĂ€lla: habr.com
