Files
ccxt_with_mt5/ccxt/pro/deribit.py
lz_db 0fab423a18 add
2025-11-16 12:31:03 +08:00

1030 lines
45 KiB
Python

# -*- coding: utf-8 -*-
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
import ccxt.async_support
from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp
import hashlib
from ccxt.base.types import Any, Balances, Int, Order, OrderBook, Str, Strings, Ticker, Tickers, Trade
from ccxt.async_support.base.ws.client import Client
from typing import List
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import NotSupported
class deribit(ccxt.async_support.deribit):
def describe(self) -> Any:
return self.deep_extend(super(deribit, self).describe(), {
'has': {
'ws': True,
'watchBalance': True,
'watchTicker': True,
'watchTickers': True,
'watchBidsAsks': True,
'watchTrades': True,
'watchTradesForSymbols': True,
'watchMyTrades': True,
'watchOrders': True,
'watchOrderBook': True,
'watchOrderBookForSymbols': True,
'watchOHLCV': True,
'watchOHLCVForSymbols': True,
},
'urls': {
'test': {
'ws': 'wss://test.deribit.com/ws/api/v2',
},
'api': {
'ws': 'wss://www.deribit.com/ws/api/v2',
},
},
'options': {
'ws': {
'timeframes': {
'1m': '1',
'3m': '3',
'5m': '5',
'15m': '15',
'30m': '30',
'1h': '60',
'2h': '120',
'4h': '180',
'6h': '360',
'12h': '720',
'1d': '1D',
},
# watchTrades replacement
'watchTradesForSymbols': {
'interval': '100ms', # 100ms, agg2, raw
},
# watchOrderBook replacement
'watchOrderBookForSymbols': {
'interval': '100ms', # 100ms, agg2, raw
'useDepthEndpoint': False, # if True, it will use the {books.group.depth.interval} endpoint instead of the {books.interval} endpoint
'depth': '20', # 1, 10, 20
'group': 'none', # none, 1, 2, 5, 10, 25, 100, 250
},
},
'currencies': ['BTC', 'ETH', 'SOL', 'USDC'],
},
'streaming': {
},
'exceptions': {
},
})
def request_id(self):
requestId = self.sum(self.safe_integer(self.options, 'requestId', 0), 1)
self.options['requestId'] = requestId
return requestId
async def watch_balance(self, params={}) -> Balances:
"""
https://docs.deribit.com/#user-portfolio-currency
watch balance and get the amount of funds available for trading or funds locked in orders
:param dict [params]: extra parameters specific to the exchange API endpoint
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
"""
await self.authenticate(params)
messageHash = 'balance'
url = self.urls['api']['ws']
currencies = self.safe_value(self.options, 'currencies', [])
channels = []
for i in range(0, len(currencies)):
currencyCode = currencies[i]
channels.append('user.portfolio.' + currencyCode)
subscribe: dict = {
'jsonrpc': '2.0',
'method': 'private/subscribe',
'params': {
'channels': channels,
},
'id': self.request_id(),
}
request = self.deep_extend(subscribe, params)
return await self.watch(url, messageHash, request, messageHash, request)
def handle_balance(self, client: Client, message):
#
# subscription
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "user.portfolio.btc",
# "data": {
# "total_pl": 0,
# "session_upl": 0,
# "session_rpl": 0,
# "projected_maintenance_margin": 0,
# "projected_initial_margin": 0,
# "projected_delta_total": 0,
# "portfolio_margining_enabled": False,
# "options_vega": 0,
# "options_value": 0,
# "options_theta": 0,
# "options_session_upl": 0,
# "options_session_rpl": 0,
# "options_pl": 0,
# "options_gamma": 0,
# "options_delta": 0,
# "margin_balance": 0.0015,
# "maintenance_margin": 0,
# "initial_margin": 0,
# "futures_session_upl": 0,
# "futures_session_rpl": 0,
# "futures_pl": 0,
# "fee_balance": 0,
# "estimated_liquidation_ratio_map": {},
# "estimated_liquidation_ratio": 0,
# "equity": 0.0015,
# "delta_total_map": {},
# "delta_total": 0,
# "currency": "BTC",
# "balance": 0.0015,
# "available_withdrawal_funds": 0.0015,
# "available_funds": 0.0015
# }
# }
# }
#
params = self.safe_value(message, 'params', {})
data = self.safe_value(params, 'data', {})
self.balance['info'] = data
currencyId = self.safe_string(data, 'currency')
currencyCode = self.safe_currency_code(currencyId)
balance = self.parse_balance(data)
self.balance[currencyCode] = balance
messageHash = 'balance'
client.resolve(self.balance, messageHash)
async def watch_ticker(self, symbol: str, params={}) -> Ticker:
"""
https://docs.deribit.com/#ticker-instrument_name-interval
watches a price ticker, a statistical calculation with the information for a specific market.
:param str symbol: unified symbol of the market to fetch the ticker for
:param dict [params]: extra parameters specific to the exchange API endpoint
:param str [params.interval]: specify aggregation and frequency of notifications. Possible values: 100ms, raw
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
"""
await self.load_markets()
market = self.market(symbol)
url = self.urls['api']['ws']
interval = self.safe_string(params, 'interval', '100ms')
params = self.omit(params, 'interval')
await self.load_markets()
if interval == 'raw':
await self.authenticate()
channel = 'ticker.' + market['id'] + '.' + interval
message: dict = {
'jsonrpc': '2.0',
'method': 'public/subscribe',
'params': {
'channels': ['ticker.' + market['id'] + '.' + interval],
},
'id': self.request_id(),
}
request = self.deep_extend(message, params)
return await self.watch(url, channel, request, channel, request)
async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
"""
https://docs.deribit.com/#ticker-instrument_name-interval
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
:param str[] [symbols]: unified symbol of the market to fetch the ticker for
:param dict [params]: extra parameters specific to the exchange API endpoint
:param str [params.interval]: specify aggregation and frequency of notifications. Possible values: 100ms, raw
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
"""
await self.load_markets()
symbols = self.market_symbols(symbols, None, False)
url = self.urls['api']['ws']
interval = self.safe_string(params, 'interval', '100ms')
params = self.omit(params, 'interval')
await self.load_markets()
if interval == 'raw':
await self.authenticate()
channels = []
for i in range(0, len(symbols)):
market = self.market(symbols[i])
channels.append('ticker.' + market['id'] + '.' + interval)
message: dict = {
'jsonrpc': '2.0',
'method': 'public/subscribe',
'params': {
'channels': channels,
},
'id': self.request_id(),
}
request = self.deep_extend(message, params)
newTickers = await self.watch_multiple(url, channels, request, channels, request)
if self.newUpdates:
tickers: dict = {}
tickers[newTickers['symbol']] = newTickers
return tickers
return self.filter_by_array(self.tickers, 'symbol', symbols)
def handle_ticker(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "ticker.BTC_USDC-PERPETUAL.raw",
# "data": {
# "timestamp": 1655393725040,
# "stats": [Object],
# "state": "open",
# "settlement_price": 21729.5891,
# "open_interest": 164.501,
# "min_price": 20792.9376,
# "max_price": 21426.225,
# "mark_price": 21109.555,
# "last_price": 21132,
# "instrument_name": "BTC_USDC-PERPETUAL",
# "index_price": 21122.3937,
# "funding_8h": -0.00022427,
# "estimated_delivery_price": 21122.3937,
# "current_funding": -0.00010782,
# "best_bid_price": 21106,
# "best_bid_amount": 1.143,
# "best_ask_price": 21113,
# "best_ask_amount": 0.327
# }
# }
# }
#
params = self.safe_value(message, 'params', {})
data = self.safe_value(params, 'data', {})
marketId = self.safe_string(data, 'instrument_name')
symbol = self.safe_symbol(marketId)
ticker = self.parse_ticker(data)
messageHash = self.safe_string(params, 'channel')
self.tickers[symbol] = ticker
client.resolve(ticker, messageHash)
async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
"""
https://docs.deribit.com/#quote-instrument_name
watches best bid & ask for symbols
:param str[] [symbols]: unified symbol of the market to fetch the ticker for
:param dict [params]: extra parameters specific to the exchange API endpoint
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
"""
await self.load_markets()
symbols = self.market_symbols(symbols, None, False)
url = self.urls['api']['ws']
channels = []
for i in range(0, len(symbols)):
market = self.market(symbols[i])
channels.append('quote.' + market['id'])
message: dict = {
'jsonrpc': '2.0',
'method': 'public/subscribe',
'params': {
'channels': channels,
},
'id': self.request_id(),
}
request = self.deep_extend(message, params)
newTickers = await self.watch_multiple(url, channels, request, channels, request)
if self.newUpdates:
tickers: dict = {}
tickers[newTickers['symbol']] = newTickers
return tickers
return self.filter_by_array(self.bidsasks, 'symbol', symbols)
def handle_bid_ask(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "quote.BTC_USDT",
# "data": {
# "best_bid_amount": 0.026,
# "best_ask_amount": 0.026,
# "best_bid_price": 63908,
# "best_ask_price": 63940,
# "instrument_name": "BTC_USDT",
# "timestamp": 1727765131750
# }
# }
# }
#
params = self.safe_dict(message, 'params', {})
data = self.safe_dict(params, 'data', {})
ticker = self.parse_ws_bid_ask(data)
symbol = ticker['symbol']
self.bidsasks[symbol] = ticker
messageHash = self.safe_string(params, 'channel')
client.resolve(ticker, messageHash)
def parse_ws_bid_ask(self, ticker, market=None):
marketId = self.safe_string(ticker, 'instrument_name')
market = self.safe_market(marketId, market)
symbol = self.safe_string(market, 'symbol')
timestamp = self.safe_integer(ticker, 'timestamp')
return self.safe_ticker({
'symbol': symbol,
'timestamp': timestamp,
'datetime': self.iso8601(timestamp),
'ask': self.safe_string(ticker, 'best_ask_price'),
'askVolume': self.safe_string(ticker, 'best_ask_amount'),
'bid': self.safe_string(ticker, 'best_bid_price'),
'bidVolume': self.safe_string(ticker, 'best_bid_amount'),
'info': ticker,
}, market)
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
"""
get the list of most recent trades for a particular symbol
https://docs.deribit.com/#trades-instrument_name-interval
:param str symbol: unified symbol of the market to fetch trades for
:param int [since]: timestamp in ms of the earliest trade to fetch
:param int [limit]: the maximum amount of trades to fetch
:param dict [params]: extra parameters specific to the exchange API endpoint
:param str [params.interval]: specify aggregation and frequency of notifications. Possible values: 100ms, raw
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
"""
params['callerMethodName'] = 'watchTrades'
return await self.watch_trades_for_symbols([symbol], since, limit, params)
async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
"""
get the list of most recent trades for a list of symbols
https://docs.deribit.com/#trades-instrument_name-interval
:param str[] symbols: unified symbol of the market to fetch trades for
:param int [since]: timestamp in ms of the earliest trade to fetch
:param int [limit]: the maximum amount of trades to fetch
:param dict [params]: extra parameters specific to the exchange API endpoint
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
"""
interval = None
interval, params = self.handle_option_and_params(params, 'watchTradesForSymbols', 'interval', '100ms')
if interval == 'raw':
await self.authenticate()
trades = await self.watch_multiple_wrapper('trades', interval, symbols, params)
if self.newUpdates:
first = self.safe_dict(trades, 0)
tradeSymbol = self.safe_string(first, 'symbol')
limit = trades.getLimit(tradeSymbol, limit)
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
def handle_trades(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "trades.BTC_USDC-PERPETUAL.100ms",
# "data": [{
# "trade_seq": 501899,
# "trade_id": "USDC-2436803",
# "timestamp": 1655397355998,
# "tick_direction": 2,
# "price": 21026,
# "mark_price": 21019.9719,
# "instrument_name": "BTC_USDC-PERPETUAL",
# "index_price": 21031.7847,
# "direction": "buy",
# "amount": 0.049
# }]
# }
# }
#
params = self.safe_dict(message, 'params', {})
channel = self.safe_string(params, 'channel', '')
parts = channel.split('.')
marketId = self.safe_string(parts, 1)
interval = self.safe_string(parts, 2)
symbol = self.safe_symbol(marketId)
market = self.safe_market(marketId)
trades = self.safe_list(params, 'data', [])
if self.safe_value(self.trades, symbol) is None:
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
self.trades[symbol] = ArrayCache(limit)
stored = self.trades[symbol]
for i in range(0, len(trades)):
trade = trades[i]
parsed = self.parse_trade(trade, market)
stored.append(parsed)
self.trades[symbol] = stored
messageHash = 'trades|' + symbol + '|' + interval
client.resolve(self.trades[symbol], messageHash)
async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
"""
get the list of trades associated with the user
https://docs.deribit.com/#user-trades-instrument_name-interval
:param str symbol: unified symbol of the market to fetch trades for. Use 'any' to watch all trades
:param int [since]: timestamp in ms of the earliest trade to fetch
:param int [limit]: the maximum amount of trades to fetch
:param dict [params]: extra parameters specific to the exchange API endpoint
:param str [params.interval]: specify aggregation and frequency of notifications. Possible values: 100ms, raw
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
"""
await self.authenticate(params)
if symbol is not None:
await self.load_markets()
symbol = self.symbol(symbol)
url = self.urls['api']['ws']
interval = self.safe_string(params, 'interval', 'raw')
params = self.omit(params, 'interval')
channel = 'user.trades.any.any.' + interval
message: dict = {
'jsonrpc': '2.0',
'method': 'private/subscribe',
'params': {
'channels': [channel],
},
'id': self.request_id(),
}
request = self.deep_extend(message, params)
trades = await self.watch(url, channel, request, channel, request)
return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
def handle_my_trades(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "user.trades.any.any.raw",
# "data": [{
# "trade_seq": 149546319,
# "trade_id": "219381310",
# "timestamp": 1655421193564,
# "tick_direction": 0,
# "state": "filled",
# "self_trade": False,
# "reduce_only": False,
# "profit_loss": 0,
# "price": 20236.5,
# "post_only": False,
# "order_type": "market",
# "order_id": "46108941243",
# "matching_id": null,
# "mark_price": 20233.96,
# "liquidity": "T",
# "instrument_name": "BTC-PERPETUAL",
# "index_price": 20253.31,
# "fee_currency": "BTC",
# "fee": 2.5e-7,
# "direction": "buy",
# "amount": 10
# }]
# }
# }
#
params = self.safe_value(message, 'params', {})
channel = self.safe_string(params, 'channel', '')
trades = self.safe_value(params, 'data', [])
cachedTrades = self.myTrades
if cachedTrades is None:
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
cachedTrades = ArrayCacheBySymbolById(limit)
parsed = self.parse_trades(trades)
marketIds: dict = {}
for i in range(0, len(parsed)):
trade = parsed[i]
cachedTrades.append(trade)
symbol = trade['symbol']
marketIds[symbol] = True
client.resolve(cachedTrades, channel)
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
"""
https://docs.deribit.com/#book-instrument_name-group-depth-interval
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
:param str symbol: unified symbol of the market to fetch the order book for
:param int [limit]: the maximum amount of order book entries to return
:param dict [params]: extra parameters specific to the exchange API endpoint
:param str [params.interval]: Frequency of notifications. Events will be aggregated over self interval. Possible values: 100ms, raw
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
"""
params['callerMethodName'] = 'watchOrderBook'
return await self.watch_order_book_for_symbols([symbol], limit, params)
async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook:
"""
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
https://docs.deribit.com/#book-instrument_name-group-depth-interval
:param str[] symbols: unified array of symbols
:param int [limit]: the maximum amount of order book entries to return
:param dict [params]: extra parameters specific to the exchange API endpoint
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
"""
interval = None
interval, params = self.handle_option_and_params(params, 'watchOrderBookForSymbols', 'interval', '100ms')
if interval == 'raw':
await self.authenticate()
descriptor = ''
useDepthEndpoint = None # for more info, see comment in .options
useDepthEndpoint, params = self.handle_option_and_params(params, 'watchOrderBookForSymbols', 'useDepthEndpoint', False)
if useDepthEndpoint:
depth = None
depth, params = self.handle_option_and_params(params, 'watchOrderBookForSymbols', 'depth', '20')
group = None
group, params = self.handle_option_and_params(params, 'watchOrderBookForSymbols', 'group', 'none')
descriptor = group + '.' + depth + '.' + interval
else:
descriptor = interval
orderbook = await self.watch_multiple_wrapper('book', descriptor, symbols, params)
return orderbook.limit()
def handle_order_book(self, client: Client, message):
#
# snapshot
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "book.BTC_USDC-PERPETUAL.raw",
# "data": {
# "type": "snapshot",
# "timestamp": 1655395057025,
# "instrument_name": "BTC_USDC-PERPETUAL",
# "change_id": 1550694837,
# "bids": [
# ["new", 20987, 0.487],
# ["new", 20986, 0.238],
# ],
# "asks": [
# ["new", 20999, 0.092],
# ["new", 21000, 1.238],
# ]
# }
# }
# }
#
# change
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "book.BTC_USDC-PERPETUAL.raw",
# "data": {
# "type": "change",
# "timestamp": 1655395168086,
# "prev_change_id": 1550724481,
# "instrument_name": "BTC_USDC-PERPETUAL",
# "change_id": 1550724483,
# "bids": [
# ["new", 20977, 0.109],
# ["delete", 20975, 0]
# ],
# "asks": []
# }
# }
# }
#
params = self.safe_value(message, 'params', {})
data = self.safe_value(params, 'data', {})
channel = self.safe_string(params, 'channel')
parts = channel.split('.')
descriptor = ''
partsLength = len(parts)
isDetailed = partsLength == 5
if isDetailed:
group = self.safe_string(parts, 2)
depth = self.safe_string(parts, 3)
interval = self.safe_string(parts, 4)
descriptor = group + '.' + depth + '.' + interval
else:
interval = self.safe_string(parts, 2)
descriptor = interval
marketId = self.safe_string(data, 'instrument_name')
symbol = self.safe_symbol(marketId)
timestamp = self.safe_integer(data, 'timestamp')
if not (symbol in self.orderbooks):
self.orderbooks[symbol] = self.counted_order_book()
storedOrderBook = self.orderbooks[symbol]
asks = self.safe_list(data, 'asks', [])
bids = self.safe_list(data, 'bids', [])
self.handle_deltas(storedOrderBook['asks'], asks)
self.handle_deltas(storedOrderBook['bids'], bids)
storedOrderBook['nonce'] = timestamp
storedOrderBook['timestamp'] = timestamp
storedOrderBook['datetime'] = self.iso8601(timestamp)
storedOrderBook['symbol'] = symbol
self.orderbooks[symbol] = storedOrderBook
messageHash = 'book|' + symbol + '|' + descriptor
client.resolve(storedOrderBook, messageHash)
def clean_order_book(self, data):
bids = self.safe_list(data, 'bids', [])
asks = self.safe_list(data, 'asks', [])
cleanedBids = []
for i in range(0, len(bids)):
cleanedBids.append([bids[i][1], bids[i][2]])
cleanedAsks = []
for i in range(0, len(asks)):
cleanedAsks.append([asks[i][1], asks[i][2]])
data['bids'] = cleanedBids
data['asks'] = cleanedAsks
return data
def handle_delta(self, bookside, delta):
price = delta[1]
amount = delta[2]
if delta[0] == 'new' or delta[0] == 'change':
bookside.storeArray([price, amount, 1])
elif delta[0] == 'delete':
bookside.storeArray([price, amount, 0])
def handle_deltas(self, bookside, deltas):
for i in range(0, len(deltas)):
self.handle_delta(bookside, deltas[i])
async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
"""
https://docs.deribit.com/#user-orders-instrument_name-raw
watches information on multiple orders made by the user
:param str symbol: unified market symbol of the market orders were made in
:param int [since]: the earliest time in ms to fetch orders for
:param int [limit]: the maximum number of order structures to retrieve
:param dict [params]: extra parameters specific to the exchange API endpoint
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
"""
await self.load_markets()
await self.authenticate(params)
if symbol is not None:
symbol = self.symbol(symbol)
url = self.urls['api']['ws']
currency = self.safe_string(params, 'currency', 'any')
interval = self.safe_string(params, 'interval', 'raw')
kind = self.safe_string(params, 'kind', 'any')
params = self.omit(params, 'interval', 'currency', 'kind')
channel = 'user.orders.' + kind + '.' + currency + '.' + interval
message: dict = {
'jsonrpc': '2.0',
'method': 'private/subscribe',
'params': {
'channels': [channel],
},
'id': self.request_id(),
}
request = self.deep_extend(message, params)
orders = await self.watch(url, channel, request, channel, request)
if self.newUpdates:
limit = orders.getLimit(symbol, limit)
return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True)
def handle_orders(self, client: Client, message):
# Does not return a snapshot of current orders
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "user.orders.any.any.raw",
# "data": {
# "web": True,
# "time_in_force": "good_til_cancelled",
# "replaced": False,
# "reduce_only": False,
# "profit_loss": 0,
# "price": 50000,
# "post_only": False,
# "order_type": "limit",
# "order_state": "open",
# "order_id": "46094375191",
# "max_show": 10,
# "last_update_timestamp": 1655401625037,
# "label": '',
# "is_liquidation": False,
# "instrument_name": "BTC-PERPETUAL",
# "filled_amount": 0,
# "direction": "sell",
# "creation_timestamp": 1655401625037,
# "commission": 0,
# "average_price": 0,
# "api": False,
# "amount": 10
# }
# }
# }
#
if self.orders is None:
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
self.orders = ArrayCacheBySymbolById(limit)
params = self.safe_value(message, 'params', {})
channel = self.safe_string(params, 'channel', '')
data = self.safe_value(params, 'data', {})
orders = []
if isinstance(data, list):
orders = self.parse_orders(data)
else:
order = self.parse_order(data)
orders = [order]
cachedOrders = self.orders
for i in range(0, len(orders)):
cachedOrders.append(orders[i])
client.resolve(self.orders, channel)
async def watch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
"""
https://docs.deribit.com/#chart-trades-instrument_name-resolution
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
:param str symbol: unified symbol of the market to fetch OHLCV data for
:param str timeframe: the length of time each candle represents
:param int [since]: timestamp in ms of the earliest candle to fetch
:param int [limit]: the maximum amount of candles to fetch
:param dict [params]: extra parameters specific to the exchange API endpoint
:returns int[][]: A list of candles ordered, open, high, low, close, volume
"""
await self.load_markets()
symbol = self.symbol(symbol)
ohlcvs = await self.watch_ohlcv_for_symbols([[symbol, timeframe]], since, limit, params)
return ohlcvs[symbol][timeframe]
async def watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], since: Int = None, limit: Int = None, params={}):
"""
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
https://docs.deribit.com/#chart-trades-instrument_name-resolution
:param str[][] symbolsAndTimeframes: array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
:param int [since]: timestamp in ms of the earliest candle to fetch
:param int [limit]: the maximum amount of candles to fetch
:param dict [params]: extra parameters specific to the exchange API endpoint
:returns int[][]: A list of candles ordered, open, high, low, close, volume
"""
symbolsLength = len(symbolsAndTimeframes)
if symbolsLength == 0 or not isinstance(symbolsAndTimeframes[0], list):
raise ArgumentsRequired(self.id + " watchOHLCVForSymbols() requires a an array of symbols and timeframes, like [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]")
symbol, timeframe, candles = await self.watch_multiple_wrapper('chart.trades', None, symbolsAndTimeframes, params)
if self.newUpdates:
limit = candles.getLimit(symbol, limit)
filtered = self.filter_by_since_limit(candles, since, limit, 0, True)
return self.create_ohlcv_object(symbol, timeframe, filtered)
def handle_ohlcv(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "chart.trades.BTC_USDC-PERPETUAL.1",
# "data": {
# "volume": 0,
# "tick": 1655403420000,
# "open": 20951,
# "low": 20951,
# "high": 20951,
# "cost": 0,
# "close": 20951
# }
# }
# }
#
params = self.safe_dict(message, 'params', {})
channel = self.safe_string(params, 'channel', '')
parts = channel.split('.')
marketId = self.safe_string(parts, 2)
rawTimeframe = self.safe_string(parts, 3)
market = self.safe_market(marketId)
symbol = market['symbol']
wsOptions = self.safe_dict(self.options, 'ws', {})
timeframes = self.safe_dict(wsOptions, 'timeframes', {})
unifiedTimeframe = self.find_timeframe(rawTimeframe, timeframes)
self.ohlcvs[symbol] = self.safe_dict(self.ohlcvs, symbol, {})
if self.safe_value(self.ohlcvs[symbol], unifiedTimeframe) is None:
limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
self.ohlcvs[symbol][unifiedTimeframe] = ArrayCacheByTimestamp(limit)
stored = self.ohlcvs[symbol][unifiedTimeframe]
ohlcv = self.safe_dict(params, 'data', {})
# data contains a single OHLCV candle
parsed = self.parse_ws_ohlcv(ohlcv, market)
stored.append(parsed)
self.ohlcvs[symbol][unifiedTimeframe] = stored
resolveData = [symbol, unifiedTimeframe, stored]
messageHash = 'chart.trades|' + symbol + '|' + rawTimeframe
client.resolve(resolveData, messageHash)
def parse_ws_ohlcv(self, ohlcv, market=None) -> list:
#
# {
# "c": "28909.0",
# "o": "28915.4",
# "h": "28915.4",
# "l": "28896.1",
# "v": "27.6919",
# "T": 1696687499999,
# "t": 1696687440000
# }
#
return [
self.safe_integer(ohlcv, 'tick'),
self.safe_number(ohlcv, 'open'),
self.safe_number(ohlcv, 'high'),
self.safe_number(ohlcv, 'low'),
self.safe_number(ohlcv, 'close'),
self.safe_number(ohlcv, 'volume'),
]
async def watch_multiple_wrapper(self, channelName: str, channelDescriptor: Str, symbolsArray=None, params={}):
await self.load_markets()
url = self.urls['api']['ws']
rawSubscriptions = []
messageHashes = []
isOHLCV = (channelName == 'chart.trades')
symbols = self.get_list_from_object_values(symbolsArray, 0) if isOHLCV else symbolsArray
self.market_symbols(symbols, None, False)
for i in range(0, len(symbolsArray)):
current = symbolsArray[i]
market = None
if isOHLCV:
market = self.market(current[0])
unifiedTf = current[1]
rawTf = self.safe_string(self.timeframes, unifiedTf, unifiedTf)
channelDescriptor = rawTf
else:
market = self.market(current)
message = channelName + '.' + market['id'] + '.' + channelDescriptor
rawSubscriptions.append(message)
messageHashes.append(channelName + '|' + market['symbol'] + '|' + channelDescriptor)
request: dict = {
'jsonrpc': '2.0',
'method': 'public/subscribe',
'params': {
'channels': rawSubscriptions,
},
'id': self.request_id(),
}
extendedRequest = self.deep_extend(request, params)
maxMessageByteLimit = 32768 - 1 # 'Message Too Big: limit 32768B'
jsonedText = self.json(extendedRequest)
if len(jsonedText) >= maxMessageByteLimit:
raise ExchangeError(self.id + ' requested subscription length over limit, try to reduce symbols amount')
return await self.watch_multiple(url, messageHashes, extendedRequest, rawSubscriptions)
def handle_message(self, client: Client, message):
#
# error
# {
# "jsonrpc": "2.0",
# "id": 1,
# "error": {
# "message": "Invalid params",
# "data": {
# "reason": "invalid format",
# "param": "nonce"
# },
# "code": -32602
# },
# "usIn": "1655391709417993",
# "usOut": "1655391709418049",
# "usDiff": 56,
# "testnet": False
# }
#
# subscribe
# {
# "jsonrpc": "2.0",
# "id": 2,
# "result": ["ticker.BTC_USDC-PERPETUAL.raw"],
# "usIn": "1655393625889396",
# "usOut": "1655393625889518",
# "usDiff": 122,
# "testnet": False
# }
#
# notification
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "ticker.BTC_USDC-PERPETUAL.raw",
# "data": {
# "timestamp": 1655393724752,
# "stats": [Object],
# "state": "open",
# "settlement_price": 21729.5891,
# "open_interest": 164.501,
# "min_price": 20792.9001,
# "max_price": 21426.1864,
# "mark_price": 21109.4757,
# "last_price": 21132,
# "instrument_name": "BTC_USDC-PERPETUAL",
# "index_price": 21122.3937,
# "funding_8h": -0.00022427,
# "estimated_delivery_price": 21122.3937,
# "current_funding": -0.00011158,
# "best_bid_price": 21106,
# "best_bid_amount": 1.143,
# "best_ask_price": 21113,
# "best_ask_amount": 0.402
# }
# }
# }
#
error = self.safe_value(message, 'error')
if error is not None:
raise ExchangeError(self.id + ' ' + self.json(error))
params = self.safe_value(message, 'params')
channel = self.safe_string(params, 'channel')
if channel is not None:
parts = channel.split('.')
channelId = self.safe_string(parts, 0)
userHandlers: dict = {
'trades': self.handle_my_trades,
'portfolio': self.handle_balance,
'orders': self.handle_orders,
}
handlers: dict = {
'ticker': self.handle_ticker,
'quote': self.handle_bid_ask,
'book': self.handle_order_book,
'trades': self.handle_trades,
'chart': self.handle_ohlcv,
'user': self.safe_value(userHandlers, self.safe_string(parts, 1)),
}
handler = self.safe_value(handlers, channelId)
if handler is not None:
handler(client, message)
return
raise NotSupported(self.id + ' no handler found for self message ' + self.json(message))
result = self.safe_value(message, 'result', {})
accessToken = self.safe_string(result, 'access_token')
if accessToken is not None:
self.handle_authentication_message(client, message)
def handle_authentication_message(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "id": 1,
# "result": {
# "token_type": "bearer",
# "scope": "account:read_write block_trade:read_write connection custody:read_write mainaccount name:ccxt trade:read_write wallet:read_write",
# "refresh_token": "1686927372328.1EzFBRmt.logRQWXkPA1oE_Tk0gRsls9Hau7YN6a321XUBnxvR4x6cryhbkKcniUJU-czA8_zKXrqQGpQmfoDwhLIjIsWCvRuu6otbg-LKWlrtTX1GQqLcPaTTHAdZGTMV-HM8HiS03QBd9MIXWRfF53sKj2hdR9nZPZ6MH1XrkpAZPB_peuEEB9wlcc3elzWEZFtCmiy1fnQ8TPHwAJMt3nuUmEcMLt_-F554qrsg_-I66D9xMiifJj4dBemdPfV_PkGPRIwIoKlxDjyv2-xfCw-4eKyo6Hu1m2h6gT1DPOTxSXcBgfBQjpi-_uY3iAIj7U6xjC46PHthEdquhEuCTZl7UfCRZSAWwZA",
# "expires_in": 31536000,
# "access_token": "1686923272328.1CkwEx-u.qHradpIulmuoeboKMEi8PkQ1_4DF8yFE2zywBTtkD32sruVC53b1HwL5OWRuh2nYAndXff4xuXIMRkkEfMAFCeq24prihxxinoS8DDVkKBxedGx4CUPJFeXjmh7wuRGqQOLg1plXOpbF3fwF2KPEkAuETwcpcVY6K9HUVjutNRfxFe2TR7CvuS9x8TATvoPeu7H1ezYl-LkKSaRifdTXuwituXgp4oDbPRyQLniEBWuYF9rY7qbABxuOJlXI1VZ63u7Bh0mGWei-KeVeqHGNpy6OgrFRPXPxa9_U7vaxCyHW3zZ9959TQ1QUMLWtUX-NLBEv3BT5eCieW9HORYIOKfsgkpd3"
# },
# "usIn": "1655391872327712",
# "usOut": "1655391872328515",
# "usDiff": 803,
# "testnet": False
# }
#
messageHash = 'authenticated'
client.resolve(message, messageHash)
return message
async def authenticate(self, params={}):
url = self.urls['api']['ws']
client = self.client(url)
time = self.milliseconds()
timeString = self.number_to_string(time)
nonce = timeString
messageHash = 'authenticated'
future = self.safe_value(client.subscriptions, messageHash)
if future is None:
self.check_required_credentials()
requestId = self.request_id()
lineBreak = "\n" # eslint-disable-line quotes
signature = self.hmac(self.encode(timeString + lineBreak + nonce + lineBreak), self.encode(self.secret), hashlib.sha256)
request: dict = {
'jsonrpc': '2.0',
'id': requestId,
'method': 'public/auth',
'params': {
'grant_type': 'client_signature',
'client_id': self.apiKey,
'timestamp': time,
'signature': signature,
'nonce': nonce,
'data': '',
},
}
future = await self.watch(url, messageHash, self.extend(request, params), messageHash)
client.subscriptions[messageHash] = future
return future