defmodule Ccxt.Pro.OrderCache do
@moduledoc false
alias Ccxt.Transpiled.Runtime
@spec put(list() | term() | nil, map(), pos_integer()) :: list()
def put(cache, message, limit) when is_map(message) do
Ccxt.Pro.IndexedArrayCache.put(cache, message,
key: &message_order_id/1,
symbol: &message_symbol/1,
limit: limit,
merge: &merge_order_message/2
)
end
@spec hashmap(list() | term() | nil) :: map()
def hashmap(cache) do
Ccxt.Pro.IndexedArrayCache.hashmap(cache,
key: &message_order_id/1,
symbol: &message_symbol/1
)
end
defp merge_order_message(message, previous) do
message
|> merge_nested_order_message(previous)
|> merge_order_trade_metadata(previous)
end
defp merge_nested_order_message(%{"o" => order} = message, %{"o" => previous_order})
when is_map(order) and is_map(previous_order) do
%{message | "o" => Map.merge(previous_order, order)}
end
defp merge_nested_order_message(message, previous) when is_map(previous) do
Map.merge(previous, message)
end
defp merge_nested_order_message(message, _previous), do: message
defp merge_order_trade_metadata(message, previous) do
previous_trades = metadata_list(previous, "__ccxt_trades")
previous_fee = metadata_value(previous, "__ccxt_fee")
order = nested_order_message(message)
message
|> maybe_put_metadata("__ccxt_trades", merge_order_trade_list(previous_trades, message))
|> maybe_put_metadata("__ccxt_fee", merge_order_fee(previous_fee, order))
end
defp merge_order_trade_list(previous_trades, message) do
order = nested_order_message(message)
if Runtime.safe_string(order, "x") == "TRADE" do
trade_id = order |> Runtime.safe_value("t") |> value_to_string()
previous_trades
|> Enum.reject(fn trade ->
trade |> nested_order_message() |> Runtime.safe_value("t") |> value_to_string() ==
trade_id
end)
|> Kernel.++([message])
else
previous_trades
end
end
defp merge_order_fee(nil, order), do: order_fee(order)
defp merge_order_fee(previous_fee, order) do
case order_fee(order) do
nil ->
previous_fee
%{currency: currency, cost: cost} = fee ->
if previous_fee[:currency] == currency do
%{fee | cost: Runtime.add_number_strings(previous_fee[:cost], cost) || cost}
else
fee
end
end
end
defp order_fee(order) do
case Runtime.safe_string(order, "n") do
nil ->
nil
cost ->
%{cost: cost, currency: Runtime.safe_currency_code(Runtime.safe_string(order, "N"))}
end
end
defp nested_order_message(%{"o" => order}) when is_map(order), do: order
defp nested_order_message(message), do: message
defp message_symbol(message) do
message
|> nested_order_message()
|> Runtime.safe_string("s")
|> safe_symbol()
end
defp message_order_id(message) do
message
|> nested_order_message()
|> Runtime.safe_value("i")
|> value_to_string()
end
defp metadata_list(nil, _key), do: []
defp metadata_list(message, key), do: List.wrap(Map.get(nested_order_message(message), key))
defp metadata_value(nil, _key), do: nil
defp metadata_value(message, key), do: Map.get(nested_order_message(message), key)
defp put_metadata(%{"o" => order} = message, key, value) when is_map(order) do
put_in(message, ["o", key], value)
end
defp put_metadata(message, key, value), do: Map.put(message, key, value)
defp maybe_put_metadata(message, _key, nil), do: message
defp maybe_put_metadata(message, _key, []), do: message
defp maybe_put_metadata(message, key, value), do: put_metadata(message, key, value)
defp value_to_string(nil), do: nil
defp value_to_string(value), do: to_string(value)
defp safe_symbol(nil), do: nil
defp safe_symbol(market_id) do
case Regex.run(~r/^([A-Z0-9]+)(USDT|FDUSD|USDC|BUSD|BTC|ETH|BNB|EUR|TRY|BRL|USD)$/, market_id) do
[_full, base, quote] -> base <> "/" <> quote
_other -> market_id
end
end
end