En el fintech, a menudo tenemos que procesar volúmenes bastante grandes de datos sobre las tasas de cambio de divisas. Obtenemos datos de diversas fuentes, y cada una de ellas tiene su propia forma de extrapolar los valores de las tasas para mañana, pasado mañana, el próximo mes e incluso los próximos tres años. Si alguien pudiera predecir las tasas, sería el momento de cerrar el negocio y simplemente cambiar dinero de un lado a otro. almacenar correctamenteAlgunas fuentes generan más confianza, mientras que otras proporcionan solo basura, con raras excepciones de valores casi correctos, pero solo para pares exóticos. Nuestro trabajo consiste en filtrar esos decenas de miles de valores por segundo y determinar qué mostrar a los clientes. Necesitamos filtrar el único valor correcto de toneladas de suciedad y barro, como lo hacen los flamencos durante la comida.

Una característica distintiva de los flamencos es su masivo pico curvado hacia abajo, con el cual filtran alimento del agua o el barro.
—
Así nació la biblioteca , que almacena el caché del estado de varios valores en intervalos de tiempo determinados. Internamente, filtra en tiempo real los datos malos y obsoletos, además de proporcionar acceso a los últimos N valores que han pasado la validación para cada clave (pares de divisas, en nuestro caso).
Supongamos que estamos recopilando tasas para tres pares de divisas. La definición más simple Vela para almacenar el estado actual sería algo así:
defmodule Pairs do
use Vela,
eurusd: [sorter: &Kernel. 0
endActualización de valores
la función hará lo siguiente secuencialmente:
- llamará al
validatorsobre el valor, si está definido (ver capítulo Validación más abajo); - agregará el valor a la lista de valores buenos si la validación es exitosa, o a la lista de errores
:__errors__en caso contrario; - llamará a la ordenación si
sorterestá definido para esta clave, o simplemente pondrá el valor al principio de la lista (LIFO, ver capítulo Ordenación más abajo); - cortará la lista de acuerdo con el parámetro
:limitpasado al crear; - devolverá la estructura actualizada
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]}También Vela implementa , por lo que se puede utilizar cualquiera de las funciones estándar para actualizar estructuras de manera profunda : , , , , y .
Validación
El validador puede definirse como:
- una función externa con un argumento (
&MyMod.my_fun/1), que recibirá solo el valor a validar; - una función externa con dos argumentos,
&MyMod.my_fun/2, que recibirá un parserie, valorpara validar; - un módulo que implementa ;
- un parámetro de configuración
umbral, y — opcionalmente —compare_by, ver capítulo Comparación más abajo.
Si la validación es exitosa, el valor se añade a la lista bajo la clave correspondiente, de lo contrario, la tupla {serie, valor} se envía a :__errors_.
Comparación
Los valores almacenados en estas filas pueden ser cualquier cosa. Para enseñarles a Vela a compararlos, es necesario pasar compare_by un parámetro en la definición de la serie (a menos que los valores no puedan ser comparados de forma estándar Kernel.</2); este parámetro debe tener tipo (Vela.value() -> number()). Por defecto, esto es simplemente & &1.
Además, en la definición de la serie se puede pasar un parámetro comparator para calcular los valores de delta (min/max); por ejemplo, al pasar Date.diff/2 como comparador, se pueden obtener deltas correctos para fechas.
Otra forma conveniente de trabajar es pasar un parámetro umbral, que define la relación máxima permitida del nuevo valor a {min, max} intervalo. Como se establece en porcentaje, la verificación no utiliza comparator, pero aún utiliza compare_by. Por ejemplo, para establecer un umbral para las fechas, se debe especificar compare_by: &DateTime.to_unix/1 (para obtener un valor entero) y threshold: 1, de modo que los nuevos valores solo se permitirán si están en ±banda intervalo de los valores actuales.
Finalmente, se puede utilizar Vela.equal?/2 para comparar dos cachés. Si los valores definen la función equal?/2 o compare/2, estas funciones se utilizarán para la comparación, de lo contrario, simplemente utilizamos ==/2.
Obteniendo valores
El procesamiento del estado actual generalmente comienza con la llamada Vela.purge/1, que elimina los valores obsoletos (si validator se basa en timestamps). Luego se puede llamar a Vela.slice/1, que devolverá palabra clave con los nombres de las filas como claves y los primeros valores actuales.
También se puede utilizar get_in/2/pop_in/2 para el acceso de bajo nivel a los valores de cada fila.
Aplicación
Vela puede resultar extremadamente útil como caché de series temporales en el estado de un proceso del tipo GenServer/Agente. Nunca queremos usar valores obsoletos de tasas, y para eso simplemente mantenemos un proceso con estado, manejado Vela, con el validador mostrado a continuación.
@impl Vela.Validator
def valid?(_key, %Rate{} = rate),
do: Rate.age(rate) < @death_agey Vela.purge/1 que elimina suavemente todos los valores obsoletos cada vez que necesitamos datos. Para acceder a los valores actuales, simplemente llamamos Vela.slice/1, y cuando se requiere un pequeño historial de la tasa (toda la serie en su totalidad), simplemente la devolvemos, ya ordenada, con valores validados.
¡Feliz caché de series temporales!
Fuente: habr.com
