Packages

Trading technical indicators library for Gleam. SMA, EMA, MACD, RSI, Stochastic, Bollinger Bands, ATR, VWAP, OBV.

Current section

Files

Jump to
ursatoro src ursatoro@filter.erl
Raw

src/ursatoro@filter.erl

-module(ursatoro@filter).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/ursatoro/filter.gleam").
-export([kalman_new/1, kalman_update/4, kalman_smooth/3, savitzky_golay/3]).
-export_type([kalman_state/0]).
-type kalman_state() :: {kalman_state, float(), float()}.
-file("src/ursatoro/filter.gleam", 21).
-spec kalman_new(float()) -> kalman_state().
kalman_new(Initial_price) ->
{kalman_state, Initial_price, 1.0}.
-file("src/ursatoro/filter.gleam", 25).
-spec kalman_update(kalman_state(), float(), float(), float()) -> kalman_state().
kalman_update(State, Observation, Q, R) ->
Predicted_cov = erlang:element(3, State) + Q,
Kalman_gain = case (Predicted_cov + R) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Predicted_cov / Gleam@denominator
end,
New_estimate = erlang:element(2, State) + (Kalman_gain * (Observation - erlang:element(
2,
State
))),
New_error_cov = (1.0 - Kalman_gain) * Predicted_cov,
{kalman_state, New_estimate, New_error_cov}.
-file("src/ursatoro/filter.gleam", 41).
-spec kalman_smooth(list(float()), float(), float()) -> {ok, list(float())} |
{error, ursatoro@util:indicator_error()}.
kalman_smooth(Prices, Q, R) ->
gleam@result:'try'(
ursatoro@util:validate_length(Prices, 1),
fun(_) -> case Prices of
[] ->
{ok, []};
[First | Rest] ->
Initial = kalman_new(First),
{Results, _} = gleam@list:fold(
Rest,
{[First], Initial},
fun(Acc, Price) ->
{Estimates, State} = Acc,
New_state = kalman_update(State, Price, Q, R),
{[erlang:element(2, New_state) | Estimates],
New_state}
end
),
{ok, lists:reverse(Results)}
end end
).
-file("src/ursatoro/filter.gleam", 167).
-spec get_at(list(float()), integer()) -> float().
get_at(Lst, Idx) ->
case Idx < 0 of
true ->
+0.0;
false ->
case gleam@list:drop(Lst, Idx) of
[Val | _] ->
Val;
[] ->
+0.0
end
end.
-file("src/ursatoro/filter.gleam", 153).
-spec apply_coeffs(list(float()), list(float()), integer(), integer()) -> float().
apply_coeffs(Prices, Coeffs, Center, Half) ->
Start = Center - Half,
gleam@list:index_fold(
Coeffs,
+0.0,
fun(Acc, Coeff, Idx) ->
Price = get_at(Prices, Start + Idx),
Acc + (Coeff * Price)
end
).
-file("src/ursatoro/filter.gleam", 135).
-spec gram_poly(float(), float(), integer()) -> float().
gram_poly(J, Half, Order) ->
case Order of
0 ->
1.0;
1 ->
case Half of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> J / Gleam@denominator
end;
K ->
Kf = erlang:float(K),
Km1 = erlang:float(K - 1),
P_prev = gram_poly(J, Half, K - 1),
P_prev2 = gram_poly(J, Half, K - 2),
Num1 = (((2.0 * Kf) - 1.0) * J) * P_prev,
Num2 = (Km1 * ((Half * Half) - (((Km1 * Km1) - 1.0) / 4.0))) * P_prev2,
case (Kf * Half) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> (Num1 - Num2) / Gleam@denominator@1
end
end.
-file("src/ursatoro/filter.gleam", 120).
-spec compute_sg_coeff(float(), float(), integer(), float()) -> float().
compute_sg_coeff(J, Half, Order, M) ->
gleam@int:range(
0,
Order,
+0.0,
fun(Acc, K) ->
Gj = gram_poly(J, Half, K),
G0 = gram_poly(+0.0, Half, K),
Kf = erlang:float(K),
Weight = case M of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> ((2.0 * Kf) + 1.0) / Gleam@denominator
end,
Acc + ((Gj * G0) * Weight)
end
).
-file("src/ursatoro/filter.gleam", 107).
-spec sg_coefficients(integer(), integer()) -> list(float()).
sg_coefficients(Half, Polyorder) ->
M = erlang:float((2 * Half) + 1),
_pipe = gleam@int:range(
- Half,
Half,
[],
fun(Acc, J) ->
Jf = erlang:float(J),
Hf = erlang:float(Half),
[compute_sg_coeff(Jf, Hf, Polyorder, M) | Acc]
end
),
lists:reverse(_pipe).
-file("src/ursatoro/filter.gleam", 66).
-spec savitzky_golay(list(float()), integer(), integer()) -> {ok, list(float())} |
{error, ursatoro@util:indicator_error()}.
savitzky_golay(Prices, Window_length, Polyorder) ->
N = erlang:length(Prices),
case ((Window_length rem 2) =:= 1) andalso (Window_length >= 3) of
false ->
{error, {invalid_period, Window_length}};
true ->
case (Polyorder >= 0) andalso (Polyorder < Window_length) of
false ->
{error, {invalid_period, Polyorder}};
true ->
case N >= Window_length of
false ->
{error, {insufficient_data, Window_length, N}};
true ->
Half = Window_length div 2,
Coeffs = sg_coefficients(Half, Polyorder),
Arr = gleam@list:index_map(
Prices,
fun(P, I) -> {I, P} end
),
Prices_arr = Prices,
Smoothed = gleam@list:map(
Arr,
fun(Entry) ->
{I@1, _} = Entry,
case (I@1 < Half) orelse (I@1 >= (N - Half)) of
true ->
get_at(Prices_arr, I@1);
false ->
apply_coeffs(
Prices_arr,
Coeffs,
I@1,
Half
)
end
end
),
{ok, Smoothed}
end
end
end.