Initial commit: 首次建仓,建立目录结构

This commit is contained in:
FXY
2026-06-11 23:49:54 +08:00
commit 4038a476b5
9396 changed files with 2372905 additions and 0 deletions

View File

@ -0,0 +1,614 @@
# -*- 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
from ccxt.base.types import Any, Bool, Int, Market, Order, OrderBook, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade
from ccxt.async_support.base.ws.client import Client
from typing import List
class paradex(ccxt.async_support.paradex):
def describe(self) -> Any:
return self.deep_extend(super(paradex, self).describe(), {
'has': {
'ws': True,
'watchFundingRate': True,
'watchFundingRates': True,
'watchTicker': True,
'watchTickers': True,
'watchOrderBook': True,
'watchOrders': True,
'watchTrades': True,
'watchTradesForSymbols': False,
'watchBalance': False,
'watchOHLCV': False,
},
'urls': {
'logo': 'https://x.com/tradeparadex/photo',
'api': {
'ws': 'wss://ws.api.prod.paradex.trade/v1',
},
'test': {
'ws': 'wss://ws.api.testnet.paradex.trade/v1',
},
'www': 'https://www.paradex.trade/',
'doc': 'https://docs.api.testnet.paradex.trade/',
'fees': 'https://docs.paradex.trade/getting-started/trading-fees',
'referral': '',
},
'options': {},
'streaming': {},
})
def request_id(self):
requestId = self.sum(self.safe_integer(self.options, 'requestId', 0), 1)
self.options['requestId'] = requestId
return requestId
async def authenticate(self, params={}):
url = self.urls['api']['ws']
client = self.client(url)
messageHash = 'authenticated'
future = client.reusableFuture('authenticated')
authenticated = self.safe_value(client.subscriptions, messageHash)
if authenticated is None:
token = await self.authenticateRest()
request: dict = {
'jsonrpc': '2.0',
'id': self.request_id(),
'method': 'auth',
'params': {
'bearer': token,
},
}
self.watch(url, messageHash, self.deep_extend(request, params), messageHash)
return await future
def handle_authentication_message(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "id": 1,
# "result": {"node_id": "73cf456f7cb78d59"}
# }
#
result = self.safe_dict(message, 'result')
if result is not None:
# client.resolve(True, messageHash)
future = self.safe_value(client.futures, 'authenticated')
if future is not None:
future.resolve(True)
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.paradex.trade/ws/web-socket-channels/trades/trades
: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
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=public-trades>`
"""
await self.load_markets()
messageHash = 'trades.'
if symbol is not None:
market = self.market(symbol)
messageHash += market['id']
else:
messageHash += 'ALL'
url = self.urls['api']['ws']
request: dict = {
'jsonrpc': '2.0',
'method': 'subscribe',
'params': {
'channel': messageHash,
},
}
trades = await self.watch(url, messageHash, self.deep_extend(request, params), messageHash)
if self.newUpdates:
limit = trades.getLimit(symbol, limit)
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
def handle_trade(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "trades.ALL",
# "data": {
# "id": "1718179273230201709233240002",
# "market": "kBONK-USD-PERP",
# "side": "BUY",
# "size": "34028",
# "price": "0.028776",
# "created_at": 1718179273230,
# "trade_type": "FILL"
# }
# }
# }
#
params = self.safe_dict(message, 'params', {})
data = self.safe_dict(params, 'data', {})
parsedTrade = self.parse_trade(data)
symbol = parsedTrade['symbol']
messageHash = self.safe_string(params, 'channel')
stored = self.safe_value(self.trades, symbol)
if stored is None:
stored = ArrayCache(self.safe_integer(self.options, 'tradesLimit', 1000))
self.trades[symbol] = stored
stored.append(parsedTrade)
client.resolve(stored, messageHash)
return message
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
"""
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
https://docs.paradex.trade/ws/web-socket-channels/order-book/order-book
: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
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/?id=order-book-structure>` indexed by market symbols
"""
await self.load_markets()
market = self.market(symbol)
messageHash = 'order_book.' + market['id'] + '.snapshot@15@100ms'
url = self.urls['api']['ws']
request: dict = {
'jsonrpc': '2.0',
'method': 'subscribe',
'params': {
'channel': messageHash,
},
}
orderbook = await self.watch(url, messageHash, self.deep_extend(request, params), messageHash)
return orderbook.limit()
def handle_order_book(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "order_book.BTC-USD-PERP.snapshot@15@50ms",
# "data": {
# "seq_no": 14127815,
# "market": "BTC-USD-PERP",
# "last_updated_at": 1718267837265,
# "update_type": "s",
# "inserts": [
# {
# "side": "BUY",
# "price": "67629.7",
# "size": "0.992"
# },
# {
# "side": "SELL",
# "price": "69378.6",
# "size": "3.137"
# }
# ],
# "updates": [],
# "deletes": []
# }
# }
# }
#
params = self.safe_dict(message, 'params', {})
data = self.safe_dict(params, 'data', {})
marketId = self.safe_string(data, 'market')
market = self.safe_market(marketId)
timestamp = self.safe_integer(data, 'last_updated_at')
symbol = market['symbol']
if not (symbol in self.orderbooks):
self.orderbooks[symbol] = self.order_book()
orderbookData = {
'bids': [],
'asks': [],
}
inserts = self.safe_list(data, 'inserts')
for i in range(0, len(inserts)):
insert = self.safe_dict(inserts, i)
side = self.safe_string(insert, 'side')
price = self.safe_string(insert, 'price')
size = self.safe_string(insert, 'size')
if side == 'BUY':
orderbookData['bids'].append([price, size])
else:
orderbookData['asks'].append([price, size])
orderbook = self.orderbooks[symbol]
snapshot = self.parse_order_book(orderbookData, symbol, timestamp, 'bids', 'asks')
snapshot['nonce'] = self.safe_integer(data, 'seq_no')
orderbook.reset(snapshot)
messageHash = self.safe_string(params, 'channel')
client.resolve(orderbook, messageHash)
async def watch_ticker(self, symbol: str, params={}) -> Ticker:
"""
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
https://docs.paradex.trade/ws/web-socket-channels/markets-summary/markets-summary
:param str symbol: 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()
symbol = self.symbol(symbol)
channel = 'markets_summary'
url = self.urls['api']['ws']
request: dict = {
'jsonrpc': '2.0',
'method': 'subscribe',
'params': {
'channel': channel,
},
}
messageHash = channel + '.' + symbol
return await self.watch(url, messageHash, self.deep_extend(request, params), messageHash)
async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
"""
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
https://docs.paradex.trade/ws/web-socket-channels/markets-summary/markets-summary
: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)
channel = 'markets_summary'
url = self.urls['api']['ws']
request: dict = {
'jsonrpc': '2.0',
'method': 'subscribe',
'params': {
'channel': channel,
},
}
messageHashes = []
if symbols is not None and isinstance(symbols, list):
for i in range(0, len(symbols)):
messageHash = channel + '.' + symbols[i]
messageHashes.append(messageHash)
else:
messageHashes.append(channel)
newTicker = await self.watch_multiple(url, messageHashes, self.deep_extend(request, params), messageHashes)
if self.newUpdates:
result: dict = {}
result[newTicker['symbol']] = newTicker
return result
return self.filter_by_array(self.tickers, 'symbol', symbols)
async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
"""
watches information on multiple orders made by the user
https://docs.paradex.trade/ws/web-socket-channels/orders/orders
: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()
messageHash = 'orders'
channel = 'orders.'
if symbol is not None:
market = self.market(symbol)
symbol = market['symbol']
channel += market['id']
messageHash += ':' + symbol
else:
channel += 'ALL'
url = self.urls['api']['ws']
request: dict = {
'jsonrpc': '2.0',
'method': 'subscribe',
'params': {
'channel': channel,
},
}
orders = await self.watch(url, messageHash, self.deep_extend(request, params), channel)
if self.newUpdates:
limit = orders.getLimit(symbol, limit)
return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True)
def handle_order(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "orders.ALL",
# "data": {
# "account": "0x4638e3041366aa71720be63e32e53e1223316c7f0d56f7aa617542ed1e7512x",
# "avg_fill_price": "26000",
# "client_id": "x1234",
# "cancel_reason": "",
# "created_at": 1681493746016,
# "flags": ["REDUCE_ONLY"],
# "id": "123456",
# "instruction": "GTC",
# "last_updated_at": 1681493746016,
# "market": "BTC-USD-PERP",
# "price": "26000",
# "remaining_size": "0",
# "side": "BUY",
# "size": "0.05",
# "status": "NEW",
# "type": "LIMIT"
# }
# }
# }
#
params = self.safe_dict(message, 'params', {})
data = self.safe_dict(params, 'data', {})
parsed = self.parse_order(data)
symbol = self.safe_string(parsed, 'symbol')
if self.orders is None:
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
self.orders = ArrayCacheBySymbolById(limit)
self.orders.append(parsed)
messageHash = 'orders'
client.resolve(self.orders, messageHash)
if symbol is not None:
symbolMessageHash = messageHash + ':' + symbol
client.resolve(self.orders, symbolMessageHash)
def handle_ticker(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "markets_summary",
# "data": {
# "symbol": "ORDI-USD-PERP",
# "oracle_price": "49.80885481",
# "mark_price": "49.80885481",
# "last_traded_price": "62.038",
# "bid": "49.822",
# "ask": "58.167",
# "volume_24h": "0",
# "total_volume": "54542628.66054200416",
# "created_at": 1718334307698,
# "underlying_price": "47.93",
# "open_interest": "6999.5",
# "funding_rate": "0.03919997509811",
# "price_change_rate_24h": ""
# }
# }
# }
#
params = self.safe_dict(message, 'params', {})
data = self.safe_dict(params, 'data', {})
marketId = self.safe_string(data, 'symbol')
market = self.safe_market(marketId)
symbol = market['symbol']
channel = self.safe_string(params, 'channel')
messageHash = channel + '.' + symbol
ticker = self.parse_ticker(data, market)
self.tickers[symbol] = ticker
client.resolve(ticker, channel)
client.resolve(ticker, messageHash)
return message
async def watch_funding_rate(self, symbol: str, params={}) -> FundingRate:
"""
watch the current funding rate for a symbol
https://docs.paradex.trade/ws/web-socket-channels/funding-data-market-symbol/funding-data-market-symbol
:param str symbol: unified market symbol
:param dict [params]: extra parameters specific to the exchange API endpoint
:returns dict: a `funding rate structure <https://docs.ccxt.com/?id=funding-rate-structure>`
"""
await self.load_markets()
symbol = self.symbol(symbol)
channel = 'funding_data'
url = self.urls['api']['ws']
request: dict = {
'jsonrpc': '2.0',
'method': 'subscribe',
'params': {
'channel': channel,
},
}
messageHash = channel + '.' + symbol
return await self.watch(url, messageHash, self.deep_extend(request, params), messageHash)
async def watch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
"""
watch the funding rate for multiple markets
https://docs.paradex.trade/ws/web-socket-channels/markets-summary/markets-summary
:param str[] [symbols]: a list of unified market symbols
:param dict [params]: extra parameters specific to the exchange API endpoint
:returns dict: a `funding rate structure <https://docs.ccxt.com/?id=funding-rate-structure>`
"""
await self.load_markets()
symbols = self.market_symbols(symbols)
channel = 'funding_data'
url = self.urls['api']['ws']
request: dict = {
'jsonrpc': '2.0',
'method': 'subscribe',
'params': {
'channel': channel,
},
}
messageHashes = []
if symbols is not None:
symbolsLength = len(symbols)
if symbolsLength > 0:
for i in range(0, len(symbols)):
messageHash = channel + '.' + symbols[i]
messageHashes.append(messageHash)
else:
messageHashes.append(channel) # if an empty array is passed, subscribe to all funding rates
else:
messageHashes.append(channel)
newFundingRates = await self.watch_multiple(url, messageHashes, self.deep_extend(request, params), messageHashes)
if self.newUpdates:
result: dict = {}
result[newFundingRates['symbol']] = newFundingRates
return result
return self.filter_by_array(self.fundingRates, 'symbol', symbols)
def handle_funding_rate(self, client: Client, message):
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "funding_data",
# "data": {
# "market": "TRUMP-USD-PERP",
# "funding_index": "-0.551694014226244835",
# "funding_premium": "-0.000509914923994872836",
# "funding_rate": "-0.00014969570582",
# "funding_rate_8h": "-0.00014969",
# "funding_period_hours": 8,
# "created_at": 1771506636154
# }
# }
# }
#
params = self.safe_dict(message, 'params', {})
data = self.safe_dict(params, 'data', {})
fundingRate = self.parse_funding_rate_ws(data)
symbol = fundingRate['symbol']
self.fundingRates[symbol] = fundingRate
channel = self.safe_string(params, 'channel')
messageHash = channel + '.' + symbol
client.resolve(fundingRate, messageHash)
def parse_funding_rate_ws(self, contract, market: Market = None) -> FundingRate:
#
# {
# "market": "TRUMP-USD-PERP",
# "funding_index": "-0.551694014226244835",
# "funding_premium": "-0.000509914923994872836",
# "funding_rate": "-0.00014969570582",
# "funding_rate_8h": "-0.00014969",
# "funding_period_hours": 8,
# "created_at": 1771506636154
# }
#
marketId = self.safe_string(contract, 'market')
symbol = self.safe_symbol(marketId, market)
timestamp = self.safe_integer(contract, 'created_at')
fundingPeriod = self.safe_string(contract, 'funding_period_hours')
return {
'info': contract,
'symbol': symbol,
'markPrice': None,
'indexPrice': None,
'interestRate': self.parse_number('0'),
'estimatedSettlePrice': None,
'timestamp': timestamp,
'datetime': self.iso8601(timestamp),
'fundingRate': self.safe_number(contract, 'funding_rate'),
'fundingTimestamp': None,
'fundingDatetime': None,
'nextFundingRate': None,
'nextFundingTimestamp': None,
'nextFundingDatetime': None,
'previousFundingRate': None,
'previousFundingTimestamp': None,
'previousFundingDatetime': None,
'interval': fundingPeriod + 'h',
}
def handle_error_message(self, client: Client, message) -> Bool:
#
# {
# "jsonrpc": "2.0",
# "id": 0,
# "error": {
# "code": -32600,
# "message": "invalid subscribe request",
# "data": "invalid channel"
# },
# "usIn": 1718179125962419,
# "usDiff": 76,
# "usOut": 1718179125962495
# }
#
error = self.safe_dict(message, 'error')
if error is None:
return True
else:
errorCode = self.safe_string(error, 'code')
if errorCode is not None:
feedback = self.id + ' ' + self.json(error)
self.throw_exactly_matched_exception(self.exceptions['exact'], '-32600', feedback)
messageString = self.safe_value(error, 'message')
if messageString is not None:
self.throw_broadly_matched_exception(self.exceptions['broad'], messageString, feedback)
return False
def handle_message(self, client: Client, message):
if not self.handle_error_message(client, message):
return
#
# auth response
#
# {
# "jsonrpc": "2.0",
# "id": 1,
# "result": {"node_id": "73cf456f7cb78d59"}
# }
#
# subscription message
#
# {
# "jsonrpc": "2.0",
# "method": "subscription",
# "params": {
# "channel": "trades.ALL",
# "data": {
# "id": "1718179273230201709233240002",
# "market": "kBONK-USD-PERP",
# "side": "BUY",
# "size": "34028",
# "price": "0.028776",
# "created_at": 1718179273230,
# "trade_type": "FILL"
# }
# }
# }
#
result = self.safe_value(message, 'result')
if result is not None:
self.handle_authentication_message(client, message)
return
data = self.safe_dict(message, 'params')
if data is not None:
channel = self.safe_string(data, 'channel')
parts = channel.split('.')
name = self.safe_string(parts, 0)
methods: dict = {
'trades': self.handle_trade,
'order_book': self.handle_order_book,
'markets_summary': self.handle_ticker,
'orders': self.handle_order,
'funding_data': self.handle_funding_rate,
}
method = self.safe_value(methods, name)
if method is not None:
method(client, message)