Skip to content

Commit

Permalink
Refactor to integrate with archethic-foundation#728 and archethic-fou…
Browse files Browse the repository at this point in the history
  • Loading branch information
netboz committed Jan 6, 2023
1 parent 94464d5 commit 8cd5ee1
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 31 deletions.
61 changes: 46 additions & 15 deletions lib/archethic/oracle_chain/services/uco_price.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,24 @@ defmodule Archethic.OracleChain.Services.UCOPrice do
fn provider ->
## Filter resuts from services marked as :error
case provider.fetch(@pairs) do
{:ok, prices} -> {true, prices}
{:error, _} -> false
{:ok, prices} ->
{true, prices}

{:error, reason} ->
Logger.warning(
"Service : #{inspect(__MODULE__)} : Cannot fetch values from
provider: #{inspect(provider)} with reason : #{inspect(reason)}. Discarding entry."
)

false
end
end,
providers()
)
## split prices in a list per currency
## split prices in a list per currency. If a service returned a list of prices of a currency,
## they will be meaned before being added to list
|> split_prices()
## compute media per currency list
## compute median per currency list
|> median_prices()

{:ok, prices}
Expand All @@ -37,14 +46,14 @@ defmodule Archethic.OracleChain.Services.UCOPrice do
@spec verify?(%{required(String.t()) => any()}) :: boolean
def verify?(prices_prior = %{}) do
case fetch() do
{:ok, prices_now} when prices_now == %{} ->
Logger.warning("Cannot fetch UCO price - reason: no data from any service.")
false

{:ok, prices_now} ->
Enum.all?(@pairs, fn pair ->
compare_price(Map.fetch!(prices_prior, pair), Map.fetch!(prices_now, pair))
end)

{:error, reason} ->
Logger.warning("Cannot fetch UCO price - reason: #{inspect(reason)}")
false
end
end

Expand Down Expand Up @@ -80,8 +89,15 @@ defmodule Archethic.OracleChain.Services.UCOPrice do
mean(xs, t + x, l + 1)
end

defp median_prices({list_of_euro_prices, list_of_usd_prices}) do
%{"eur" => median(list_of_euro_prices), "usd" => median(list_of_usd_prices)}
defp median_prices(%{} = map_prices) do
Enum.reduce(map_prices, %{}, fn {currency, values}, acc ->
Map.put(acc, currency, median(values))
end)
end

## To avoid all calculation from general clause to follow
defp median([price]) do
price
end

defp median(prices) do
Expand Down Expand Up @@ -115,18 +131,33 @@ defmodule Archethic.OracleChain.Services.UCOPrice do
Application.get_env(:archethic, __MODULE__) |> Keyword.fetch!(:providers)
end

defp split_prices(list_of_map_of_prices) do
split_prices(list_of_map_of_prices, {[], []})
defp split_prices(list_of_maps_of_prices) do
split_prices(list_of_maps_of_prices, %{})
end

defp split_prices([], acc) do
acc
end

defp split_prices(
[%{"eur" => euro_price, "usd" => usd_price} | other_prices],
{euro_prices, usd_prices}
[%{} = prices | other_prices],
aggregated_data
) do
split_prices(other_prices, {euro_prices ++ [euro_price], usd_prices ++ [usd_price]})
new_aggregated_data =
Enum.reduce(prices, aggregated_data, fn
## Assert we have at least one value for the currency
{currency, [_ | _] = values}, acc ->
Map.update(acc, currency, [median(values)], fn previous_values ->
previous_values ++ [mean(values)]
end)

other, acc ->
Logger.warning(
"No or Unexpected value : #{inspect(other)} while aggregating service result"
)
acc
end)

split_prices(other_prices, new_aggregated_data)
end
end
32 changes: 16 additions & 16 deletions test/archethic/oracle_chain/services/uco_price_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule Archethic.OracleChain.Services.UCOPriceTest do
|> expect(:fetch, fn pairs ->
res =
Enum.map(pairs, fn pair ->
{pair, :rand.uniform_real()}
{pair, [:rand.uniform_real()]}
end)
|> Enum.into(%{})

Expand All @@ -21,7 +21,7 @@ defmodule Archethic.OracleChain.Services.UCOPriceTest do
|> expect(:fetch, fn pairs ->
res =
Enum.map(pairs, fn pair ->
{pair, :rand.uniform_real()}
{pair, [:rand.uniform_real()]}
end)
|> Enum.into(%{})

Expand All @@ -32,7 +32,7 @@ defmodule Archethic.OracleChain.Services.UCOPriceTest do
|> expect(:fetch, fn pairs ->
res =
Enum.map(pairs, fn pair ->
{pair, :rand.uniform_real()}
{pair, [:rand.uniform_real()]}
end)
|> Enum.into(%{})

Expand All @@ -46,17 +46,17 @@ defmodule Archethic.OracleChain.Services.UCOPriceTest do
test "should return true if the prices are the good one" do
MockUCOPriceProvider1
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.20, "usd" => 0.12}}
{:ok, [%{"eur" => 0.20, "usd" => 0.12}]}
end)

MockUCOPriceProvider2
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.20, "usd" => 0.12}}
{:ok, [%{"eur" => 0.20, "usd" => 0.12}]}
end)

MockUCOPriceProvider3
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.20, "usd" => 0.12}}
{:ok, [%{"eur" => 0.20, "usd" => 0.12}]}
end)

assert true == UCOPrice.verify?(%{"eur" => 0.20, "usd" => 0.12})
Expand All @@ -65,17 +65,17 @@ defmodule Archethic.OracleChain.Services.UCOPriceTest do
test "should return false if the prices are not the good one" do
MockUCOPriceProvider1
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.20, "usd" => 0.12}}
{:ok, [%{"eur" => 0.20, "usd" => 0.12}]}
end)

MockUCOPriceProvider2
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.20, "usd" => 0.12}}
{:ok, [%{"eur" => 0.20, "usd" => 0.12}]}
end)

MockUCOPriceProvider3
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.20, "usd" => 0.12}}
{:ok, [%{"eur" => 0.20, "usd" => 0.12}]}
end)

assert false == UCOPrice.verify?(%{"eur" => 0.10, "usd" => 0.14})
Expand All @@ -84,17 +84,17 @@ defmodule Archethic.OracleChain.Services.UCOPriceTest do
test "should return the median value when multiple providers queried" do
MockUCOPriceProvider1
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.20, "usd" => 0.12}}
{:ok, [%{"eur" => 0.20, "usd" => 0.12}]}
end)

MockUCOPriceProvider2
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.30, "usd" => 0.12}}
{:ok, [%{"eur" => 0.30, "usd" => 0.12}]}
end)

MockUCOPriceProvider3
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.40, "usd" => 0.12}}
{:ok, [%{"eur" => 0.40, "usd" => 0.12}]}
end)

assert false == UCOPrice.verify?(%{"eur" => 0.30, "usd" => 0.12})
Expand Down Expand Up @@ -123,22 +123,22 @@ defmodule Archethic.OracleChain.Services.UCOPriceTest do
## Define mocks expectations
MockUCOPriceProvider1
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.20, "usd" => 0.12}}
{:ok, [%{"eur" => 0.20, "usd" => 0.12}]}
end)

MockUCOPriceProvider2
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.30, "usd" => 0.12}}
{:ok, [%{"eur" => 0.30, "usd" => 0.12}]}
end)

MockUCOPriceProvider3
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.40, "usd" => 0.12}}
{:ok, [%{"eur" => 0.40, "usd" => 0.12}]}
end)

MockUCOPriceProvider4
|> expect(:fetch, fn _pairs ->
{:ok, %{"eur" => 0.50, "usd" => 0.12}}
{:ok, [%{"eur" => 0.50, "usd" => 0.12}]}
end)

## Restore original environment
Expand Down

0 comments on commit 8cd5ee1

Please sign in to comment.