mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
* Deribit: Add futureComboPairToString func, remove redundant endpoints and fix test issues * Deribit: Remove redundant getCurrencyIndexPrice constant * Deribit: Improve futureComboPairToString speed and add test cases * Deribit: Refactor futureComboPairToString for improved readability and performance
2741 lines
100 KiB
Go
2741 lines
100 KiB
Go
package deribit
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/thrasher-corp/gocryptotrader/common"
|
|
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
|
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/nonce"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
|
"github.com/thrasher-corp/gocryptotrader/types"
|
|
)
|
|
|
|
// Exchange implements exchange.IBotExchange and contains additional specific api methods for interacting with Deribit
|
|
type Exchange struct {
|
|
exchange.Base
|
|
}
|
|
|
|
const (
|
|
deribitAPIVersion = "/api/v2"
|
|
tradeBaseURL = "https://www.deribit.com/"
|
|
tradeSpot = "spot/"
|
|
tradeFutures = "futures/"
|
|
tradeOptions = "options/"
|
|
tradeFuturesCombo = "futures-spreads/"
|
|
tradeOptionsCombo = "combos/"
|
|
|
|
perpString = "PERPETUAL"
|
|
|
|
// Public endpoints
|
|
// Market Data
|
|
getBookByCurrency = "public/get_book_summary_by_currency"
|
|
getBookByInstrument = "public/get_book_summary_by_instrument"
|
|
getContractSize = "public/get_contract_size"
|
|
getCurrencies = "public/get_currencies"
|
|
getDeliveryPrices = "public/get_delivery_prices"
|
|
getFundingChartData = "public/get_funding_chart_data"
|
|
getFundingRateHistory = "public/get_funding_rate_history"
|
|
getFundingRateValue = "public/get_funding_rate_value"
|
|
getHistoricalVolatility = "public/get_historical_volatility"
|
|
getIndexPrice = "public/get_index_price"
|
|
getIndexPriceNames = "public/get_index_price_names"
|
|
getInstrument = "public/get_instrument"
|
|
getInstruments = "public/get_instruments"
|
|
getLastSettlementsByCurrency = "public/get_last_settlements_by_currency"
|
|
getLastSettlementsByInstrument = "public/get_last_settlements_by_instrument"
|
|
getLastTradesByCurrency = "public/get_last_trades_by_currency"
|
|
getLastTradesByCurrencyAndTime = "public/get_last_trades_by_currency_and_time"
|
|
getLastTradesByInstrument = "public/get_last_trades_by_instrument"
|
|
getLastTradesByInstrumentAndTime = "public/get_last_trades_by_instrument_and_time"
|
|
getMarkPriceHistory = "public/get_mark_price_history"
|
|
getOrderbook = "public/get_order_book"
|
|
getOrderbookByInstrumentID = "public/get_order_book_by_instrument_id"
|
|
getTradeVolumes = "public/get_trade_volumes"
|
|
getTradingViewChartData = "public/get_tradingview_chart_data"
|
|
getVolatilityIndex = "public/get_volatility_index_data"
|
|
getTicker = "public/ticker"
|
|
|
|
// Authenticated endpoints
|
|
|
|
// wallet eps
|
|
cancelTransferByID = "private/cancel_transfer_by_id"
|
|
cancelWithdrawal = "private/cancel_withdrawal"
|
|
createDepositAddress = "private/create_deposit_address"
|
|
getCurrentDepositAddress = "private/get_current_deposit_address"
|
|
getDeposits = "private/get_deposits"
|
|
getTransfers = "private/get_transfers"
|
|
getWithdrawals = "private/get_withdrawals"
|
|
submitTransferBetweenSubAccounts = "private/submit_transfer_between_subaccounts"
|
|
submitTransferToSubaccount = "private/submit_transfer_to_subaccount"
|
|
submitTransferToUser = "private/submit_transfer_to_user"
|
|
submitWithdraw = "private/withdraw"
|
|
|
|
// trading endpoints
|
|
submitBuy = "private/buy"
|
|
submitSell = "private/sell"
|
|
submitEdit = "private/edit"
|
|
editByLabel = "private/edit_by_label"
|
|
submitCancel = "private/cancel"
|
|
submitCancelAll = "private/cancel_all"
|
|
submitCancelAllByCurrency = "private/cancel_all_by_currency"
|
|
submitCancelAllByKind = "private/cancel_all_by_kind_or_type"
|
|
submitCancelAllByInstrument = "private/cancel_all_by_instrument"
|
|
submitCancelByLabel = "private/cancel_by_label"
|
|
submitCancelQuotes = "private/cancel_quotes"
|
|
submitClosePosition = "private/close_position"
|
|
getMargins = "private/get_margins"
|
|
getMMPConfig = "private/get_mmp_config"
|
|
getOpenOrdersByCurrency = "private/get_open_orders_by_currency"
|
|
getOpenOrdersByLabel = "private/get_open_orders_by_label"
|
|
getOpenOrdersByInstrument = "private/get_open_orders_by_instrument"
|
|
getOrderHistoryByCurrency = "private/get_order_history_by_currency"
|
|
getOrderHistoryByInstrument = "private/get_order_history_by_instrument"
|
|
getOrderMarginByIDs = "private/get_order_margin_by_ids"
|
|
getOrderState = "private/get_order_state"
|
|
getOrderStateByLabel = "private/get_order_state_by_label"
|
|
getTriggerOrderHistory = "private/get_trigger_order_history"
|
|
getUserTradesByCurrency = "private/get_user_trades_by_currency"
|
|
getUserTradesByCurrencyAndTime = "private/get_user_trades_by_currency_and_time"
|
|
getUserTradesByInstrument = "private/get_user_trades_by_instrument"
|
|
getUserTradesByInstrumentAndTime = "private/get_user_trades_by_instrument_and_time"
|
|
getUserTradesByOrder = "private/get_user_trades_by_order"
|
|
resetMMP = "private/reset_mmp"
|
|
setMMPConfig = "private/set_mmp_config"
|
|
getSettlementHistoryByInstrument = "private/get_settlement_history_by_instrument"
|
|
getSettlementHistoryByCurrency = "private/get_settlement_history_by_currency"
|
|
|
|
// account management eps
|
|
getAnnouncements = "public/get_announcements"
|
|
changeAPIKeyName = "private/change_api_key_name"
|
|
changeMarginModel = "private/change_margin_model"
|
|
changeScopeInAPIKey = "private/change_scope_in_api_key"
|
|
changeSubAccountName = "private/change_subaccount_name"
|
|
createAPIKey = "private/create_api_key"
|
|
createSubAccount = "private/create_subaccount"
|
|
disableAPIKey = "private/disable_api_key"
|
|
editAPIKey = "private/edit_api_key"
|
|
enableAffiliateProgram = "private/enable_affiliate_program"
|
|
enableAPIKey = "private/enable_api_key"
|
|
getAccessLog = "private/get_access_log"
|
|
getAccountSummary = "private/get_account_summary"
|
|
getAffiliateProgramInfo = "private/get_affiliate_program_info"
|
|
getEmailLanguage = "private/get_email_language"
|
|
getNewAnnouncements = "private/get_new_announcements"
|
|
getPosition = "private/get_position"
|
|
getPositions = "private/get_positions"
|
|
getSubAccounts = "private/get_subaccounts"
|
|
getSubAccountDetails = "private/get_subaccounts_details"
|
|
getTransactionLog = "private/get_transaction_log"
|
|
getUserLocks = "private/get_user_locks"
|
|
listAPIKeys = "private/list_api_keys"
|
|
listCustodyAccounts = "private/list_custody_accounts"
|
|
removeAPIKey = "private/remove_api_key"
|
|
removeSubAccount = "private/remove_subaccount"
|
|
resetAPIKey = "private/reset_api_key"
|
|
setAnnouncementAsRead = "private/set_announcement_as_read"
|
|
setEmailForSubAccount = "private/set_email_for_subaccount"
|
|
setEmailLanguage = "private/set_email_language"
|
|
setSelfTradingConfig = "private/set_self_trading_config"
|
|
toggleNotificationsFromSubAccount = "private/toggle_notifications_from_subaccount"
|
|
togglePortfolioMargining = "private/toggle_portfolio_margining"
|
|
toggleSubAccountLogin = "private/toggle_subaccount_login"
|
|
|
|
// Combo Books Endpoints
|
|
getComboDetails = "public/get_combo_details"
|
|
getComboIDs = "public/get_combo_ids"
|
|
getCombos = "public/get_combos"
|
|
createCombos = "private/create_combo"
|
|
|
|
// Block Trades Endpoints
|
|
executeBlockTrades = "private/execute_block_trade"
|
|
getBlockTrades = "private/get_block_trade"
|
|
getLastBlockTradesByCurrency = "private/get_last_block_trades_by_currency"
|
|
invalidateBlockTradesSignature = "private/invalidate_block_trade_signature"
|
|
movePositions = "private/move_positions"
|
|
simulateBlockPosition = "private/simulate_block_trade"
|
|
verifyBlockTrades = "private/verify_block_trade"
|
|
|
|
// roles
|
|
|
|
roleMaker = "maker"
|
|
roleTaker = "taker"
|
|
)
|
|
|
|
// GetBookSummaryByCurrency gets book summary data for currency requested
|
|
func (e *Exchange) GetBookSummaryByCurrency(ctx context.Context, ccy currency.Code, kind string) ([]BookSummaryData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
var resp []BookSummaryData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getBookByCurrency, params), &resp)
|
|
}
|
|
|
|
// GetBookSummaryByInstrument gets book summary data for instrument requested
|
|
func (e *Exchange) GetBookSummaryByInstrument(ctx context.Context, instrument string) ([]BookSummaryData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var resp []BookSummaryData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getBookByInstrument, params), &resp)
|
|
}
|
|
|
|
// GetContractSize gets contract size for instrument requested
|
|
func (e *Exchange) GetContractSize(ctx context.Context, instrument string) (*ContractSizeData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var resp *ContractSizeData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getContractSize, params), &resp)
|
|
}
|
|
|
|
// GetCurrencies gets all cryptocurrencies supported by the API
|
|
func (e *Exchange) GetCurrencies(ctx context.Context) ([]CurrencyData, error) {
|
|
var resp []CurrencyData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, getCurrencies, &resp)
|
|
}
|
|
|
|
// GetDeliveryPrices gets all delivery prices for the given inde name
|
|
func (e *Exchange) GetDeliveryPrices(ctx context.Context, indexName string, offset, count int64) (*IndexDeliveryPrice, error) {
|
|
if indexName == "" {
|
|
return nil, errUnsupportedIndexName
|
|
}
|
|
params := url.Values{}
|
|
params.Set("index_name", indexName)
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatInt(offset, 10))
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
var resp *IndexDeliveryPrice
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getDeliveryPrices, params), &resp)
|
|
}
|
|
|
|
// GetFundingChartData gets funding chart data for the requested instrument and time length
|
|
// supported lengths: 8h, 24h, 1m <-(1month)
|
|
func (e *Exchange) GetFundingChartData(ctx context.Context, instrument, length string) (*FundingChartData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("length", length)
|
|
var resp *FundingChartData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getFundingChartData, params), &resp)
|
|
}
|
|
|
|
// GetFundingRateHistory retrieves hourly historical interest rate for requested PERPETUAL instrument.
|
|
func (e *Exchange) GetFundingRateHistory(ctx context.Context, instrumentName string, startTime, endTime time.Time) ([]FundingRateHistory, error) {
|
|
params, err := checkInstrument(instrumentName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = common.StartEndTimeCheck(startTime, endTime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("start_timestamp", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
params.Set("end_timestamp", strconv.FormatInt(endTime.UnixMilli(), 10))
|
|
var resp []FundingRateHistory
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, nonMatchingEPL, common.EncodeURLValues(getFundingRateHistory, params), &resp)
|
|
}
|
|
|
|
// GetFundingRateValue gets funding rate value data.
|
|
func (e *Exchange) GetFundingRateValue(ctx context.Context, instrument string, startTime, endTime time.Time) (float64, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
err = common.StartEndTimeCheck(startTime, endTime)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
params.Set("start_timestamp", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
params.Set("end_timestamp", strconv.FormatInt(endTime.UnixMilli(), 10))
|
|
var resp float64
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getFundingRateValue, params), &resp)
|
|
}
|
|
|
|
// GetHistoricalVolatility gets historical volatility data
|
|
func (e *Exchange) GetHistoricalVolatility(ctx context.Context, ccy currency.Code) ([]HistoricalVolatilityData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
var data []HistoricalVolatilityData
|
|
return data, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getHistoricalVolatility, params), &data)
|
|
}
|
|
|
|
// GetIndexPrice gets price data for the requested index
|
|
func (e *Exchange) GetIndexPrice(ctx context.Context, index string) (*IndexPriceData, error) {
|
|
if index == "" {
|
|
return nil, errUnsupportedIndexName
|
|
}
|
|
params := url.Values{}
|
|
params.Set("index_name", index)
|
|
var resp *IndexPriceData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getIndexPrice, params), &resp)
|
|
}
|
|
|
|
// GetIndexPriceNames gets names of indexes
|
|
func (e *Exchange) GetIndexPriceNames(ctx context.Context) ([]string, error) {
|
|
var resp []string
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, getIndexPriceNames, &resp)
|
|
}
|
|
|
|
// GetInstrument retrieves instrument detail
|
|
func (e *Exchange) GetInstrument(ctx context.Context, instrument string) (*InstrumentData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var resp *InstrumentData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, nonMatchingEPL,
|
|
common.EncodeURLValues(getInstrument, params), &resp)
|
|
}
|
|
|
|
// GetInstruments gets data for all available instruments
|
|
func (e *Exchange) GetInstruments(ctx context.Context, ccy currency.Code, kind string, expired bool) ([]*InstrumentData, error) {
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
if expired {
|
|
params.Set("expired", "true")
|
|
}
|
|
var resp []*InstrumentData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getInstruments, params), &resp)
|
|
}
|
|
|
|
// GetLastSettlementsByCurrency gets last settlement data by currency
|
|
func (e *Exchange) GetLastSettlementsByCurrency(ctx context.Context, ccy currency.Code, settlementType, continuation string, count int64, searchStartTime time.Time) (*SettlementsData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if settlementType != "" {
|
|
params.Set("type", settlementType)
|
|
}
|
|
if continuation != "" {
|
|
params.Set("continuation", continuation)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if !searchStartTime.IsZero() {
|
|
params.Set("search_start_timestamp", strconv.FormatInt(searchStartTime.UnixMilli(), 10))
|
|
}
|
|
var resp *SettlementsData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getLastSettlementsByCurrency, params), &resp)
|
|
}
|
|
|
|
// GetLastSettlementsByInstrument gets last settlement data for requested instrument
|
|
func (e *Exchange) GetLastSettlementsByInstrument(ctx context.Context, instrument, settlementType, continuation string, count int64, startTime time.Time) (*SettlementsData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if settlementType != "" {
|
|
params.Set("type", settlementType)
|
|
}
|
|
if continuation != "" {
|
|
params.Set("continuation", continuation)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if !startTime.IsZero() {
|
|
params.Set("search_start_timestamp", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
}
|
|
var resp *SettlementsData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getLastSettlementsByInstrument, params), &resp)
|
|
}
|
|
|
|
// GetLastTradesByCurrency gets last trades for requested currency
|
|
func (e *Exchange) GetLastTradesByCurrency(ctx context.Context, ccy currency.Code, kind, startID, endID, sorting string, count int64, includeOld bool) (*PublicTradesData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
if startID != "" {
|
|
params.Set("start_id", startID)
|
|
}
|
|
if endID != "" {
|
|
params.Set("end_id", endID)
|
|
}
|
|
if sorting != "" {
|
|
params.Set("sorting", sorting)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if includeOld {
|
|
params.Set("include_old", "true")
|
|
}
|
|
var resp *PublicTradesData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getLastTradesByCurrency, params), &resp)
|
|
}
|
|
|
|
// GetLastTradesByCurrencyAndTime gets last trades for requested currency and time intervals
|
|
func (e *Exchange) GetLastTradesByCurrencyAndTime(ctx context.Context, ccy currency.Code, kind, sorting string, count int64, startTime, endTime time.Time) (*PublicTradesData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
if sorting != "" {
|
|
params.Set("sorting", sorting)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
err := common.StartEndTimeCheck(startTime, endTime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("start_timestamp", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
params.Set("end_timestamp", strconv.FormatInt(endTime.UnixMilli(), 10))
|
|
var resp *PublicTradesData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getLastTradesByCurrencyAndTime, params), &resp)
|
|
}
|
|
|
|
// GetLastTradesByInstrument gets last trades for requested instrument requested
|
|
func (e *Exchange) GetLastTradesByInstrument(ctx context.Context, instrument, startSeq, endSeq, sorting string, count int64, includeOld bool) (*PublicTradesData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if startSeq != "" {
|
|
params.Set("start_seq", startSeq)
|
|
}
|
|
if endSeq != "" {
|
|
params.Set("end_seq", endSeq)
|
|
}
|
|
if sorting != "" {
|
|
params.Set("sorting", sorting)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if includeOld {
|
|
params.Set("include_old", "true")
|
|
}
|
|
var resp *PublicTradesData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getLastTradesByInstrument, params), &resp)
|
|
}
|
|
|
|
// GetLastTradesByInstrumentAndTime gets last trades for requested instrument requested and time intervals
|
|
func (e *Exchange) GetLastTradesByInstrumentAndTime(ctx context.Context, instrument, sorting string, count int64, startTime, endTime time.Time) (*PublicTradesData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if sorting != "" {
|
|
params.Set("sorting", sorting)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
err = common.StartEndTimeCheck(startTime, endTime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("start_timestamp", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
params.Set("end_timestamp", strconv.FormatInt(endTime.UnixMilli(), 10))
|
|
var resp *PublicTradesData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getLastTradesByInstrumentAndTime, params), &resp)
|
|
}
|
|
|
|
// GetMarkPriceHistory gets data for mark price history
|
|
func (e *Exchange) GetMarkPriceHistory(ctx context.Context, instrument string, startTime, endTime time.Time) ([]MarkPriceHistory, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = common.StartEndTimeCheck(startTime, endTime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("start_timestamp", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
params.Set("end_timestamp", strconv.FormatInt(endTime.UnixMilli(), 10))
|
|
var resp []MarkPriceHistory
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getMarkPriceHistory, params), &resp)
|
|
}
|
|
|
|
func checkInstrument(instrumentName string) (url.Values, error) {
|
|
if instrumentName == "" {
|
|
return nil, fmt.Errorf("%w, instrument_name is missing", errInvalidInstrumentName)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("instrument_name", instrumentName)
|
|
return params, nil
|
|
}
|
|
|
|
// GetOrderbook gets data orderbook of requested instrument
|
|
func (e *Exchange) GetOrderbook(ctx context.Context, instrument string, depth int64) (*Orderbook, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if depth != 0 {
|
|
params.Set("depth", strconv.FormatInt(depth, 10))
|
|
}
|
|
var resp *Orderbook
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getOrderbook, params), &resp)
|
|
}
|
|
|
|
// GetOrderbookByInstrumentID retrieves orderbook by instrument ID
|
|
func (e *Exchange) GetOrderbookByInstrumentID(ctx context.Context, instrumentID int64, depth float64) (*Orderbook, error) {
|
|
if instrumentID == 0 {
|
|
return nil, errInvalidInstrumentID
|
|
}
|
|
params := url.Values{}
|
|
params.Set("instrument_id", strconv.FormatInt(instrumentID, 10))
|
|
if depth != 0 {
|
|
params.Set("depth", strconv.FormatFloat(depth, 'f', -1, 64))
|
|
}
|
|
var resp *Orderbook
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getOrderbookByInstrumentID, params), &resp)
|
|
}
|
|
|
|
// GetSupportedIndexNames retrieves the identifiers of all supported Price Indexes
|
|
// 'type' represents Type of a cryptocurrency price index. possible 'all', 'spot', 'derivative'
|
|
func (e *Exchange) GetSupportedIndexNames(ctx context.Context, priceIndexType string) ([]string, error) {
|
|
params := url.Values{}
|
|
if priceIndexType != "" {
|
|
params.Set("type", priceIndexType)
|
|
}
|
|
var resp []string
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, nonMatchingEPL, common.EncodeURLValues("public/get_supported_index_names", params), &resp)
|
|
}
|
|
|
|
// GetTradeVolumes gets trade volumes' data of all instruments
|
|
func (e *Exchange) GetTradeVolumes(ctx context.Context, extended bool) ([]TradeVolumesData, error) {
|
|
params := url.Values{}
|
|
if extended {
|
|
params.Set("extended", "true")
|
|
}
|
|
var resp []TradeVolumesData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getTradeVolumes, params), &resp)
|
|
}
|
|
|
|
// GetTradingViewChart gets volatility index data for the requested instrument
|
|
func (e *Exchange) GetTradingViewChart(ctx context.Context, instrument, resolution string, startTime, endTime time.Time) (*TVChartData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = common.StartEndTimeCheck(startTime, endTime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resolution == "" {
|
|
return nil, errResolutionNotSet
|
|
}
|
|
params.Set("start_timestamp", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
params.Set("end_timestamp", strconv.FormatInt(endTime.UnixMilli(), 10))
|
|
params.Set("resolution", resolution)
|
|
var resp *TVChartData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getTradingViewChartData, params), &resp)
|
|
}
|
|
|
|
// GetResolutionFromInterval returns the string representation of intervals given kline.Interval instance.
|
|
func (e *Exchange) GetResolutionFromInterval(interval kline.Interval) (string, error) {
|
|
switch interval {
|
|
case kline.HundredMilliseconds:
|
|
return "100ms", nil
|
|
case kline.OneMin:
|
|
return "1", nil
|
|
case kline.ThreeMin:
|
|
return "3", nil
|
|
case kline.FiveMin:
|
|
return "5", nil
|
|
case kline.TenMin:
|
|
return "10", nil
|
|
case kline.FifteenMin:
|
|
return "15", nil
|
|
case kline.ThirtyMin:
|
|
return "30", nil
|
|
case kline.OneHour:
|
|
return "60", nil
|
|
case kline.TwoHour:
|
|
return "120", nil
|
|
case kline.ThreeHour:
|
|
return "180", nil
|
|
case kline.SixHour:
|
|
return "360", nil
|
|
case kline.TwelveHour:
|
|
return "720", nil
|
|
case kline.OneDay:
|
|
return "1D", nil
|
|
case kline.Raw:
|
|
return interval.Word(), nil
|
|
default:
|
|
return "", kline.ErrUnsupportedInterval
|
|
}
|
|
}
|
|
|
|
// GetVolatilityIndex gets volatility index for the requested currency
|
|
func (e *Exchange) GetVolatilityIndex(ctx context.Context, ccy currency.Code, resolution string, startTime, endTime time.Time) ([]VolatilityIndexData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
err := common.StartEndTimeCheck(startTime, endTime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resolution == "" {
|
|
return nil, errResolutionNotSet
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
params.Set("start_timestamp", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
params.Set("end_timestamp", strconv.FormatInt(endTime.UnixMilli(), 10))
|
|
params.Set("resolution", resolution)
|
|
var resp VolatilityIndexRawData
|
|
err = e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL,
|
|
common.EncodeURLValues(getVolatilityIndex, params), &resp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
response := make([]VolatilityIndexData, len(resp.Data))
|
|
for x := range resp.Data {
|
|
response[x] = VolatilityIndexData{
|
|
TimestampMS: time.UnixMilli(int64(resp.Data[x][0])),
|
|
Open: resp.Data[x][1],
|
|
High: resp.Data[x][2],
|
|
Low: resp.Data[x][3],
|
|
Close: resp.Data[x][4],
|
|
}
|
|
}
|
|
return response, nil
|
|
}
|
|
|
|
// GetPublicTicker gets public ticker data of the instrument requested
|
|
func (e *Exchange) GetPublicTicker(ctx context.Context, instrument string) (*TickerData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var resp *TickerData
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getTicker, params), &resp)
|
|
}
|
|
|
|
// SendHTTPRequest sends an unauthenticated HTTP request
|
|
func (e *Exchange) SendHTTPRequest(ctx context.Context, ep exchange.URL, epl request.EndpointLimit, path string, result any) error {
|
|
endpoint, err := e.API.Endpoints.GetURL(ep)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
data := &struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
ID int64 `json:"id"`
|
|
Data any `json:"result"`
|
|
}{
|
|
Data: result,
|
|
}
|
|
return e.SendPayload(ctx, epl, func() (*request.Item, error) {
|
|
return &request.Item{
|
|
Method: http.MethodGet,
|
|
Path: endpoint + deribitAPIVersion + "/" + path,
|
|
Result: data,
|
|
Verbose: e.Verbose,
|
|
HTTPDebugging: e.HTTPDebugging,
|
|
HTTPRecording: e.HTTPRecording,
|
|
HTTPMockDataSliceLimit: e.HTTPMockDataSliceLimit,
|
|
}, nil
|
|
}, request.UnauthenticatedRequest)
|
|
}
|
|
|
|
// GetAccountSummary gets account summary data for the requested instrument
|
|
func (e *Exchange) GetAccountSummary(ctx context.Context, ccy currency.Code, extended bool) (*AccountSummaryData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if extended {
|
|
params.Set("extended", "true")
|
|
}
|
|
var resp *AccountSummaryData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getAccountSummary, params, &resp)
|
|
}
|
|
|
|
// CancelWithdrawal cancels withdrawal request for a given currency by its id
|
|
func (e *Exchange) CancelWithdrawal(ctx context.Context, ccy currency.Code, id int64) (*CancelWithdrawalData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if id <= 0 {
|
|
return nil, fmt.Errorf("%w, withdrawal id has to be positive integer", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
params.Set("id", strconv.FormatInt(id, 10))
|
|
var resp *CancelWithdrawalData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
cancelWithdrawal, params, &resp)
|
|
}
|
|
|
|
// CancelTransferByID cancels transfer by ID through the websocket connection.
|
|
func (e *Exchange) CancelTransferByID(ctx context.Context, ccy currency.Code, tfa string, id int64) (*AccountSummaryData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if id <= 0 {
|
|
return nil, fmt.Errorf("%w, transfer id has to be positive integer", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if tfa != "" {
|
|
params.Set("tfa", tfa)
|
|
}
|
|
params.Set("id", strconv.FormatInt(id, 10))
|
|
var resp *AccountSummaryData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, cancelTransferByID, params, &resp)
|
|
}
|
|
|
|
// CreateDepositAddress creates a deposit address for the currency requested
|
|
func (e *Exchange) CreateDepositAddress(ctx context.Context, ccy currency.Code) (*DepositAddressData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
var resp *DepositAddressData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
createDepositAddress, params, &resp)
|
|
}
|
|
|
|
// GetCurrentDepositAddress gets the current deposit address for the requested currency
|
|
func (e *Exchange) GetCurrentDepositAddress(ctx context.Context, ccy currency.Code) (*DepositAddressData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
var resp *DepositAddressData
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getCurrentDepositAddress, params, &resp)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if resp == nil {
|
|
return nil, common.ErrNoResponse
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// GetDeposits gets the deposits of a given currency
|
|
func (e *Exchange) GetDeposits(ctx context.Context, ccy currency.Code, count, offset int64) (*DepositsData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if offset != 0 {
|
|
params.Set("offset", strconv.FormatInt(offset, 10))
|
|
}
|
|
var resp *DepositsData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getDeposits, params, &resp)
|
|
}
|
|
|
|
// GetTransfers gets transfers data for the requested currency
|
|
func (e *Exchange) GetTransfers(ctx context.Context, ccy currency.Code, count, offset int64) (*TransfersData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if offset != 0 {
|
|
params.Set("offset", strconv.FormatInt(offset, 10))
|
|
}
|
|
var resp *TransfersData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getTransfers, params, &resp)
|
|
}
|
|
|
|
// GetWithdrawals gets withdrawals data for a requested currency
|
|
func (e *Exchange) GetWithdrawals(ctx context.Context, ccy currency.Code, count, offset int64) (*WithdrawalsData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if offset != 0 {
|
|
params.Set("offset", strconv.FormatInt(offset, 10))
|
|
}
|
|
var resp *WithdrawalsData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getWithdrawals, params, &resp)
|
|
}
|
|
|
|
// SubmitTransferBetweenSubAccounts transfer funds between two (sub)accounts.
|
|
// Id of the source (sub)account. Can be found in My Account >> Subaccounts tab. By default, it is the Id of the account which made the request.
|
|
// However, if a different "source" is specified, the user must possess the mainaccount scope, and only other subaccounts can be designated as the source.
|
|
func (e *Exchange) SubmitTransferBetweenSubAccounts(ctx context.Context, ccy currency.Code, amount float64, destinationID int64, source string) (*TransferData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if destinationID <= 0 {
|
|
return nil, errInvalidDestinationID
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
params.Set("destination", strconv.FormatInt(destinationID, 10))
|
|
params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
|
if source != "" {
|
|
params.Set("source", source)
|
|
}
|
|
var resp *TransferData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, submitTransferBetweenSubAccounts, params, &resp)
|
|
}
|
|
|
|
// SubmitTransferToSubAccount submits a request to transfer a currency to a subaccount
|
|
func (e *Exchange) SubmitTransferToSubAccount(ctx context.Context, ccy currency.Code, amount float64, destinationID int64) (*TransferData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if destinationID <= 0 {
|
|
return nil, errInvalidDestinationID
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
params.Set("destination", strconv.FormatInt(destinationID, 10))
|
|
params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
|
var resp *TransferData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
submitTransferToSubaccount, params, &resp)
|
|
}
|
|
|
|
// SubmitTransferToUser submits a request to transfer a currency to another user
|
|
func (e *Exchange) SubmitTransferToUser(ctx context.Context, ccy currency.Code, tfa, destinationAddress string, amount float64) (*TransferData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if destinationAddress == "" {
|
|
return nil, errInvalidCryptoAddress
|
|
}
|
|
params := url.Values{}
|
|
if tfa != "" {
|
|
params.Set("tfa", tfa)
|
|
}
|
|
params.Set("currency", ccy.String())
|
|
params.Set("destination", destinationAddress)
|
|
params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
|
var resp *TransferData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, submitTransferToUser, params, &resp)
|
|
}
|
|
|
|
// SubmitWithdraw submits a withdrawal request to the exchange for the requested currency
|
|
func (e *Exchange) SubmitWithdraw(ctx context.Context, ccy currency.Code, address, priority string, amount float64) (*WithdrawData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if address == "" {
|
|
return nil, errInvalidCryptoAddress
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
params.Set("address", address)
|
|
if priority != "" {
|
|
params.Set("priority", priority)
|
|
}
|
|
params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
|
var resp *WithdrawData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
submitWithdraw, params, &resp)
|
|
}
|
|
|
|
// GetAnnouncements retrieves announcements. Default "start_timestamp" parameter value is current timestamp, "count" parameter value must be between 1 and 50, default is 5.
|
|
func (e *Exchange) GetAnnouncements(ctx context.Context, startTime time.Time, count int64) ([]Announcement, error) {
|
|
params := url.Values{}
|
|
if !startTime.IsZero() {
|
|
params.Set("start_time", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
}
|
|
if count > 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
var resp []Announcement
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getAnnouncements, params), &resp)
|
|
}
|
|
|
|
// ChangeAPIKeyName changes the name of the api key requested
|
|
func (e *Exchange) ChangeAPIKeyName(ctx context.Context, id int64, name string) (*APIKeyData, error) {
|
|
if id <= 0 {
|
|
return nil, fmt.Errorf("%w, invalid api key id", errInvalidID)
|
|
}
|
|
if !alphaNumericRegExp.MatchString(name) {
|
|
return nil, errUnacceptableAPIKey
|
|
}
|
|
params := url.Values{}
|
|
params.Set("id", strconv.FormatInt(id, 10))
|
|
params.Set("name", name)
|
|
var resp *APIKeyData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
changeAPIKeyName, params, &resp)
|
|
}
|
|
|
|
// ChangeMarginModel change margin model
|
|
// Margin model: 'cross_pm', 'cross_sm', 'segregated_pm', 'segregated_sm'
|
|
// 'dry_run': If true request returns the result without switching the margining model. Default: false
|
|
func (e *Exchange) ChangeMarginModel(ctx context.Context, userID int64, marginModel string, dryRun bool) ([]TogglePortfolioMarginResponse, error) {
|
|
if marginModel == "" {
|
|
return nil, errInvalidMarginModel
|
|
}
|
|
params := url.Values{}
|
|
params.Set("margin_model", marginModel)
|
|
if userID != 0 {
|
|
params.Set("user_id", strconv.FormatInt(userID, 10))
|
|
}
|
|
if dryRun {
|
|
params.Set("dry_run", "true")
|
|
}
|
|
var resp []TogglePortfolioMarginResponse
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, changeMarginModel, params, &resp)
|
|
}
|
|
|
|
// ChangeScopeInAPIKey changes the scope of the api key requested
|
|
func (e *Exchange) ChangeScopeInAPIKey(ctx context.Context, id int64, maxScope string) (*APIKeyData, error) {
|
|
if id <= 0 {
|
|
return nil, fmt.Errorf("%w, invalid api key id", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("id", strconv.FormatInt(id, 10))
|
|
params.Set("max_scope", maxScope)
|
|
var resp *APIKeyData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
changeScopeInAPIKey, params, &resp)
|
|
}
|
|
|
|
// ChangeSubAccountName changes the name of the requested subaccount id
|
|
func (e *Exchange) ChangeSubAccountName(ctx context.Context, sid int64, name string) error {
|
|
if sid <= 0 {
|
|
return fmt.Errorf("%w, invalid subaccount user id", errInvalidID)
|
|
}
|
|
if name == "" {
|
|
return errInvalidUsername
|
|
}
|
|
params := url.Values{}
|
|
params.Set("sid", strconv.FormatInt(sid, 10))
|
|
params.Set("name", name)
|
|
var resp string
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
changeSubAccountName, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return errSubAccountNameChangeFailed
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CreateAPIKey creates an api key based on the provided settings
|
|
func (e *Exchange) CreateAPIKey(ctx context.Context, maxScope, name string, defaultKey bool) (*APIKeyData, error) {
|
|
params := url.Values{}
|
|
params.Set("max_scope", maxScope)
|
|
if name != "" {
|
|
params.Set("name", name)
|
|
}
|
|
if defaultKey {
|
|
params.Set("default", "true")
|
|
}
|
|
var resp *APIKeyData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
createAPIKey, params, &resp)
|
|
}
|
|
|
|
// CreateSubAccount creates a new subaccount
|
|
func (e *Exchange) CreateSubAccount(ctx context.Context) (*SubAccountData, error) {
|
|
var resp *SubAccountData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
createSubAccount, nil, &resp)
|
|
}
|
|
|
|
// DisableAPIKey disables the api key linked to the provided id
|
|
func (e *Exchange) DisableAPIKey(ctx context.Context, id int64) (*APIKeyData, error) {
|
|
if id <= 0 {
|
|
return nil, fmt.Errorf("%w, invalid api key id", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("id", strconv.FormatInt(id, 10))
|
|
var resp *APIKeyData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
disableAPIKey, params, &resp)
|
|
}
|
|
|
|
// EditAPIKey edits existing API key. At least one parameter is required.
|
|
// Describes maximal access for tokens generated with given key, possible values:
|
|
// trade:[read, read_write, none],
|
|
// wallet:[read, read_write, none],
|
|
// account:[read, read_write, none],
|
|
// block_trade:[read, read_write, none].
|
|
func (e *Exchange) EditAPIKey(ctx context.Context, id int64, maxScope, name string, enabled bool, enabledFeatures, ipWhitelist []string) (*APIKeyData, error) {
|
|
if id == 0 {
|
|
return nil, errInvalidAPIKeyID
|
|
}
|
|
if maxScope == "" {
|
|
return nil, errMaxScopeIsRequired
|
|
}
|
|
params := url.Values{}
|
|
if name != "" {
|
|
params.Set("name", name)
|
|
}
|
|
if enabled {
|
|
params.Set("enabled", "true")
|
|
}
|
|
if len(enabledFeatures) > 0 {
|
|
enabledFeaturesByte, err := json.Marshal(enabledFeatures)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("enabled_features", string(enabledFeaturesByte))
|
|
}
|
|
if len(ipWhitelist) > 0 {
|
|
ipWhitelistByte, err := json.Marshal(ipWhitelist)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("ip_whitelist", string(ipWhitelistByte))
|
|
}
|
|
var resp *APIKeyData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, editAPIKey, params, &resp)
|
|
}
|
|
|
|
// EnableAffiliateProgram enables the affiliate program
|
|
func (e *Exchange) EnableAffiliateProgram(ctx context.Context) error {
|
|
var resp string
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
enableAffiliateProgram, nil, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return errors.New("could not enable affiliate program")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EnableAPIKey enables the api key linked to the provided id
|
|
func (e *Exchange) EnableAPIKey(ctx context.Context, id int64) (*APIKeyData, error) {
|
|
if id <= 0 {
|
|
return nil, fmt.Errorf("%w, invalid api key id", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("id", strconv.FormatInt(id, 10))
|
|
var resp *APIKeyData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
enableAPIKey, params, &resp)
|
|
}
|
|
|
|
// GetAccessLog lists access logs for the user
|
|
func (e *Exchange) GetAccessLog(ctx context.Context, offset, count int64) (*AccessLog, error) {
|
|
params := url.Values{}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatInt(offset, 10))
|
|
}
|
|
if count > 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
var resp *AccessLog
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getAccessLog, params, &resp)
|
|
}
|
|
|
|
// GetAffiliateProgramInfo gets the affiliate program info
|
|
func (e *Exchange) GetAffiliateProgramInfo(ctx context.Context) (*AffiliateProgramInfo, error) {
|
|
var resp *AffiliateProgramInfo
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getAffiliateProgramInfo, nil, &resp)
|
|
}
|
|
|
|
// GetEmailLanguage gets the current language set for the email
|
|
func (e *Exchange) GetEmailLanguage(ctx context.Context) (string, error) {
|
|
var resp string
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getEmailLanguage, nil, &resp)
|
|
}
|
|
|
|
// GetNewAnnouncements gets new announcements
|
|
func (e *Exchange) GetNewAnnouncements(ctx context.Context) ([]Announcement, error) {
|
|
var resp []Announcement
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getNewAnnouncements, nil, &resp)
|
|
}
|
|
|
|
// GetPosition gets the data of all positions in the requested instrument name
|
|
func (e *Exchange) GetPosition(ctx context.Context, instrument string) (*PositionData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var resp *PositionData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getPosition, params, &resp)
|
|
}
|
|
|
|
// GetSubAccounts gets all subaccounts' data
|
|
func (e *Exchange) GetSubAccounts(ctx context.Context, withPortfolio bool) ([]SubAccountData, error) {
|
|
params := url.Values{}
|
|
if withPortfolio {
|
|
params.Set("with_portfolio", "true")
|
|
}
|
|
var resp []SubAccountData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getSubAccounts, params, &resp)
|
|
}
|
|
|
|
// GetSubAccountDetails retrieves sub accounts detail information.
|
|
func (e *Exchange) GetSubAccountDetails(ctx context.Context, ccy currency.Code, withOpenOrders bool) ([]SubAccountDetail, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if withOpenOrders {
|
|
params.Set("with_open_orders", "true")
|
|
}
|
|
var resp []SubAccountDetail
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getSubAccountDetails, params, &resp)
|
|
}
|
|
|
|
// GetPositions gets positions data of the user account
|
|
func (e *Exchange) GetPositions(ctx context.Context, ccy currency.Code, kind string) ([]PositionData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
var resp []PositionData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getPositions, params, &resp)
|
|
}
|
|
|
|
// GetTransactionLog gets transaction logs' data
|
|
func (e *Exchange) GetTransactionLog(ctx context.Context, ccy currency.Code, query string, startTime, endTime time.Time, count, continuation int64) (*TransactionsData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if query != "" {
|
|
params.Set("query", query)
|
|
}
|
|
err := common.StartEndTimeCheck(startTime, endTime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("start_timestamp", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
params.Set("end_timestamp", strconv.FormatInt(endTime.UnixMilli(), 10))
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if continuation != 0 {
|
|
params.Set("continuation", strconv.FormatInt(continuation, 10))
|
|
}
|
|
var resp *TransactionsData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getTransactionLog, params, &resp)
|
|
}
|
|
|
|
// GetUserLocks retrieves information about locks on user account.
|
|
func (e *Exchange) GetUserLocks(ctx context.Context) ([]UserLock, error) {
|
|
var resp []UserLock
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getUserLocks, nil, &resp)
|
|
}
|
|
|
|
// ListAPIKeys lists all the api keys associated with a user account
|
|
func (e *Exchange) ListAPIKeys(ctx context.Context, tfa string) ([]APIKeyData, error) {
|
|
params := url.Values{}
|
|
if tfa != "" {
|
|
params.Set("tfa", tfa)
|
|
}
|
|
var resp []APIKeyData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
listAPIKeys, params, &resp)
|
|
}
|
|
|
|
// GetCustodyAccounts retrieves user custody accounts list.
|
|
func (e *Exchange) GetCustodyAccounts(ctx context.Context, ccy currency.Code) ([]CustodyAccount, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
var resp []CustodyAccount
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, listCustodyAccounts, params, &resp)
|
|
}
|
|
|
|
// RemoveAPIKey removes api key vid ID
|
|
func (e *Exchange) RemoveAPIKey(ctx context.Context, id int64) error {
|
|
if id <= 0 {
|
|
return fmt.Errorf("%w, invalid api key id", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("id", strconv.FormatInt(id, 10))
|
|
var resp any
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, removeAPIKey, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return errors.New("removal of the api key requested failed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RemoveSubAccount removes a subaccount given its id
|
|
func (e *Exchange) RemoveSubAccount(ctx context.Context, subAccountID int64) error {
|
|
params := url.Values{}
|
|
params.Set("subaccount_id", strconv.FormatInt(subAccountID, 10))
|
|
var resp string
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, removeSubAccount, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return fmt.Errorf("removal of sub account %v failed", subAccountID)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ResetAPIKey resets the api key to its default settings
|
|
func (e *Exchange) ResetAPIKey(ctx context.Context, id int64) (*APIKeyData, error) {
|
|
if id <= 0 {
|
|
return nil, fmt.Errorf("%w, invalid api key id", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("id", strconv.FormatInt(id, 10))
|
|
var resp *APIKeyData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
resetAPIKey, params, &resp)
|
|
}
|
|
|
|
// SetAnnouncementAsRead sets an announcement as read
|
|
func (e *Exchange) SetAnnouncementAsRead(ctx context.Context, id int64) error {
|
|
if id <= 0 {
|
|
return fmt.Errorf("%w, invalid announcement id", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("announcement_id", strconv.FormatInt(id, 10))
|
|
var resp string
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
setAnnouncementAsRead, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return fmt.Errorf("setting announcement %v as read failed", id)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetEmailForSubAccount links an email given to the designated subaccount
|
|
func (e *Exchange) SetEmailForSubAccount(ctx context.Context, sid int64, email string) error {
|
|
if sid <= 0 {
|
|
return fmt.Errorf("%w, invalid subaccount user id", errInvalidID)
|
|
}
|
|
if !common.MatchesEmailPattern(email) {
|
|
return errInvalidEmailAddress
|
|
}
|
|
params := url.Values{}
|
|
params.Set("sid", strconv.FormatInt(sid, 10))
|
|
params.Set("email", email)
|
|
var resp any
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
setEmailForSubAccount, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return fmt.Errorf("could not link email (%v) to subaccount %v", email, sid)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetEmailLanguage sets a requested language for an email
|
|
func (e *Exchange) SetEmailLanguage(ctx context.Context, language string) error {
|
|
if language == "" {
|
|
return errLanguageIsRequired
|
|
}
|
|
params := url.Values{}
|
|
params.Set("language", language)
|
|
var resp string
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, setEmailLanguage, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return fmt.Errorf("could not set the email language to %v", language)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetSelfTradingConfig configure self trading behavior
|
|
// mode: Self trading prevention behavior. Possible values: 'reject_taker', 'cancel_maker'
|
|
// extended_to_subaccounts: If value is true trading is prevented between subaccounts of given account
|
|
func (e *Exchange) SetSelfTradingConfig(ctx context.Context, mode string, extendedToSubaccounts bool) (string, error) {
|
|
if mode == "" {
|
|
return "", errTradeModeIsRequired
|
|
}
|
|
params := url.Values{}
|
|
params.Set("mode", mode)
|
|
if extendedToSubaccounts {
|
|
params.Set("extended_to_subaccounts", "true")
|
|
} else {
|
|
params.Set("extended_to_subaccounts", "false")
|
|
}
|
|
var resp string
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, setSelfTradingConfig, params, &resp)
|
|
}
|
|
|
|
// ToggleNotificationsFromSubAccount toggles the notifications from a subaccount specified
|
|
func (e *Exchange) ToggleNotificationsFromSubAccount(ctx context.Context, sid int64, state bool) error {
|
|
if sid <= 0 {
|
|
return fmt.Errorf("%w, invalid subaccount user id", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("sid", strconv.FormatInt(sid, 10))
|
|
params.Set("state", strconv.FormatBool(state))
|
|
var resp string
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
toggleNotificationsFromSubAccount, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return fmt.Errorf("toggling notifications for subaccount %v to %v failed", sid, state)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// TogglePortfolioMargining toggle between SM and PM models.
|
|
func (e *Exchange) TogglePortfolioMargining(ctx context.Context, userID int64, enabled, dryRun bool) ([]TogglePortfolioMarginResponse, error) {
|
|
if userID == 0 {
|
|
return nil, errUserIDRequired
|
|
}
|
|
params := url.Values{}
|
|
params.Set("user_id", strconv.FormatInt(userID, 10))
|
|
params.Set("enabled", strconv.FormatBool(enabled))
|
|
if dryRun {
|
|
params.Set("dry_run", "true")
|
|
}
|
|
var resp []TogglePortfolioMarginResponse
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, togglePortfolioMargining, params, &resp)
|
|
}
|
|
|
|
// ToggleSubAccountLogin toggles access for subaccount login
|
|
func (e *Exchange) ToggleSubAccountLogin(ctx context.Context, subAccountUserID int64, state bool) error {
|
|
if subAccountUserID <= 0 {
|
|
return fmt.Errorf("%w, invalid subaccount user id", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("sid", strconv.FormatInt(subAccountUserID, 10))
|
|
params.Set("state", strconv.FormatBool(state))
|
|
var resp string
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, toggleSubAccountLogin, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return fmt.Errorf("toggling login access for subaccount %v to %v failed", subAccountUserID, state)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SubmitBuy submits a private buy request through the websocket connection.
|
|
func (e *Exchange) SubmitBuy(ctx context.Context, arg *OrderBuyAndSellParams) (*PrivateTradeData, error) {
|
|
if arg == nil || *arg == (OrderBuyAndSellParams{}) {
|
|
return nil, fmt.Errorf("%w parameter is required", common.ErrNilPointer)
|
|
}
|
|
params, err := checkInstrument(arg.Instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("amount", strconv.FormatFloat(arg.Amount, 'f', -1, 64))
|
|
if arg.OrderType != "" {
|
|
params.Set("type", arg.OrderType)
|
|
}
|
|
if arg.Price != 0 {
|
|
params.Set("price", strconv.FormatFloat(arg.Amount, 'f', -1, 64))
|
|
}
|
|
if arg.Label != "" {
|
|
params.Set("label", arg.Label)
|
|
}
|
|
if arg.TimeInForce != "" {
|
|
params.Set("time_in_force", arg.TimeInForce)
|
|
}
|
|
if arg.MaxShow != 0 {
|
|
params.Set("max_show", strconv.FormatFloat(arg.MaxShow, 'f', -1, 64))
|
|
}
|
|
if arg.PostOnly {
|
|
params.Set("post_only", "true")
|
|
}
|
|
if arg.RejectPostOnly {
|
|
params.Set("reject_post_only", "true")
|
|
}
|
|
if arg.ReduceOnly {
|
|
params.Set("reduce_only", "true")
|
|
}
|
|
if arg.MMP {
|
|
params.Set("mmp", "true")
|
|
}
|
|
if arg.TriggerPrice != 0 {
|
|
params.Set("trigger_price", strconv.FormatFloat(arg.TriggerPrice, 'f', -1, 64))
|
|
}
|
|
if arg.Trigger != "" {
|
|
params.Set("trigger", arg.Trigger)
|
|
}
|
|
if arg.Advanced != "" {
|
|
params.Set("advanced", arg.Advanced)
|
|
}
|
|
var resp *PrivateTradeData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet,
|
|
submitBuy, params, &resp)
|
|
}
|
|
|
|
// SubmitSell submits a sell request with the parameters provided
|
|
func (e *Exchange) SubmitSell(ctx context.Context, arg *OrderBuyAndSellParams) (*PrivateTradeData, error) {
|
|
if arg == nil || *arg == (OrderBuyAndSellParams{}) {
|
|
return nil, fmt.Errorf("%w argument is required", common.ErrNilPointer)
|
|
}
|
|
params, err := checkInstrument(arg.Instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("amount", strconv.FormatFloat(arg.Amount, 'f', -1, 64))
|
|
if arg.OrderType != "" {
|
|
params.Set("type", arg.OrderType)
|
|
}
|
|
if arg.Label != "" {
|
|
params.Set("label", arg.Label)
|
|
}
|
|
if arg.TimeInForce != "" {
|
|
params.Set("time_in_force", arg.TimeInForce)
|
|
}
|
|
if arg.MaxShow != 0 {
|
|
params.Set("max_show", strconv.FormatFloat(arg.MaxShow, 'f', -1, 64))
|
|
}
|
|
if (arg.OrderType == "limit" || arg.OrderType == "stop_limit") && arg.Price <= 0 {
|
|
return nil, errInvalidPrice
|
|
}
|
|
params.Set("price", strconv.FormatFloat(arg.Price, 'f', -1, 64))
|
|
if arg.PostOnly {
|
|
params.Set("post_only", "true")
|
|
}
|
|
if arg.RejectPostOnly {
|
|
params.Set("reject_post_only", "true")
|
|
}
|
|
if arg.ReduceOnly {
|
|
params.Set("reduce_only", "true")
|
|
}
|
|
if arg.MMP {
|
|
params.Set("mmp", "true")
|
|
}
|
|
if arg.TriggerPrice != 0 {
|
|
params.Set("trigger_price", strconv.FormatFloat(arg.TriggerPrice, 'f', -1, 64))
|
|
}
|
|
if arg.Trigger != "" {
|
|
params.Set("trigger", arg.Trigger)
|
|
}
|
|
if arg.Advanced != "" {
|
|
params.Set("advanced", arg.Advanced)
|
|
}
|
|
var resp *PrivateTradeData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestSpot, matchingEPL, http.MethodGet, submitSell, params, &resp)
|
|
}
|
|
|
|
// SubmitEdit submits an edit order request
|
|
func (e *Exchange) SubmitEdit(ctx context.Context, arg *OrderBuyAndSellParams) (*PrivateTradeData, error) {
|
|
if arg == nil || *arg == (OrderBuyAndSellParams{}) {
|
|
return nil, fmt.Errorf("%w parameter is required", common.ErrNilPointer)
|
|
}
|
|
if arg.OrderID == "" {
|
|
return nil, fmt.Errorf("%w, order id is required", errInvalidID)
|
|
}
|
|
if arg.Amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
params := url.Values{}
|
|
params.Set("order_id", arg.OrderID)
|
|
params.Set("amount", strconv.FormatFloat(arg.Amount, 'f', -1, 64))
|
|
if arg.PostOnly {
|
|
params.Set("post_only", "true")
|
|
}
|
|
if arg.RejectPostOnly {
|
|
params.Set("reject_post_only", "true")
|
|
}
|
|
if arg.ReduceOnly {
|
|
params.Set("reduce_only", "true")
|
|
}
|
|
if arg.MMP {
|
|
params.Set("mmp", "true")
|
|
}
|
|
if arg.TriggerPrice != 0 {
|
|
params.Set("trigger_price", strconv.FormatFloat(arg.TriggerPrice, 'f', -1, 64))
|
|
}
|
|
if arg.Advanced != "" {
|
|
params.Set("advanced", arg.Advanced)
|
|
}
|
|
if arg.Price > 0 {
|
|
params.Set("price", strconv.FormatFloat(arg.Price, 'f', -1, 64))
|
|
}
|
|
var resp *PrivateTradeData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet, submitEdit, params, &resp)
|
|
}
|
|
|
|
// EditOrderByLabel submits an edit order request sorted via label
|
|
func (e *Exchange) EditOrderByLabel(ctx context.Context, arg *OrderBuyAndSellParams) (*PrivateTradeData, error) {
|
|
if arg == nil || *arg == (OrderBuyAndSellParams{}) {
|
|
return nil, fmt.Errorf("%w parameter is required", common.ErrNilPointer)
|
|
}
|
|
params, err := checkInstrument(arg.Instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if arg.Amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if arg.Label != "" {
|
|
params.Set("label", arg.Label)
|
|
}
|
|
params.Set("amount", strconv.FormatFloat(arg.Amount, 'f', -1, 64))
|
|
if arg.PostOnly {
|
|
params.Set("post_only", "true")
|
|
}
|
|
if arg.RejectPostOnly {
|
|
params.Set("reject_post_only", "true")
|
|
}
|
|
if arg.ReduceOnly {
|
|
params.Set("reduce_only", "true")
|
|
}
|
|
if arg.MMP {
|
|
params.Set("mmp", "true")
|
|
}
|
|
if arg.TriggerPrice != 0 {
|
|
params.Set("trigger_price", strconv.FormatFloat(arg.TriggerPrice, 'f', -1, 64))
|
|
}
|
|
if arg.Advanced != "" {
|
|
params.Set("advanced", arg.Advanced)
|
|
}
|
|
var resp *PrivateTradeData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
editByLabel, params, &resp)
|
|
}
|
|
|
|
// SubmitCancel sends a request to cancel the order via its orderID
|
|
func (e *Exchange) SubmitCancel(ctx context.Context, orderID string) (*PrivateCancelData, error) {
|
|
if orderID == "" {
|
|
return nil, fmt.Errorf("%w, no order ID specified", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("order_id", orderID)
|
|
var resp *PrivateCancelData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet,
|
|
submitCancel, params, &resp)
|
|
}
|
|
|
|
// SubmitCancelAll sends a request to cancel all user orders in all currencies and instruments
|
|
func (e *Exchange) SubmitCancelAll(ctx context.Context, detailed bool) (*MultipleCancelResponse, error) {
|
|
params := url.Values{}
|
|
if detailed {
|
|
params.Set("detailed", "true")
|
|
}
|
|
var resp *MultipleCancelResponse
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet,
|
|
submitCancelAll, params, &resp)
|
|
}
|
|
|
|
// SubmitCancelAllByCurrency sends a request to cancel all user orders for the specified currency
|
|
// returns the total number of successfully cancelled orders
|
|
func (e *Exchange) SubmitCancelAllByCurrency(ctx context.Context, ccy currency.Code, kind, orderType string, detailed bool) (*MultipleCancelResponse, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
if orderType != "" {
|
|
params.Set("type", orderType)
|
|
}
|
|
if detailed {
|
|
params.Set("detailed", "true")
|
|
}
|
|
var resp *MultipleCancelResponse
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet,
|
|
submitCancelAllByCurrency, params, &resp)
|
|
}
|
|
|
|
// SubmitCancelAllByKind cancels all orders in currency(currencies), optionally filtered by instrument kind and/or order type.
|
|
// 'kind' instrument kind . Possible values: 'future', 'option', 'spot', 'future_combo', 'option_combo', 'combo', 'any'
|
|
// returns the total number of successfully cancelled orders
|
|
func (e *Exchange) SubmitCancelAllByKind(ctx context.Context, ccy currency.Code, kind, orderType string, detailed bool) (*MultipleCancelResponse, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
if orderType != "" {
|
|
params.Set("type", orderType)
|
|
}
|
|
if detailed {
|
|
params.Set("detailed", "true")
|
|
}
|
|
var resp *MultipleCancelResponse
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestSpot, matchingEPL, http.MethodGet, submitCancelAllByKind, params, &resp)
|
|
}
|
|
|
|
// SubmitCancelAllByInstrument sends a request to cancel all user orders for the specified instrument
|
|
// returns the total number of successfully cancelled orders
|
|
func (e *Exchange) SubmitCancelAllByInstrument(ctx context.Context, instrument, orderType string, detailed, includeCombos bool) (*MultipleCancelResponse, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if orderType != "" {
|
|
params.Set("type", orderType)
|
|
}
|
|
if detailed {
|
|
params.Set("detailed", "true")
|
|
}
|
|
if includeCombos {
|
|
params.Set("include_combos", "true")
|
|
}
|
|
var resp *MultipleCancelResponse
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet,
|
|
submitCancelAllByInstrument, params, &resp)
|
|
}
|
|
|
|
// SubmitCancelByLabel sends a request to cancel all user orders for the specified label
|
|
// returns the total number of successfully cancelled orders
|
|
func (e *Exchange) SubmitCancelByLabel(ctx context.Context, label string, ccy currency.Code, detailed bool) (*MultipleCancelResponse, error) {
|
|
params := url.Values{}
|
|
params.Set("label", label)
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
if detailed {
|
|
params.Set("detailed", "true")
|
|
}
|
|
var resp *MultipleCancelResponse
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet,
|
|
submitCancelByLabel, params, &resp)
|
|
}
|
|
|
|
// SubmitCancelQuotes cancels quotes based on the provided type.
|
|
// Delta cancels quotes within a Delta range defined by MinDelta and MaxDelta.
|
|
// quote_set_id cancels quotes by a specific Quote Set identifier.
|
|
// instrument cancels all quotes associated with a particular instrument. kind cancels all quotes for a certain kind.
|
|
// currency cancels all quotes in a specified currency. "all" cancels all quotes.
|
|
//
|
|
// possible cancel_type values are delta, 'quote_set_id', 'instrument', 'instrument_kind', 'currency', and 'all'
|
|
// possible kind values are future 'option', 'spot', 'future_combo', 'option_combo', 'combo', and 'any'
|
|
func (e *Exchange) SubmitCancelQuotes(ctx context.Context, ccy currency.Code, minDelta, maxDelta float64, cancelType, quoteSetID, instrumentName, kind string, detailed bool) (*MultipleCancelResponse, error) {
|
|
if cancelType == "" {
|
|
return nil, errors.New("cancel type is required")
|
|
}
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("cancel_type", cancelType)
|
|
params.Set("currency", ccy.String())
|
|
if detailed {
|
|
params.Set("detailed", "true")
|
|
}
|
|
if minDelta > 0 {
|
|
params.Set("min_delta", strconv.FormatFloat(minDelta, 'f', -1, 64))
|
|
}
|
|
if maxDelta > 0 {
|
|
params.Set("max_delta", strconv.FormatFloat(maxDelta, 'f', -1, 64))
|
|
}
|
|
if quoteSetID != "" {
|
|
params.Set("quote_set_id", quoteSetID)
|
|
}
|
|
if instrumentName != "" {
|
|
params.Set("instrument_name", instrumentName)
|
|
}
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
var resp *MultipleCancelResponse
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet, submitCancelQuotes, params, &resp)
|
|
}
|
|
|
|
// SubmitClosePosition sends a request to cancel all user orders for the specified label
|
|
func (e *Exchange) SubmitClosePosition(ctx context.Context, instrument, orderType string, price float64) (*PrivateTradeData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if orderType != "" {
|
|
params.Set("type", orderType)
|
|
}
|
|
params.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
|
|
var resp *PrivateTradeData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet,
|
|
submitClosePosition, params, &resp)
|
|
}
|
|
|
|
// GetMargins sends a request to fetch account margins data
|
|
func (e *Exchange) GetMargins(ctx context.Context, instrument string, amount, price float64) (*MarginsData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if price <= 0 {
|
|
return nil, errInvalidPrice
|
|
}
|
|
params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
|
params.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
|
|
var resp *MarginsData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getMargins, params, &resp)
|
|
}
|
|
|
|
// GetMMPConfig sends a request to fetch the config for MMP of the requested currency
|
|
func (e *Exchange) GetMMPConfig(ctx context.Context, ccy currency.Code) (*MMPConfigData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
var resp *MMPConfigData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getMMPConfig, params, &resp)
|
|
}
|
|
|
|
// GetOpenOrdersByCurrency retrieves open orders data sorted by requested params
|
|
func (e *Exchange) GetOpenOrdersByCurrency(ctx context.Context, ccy currency.Code, kind, orderType string) ([]OrderData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
if orderType != "" {
|
|
params.Set("type", orderType)
|
|
}
|
|
var resp []OrderData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getOpenOrdersByCurrency, params, &resp)
|
|
}
|
|
|
|
// GetOpenOrdersByLabel retrieves open orders using label and currency
|
|
func (e *Exchange) GetOpenOrdersByLabel(ctx context.Context, ccy currency.Code, label string) ([]OrderData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if label != "" {
|
|
params.Set("label", label)
|
|
}
|
|
var resp []OrderData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getOpenOrdersByLabel, params, &resp)
|
|
}
|
|
|
|
// GetOpenOrdersByInstrument sends a request to fetch open orders data sorted by requested params
|
|
func (e *Exchange) GetOpenOrdersByInstrument(ctx context.Context, instrument, orderType string) ([]OrderData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if orderType != "" {
|
|
params.Set("type", orderType)
|
|
}
|
|
var resp []OrderData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getOpenOrdersByInstrument, params, &resp)
|
|
}
|
|
|
|
// GetOrderHistoryByCurrency sends a request to fetch order history according to given params and currency
|
|
func (e *Exchange) GetOrderHistoryByCurrency(ctx context.Context, ccy currency.Code, kind string, count, offset int64, includeOld, includeUnfilled bool) ([]OrderData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if offset != 0 {
|
|
params.Set("offset", strconv.FormatInt(offset, 10))
|
|
}
|
|
if includeOld {
|
|
params.Set("include_old", "true")
|
|
}
|
|
if includeUnfilled {
|
|
params.Set("include_unfilled", "true")
|
|
}
|
|
var resp []OrderData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getOrderHistoryByCurrency, params, &resp)
|
|
}
|
|
|
|
// GetOrderHistoryByInstrument sends a request to fetch order history according to given params and instrument
|
|
func (e *Exchange) GetOrderHistoryByInstrument(ctx context.Context, instrument string, count, offset int64, includeOld, includeUnfilled bool) ([]OrderData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if offset != 0 {
|
|
params.Set("offset", strconv.FormatInt(offset, 10))
|
|
}
|
|
if includeOld {
|
|
params.Set("include_old", "true")
|
|
}
|
|
if includeUnfilled {
|
|
params.Set("include_unfilled", "true")
|
|
}
|
|
var resp []OrderData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestSpot, nonMatchingEPL, http.MethodGet,
|
|
getOrderHistoryByInstrument, params, &resp)
|
|
}
|
|
|
|
// GetOrderMarginsByID sends a request to fetch order margins data according to their ids
|
|
func (e *Exchange) GetOrderMarginsByID(ctx context.Context, ids []string) ([]InitialMarginInfo, error) {
|
|
if len(ids) == 0 {
|
|
return nil, fmt.Errorf("%w, order ids cannot be empty", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
for a := range ids {
|
|
params.Add("ids[]", ids[a])
|
|
}
|
|
var resp []InitialMarginInfo
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestSpot, nonMatchingEPL, http.MethodGet,
|
|
getOrderMarginByIDs, params, &resp)
|
|
}
|
|
|
|
// GetOrderState sends a request to fetch order state of the order id provided
|
|
func (e *Exchange) GetOrderState(ctx context.Context, orderID string) (*OrderData, error) {
|
|
if orderID == "" {
|
|
return nil, fmt.Errorf("%w, no order ID specified", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("order_id", orderID)
|
|
var resp *OrderData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getOrderState, params, &resp)
|
|
}
|
|
|
|
// GetOrderStateByLabel retrieves an order state by label and currency
|
|
func (e *Exchange) GetOrderStateByLabel(ctx context.Context, ccy currency.Code, label string) ([]OrderData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if label != "" {
|
|
params.Set("label", label)
|
|
}
|
|
var resp []OrderData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getOrderStateByLabel, params, &resp)
|
|
}
|
|
|
|
// GetTriggerOrderHistory sends a request to fetch order state of the order id provided
|
|
func (e *Exchange) GetTriggerOrderHistory(ctx context.Context, ccy currency.Code, instrumentName, continuation string, count int64) (*OrderData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if instrumentName != "" {
|
|
params.Set("instrument_name", instrumentName)
|
|
}
|
|
if continuation != "" {
|
|
params.Set("continuation", continuation)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
var resp *OrderData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getTriggerOrderHistory, params, &resp)
|
|
}
|
|
|
|
// GetUserTradesByCurrency sends a request to fetch user trades sorted by currency
|
|
func (e *Exchange) GetUserTradesByCurrency(ctx context.Context, ccy currency.Code, kind, startID, endID, sorting string, count int64, includeOld bool) (*UserTradesData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
if startID != "" {
|
|
params.Set("start_id", startID)
|
|
}
|
|
if endID != "" {
|
|
params.Set("end_id", endID)
|
|
}
|
|
if sorting != "" {
|
|
params.Set("sorting", sorting)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if includeOld {
|
|
params.Set("include_old", "true")
|
|
}
|
|
var resp *UserTradesData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getUserTradesByCurrency, params, &resp)
|
|
}
|
|
|
|
// GetUserTradesByCurrencyAndTime sends a request to fetch user trades sorted by currency and time
|
|
func (e *Exchange) GetUserTradesByCurrencyAndTime(ctx context.Context, ccy currency.Code, kind, sorting string, count int64, startTime, endTime time.Time) (*UserTradesData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if kind != "" {
|
|
params.Set("kind", kind)
|
|
}
|
|
if sorting != "" {
|
|
params.Set("sorting", sorting)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if !startTime.IsZero() {
|
|
params.Set("start_timestamp", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
}
|
|
if !endTime.IsZero() {
|
|
params.Set("end_timestamp", strconv.FormatInt(endTime.UnixMilli(), 10))
|
|
}
|
|
var resp *UserTradesData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getUserTradesByCurrencyAndTime, params, &resp)
|
|
}
|
|
|
|
// GetUserTradesByInstrument sends a request to fetch user trades sorted by instrument
|
|
func (e *Exchange) GetUserTradesByInstrument(ctx context.Context, instrument, sorting string, startSeq, endSeq, count int64, includeOld bool) (*UserTradesData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if startSeq != 0 {
|
|
params.Set("start_seq", strconv.FormatInt(startSeq, 10))
|
|
}
|
|
if endSeq != 0 {
|
|
params.Set("end_seq", strconv.FormatInt(endSeq, 10))
|
|
}
|
|
if sorting != "" {
|
|
params.Set("sorting", sorting)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if includeOld {
|
|
params.Set("include_old", "true")
|
|
}
|
|
var resp *UserTradesData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getUserTradesByInstrument, params, &resp)
|
|
}
|
|
|
|
// GetUserTradesByInstrumentAndTime sends a request to fetch user trades sorted by instrument and time
|
|
func (e *Exchange) GetUserTradesByInstrumentAndTime(ctx context.Context, instrument, sorting string, count int64, startTime, endTime time.Time) (*UserTradesData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if sorting != "" {
|
|
params.Set("sorting", sorting)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
err = common.StartEndTimeCheck(startTime, endTime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("start_timestamp", strconv.FormatInt(startTime.UnixMilli(), 10))
|
|
params.Set("end_timestamp", strconv.FormatInt(endTime.UnixMilli(), 10))
|
|
var resp *UserTradesData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getUserTradesByInstrumentAndTime, params, &resp)
|
|
}
|
|
|
|
// GetUserTradesByOrder sends a request to get user trades fetched by orderID
|
|
func (e *Exchange) GetUserTradesByOrder(ctx context.Context, orderID, sorting string) (*UserTradesData, error) {
|
|
if orderID == "" {
|
|
return nil, fmt.Errorf("%w, no order ID specified", errInvalidID)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("order_id", orderID)
|
|
if sorting != "" {
|
|
params.Set("sorting", sorting)
|
|
}
|
|
var resp *UserTradesData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getUserTradesByOrder, params, &resp)
|
|
}
|
|
|
|
// ResetMMP sends a request to reset MMP for a currency provided
|
|
func (e *Exchange) ResetMMP(ctx context.Context, ccy currency.Code) error {
|
|
if ccy.IsEmpty() {
|
|
return currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
var resp string
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, resetMMP, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return fmt.Errorf("mmp could not be reset for %v", ccy.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetMMPConfig sends a request to set the given parameter values to the mmp config for the provided currency
|
|
func (e *Exchange) SetMMPConfig(ctx context.Context, ccy currency.Code, interval kline.Interval, frozenTime int64, quantityLimit, deltaLimit float64) error {
|
|
if ccy.IsEmpty() {
|
|
return currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
intervalString, err := e.GetResolutionFromInterval(interval)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
params.Set("interval", intervalString)
|
|
params.Set("frozen_time", strconv.FormatInt(frozenTime, 10))
|
|
if quantityLimit != 0 {
|
|
params.Set("quantity_limit", strconv.FormatFloat(quantityLimit, 'f', -1, 64))
|
|
}
|
|
if deltaLimit != 0 {
|
|
params.Set("delta_limit", strconv.FormatFloat(deltaLimit, 'f', -1, 64))
|
|
}
|
|
var resp string
|
|
err = e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
setMMPConfig, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return fmt.Errorf("mmp data could not be set for %v", ccy.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetSettlementHistoryByInstrument sends a request to fetch settlement history data sorted by instrument
|
|
func (e *Exchange) GetSettlementHistoryByInstrument(ctx context.Context, instrument, settlementType, continuation string, count int64, searchStartTimeStamp time.Time) (*PrivateSettlementsHistoryData, error) {
|
|
params, err := checkInstrument(instrument)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if settlementType != "" {
|
|
params.Set("settlement_type", settlementType)
|
|
}
|
|
if continuation != "" {
|
|
params.Set("continuation", continuation)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if !searchStartTimeStamp.IsZero() {
|
|
params.Set("search_start_timestamp", strconv.FormatInt(searchStartTimeStamp.UnixMilli(), 10))
|
|
}
|
|
var resp *PrivateSettlementsHistoryData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getSettlementHistoryByInstrument, params, &resp)
|
|
}
|
|
|
|
// GetSettlementHistoryByCurency sends a request to fetch settlement history data sorted by currency
|
|
func (e *Exchange) GetSettlementHistoryByCurency(ctx context.Context, ccy currency.Code, settlementType, continuation string, count int64, searchStartTimeStamp time.Time) (*PrivateSettlementsHistoryData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if settlementType != "" {
|
|
params.Set("settlement_type", settlementType)
|
|
}
|
|
if continuation != "" {
|
|
params.Set("continuation", continuation)
|
|
}
|
|
if count != 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
if !searchStartTimeStamp.IsZero() {
|
|
params.Set("search_start_timestamp", strconv.FormatInt(searchStartTimeStamp.UnixMilli(), 10))
|
|
}
|
|
var resp *PrivateSettlementsHistoryData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet,
|
|
getSettlementHistoryByCurrency, params, &resp)
|
|
}
|
|
|
|
// SendHTTPAuthRequest sends an authenticated request to deribit api
|
|
func (e *Exchange) SendHTTPAuthRequest(ctx context.Context, ep exchange.URL, epl request.EndpointLimit, method, path string, params url.Values, result any) error {
|
|
endpoint, err := e.API.Endpoints.GetURL(ep)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
creds, err := e.GetCredentials(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("%w, %v", request.ErrAuthRequestFailed, err)
|
|
}
|
|
req := method + "\n" + deribitAPIVersion + "/" + common.EncodeURLValues(path, params) + "\n\n"
|
|
n := e.Requester.GetNonce(nonce.UnixNano).String()
|
|
ts := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
|
tsReq := []byte(ts + "\n" + n + "\n" + req)
|
|
hmac, err := crypto.GetHMAC(crypto.HashSHA256, tsReq, []byte(creds.Secret))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
headers := make(map[string]string)
|
|
headerString := "deri-hmac-sha256 id=" + creds.Key + ",ts=" + ts + ",sig=" + hex.EncodeToString(hmac) + ",nonce=" + n
|
|
headers["Authorization"] = headerString
|
|
headers["Content-Type"] = "application/json"
|
|
var tempData struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
ID int64 `json:"id"`
|
|
Data json.RawMessage `json:"result"`
|
|
Error ErrInfo `json:"error"`
|
|
}
|
|
err = e.SendPayload(ctx, epl, func() (*request.Item, error) {
|
|
return &request.Item{
|
|
Method: method,
|
|
Path: endpoint + deribitAPIVersion + "/" + common.EncodeURLValues(path, params),
|
|
Headers: headers,
|
|
Result: &tempData,
|
|
Verbose: e.Verbose,
|
|
HTTPDebugging: e.HTTPDebugging,
|
|
HTTPRecording: e.HTTPRecording,
|
|
HTTPMockDataSliceLimit: e.HTTPMockDataSliceLimit,
|
|
}, nil
|
|
}, request.AuthenticatedRequest)
|
|
if err != nil {
|
|
return fmt.Errorf("%w %v", request.ErrAuthRequestFailed, err)
|
|
}
|
|
if tempData.Error.Code != 0 {
|
|
var errParamInfo string
|
|
if tempData.Error.Data.Param != "" {
|
|
errParamInfo = fmt.Sprintf(" param: %s reason: %s", tempData.Error.Data.Param, tempData.Error.Data.Reason)
|
|
}
|
|
return fmt.Errorf("%w code: %d msg: %s%s", request.ErrAuthRequestFailed, tempData.Error.Code, tempData.Error.Message, errParamInfo)
|
|
}
|
|
return json.Unmarshal(tempData.Data, result)
|
|
}
|
|
|
|
// Combo Books endpoints'
|
|
|
|
// GetComboIDs Retrieves available combos.
|
|
// This method can be used to get the list of all combos, or only the list of combos in the given state.
|
|
func (e *Exchange) GetComboIDs(ctx context.Context, ccy currency.Code, state string) ([]string, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if state != "" {
|
|
params.Set("state", state)
|
|
}
|
|
var resp []string
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getComboIDs, params), &resp)
|
|
}
|
|
|
|
// GetComboDetails retrieves information about a combo
|
|
func (e *Exchange) GetComboDetails(ctx context.Context, comboID string) (*ComboDetail, error) {
|
|
if comboID == "" {
|
|
return nil, errInvalidComboID
|
|
}
|
|
params := url.Values{}
|
|
params.Set("combo_id", comboID)
|
|
var resp *ComboDetail
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getComboDetails, params), &resp)
|
|
}
|
|
|
|
// GetCombos retrieves information about active combos
|
|
func (e *Exchange) GetCombos(ctx context.Context, ccy currency.Code) ([]ComboDetail, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
var resp []ComboDetail
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getCombos, params), &resp)
|
|
}
|
|
|
|
// CreateCombo verifies and creates a combo book or returns an existing combo matching given trades
|
|
func (e *Exchange) CreateCombo(ctx context.Context, args []ComboParam) (*ComboDetail, error) {
|
|
if len(args) == 0 {
|
|
return nil, errNoArgumentPassed
|
|
}
|
|
instrument := args[0].InstrumentName
|
|
for x := range args {
|
|
if args[x].InstrumentName == "" {
|
|
return nil, fmt.Errorf("%w, empty string", errInvalidInstrumentName)
|
|
} else if instrument != args[x].InstrumentName {
|
|
return nil, errDifferentInstruments
|
|
}
|
|
args[x].Direction = strings.ToLower(args[x].Direction)
|
|
if args[x].Direction != sideBUY && args[x].Direction != sideSELL {
|
|
return nil, errInvalidOrderSideOrDirection
|
|
}
|
|
if args[x].Amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
}
|
|
argsByte, err := json.Marshal(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params := url.Values{}
|
|
params.Set("trades", string(argsByte))
|
|
var resp *ComboDetail
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, createCombos, params, &resp)
|
|
}
|
|
|
|
// ExecuteBlockTrade executes a block trade request
|
|
// The whole request have to be exact the same as in private/verify_block_trade, only role field should be set appropriately - it basically means that both sides have to agree on the same timestamp, nonce, trades fields and server will assure that role field is different between sides (each party accepted own role).
|
|
// Using the same timestamp and nonce by both sides in private/verify_block_trade assures that even if unintentionally both sides execute given block trade with valid counterparty_signature, the given block trade will be executed only once
|
|
func (e *Exchange) ExecuteBlockTrade(ctx context.Context, timestampMS time.Time, tradeNonce, role string, ccy currency.Code, trades []BlockTradeParam) ([]BlockTradeResponse, error) {
|
|
if tradeNonce == "" {
|
|
return nil, errMissingNonce
|
|
}
|
|
if role != roleMaker && role != roleTaker {
|
|
return nil, errInvalidTradeRole
|
|
}
|
|
if len(trades) == 0 {
|
|
return nil, errNoArgumentPassed
|
|
}
|
|
for x := range trades {
|
|
if trades[x].InstrumentName == "" {
|
|
return nil, fmt.Errorf("%w, empty string", errInvalidInstrumentName)
|
|
}
|
|
trades[x].Direction = strings.ToLower(trades[x].Direction)
|
|
if trades[x].Direction != sideBUY && trades[x].Direction != sideSELL {
|
|
return nil, errInvalidOrderSideOrDirection
|
|
}
|
|
if trades[x].Amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if trades[x].Price < 0 {
|
|
return nil, fmt.Errorf("%w, trade price can't be negative", errInvalidPrice)
|
|
}
|
|
}
|
|
signature, err := e.VerifyBlockTrade(ctx, timestampMS, tradeNonce, role, ccy, trades)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
values, err := json.Marshal(trades)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
params.Set("trades", string(values))
|
|
params.Set("nonce", tradeNonce)
|
|
params.Set("role", role)
|
|
params.Set("counterparty_signature", signature)
|
|
params.Set("timestamp", strconv.FormatInt(timestampMS.UnixMilli(), 10))
|
|
var resp []BlockTradeResponse
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet, executeBlockTrades, params, &resp)
|
|
}
|
|
|
|
// VerifyBlockTrade verifies and creates block trade signature
|
|
func (e *Exchange) VerifyBlockTrade(ctx context.Context, timestampMS time.Time, tradeNonce, role string, ccy currency.Code, trades []BlockTradeParam) (string, error) {
|
|
if tradeNonce == "" {
|
|
return "", errMissingNonce
|
|
}
|
|
if role != roleMaker && role != roleTaker {
|
|
return "", errInvalidTradeRole
|
|
}
|
|
if len(trades) == 0 {
|
|
return "", errNoArgumentPassed
|
|
}
|
|
if timestampMS.IsZero() {
|
|
return "", errZeroTimestamp
|
|
}
|
|
for x := range trades {
|
|
if trades[x].InstrumentName == "" {
|
|
return "", fmt.Errorf("%w, empty string", errInvalidInstrumentName)
|
|
}
|
|
trades[x].Direction = strings.ToLower(trades[x].Direction)
|
|
if trades[x].Direction != sideBUY && trades[x].Direction != sideSELL {
|
|
return "", errInvalidOrderSideOrDirection
|
|
}
|
|
if trades[x].Amount <= 0 {
|
|
return "", errInvalidAmount
|
|
}
|
|
if trades[x].Price < 0 {
|
|
return "", fmt.Errorf("%w, trade price can't be negative", errInvalidPrice)
|
|
}
|
|
}
|
|
values, err := json.Marshal(trades)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
params := url.Values{}
|
|
params.Set("timestamp", strconv.FormatInt(timestampMS.UnixMilli(), 10))
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
params.Set("nonce", tradeNonce)
|
|
params.Set("role", role)
|
|
params.Set("trades", string(values))
|
|
resp := &struct {
|
|
Signature string `json:"signature"`
|
|
}{}
|
|
return resp.Signature, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet, verifyBlockTrades, params, resp)
|
|
}
|
|
|
|
// InvalidateBlockTradeSignature user at any time (before the private/execute_block_trade is called) can invalidate its own signature effectively cancelling block trade
|
|
func (e *Exchange) InvalidateBlockTradeSignature(ctx context.Context, signature string) error {
|
|
if signature == "" {
|
|
return errMissingSignature
|
|
}
|
|
params := url.Values{}
|
|
params.Set("signature", signature)
|
|
var resp string
|
|
err := e.SendHTTPAuthRequest(ctx, exchange.RestSpot, nonMatchingEPL, http.MethodGet, invalidateBlockTradesSignature, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp != "ok" {
|
|
return fmt.Errorf("server response: %s", resp)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetUserBlockTrade returns information about users block trade
|
|
func (e *Exchange) GetUserBlockTrade(ctx context.Context, id string) ([]BlockTradeData, error) {
|
|
if id == "" {
|
|
return nil, errMissingBlockTradeID
|
|
}
|
|
params := url.Values{}
|
|
params.Set("id", id)
|
|
var resp []BlockTradeData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getBlockTrades, params, &resp)
|
|
}
|
|
|
|
// GetTime retrieves the current time (in milliseconds). This API endpoint can be used to check the clock skew between your software and Deribit's systems.
|
|
func (e *Exchange) GetTime(ctx context.Context) (time.Time, error) {
|
|
var timestamp types.Time
|
|
if err := e.SendHTTPRequest(ctx, exchange.RestSpot, nonMatchingEPL, "public/get_time", ×tamp); err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
return timestamp.Time(), nil
|
|
}
|
|
|
|
// GetLastBlockTradesByCurrency returns list of last users block trades
|
|
func (e *Exchange) GetLastBlockTradesByCurrency(ctx context.Context, ccy currency.Code, startID, endID string, count int64) ([]BlockTradeData, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
if startID != "" {
|
|
params.Set("start_id", startID)
|
|
}
|
|
if endID != "" {
|
|
params.Set("end_id", endID)
|
|
}
|
|
if count > 0 {
|
|
params.Set("count", strconv.FormatInt(count, 10))
|
|
}
|
|
var resp []BlockTradeData
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, getLastBlockTradesByCurrency, params, &resp)
|
|
}
|
|
|
|
// MovePositions moves positions from source subaccount to target subaccount
|
|
func (e *Exchange) MovePositions(ctx context.Context, ccy currency.Code, sourceSubAccountUID, targetSubAccountUID int64, trades []BlockTradeParam) ([]BlockTradeMoveResponse, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if sourceSubAccountUID == 0 {
|
|
return nil, fmt.Errorf("%w source sub-account id", errMissingSubAccountID)
|
|
}
|
|
if targetSubAccountUID == 0 {
|
|
return nil, fmt.Errorf("%w target sub-account id", errMissingSubAccountID)
|
|
}
|
|
for x := range trades {
|
|
if trades[x].InstrumentName == "" {
|
|
return nil, fmt.Errorf("%w, empty string", errInvalidInstrumentName)
|
|
}
|
|
if trades[x].Amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if trades[x].Price < 0 {
|
|
return nil, fmt.Errorf("%w, trade price can't be negative", errInvalidPrice)
|
|
}
|
|
}
|
|
values, err := json.Marshal(trades)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
params.Set("source_uid", strconv.FormatInt(sourceSubAccountUID, 10))
|
|
params.Set("target_uid", strconv.FormatInt(targetSubAccountUID, 10))
|
|
params.Set("trades", string(values))
|
|
var resp []BlockTradeMoveResponse
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, nonMatchingEPL, http.MethodGet, movePositions, params, &resp)
|
|
}
|
|
|
|
// SimulateBlockTrade checks if a block trade can be executed
|
|
func (e *Exchange) SimulateBlockTrade(ctx context.Context, role string, trades []BlockTradeParam) (bool, error) {
|
|
if role != roleMaker && role != roleTaker {
|
|
return false, errInvalidTradeRole
|
|
}
|
|
if len(trades) == 0 {
|
|
return false, errNoArgumentPassed
|
|
}
|
|
for x := range trades {
|
|
if trades[x].InstrumentName == "" {
|
|
return false, fmt.Errorf("%w, empty string", errInvalidInstrumentName)
|
|
}
|
|
trades[x].Direction = strings.ToLower(trades[x].Direction)
|
|
if trades[x].Direction != sideBUY && trades[x].Direction != sideSELL {
|
|
return false, errInvalidOrderSideOrDirection
|
|
}
|
|
if trades[x].Amount <= 0 {
|
|
return false, errInvalidAmount
|
|
}
|
|
if trades[x].Price < 0 {
|
|
return false, fmt.Errorf("%w, trade price can't be negative", errInvalidPrice)
|
|
}
|
|
}
|
|
values, err := json.Marshal(trades)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
params := url.Values{}
|
|
params.Set("role", role)
|
|
params.Set("trades", string(values))
|
|
var resp bool
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestFutures, matchingEPL, http.MethodGet, simulateBlockPosition, params, &resp)
|
|
}
|
|
|
|
// GetLockedStatus retrieves information about locked currencies
|
|
func (e *Exchange) GetLockedStatus(ctx context.Context) (*LockedCurrenciesStatus, error) {
|
|
var resp *LockedCurrenciesStatus
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, nonMatchingEPL, "public/status", &resp)
|
|
}
|
|
|
|
// EnableCancelOnDisconnect enable Cancel On Disconnect for the connection.
|
|
// After enabling Cancel On Disconnect all orders created by the connection will be removed when the connection is closed.
|
|
func (e *Exchange) EnableCancelOnDisconnect(ctx context.Context, scope string) (string, error) {
|
|
params := url.Values{}
|
|
if scope != "" {
|
|
params.Set("scope", scope)
|
|
}
|
|
var resp string
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestSpot, nonMatchingEPL, http.MethodGet, "private/enable_cancel_on_disconnect", params, &resp)
|
|
}
|
|
|
|
// ExchangeToken generates a token for a new subject id. This method can be used to switch between subaccounts.
|
|
func (e *Exchange) ExchangeToken(ctx context.Context, refreshToken string, subjectID int64) (*RefreshTokenInfo, error) {
|
|
if refreshToken == "" {
|
|
return nil, errRefreshTokenRequired
|
|
}
|
|
if subjectID == 0 {
|
|
return nil, errSubjectIDRequired
|
|
}
|
|
params := url.Values{}
|
|
params.Set("refresh_token", refreshToken)
|
|
params.Set("subject_id", strconv.FormatInt(subjectID, 10))
|
|
var resp *RefreshTokenInfo
|
|
return resp, e.SendHTTPAuthRequest(ctx, exchange.RestSpot, nonMatchingEPL, http.MethodGet, "public/exchange_token", params, &resp)
|
|
}
|
|
|
|
// ForkToken generates a token for a new named session. This method can be used only with session scoped tokens.
|
|
func (e *Exchange) ForkToken(ctx context.Context, refreshToken, sessionName string) (*RefreshTokenInfo, error) {
|
|
if refreshToken == "" {
|
|
return nil, errRefreshTokenRequired
|
|
}
|
|
if sessionName == "" {
|
|
return nil, errSessionNameRequired
|
|
}
|
|
params := url.Values{}
|
|
params.Set("refresh_token", refreshToken)
|
|
params.Set("session_name", sessionName)
|
|
var resp *RefreshTokenInfo
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, nonMatchingEPL, common.EncodeURLValues("public/fork_token", params), &resp)
|
|
}
|
|
|
|
// GetAssetKind returns the asset type (kind) string representation.
|
|
func (e *Exchange) GetAssetKind(assetType asset.Item) string {
|
|
switch assetType {
|
|
case asset.Options:
|
|
return "option"
|
|
case asset.Futures:
|
|
return "future"
|
|
case asset.FutureCombo, asset.OptionCombo, asset.Spot:
|
|
return assetType.String()
|
|
default:
|
|
return "any"
|
|
}
|
|
}
|
|
|
|
// StringToAssetKind returns the asset type (kind) from a string representation.
|
|
func (e *Exchange) StringToAssetKind(assetType string) (asset.Item, error) {
|
|
assetType = strings.ToLower(assetType)
|
|
switch assetType {
|
|
case "option":
|
|
return asset.Options, nil
|
|
case "future":
|
|
return asset.Futures, nil
|
|
case "future_combo":
|
|
return asset.FutureCombo, nil
|
|
case "option_combo":
|
|
return asset.OptionCombo, nil
|
|
default:
|
|
return asset.Empty, nil
|
|
}
|
|
}
|
|
|
|
// getAssetPairByInstrument is able to determine the asset type and currency pair
|
|
// based on the received instrument ID
|
|
func getAssetPairByInstrument(instrument string) (asset.Item, currency.Pair, error) {
|
|
if instrument == "" {
|
|
return asset.Empty, currency.EMPTYPAIR, currency.ErrSymbolStringEmpty
|
|
}
|
|
|
|
item, err := getAssetFromInstrument(instrument)
|
|
if err != nil {
|
|
return asset.Empty, currency.EMPTYPAIR, err
|
|
}
|
|
cp, err := currency.NewPairFromString(instrument)
|
|
if err != nil {
|
|
return asset.Empty, currency.EMPTYPAIR, err
|
|
}
|
|
return item, cp, nil
|
|
}
|
|
|
|
// getAssetFromInstrument extrapolates the asset type from the instrument formatting as each type is unique
|
|
func getAssetFromInstrument(instrument string) (asset.Item, error) {
|
|
currencyParts := strings.Split(instrument, currency.DashDelimiter)
|
|
partsLen := len(currencyParts)
|
|
currencySuffix := currencyParts[partsLen-1]
|
|
hasUnderscore := strings.Contains(instrument, currency.UnderscoreDelimiter)
|
|
switch {
|
|
case partsLen == 1 && !hasUnderscore: // no pair delimiter found
|
|
return asset.Empty, fmt.Errorf("%w %s", errUnsupportedInstrumentFormat, instrument)
|
|
case partsLen == 1: // spot pairs use underscore eg BTC_USDC
|
|
return asset.Spot, nil
|
|
case partsLen == 2: // futures pairs use single dash eg ETH_USDC-PERPETUAL, BTC-12SEP25
|
|
return asset.Futures, nil
|
|
case currencySuffix == "C", currencySuffix == "P": // options end in P or C to denote puts or calls eg BTC-26SEP25-30000-C
|
|
return asset.Options, nil
|
|
case partsLen >= 3 && currencyParts[partsLen-2] == "FS" && strings.Contains(currencySuffix, currency.UnderscoreDelimiter):
|
|
// futures combos have underlying-FS-shortLeg_longLeg
|
|
// eg BTC-FS-28NOV25_PERP or BTC-USDC-FS-28NOV25_PERP
|
|
return asset.FutureCombo, nil
|
|
case partsLen == 4: // option combos with more than 3 parts eg BTC_USDC-PS-19SEP25-113000_111000
|
|
return asset.OptionCombo, nil
|
|
default: // deribit has changed their format and needs a review
|
|
return asset.Empty, fmt.Errorf("%w %s", errUnsupportedInstrumentFormat, instrument)
|
|
}
|
|
}
|
|
|
|
func calculateTradingFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
|
|
assetType, err := getAssetFromInstrument(feeBuilder.Pair.String())
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
switch assetType {
|
|
case asset.Futures, asset.FutureCombo:
|
|
switch {
|
|
case strings.HasSuffix(feeBuilder.Pair.String(), "USDC-PERPETUAL"):
|
|
if feeBuilder.IsMaker {
|
|
return 0, nil
|
|
}
|
|
return feeBuilder.Amount * feeBuilder.PurchasePrice * 0.0005, nil
|
|
case strings.HasPrefix(feeBuilder.Pair.String(), currencyBTC),
|
|
strings.HasPrefix(feeBuilder.Pair.String(), currencyETH):
|
|
if strings.HasSuffix(feeBuilder.Pair.String(), perpString) {
|
|
if feeBuilder.IsMaker {
|
|
return 0, nil
|
|
}
|
|
return feeBuilder.Amount * feeBuilder.PurchasePrice * 0.0005, nil
|
|
}
|
|
// weekly futures contracts
|
|
if feeBuilder.IsMaker {
|
|
return feeBuilder.Amount * feeBuilder.PurchasePrice * 0.0001, nil
|
|
}
|
|
return feeBuilder.Amount * feeBuilder.PurchasePrice * 0.0005, nil
|
|
case strings.HasPrefix(feeBuilder.Pair.String(), "SOL"): // perpetual and weekly SOL contracts
|
|
if feeBuilder.IsMaker {
|
|
return feeBuilder.Amount * feeBuilder.PurchasePrice * 0.00002, nil
|
|
}
|
|
return feeBuilder.Amount * feeBuilder.PurchasePrice * 0.0005, nil
|
|
}
|
|
case asset.Options, asset.OptionCombo:
|
|
switch {
|
|
case strings.HasPrefix(feeBuilder.Pair.String(), currencyBTC),
|
|
strings.HasPrefix(feeBuilder.Pair.String(), currencyETH):
|
|
return feeBuilder.Amount * feeBuilder.PurchasePrice * 0.0003, nil
|
|
case strings.HasPrefix(feeBuilder.Pair.String(), "SOL"):
|
|
if feeBuilder.IsMaker {
|
|
return 0, nil
|
|
}
|
|
return feeBuilder.Amount * feeBuilder.PurchasePrice * 0.0003, nil
|
|
}
|
|
}
|
|
return 0, fmt.Errorf("%w asset: %s", asset.ErrNotSupported, assetType.String())
|
|
}
|
|
|
|
// getOfflineTradeFee calculates the worst case-scenario trading fee
|
|
func getOfflineTradeFee(price, amount float64) float64 {
|
|
return 0.0003 * price * amount
|
|
}
|
|
|
|
func formatFuturesTradablePair(pair currency.Pair) string {
|
|
var instrumentID string
|
|
if result := strings.Split(pair.String(), currency.DashDelimiter); len(result) == 3 {
|
|
instrumentID = strings.Join(result[:2], currency.UnderscoreDelimiter) + currency.DashDelimiter + result[2]
|
|
} else {
|
|
instrumentID = pair.String()
|
|
}
|
|
return instrumentID
|
|
}
|
|
|
|
// optionPairToString formats an options pair as: SYMBOL-EXPIRE-STRIKE-TYPE
|
|
// SYMBOL may be a currency (BTC) or a pair (XRP_USDC)
|
|
// EXPIRE is DDMMMYY
|
|
// STRIKE may include a d for decimal point in linear options
|
|
// TYPE is Call or Put
|
|
func optionPairToString(pair currency.Pair) string {
|
|
initialDelimiter := currency.DashDelimiter
|
|
q := pair.Quote.String()
|
|
if strings.HasPrefix(q, "USDC") && len(q) > 11 { // Linear option
|
|
initialDelimiter = currency.UnderscoreDelimiter
|
|
// Replace a capital D with d for decimal place in Strike price
|
|
// Char 11 is either the hyphen before Strike price or first digit
|
|
q = q[:11] + strings.Replace(q[11:], "D", "d", 1)
|
|
}
|
|
return pair.Base.String() + initialDelimiter + q
|
|
}
|
|
|
|
// optionComboPairToString formats an option combo pair to deribit request format
|
|
// e.g. XRP-USDC-CS-26SEP25-3D3_3D5 -> XRP_USDC-CS-26SEP25-3d3_3d5
|
|
func optionComboPairToString(pair currency.Pair) string {
|
|
parts := strings.Split(pair.String(), "-")
|
|
// Deribit uses lowercase 'd' to represent the decimal point
|
|
lastIdx := len(parts) - 1
|
|
parts[lastIdx] = strings.ReplaceAll(parts[lastIdx], "D", "d")
|
|
// Leave unchanged when:
|
|
// * length <= 3 (not enough info to be a combo needing underscore)
|
|
// * length == 4 and second token is not USDC (original logic kept as-is)
|
|
if len(parts) <= 3 || (len(parts) == 4 && parts[1] != "USDC") {
|
|
return strings.Join(parts, "-")
|
|
}
|
|
// Otherwise insert underscore after base (covers:
|
|
// * any length > 4
|
|
// * length == 4 with USDC as second token)
|
|
return parts[0] + "_" + strings.Join(parts[1:], "-")
|
|
}
|
|
|
|
// futureComboPairToString formats a future combo pair to deribit request format
|
|
// e.g. ETH-USDC-FS-28NOV25_PERP -> ETH_USDC-FS-28NOV25_PERP (linear)
|
|
// e.g. BTC-FS-28NOV25_PERP -> BTC-FS-28NOV25_PERP (inverse, unchanged)
|
|
func futureComboPairToString(pair currency.Pair) string {
|
|
s := pair.String()
|
|
before, after, found := strings.Cut(s, "-")
|
|
if !found {
|
|
return s
|
|
}
|
|
|
|
// Must have "USDC-" immediately after first dash (linear combo)
|
|
if len(after) < 5 || after[:5] != "USDC-" {
|
|
return s
|
|
}
|
|
|
|
// Need at least one more dash after "USDC-" to have 4+ parts
|
|
if !strings.Contains(after[5:], "-") {
|
|
return s
|
|
}
|
|
|
|
return before + "_" + after
|
|
}
|