Current section

Files

Jump to
bingex lib bingex swap swap.ex
Raw

lib/bingex/swap/swap.ex

defmodule Bingex.Swap do
@moduledoc """
Provides an interface to interact with the BingX Swap API.
"""
alias Bingex.Model.Order
alias Bingex.{Helpers, Types, Convertors, API, HTTP}
alias Bingex.Swap.Marshal
alias Bingex.Swap.Data.{
PlaceTestOrder,
CancelAllOrders,
GetServerTime,
GetPositions,
GetPositionsHistory,
PlaceOrder,
PlaceOrders,
CloseAllPositions,
SetLeverage,
GetLeverage,
GetIncomeHistory,
GetOrdersHistory,
GetBalance,
GetContracts,
GetQuotes
}
# Trade API
@api_v1_path "/openApi/swap/v1"
@api_v2_path "/openApi/swap/v2"
@api_v3_path "/openApi/swap/v3"
@trade_api_v1_path @api_v1_path <> "/trade"
@trade_api_v2_path @api_v2_path <> "/trade"
@user_api_v2_path @api_v2_path <> "/user"
@user_api_v3_path @api_v3_path <> "/user"
@place_order_path @trade_api_v2_path <> "/order"
@place_test_order_path @trade_api_v2_path <> "/order/test"
@place_orders_path @trade_api_v2_path <> "/batchOrders"
@close_all_positions_path @trade_api_v2_path <> "/closeAllPositions"
@get_leverage_path @trade_api_v2_path <> "/leverage"
@get_positions_history_path @trade_api_v1_path <> "/positionHistory"
@get_positions_path @user_api_v2_path <> "/positions"
@cancel_all_orders_path @trade_api_v2_path <> "/allOpenOrders"
@set_margin_mode_path @trade_api_v2_path <> "/marginType"
@get_margin_mode_path @trade_api_v2_path <> "/marginType"
@set_leverage_path @trade_api_v2_path <> "/leverage"
@get_orders_history_path @trade_api_v1_path <> "/fullOrder"
@get_balance_path @user_api_v3_path <> "/balance"
@get_contracts_path @api_v2_path <> "/quote/contracts"
@get_quotes_path @api_v2_path <> "/quote/premiumIndex"
@get_server_time_path @api_v2_path <> "/server/time"
@get_income_history_path @user_api_v2_path <> "/income"
#
# Interface
#
@doc """
Query the capital flow of the perpetual contract under the current account.
https://bingx-api.github.io/docs/#/en-us/swapV2/account-api.html#Get%20Account%20Profit%20and%20Loss%20Fund%20Flow
"""
@spec get_income_history(
Types.symbol(),
Types.api_key(),
Types.secret_key(),
opts :: [
income_type: Types.income_type(),
start_time: pos_integer(),
end_time: pos_integer(),
limit: pos_integer()
]
) :: Types.request_result()
def get_income_history(symbol, api_key, secret_key, opts \\ [])
when is_binary(symbol) and is_binary(api_key) and is_binary(secret_key) do
request = get_income_history_request(symbol, api_key, secret_key, opts)
with {:ok, reply, meta} <- request(request) do
{:ok, GetIncomeHistory.embed_in_reply(reply), meta}
end
end
@doc """
Requests to get current server time.
https://bingx-api.github.io/docs/#/en-us/swapV2/base-info.html#Get%20Server%20Time
"""
@spec get_server_time() :: Types.request_result(API.Reply.t(GetServerTime.t()))
def get_server_time() do
request = get_server_time_request()
with {:ok, reply, meta} <- request(request) do
{:ok, GetServerTime.embed_in_reply(reply), meta}
end
end
@doc """
Requests to get current swap balance.
https://bingx-api.github.io/docs/#/en-us/swapV2/account-api.html#Query%20account%20data
"""
@spec get_balance(Types.api_key(), Types.secret_key()) ::
Types.request_result(API.Reply.t(GetBalance.t()))
def get_balance(api_key, secret_key)
when is_binary(api_key) and is_binary(secret_key) do
request = get_balance_request(api_key, secret_key)
with {:ok, reply, meta} <- request(request) do
{:ok, GetBalance.embed_in_reply(reply), meta}
end
end
@doc """
Requests to place a test order using order data with account credentials.
https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Test%20Order
"""
@spec place_test_order(Order.t(), Types.api_key(), Types.secret_key()) ::
Types.request_result(API.Reply.t(PlaceTestOrder.t()))
def place_test_order(%Order{} = order, api_key, secret_key)
when is_binary(api_key) and is_binary(secret_key) do
request = place_test_order_request(order, api_key, secret_key)
with {:ok, reply, meta} <- request(request) do
{:ok, PlaceTestOrder.embed_in_reply(reply), meta}
end
end
@doc """
Requests to place an order using order data with account credentials.
https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Place%20order
"""
@spec place_order(Order.t(), Types.api_key(), Types.secret_key()) ::
Types.request_result(API.Reply.t(PlaceOrder.t()))
def place_order(%Order{} = order, api_key, secret_key)
when is_binary(api_key) and is_binary(secret_key) do
request = place_order_request(order, api_key, secret_key)
with {:ok, reply, meta} <- request(request) do
{:ok, PlaceOrder.embed_in_reply(reply), meta}
end
end
@doc """
Requests to place bunch of orders using list of order data with account credentials.
https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Place%20multiple%20orders
"""
@spec place_orders([Order.t()], Types.api_key(), Types.secret_key()) ::
Types.request_result(API.Reply.t(PlaceOrders.t()))
def place_orders(orders, api_key, secret_key)
when is_list(orders) and is_binary(api_key) and is_binary(secret_key) do
request = place_orders_request(orders, api_key, secret_key)
with {:ok, reply, meta} <- request(request) do
{:ok, PlaceOrders.embed_in_reply(reply), meta}
end
end
@doc """
Requests to cancel all orders by their market symbol (ex. BTC-USDT) with account credentials.
https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Cancel%20All%20Open%20Orders
"""
@spec cancel_all_orders(
Types.symbol(),
Types.api_key(),
Types.secret_key()
) :: Types.request_result(API.Reply.t(CancelAllOrders.t()))
def cancel_all_orders(symbol, api_key, secret_key)
when is_binary(symbol) and is_binary(api_key) and is_binary(secret_key) do
request = cancel_all_orders_request(symbol, api_key, secret_key)
with {:ok, reply, meta} <- request(request) do
{:ok, CancelAllOrders.embed_in_reply(reply), meta}
end
end
@doc """
Request to close all positions by market symbol (ex. BTC-USDT) with account credentials.
https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Close%20All%20Positions
"""
@spec close_all_positions(
Types.symbol(),
Types.api_key(),
Types.secret_key()
) :: Types.request_result(API.Reply.t(CloseAllPositions.t()))
def close_all_positions(symbol, api_key, secret_key)
when is_binary(symbol) and is_binary(api_key) and is_binary(secret_key) do
request = close_all_positions_request(symbol, api_key, secret_key)
with {:ok, reply, meta} <- request(request) do
{:ok, CloseAllPositions.embed_in_reply(reply), meta}
end
end
@doc """
Request the opening leverage and available positions of the user in the specified symbol contract.
https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Leverage%20and%20Available%20Positions
"""
@spec get_leverage(
Types.symbol(),
Types.api_key(),
Types.secret_key()
) :: Types.request_result()
def get_leverage(symbol, api_key, secret_key)
when is_binary(symbol) and is_binary(api_key) and is_binary(secret_key) do
request = get_leverage_request(symbol, api_key, secret_key)
with {:ok, reply, meta} <- request(request) do
{:ok, GetLeverage.embed_in_reply(reply), meta}
end
end
@doc """
Request to set user's leverage amount by market symbol, position side and account credentials.
Position side can be either `:crossed` or `:isolated`.
Currently, BingX allows leverage from 1 to 125.
https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Set%20Leverage
"""
@spec set_leverage(
Types.symbol(),
Order.position_side(),
Types.leverage(),
Types.api_key(),
Types.secret_key()
) ::
Types.request_result(API.Reply.t(SetLeverage.t()))
def set_leverage(symbol, position_side, leverage, api_key, secret_key)
when is_binary(symbol) and
is_atom(position_side) and
is_integer(leverage) and
leverage > 0 and
is_binary(api_key) and
is_binary(secret_key) do
request =
set_leverage_request(
symbol,
position_side,
leverage,
api_key,
secret_key
)
with {:ok, reply, meta} <- request(request) do
{:ok, SetLeverage.embed_in_reply(reply), meta}
end
end
@doc """
Query the user's margin mode on the specified symbol contract.
https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Margin%20Type
"""
@spec get_margin_mode(
Types.symbol(),
Types.api_key(),
Types.secret_key()
) :: Types.request_result()
def get_margin_mode(symbol, api_key, secret_key)
when is_binary(symbol) and
is_binary(api_key) and
is_binary(secret_key) do
get_margin_mode_request(symbol, api_key, secret_key)
|> request()
end
@doc """
Request to set user's margin mode by market symbol and account credentials.
Margin mode can be either `:crossed` or `:isolated`.
https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Change%20Margin%20Type
"""
@spec set_margin_mode(
Types.symbol(),
Order.margin_mode(),
Types.api_key(),
Types.secret_key()
) :: Types.request_result()
def set_margin_mode(symbol, margin_mode, api_key, secret_key)
when is_binary(symbol) and
is_atom(margin_mode) and
is_binary(api_key) and
is_binary(secret_key) do
set_margin_mode_request(symbol, margin_mode, api_key, secret_key)
|> request()
end
@doc """
Request to the positions state of perpetual contracts by with account credentials.
https://bingx-api.github.io/docs/#/en-us/swapV2/account-api.html#Query%20position%20data
"""
@spec get_positions(
Types.symbol(),
Types.api_key(),
Types.secret_key()
) :: Types.request_result(API.Reply.t(GetPositions.t()))
def get_positions(
symbol,
api_key,
secret_key
)
when is_binary(symbol) and
is_binary(api_key) and
is_binary(secret_key) do
request =
get_positions_request(
symbol,
api_key,
secret_key
)
with {:ok, reply, meta} <- request(request) do
{:ok, GetPositions.embed_in_reply(reply), meta}
end
end
@doc """
Request to the position history of perpetual contracts by an optional
period (start_time, end_time) with account credentials.
https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Position%20History
"""
@spec get_positions_history(
Types.symbol(),
start_time :: pos_integer(),
end_time :: pos_integer(),
Types.api_key(),
Types.secret_key()
) :: Types.request_result(API.Reply.t(GetPositionsHistory.t()))
def get_positions_history(
symbol,
start_time,
end_time,
api_key,
secret_key
)
when is_binary(symbol) and
is_integer(start_time) and
is_integer(end_time) and
is_binary(api_key) and
is_binary(secret_key) do
request =
get_positions_history_request(
symbol,
start_time,
end_time,
api_key,
secret_key
)
with {:ok, reply, meta} <- request(request) do
{:ok, GetPositionsHistory.embed_in_reply(reply), meta}
end
end
@doc """
Request to get all user's orders (pending, active, ...) by an optional
period (start_time, end_time) and an optional limit of returned amount
of orders with account credentials.
https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#All%20Orders
"""
@spec get_orders_history(
Types.symbol(),
Types.api_key(),
Types.secret_key(),
opts :: [
start_time: pos_integer(),
end_time: pos_integer(),
limit: pos_integer()
]
) :: Types.request_result(API.Reply.t(GetOrdersHistory.t()))
def get_orders_history(symbol, api_key, secret_key, opts \\ [])
when is_binary(symbol) and
is_binary(api_key) and
is_binary(secret_key) do
request =
get_orders_history_request(
symbol,
api_key,
secret_key,
opts
)
with {:ok, reply, meta} <- request(request) do
{:ok, GetOrdersHistory.embed_in_reply(reply), meta}
end
end
@doc """
Query USDT-M futures symbols and their support status.
https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#USDT-M%20Perp%20Futures%20symbols
"""
@spec get_contracts(symbol :: nil | binary()) :: Types.request_result(API.Reply.t(GetQuotes.t()))
def get_contracts(symbol \\ nil) when is_binary(symbol) or is_nil(symbol) do
request = get_contracts_request(symbol)
with {:ok, reply, meta} <- request(request) do
{:ok, GetContracts.embed_in_reply(reply), meta}
end
end
@doc """
Query price and funding rate.
https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Mark%20Price%20and%20Funding%20Rate
"""
@spec get_quotes(symbol :: nil | binary()) :: Types.request_result(API.Reply.t(GetQuotes.t()))
def get_quotes(symbol \\ nil) when is_binary(symbol) or is_nil(symbol) do
request = get_quotes_request(symbol)
with {:ok, reply, meta} <- request(request) do
{:ok, GetQuotes.embed_in_reply(reply), meta}
end
end
#
# Helpers
#
@spec request(HTTP.Request.t()) :: Types.request_result()
defp request(%HTTP.Request{} = request) do
metadata = %{request: request}
case HTTP.request(request) do
{:ok, response} ->
metadata = Map.put(metadata, :response, response)
case API.process_response(response) do
{:ok, reply} -> {:ok, reply, metadata}
{:error, error} -> {:error, error, metadata}
end
{:error, error} ->
{:error, error, metadata}
end
end
defp place_test_order_request(order, api_key, secret_key) do
order_params = Marshal.transform_order(order)
HTTP.create_signed_request(
:post,
@place_test_order_path,
order_params,
api_key,
secret_key
)
end
defp place_order_request(order, api_key, secret_key) do
order_params = Marshal.transform_order(order)
HTTP.create_signed_request(
:post,
@place_order_path,
order_params,
api_key,
secret_key
)
end
defp get_balance_request(api_key, secret_key) do
HTTP.create_signed_request(
:get,
@get_balance_path,
api_key,
secret_key
)
end
defp place_orders_request(orders, api_key, secret_key) do
raw_orders = Marshal.serialize_orders(orders)
params = [{"batchOrders", raw_orders}]
HTTP.create_signed_request(
:post,
@place_orders_path,
params,
api_key,
secret_key
)
end
defp cancel_all_orders_request(symbol, api_key, secret_key) do
params = [{"symbol", symbol}]
HTTP.create_signed_request(
:delete,
@cancel_all_orders_path,
params,
api_key,
secret_key
)
end
defp close_all_positions_request(symbol, api_key, secret_key) do
params = [{"symbol", symbol}]
HTTP.create_signed_request(
:post,
@close_all_positions_path,
params,
api_key,
secret_key
)
end
defp get_leverage_request(symbol, api_key, secret_key) do
params = [{"symbol", symbol}]
HTTP.create_signed_request(
:get,
@get_leverage_path,
params,
api_key,
secret_key
)
end
defp get_margin_mode_request(symbol, api_key, secret_key) do
params = [
{"symbol", symbol}
]
HTTP.create_signed_request(
:get,
@get_margin_mode_path,
params,
api_key,
secret_key
)
end
defp set_margin_mode_request(symbol, margin_mode, api_key, secret_key) do
params = [
{"symbol", symbol},
{"marginType", Convertors.encode_margin_mode(margin_mode)}
]
HTTP.create_signed_request(
:post,
@set_margin_mode_path,
params,
api_key,
secret_key
)
end
defp set_leverage_request(
symbol,
position_side,
leverage,
api_key,
secret_key
) do
params = [
{"symbol", symbol},
{"side", Convertors.encode_position_side(position_side)},
{"leverage", leverage}
]
HTTP.create_signed_request(
:post,
@set_leverage_path,
params,
api_key,
secret_key
)
end
defp get_positions_request(
symbol,
api_key,
secret_key
) do
params = [
{"symbol", symbol},
{"timestamp", Helpers.timestamp()}
]
HTTP.create_signed_request(
:get,
@get_positions_path,
params,
api_key,
secret_key
)
end
defp get_positions_history_request(
symbol,
start_time,
end_time,
api_key,
secret_key
) do
params = [
{"symbol", symbol},
{"timestamp", Helpers.timestamp()},
{"startTs", start_time},
{"endTs", end_time}
]
HTTP.create_signed_request(
:get,
@get_positions_history_path,
params,
api_key,
secret_key
)
end
defp get_orders_history_request(
symbol,
api_key,
secret_key,
opts
) do
start_time = Keyword.get(opts, :start_time)
end_time = Keyword.get(opts, :end_time)
limit = Keyword.get(opts, :limit, 50)
params = [
{"symbol", symbol},
{"startTime", start_time},
{"endTime", end_time},
{"limit", limit}
]
HTTP.create_signed_request(
:get,
@get_orders_history_path,
params,
api_key,
secret_key
)
end
defp get_contracts_request(symbol) do
params = [{"symbol", symbol}]
HTTP.create_request(:get, @get_contracts_path, params)
end
defp get_quotes_request(symbol) do
params = [{"symbol", symbol}]
HTTP.create_request(:get, @get_quotes_path, params)
end
defp get_server_time_request() do
HTTP.create_request(:get, @get_server_time_path)
end
defp get_income_history_request(symbol, api_key, secret_key, opts) do
income_type =
case Keyword.fetch(opts, :income_type) do
{:ok, value} -> Convertors.encode_income_type(value)
:error -> nil
end
start_time = Keyword.get(opts, :start_time)
end_time = Keyword.get(opts, :end_time)
limit = Keyword.get(opts, :limit)
params = [
{"symbol", symbol},
{"incomeType", income_type},
{"startTime", start_time},
{"endTime", end_time},
{"limit", limit}
]
HTTP.create_signed_request(
:get,
@get_income_history_path,
params,
api_key,
secret_key
)
end
end