defmodule Dwarves.BinanceSpot do alias Binance.Rest.HTTPClient @endpoint Application.get_env( :dwarves_binancex, :spot_url, "https://api.binance.com" ) @testnet_endpoint Application.get_env( :dwarves_binancex, :spot_testnet_url, "https://testnet.binance.vision" ) require Logger def get_endpoint(is_testnet) do case is_testnet do true -> @testnet_endpoint false -> @endpoint end end # Server @doc """ Pings binance API. Returns `{:ok, %{}}` if successful, `{:error, reason}` otherwise """ def ping(is_testnet \\ false) do endpoint = get_endpoint(is_testnet) HTTPClient.get_binance("#{endpoint}/sapi/v1/ping", []) end # Wallet @doc """ Get Daily Account Snapshot. Returns `{:ok, %{}}` or `{:error, reason}`. In the case of a error on binance, for example with invalid parameters, `{:error, {:binance_error, %{code: code, msg: msg}}}` will be returned. Please read https://binance-docs.github.io/apidocs/spot/en/#daily-account-snapshot-user_data to understand all the parameters ## Examples ``` account_snapshot(%{"type" => "FUTURES", "start_time" => 1642032000000, "end_time" => 1642032000000}, "api_key", "api_secret") ``` Result: ``` {:ok, %{ code: 200, msg: "", snapshotVos: [...] } or {:error, {:binance_error, %{ code: -3026, msg: "request param 'type' wrong, shoud be in ('SPOT', 'MARGIN', 'FUTURES')" } } } ``` """ def account_snapshot( %{"type" => type} = params, api_key, api_secret, is_testnet \\ false ) do arguments = %{ type: type, recvWindow: get_receiving_window(params["receiving_window"]), timestamp: get_timestamp(params["timestamp"]) } |> Map.merge( unless( is_nil(params["start_time"]), do: %{startTime: params["start_time"]}, else: %{} ) ) |> Map.merge( unless( is_nil(params["end_time"]), do: %{endTime: params["end_time"]}, else: %{} ) ) |> Map.merge( unless( is_nil(params["limit"]), do: %{limit: params["limit"]}, else: %{} ) ) endpoint = get_endpoint(is_testnet) case HTTPClient.get_binance( "#{endpoint}/sapi/v1/accountSnapshot", arguments, api_secret, api_key ) do {:error, %{"code" => code, "msg" => msg}} -> {:error, {:binance_error, %{code: code, msg: msg}}} data -> data end end # Corporate (sub-account) @doc """ Universal Transfer (For Master Account). Returns `{:ok, %{}}` or `{:error, reason}`. In the case of a error on binance, for example with invalid parameters, `{:error, {:binance_error, %{code: code, msg: msg}}}` will be returned. Please read https://binance-docs.github.io/apidocs/spot/en/#universal-transfer-for-master-account to understand all the parameters ## Examples ``` universal_transfer(%{"from_email" => "email@gmail.com", "to_email" => "email@gmail.com", "from_account_type" => "USDT_FUTURE", "to_account_type" => "SPOT", "asset" => "USDT", "amount" => 100},"api_key", "secret_key") ``` Result: ``` {:ok, %{ tranId: "tranId" } or {:error, {:binance_error, %{ code: -1000, msg: "No enum constant com.binance.accountsubuser.enums.TranferWay.FUTURE_TO_FUTURE" } } } ``` """ def universal_transfer( %{ "from_account_type" => from_account_type, "to_account_type" => to_account_type, "asset" => asset, "amount" => amount } = params, api_key, api_secret ) do arguments = %{ fromAccountType: from_account_type, toAccountType: to_account_type, asset: asset, amount: amount, recvWindow: get_receiving_window(params["receiving_window"]), timestamp: get_timestamp(params["timestamp"]) } |> Map.merge( unless( is_nil(params["from_email"]), do: %{fromEmail: params["from_email"]}, else: %{} ) ) |> Map.merge( unless( is_nil(params["to_email"]), do: %{toEmail: params["to_email"]}, else: %{} ) ) |> Map.merge( unless( is_nil(params["client_tran_id"]), do: %{clientTranId: params["client_tran_id"]}, else: %{} ) ) endpoint = get_endpoint(false) case HTTPClient.signed_request_binance( "#{endpoint}/sapi/v1/sub-account/universalTransfer", arguments, :post, api_secret, api_key ) do {:ok, %{"code" => code, "msg" => msg}} -> {:error, {:binance_error, %{code: code, msg: msg}}} data -> data end end # Misc defp get_timestamp(timestamp) do case timestamp do # timestamp needs to be in milliseconds nil -> :os.system_time(:millisecond) val -> val end end defp get_receiving_window(receiving_window) do case receiving_window do nil -> 5000 val -> val end end end