Current section

Files

Jump to
bingex lib bingex swap.ex
Raw

lib/bingex/swap.ex

defmodule Bingex.Swap do
@moduledoc """
Provides an interface to interact with the BingX Swap API.
This module allows users to perform various trading operations, including placing orders, retrieving account balances, managing leverage, and querying market data. It handles authentication, request signing, and response processing to ensure seamless communication with the BingX Swap API.
Key features:
- Place and test orders
- Manage positions and leverage settings
- Retrieve account balances and order history
- Fetch market data, contracts, and quotes
- Query server time for request synchronization
All requests requiring authentication will be signed using an API key and secret key.
"""
alias Bingex.{Types, Convertors, Order, API, HTTP}
alias Bingex.Swap.{
Marshal,
PlaceTestOrderData,
CancelAllOrdersData,
GetServerTimeData,
PlaceOrderData,
PlaceOrdersData,
CloseAllPositionsData,
SetLeverageData,
GetOrdersHistoryData,
GetBalanceData,
GetContractsData,
GetQuotesData
}
# Trade API
@api_v1 "/openApi/swap/v1"
@api_v2 "/openApi/swap/v2"
@api_v3 "/openApi/swap/v3"
@trade_api_v1 @api_v1 <> "/trade"
@trade_api_v2 @api_v2 <> "/trade"
@user_api_v3 @api_v3 <> "/user"
@place_order_path @trade_api_v2 <> "/order"
@place_test_order_path @trade_api_v2 <> "/order/test"
@place_orders_path @trade_api_v2 <> "/batchOrders"
@close_all_positions_path @trade_api_v2 <> "/closeAllPositions"
@cancel_all_orders_path @trade_api_v2 <> "/allOpenOrders"
@set_margin_mode_path @trade_api_v2 <> "/marginType"
@set_leverage_path @trade_api_v2 <> "/leverage"
@get_orders_history_path @trade_api_v1 <> "/fullOrder"
@get_balance_path @user_api_v3 <> "/balance"
@get_contracts_path @api_v2 <> "/quote/contracts"
@get_quotes_path @api_v2 <> "/quote/premiumIndex"
@get_server_time_path @api_v2 <> "/server/time"
#
# Interface
#
@doc """
Requests to get current server time.
"""
@spec get_server_time() ::
{:ok, API.Reply.t(GetServerTimeData.t()), Types.request_metadata()}
| {:error, API.Error.t(), Types.request_metadata()}
| {:error, HTTP.Error.t(), Types.failed_request_metadata()}
def get_server_time() do
request = get_server_time_request()
with(
{:ok, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, GetServerTimeData.embed_in_reply(reply), {request, response}}
end
end
@doc """
Requests to get current swap balance.
"""
@spec get_balance(api_key :: binary(), secret_key :: binary()) ::
{:ok, API.Reply.t(GetBalanceData.t()), Types.request_metadata()}
| {:error, API.Error.t(), Types.request_metadata()}
| {:error, HTTP.Error.t(), Types.failed_request_metadata()}
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, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, GetBalanceData.embed_in_reply(reply), {request, response}}
end
end
@doc """
Requests to place a test order using order data with account credentials.
"""
@spec place_test_order(Order.t(), api_key :: binary(), secret_key :: binary()) ::
{:ok, API.Reply.t(PlaceOrderData.t()), Types.request_metadata()}
| {:error, API.Error.t(), Types.request_metadata()}
| {:error, HTTP.Error.t(), Types.failed_request_metadata()}
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, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, PlaceTestOrderData.embed_in_reply(reply), {request, response}}
end
end
@doc """
Requests to place an order using order data with account credentials.
"""
@spec place_order(Order.t(), api_key :: binary(), secret_key :: binary()) ::
{:ok, API.Reply.t(PlaceOrderData.t()), Types.request_metadata()}
| {:error, API.Error.t(), Types.request_metadata()}
| {:error, HTTP.Error.t(), Types.failed_request_metadata()}
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, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, PlaceOrderData.embed_in_reply(reply), {request, response}}
end
end
@doc """
Requests to place bunch of orders using list of order data with account credentials.
"""
@spec place_orders([Order.t()], api_key :: binary(), secret_key :: binary()) ::
{:ok, API.Reply.t(PlaceOrdersData.t()), Types.request_metadata()}
| {:error, API.Error.t(), Types.request_metadata()}
| {:error, HTTP.Error.t(), Types.failed_request_metadata()}
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, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, PlaceOrdersData.embed_in_reply(reply), {request, response}}
end
end
@doc """
Requests to cancel all orders by their market symbol (ex. BTC-USDT) with account credentials.
"""
@spec cancel_all_orders(
symbol :: binary(),
api_key :: binary(),
secret_key :: binary()
) ::
{:ok, API.Reply.t(CancelAllOrdersData.t()), Types.request_metadata()}
| {:error, API.Error.t(), Types.request_metadata()}
| {:error, HTTP.Error.t(), Types.failed_request_metadata()}
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, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, CancelAllOrdersData.embed_in_reply(reply), {request, response}}
end
end
@doc """
Request to close all positions by market symbol (ex. BTC-USDT) with account credentials.
"""
@spec close_all_positions(
symbol :: binary(),
api_key :: binary(),
secret_key :: binary()
) ::
{:ok, API.Reply.t(CloseAllPositionsData.t()), Types.request_metadata()}
| {:error, API.Error.t(), Types.request_metadata()}
| {:error, HTTP.Error.t(), Types.failed_request_metadata()}
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, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, CloseAllPositionsData.embed_in_reply(reply), {request, response}}
end
end
@doc """
Request to set user's margin mode by market symbol and account credentials.
Margin mode can be either `:crossed` or `:isolated`.
"""
@spec set_margin_mode(
symbol :: binary(),
Order.margin_mode(),
api_key :: binary(),
secret_key :: binary()
) ::
{:ok, API.Reply.t(), Types.request_metadata()}
| {:error, API.Error.t(), Types.request_metadata()}
| {:error, HTTP.Error.t(), Types.failed_request_metadata()}
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
request = set_margin_mode_request(symbol, margin_mode, api_key, secret_key)
with(
{:ok, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, reply, {request, response}}
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.
"""
@spec set_leverage(
symbol :: binary(),
Order.position_side(),
leverage :: non_neg_integer(),
api_key :: binary(),
secret_key :: binary()
) ::
{:ok, API.Reply.t(SetLeverageData.t()), Types.request_metadata()}
| {:error, API.Error.t(), Types.request_metadata()}
| {:error, HTTP.Error.t(), Types.failed_request_metadata()}
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, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, SetLeverageData.embed_in_reply(reply), {request, response}}
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.
"""
@spec get_orders_history(
symbol :: binary(),
start_time :: nil | non_neg_integer(),
end_time :: nil | non_neg_integer(),
limit :: non_neg_integer(),
api_key :: binary(),
secret_key :: binary()
) ::
{:ok, API.Reply.t(GetOrdersHistoryData.t()), Types.request_metadata()}
| {:error, API.Error.t(), Types.request_metadata()}
| {:error, HTTP.Error.t(), Types.failed_request_metadata()}
def get_orders_history(
symbol,
start_time \\ nil,
end_time \\ nil,
limit \\ 50,
api_key,
secret_key
)
when is_binary(symbol) and
(is_integer(start_time) or start_time == nil) and
(is_integer(end_time) or end_time == nil) and
is_integer(limit) and
is_binary(api_key) and
is_binary(secret_key) do
request =
get_orders_history_request(
symbol,
start_time,
end_time,
limit,
api_key,
secret_key
)
with(
{:ok, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, GetOrdersHistoryData.embed_in_reply(reply), {request, response}}
end
end
def get_contracts(symbol \\ nil) when is_binary(symbol) or is_nil(symbol) do
request = get_contracts_request(symbol)
with(
{:ok, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, GetContractsData.embed_in_reply(reply), {request, response}}
end
end
def get_quotes(symbol \\ nil) when is_binary(symbol) or is_nil(symbol) do
request = get_quotes_request(symbol)
with(
{:ok, response} <- request(request),
{:ok, reply} <- process_response(response, request)
) do
{:ok, GetQuotesData.embed_in_reply(reply), {request, response}}
end
end
#
# Helpers
#
defp process_response(%HTTP.Response{} = response, request) do
with {:error, error} <- API.process_response(response) do
{:error, error, {request, response}}
end
end
defp request(%HTTP.Request{} = request) do
with {:error, error} <- HTTP.request(request) do
{:error, error, request}
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 set_margin_mode_request(symbol, margin_mode, api_key, secret_key) do
params = [
{"symbol", symbol},
{"marginType", Convertors.decode_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.decode_position_side(position_side)},
{"leverage", leverage}
]
HTTP.create_signed_request(
:post,
@set_leverage_path,
params,
api_key,
secret_key
)
end
defp get_orders_history_request(
symbol,
start_time,
end_time,
limit,
api_key,
secret_key
) do
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
end