Current section
Files
Jump to
Current section
Files
src/ursatoro@util.erl
-module(ursatoro@util).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/ursatoro/util.gleam").
-export([validate_period/1, validate_length/2, bd_to_float/1, bds_to_floats/1, float_to_bd/1, int_to_bd/1, safe_divide/2, bd_sum/1, bd_mean/1, bd_max/2, bd_min/2, bd_abs/1, sma_values/2, true_range/2, hundred/0, bd_sqrt/1, bd_stddev_population/1]).
-export_type([indicator_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type indicator_error() :: {insufficient_data, integer(), integer()} |
{invalid_period, integer()} |
division_by_zero.
-file("src/ursatoro/util.gleam", 25).
-spec validate_period(integer()) -> {ok, nil} | {error, indicator_error()}.
validate_period(Period) ->
case Period > 0 of
true ->
{ok, nil};
false ->
{error, {invalid_period, Period}}
end.
-file("src/ursatoro/util.gleam", 32).
-spec validate_length(list(any()), integer()) -> {ok, nil} |
{error, indicator_error()}.
validate_length(Items, Required) ->
Got = erlang:length(Items),
case Got >= Required of
true ->
{ok, nil};
false ->
{error, {insufficient_data, Required, Got}}
end.
-file("src/ursatoro/util.gleam", 45).
-spec bd_to_float(bigdecimal:big_decimal()) -> float().
bd_to_float(Value) ->
S = bigdecimal:to_plain_string(Value),
case gleam_stdlib:parse_float(S) of
{ok, F} ->
F;
{error, nil} ->
case gleam_stdlib:parse_float(<<S/binary, ".0"/utf8>>) of
{ok, F@1} ->
F@1;
{error, nil} ->
+0.0
end
end.
-file("src/ursatoro/util.gleam", 59).
-spec bds_to_floats(list(bigdecimal:big_decimal())) -> list(float()).
bds_to_floats(Values) ->
gleam@list:map(Values, fun bd_to_float/1).
-file("src/ursatoro/util.gleam", 63).
-spec float_to_bd(float()) -> bigdecimal:big_decimal().
float_to_bd(Value) ->
bigdecimal:from_float(Value).
-file("src/ursatoro/util.gleam", 67).
-spec int_to_bd(integer()) -> bigdecimal:big_decimal().
int_to_bd(Value) ->
S = erlang:integer_to_binary(Value),
case bigdecimal:from_string(S) of
{ok, Bd} ->
Bd;
{error, nil} ->
bigdecimal:zero()
end.
-file("src/ursatoro/util.gleam", 77).
-spec safe_divide(bigdecimal:big_decimal(), bigdecimal:big_decimal()) -> {ok,
bigdecimal:big_decimal()} |
{error, indicator_error()}.
safe_divide(Dividend, Divisor) ->
case bigdecimal:signum(Divisor) of
0 ->
{error, division_by_zero};
_ ->
{ok, bigdecimal:divide(Dividend, Divisor, half_up)}
end.
-file("src/ursatoro/util.gleam", 87).
-spec bd_sum(list(bigdecimal:big_decimal())) -> bigdecimal:big_decimal().
bd_sum(Values) ->
bigdecimal:sum(Values).
-file("src/ursatoro/util.gleam", 91).
-spec bd_mean(list(bigdecimal:big_decimal())) -> {ok, bigdecimal:big_decimal()} |
{error, indicator_error()}.
bd_mean(Values) ->
case Values of
[] ->
{error, division_by_zero};
_ ->
Sum = bigdecimal:sum(Values),
Count = int_to_bd(erlang:length(Values)),
safe_divide(Sum, Count)
end.
-file("src/ursatoro/util.gleam", 102).
-spec bd_max(bigdecimal:big_decimal(), bigdecimal:big_decimal()) -> bigdecimal:big_decimal().
bd_max(A, B) ->
case bigdecimal:compare(A, B) of
gt ->
A;
eq ->
A;
lt ->
B
end.
-file("src/ursatoro/util.gleam", 110).
-spec bd_min(bigdecimal:big_decimal(), bigdecimal:big_decimal()) -> bigdecimal:big_decimal().
bd_min(A, B) ->
case bigdecimal:compare(A, B) of
lt ->
A;
eq ->
A;
gt ->
B
end.
-file("src/ursatoro/util.gleam", 118).
-spec bd_abs(bigdecimal:big_decimal()) -> bigdecimal:big_decimal().
bd_abs(Value) ->
bigdecimal:absolute_value(Value).
-file("src/ursatoro/util.gleam", 205).
-spec sma_values(list(bigdecimal:big_decimal()), integer()) -> {ok,
list(bigdecimal:big_decimal())} |
{error, indicator_error()}.
sma_values(Values, Period) ->
gleam@result:'try'(
validate_period(Period),
fun(_) ->
gleam@result:'try'(
validate_length(Values, Period),
fun(_) ->
Period_bd = int_to_bd(Period),
{Results@1, _, _, _} = gleam@list:fold(
Values,
{[], gleam@deque:new(), bigdecimal:zero(), 0},
fun(Acc, Value) ->
{Results, Window, Window_sum, Count} = Acc,
New_window = gleam@deque:push_back(Window, Value),
New_sum = bigdecimal:add(Window_sum, Value),
New_count = Count + 1,
case New_count > Period of
true ->
case gleam@deque:pop_front(New_window) of
{ok, {Oldest, Trimmed_window}} ->
Trimmed_sum = bigdecimal:subtract(
New_sum,
Oldest
),
Sma = bigdecimal:divide(
Trimmed_sum,
Period_bd,
half_up
),
{[Sma | Results],
Trimmed_window,
Trimmed_sum,
Period};
{error, nil} ->
{Results,
New_window,
New_sum,
New_count}
end;
false ->
case New_count =:= Period of
true ->
Sma@1 = bigdecimal:divide(
New_sum,
Period_bd,
half_up
),
{[Sma@1 | Results],
New_window,
New_sum,
New_count};
false ->
{Results,
New_window,
New_sum,
New_count}
end
end
end
),
{ok, lists:reverse(Results@1)}
end
)
end
).
-file("src/ursatoro/util.gleam", 254).
-spec true_range(ursatoro@candle:candle(), ursatoro@candle:candle()) -> bigdecimal:big_decimal().
true_range(Prev, Current) ->
High_low = bigdecimal:subtract(
ursatoro@candle:high(Current),
ursatoro@candle:low(Current)
),
High_prev_close = bd_abs(
bigdecimal:subtract(
ursatoro@candle:high(Current),
ursatoro@candle:close(Prev)
)
),
Low_prev_close = bd_abs(
bigdecimal:subtract(
ursatoro@candle:low(Current),
ursatoro@candle:close(Prev)
)
),
_pipe = High_low,
_pipe@1 = bd_max(_pipe, High_prev_close),
bd_max(_pipe@1, Low_prev_close).
-file("src/ursatoro/util.gleam", 265).
-spec hundred() -> bigdecimal:big_decimal().
hundred() ->
int_to_bd(100).
-file("src/ursatoro/util.gleam", 136).
-spec newton_sqrt(bigdecimal:big_decimal(), bigdecimal:big_decimal(), integer()) -> bigdecimal:big_decimal().
newton_sqrt(Value, Guess, Iterations) ->
case Iterations >= 50 of
true ->
bigdecimal:rescale(Guess, 10, half_up);
false ->
Two = int_to_bd(2),
Divided = bigdecimal:divide(Value, Guess, half_up),
Next = bigdecimal:divide(
bigdecimal:add(Guess, Divided),
Two,
half_up
),
Diff = bd_abs(bigdecimal:subtract(Next, Guess)),
Threshold = bigdecimal:rescale(bigdecimal:one(), 10 + 2, half_up),
case bigdecimal:compare(Diff, Threshold) of
lt ->
bigdecimal:rescale(Next, 10, half_up);
eq ->
bigdecimal:rescale(Next, 10, half_up);
gt ->
newton_sqrt(Value, Next, Iterations + 1)
end
end.
-file("src/ursatoro/util.gleam", 124).
?DOC(
" Square root via Newton's method (Babylonian method).\n"
" Returns zero for zero/negative inputs.\n"
).
-spec bd_sqrt(bigdecimal:big_decimal()) -> bigdecimal:big_decimal().
bd_sqrt(Value) ->
case bigdecimal:signum(Value) of
S when S =< 0 ->
bigdecimal:zero();
_ ->
Two = int_to_bd(2),
Initial = bigdecimal:divide(Value, Two, half_up),
newton_sqrt(Value, Initial, 0)
end.
-file("src/ursatoro/util.gleam", 188).
-spec bd_stddev_population(list(bigdecimal:big_decimal())) -> {ok,
bigdecimal:big_decimal()} |
{error, indicator_error()}.
bd_stddev_population(Values) ->
gleam@result:'try'(
bd_mean(Values),
fun(Mean) ->
N = int_to_bd(erlang:length(Values)),
Sum_sq = gleam@list:fold(
Values,
bigdecimal:zero(),
fun(Acc, V) ->
Diff = bigdecimal:subtract(V, Mean),
Sq = bigdecimal:multiply(Diff, Diff),
bigdecimal:add(Acc, Sq)
end
),
gleam@result:'try'(
safe_divide(Sum_sq, N),
fun(Variance) -> {ok, bd_sqrt(Variance)} end
)
end
).