Initial commit: 首次建仓,建立目录结构
This commit is contained in:
224
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/__init__.py
Normal file
224
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/__init__.py
Normal file
@ -0,0 +1,224 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""CCXT: CryptoCurrency eXchange Trading Library (Async)"""
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
__version__ = '4.5.56'
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
from ccxt.async_support.base.exchange import Exchange # noqa: F401
|
||||
|
||||
# CCXT Pro exchanges (now this is mainly used for importing exchanges in WS tests)
|
||||
|
||||
# DO_NOT_REMOVE__ERROR_IMPORTS_START
|
||||
from ccxt.base.errors import BaseError # noqa: F401
|
||||
from ccxt.base.errors import ExchangeError # noqa: F401
|
||||
from ccxt.base.errors import AuthenticationError # noqa: F401
|
||||
from ccxt.base.errors import PermissionDenied # noqa: F401
|
||||
from ccxt.base.errors import AccountNotEnabled # noqa: F401
|
||||
from ccxt.base.errors import AccountSuspended # noqa: F401
|
||||
from ccxt.base.errors import ArgumentsRequired # noqa: F401
|
||||
from ccxt.base.errors import BadRequest # noqa: F401
|
||||
from ccxt.base.errors import BadSymbol # noqa: F401
|
||||
from ccxt.base.errors import OperationRejected # noqa: F401
|
||||
from ccxt.base.errors import NoChange # noqa: F401
|
||||
from ccxt.base.errors import MarginModeAlreadySet # noqa: F401
|
||||
from ccxt.base.errors import MarketClosed # noqa: F401
|
||||
from ccxt.base.errors import ManualInteractionNeeded # noqa: F401
|
||||
from ccxt.base.errors import RestrictedLocation # noqa: F401
|
||||
from ccxt.base.errors import InsufficientFunds # noqa: F401
|
||||
from ccxt.base.errors import InvalidAddress # noqa: F401
|
||||
from ccxt.base.errors import AddressPending # noqa: F401
|
||||
from ccxt.base.errors import InvalidOrder # noqa: F401
|
||||
from ccxt.base.errors import OrderNotFound # noqa: F401
|
||||
from ccxt.base.errors import OrderNotCached # noqa: F401
|
||||
from ccxt.base.errors import OrderImmediatelyFillable # noqa: F401
|
||||
from ccxt.base.errors import OrderNotFillable # noqa: F401
|
||||
from ccxt.base.errors import DuplicateOrderId # noqa: F401
|
||||
from ccxt.base.errors import ContractUnavailable # noqa: F401
|
||||
from ccxt.base.errors import NotSupported # noqa: F401
|
||||
from ccxt.base.errors import InvalidProxySettings # noqa: F401
|
||||
from ccxt.base.errors import ExchangeClosedByUser # noqa: F401
|
||||
from ccxt.base.errors import OperationFailed # noqa: F401
|
||||
from ccxt.base.errors import NetworkError # noqa: F401
|
||||
from ccxt.base.errors import DDoSProtection # noqa: F401
|
||||
from ccxt.base.errors import RateLimitExceeded # noqa: F401
|
||||
from ccxt.base.errors import ExchangeNotAvailable # noqa: F401
|
||||
from ccxt.base.errors import OnMaintenance # noqa: F401
|
||||
from ccxt.base.errors import InvalidNonce # noqa: F401
|
||||
from ccxt.base.errors import ChecksumError # noqa: F401
|
||||
from ccxt.base.errors import RequestTimeout # noqa: F401
|
||||
from ccxt.base.errors import BadResponse # noqa: F401
|
||||
from ccxt.base.errors import NullResponse # noqa: F401
|
||||
from ccxt.base.errors import CancelPending # noqa: F401
|
||||
from ccxt.base.errors import UnsubscribeError # noqa: F401
|
||||
from ccxt.base.errors import error_hierarchy # noqa: F401
|
||||
# DO_NOT_REMOVE__ERROR_IMPORTS_END
|
||||
|
||||
from ccxt.pro.aftermath import aftermath # noqa: F401
|
||||
from ccxt.pro.alpaca import alpaca # noqa: F401
|
||||
from ccxt.pro.apex import apex # noqa: F401
|
||||
from ccxt.pro.arkham import arkham # noqa: F401
|
||||
from ccxt.pro.ascendex import ascendex # noqa: F401
|
||||
from ccxt.pro.aster import aster # noqa: F401
|
||||
from ccxt.pro.backpack import backpack # noqa: F401
|
||||
from ccxt.pro.bequant import bequant # noqa: F401
|
||||
from ccxt.pro.binance import binance # noqa: F401
|
||||
from ccxt.pro.binancecoinm import binancecoinm # noqa: F401
|
||||
from ccxt.pro.binanceus import binanceus # noqa: F401
|
||||
from ccxt.pro.binanceusdm import binanceusdm # noqa: F401
|
||||
from ccxt.pro.bingx import bingx # noqa: F401
|
||||
from ccxt.pro.bitfinex import bitfinex # noqa: F401
|
||||
from ccxt.pro.bitget import bitget # noqa: F401
|
||||
from ccxt.pro.bithumb import bithumb # noqa: F401
|
||||
from ccxt.pro.bitmart import bitmart # noqa: F401
|
||||
from ccxt.pro.bitmex import bitmex # noqa: F401
|
||||
from ccxt.pro.bitopro import bitopro # noqa: F401
|
||||
from ccxt.pro.bitrue import bitrue # noqa: F401
|
||||
from ccxt.pro.bitstamp import bitstamp # noqa: F401
|
||||
from ccxt.pro.bittrade import bittrade # noqa: F401
|
||||
from ccxt.pro.bitvavo import bitvavo # noqa: F401
|
||||
from ccxt.pro.blockchaincom import blockchaincom # noqa: F401
|
||||
from ccxt.pro.blofin import blofin # noqa: F401
|
||||
from ccxt.pro.bullish import bullish # noqa: F401
|
||||
from ccxt.pro.bybit import bybit # noqa: F401
|
||||
from ccxt.pro.bybiteu import bybiteu # noqa: F401
|
||||
from ccxt.pro.bydfi import bydfi # noqa: F401
|
||||
from ccxt.pro.cex import cex # noqa: F401
|
||||
from ccxt.pro.coinbase import coinbase # noqa: F401
|
||||
from ccxt.pro.coinbaseadvanced import coinbaseadvanced # noqa: F401
|
||||
from ccxt.pro.coinbaseexchange import coinbaseexchange # noqa: F401
|
||||
from ccxt.pro.coinbaseinternational import coinbaseinternational # noqa: F401
|
||||
from ccxt.pro.coincheck import coincheck # noqa: F401
|
||||
from ccxt.pro.coinex import coinex # noqa: F401
|
||||
from ccxt.pro.coinone import coinone # noqa: F401
|
||||
from ccxt.pro.cryptocom import cryptocom # noqa: F401
|
||||
from ccxt.pro.deepcoin import deepcoin # noqa: F401
|
||||
from ccxt.pro.deribit import deribit # noqa: F401
|
||||
from ccxt.pro.derive import derive # noqa: F401
|
||||
from ccxt.pro.dydx import dydx # noqa: F401
|
||||
from ccxt.pro.exmo import exmo # noqa: F401
|
||||
from ccxt.pro.gate import gate # noqa: F401
|
||||
from ccxt.pro.gateio import gateio # noqa: F401
|
||||
from ccxt.pro.gemini import gemini # noqa: F401
|
||||
from ccxt.pro.grvt import grvt # noqa: F401
|
||||
from ccxt.pro.hashkey import hashkey # noqa: F401
|
||||
from ccxt.pro.hitbtc import hitbtc # noqa: F401
|
||||
from ccxt.pro.hollaex import hollaex # noqa: F401
|
||||
from ccxt.pro.htx import htx # noqa: F401
|
||||
from ccxt.pro.huobi import huobi # noqa: F401
|
||||
from ccxt.pro.hyperliquid import hyperliquid # noqa: F401
|
||||
from ccxt.pro.independentreserve import independentreserve # noqa: F401
|
||||
from ccxt.pro.kraken import kraken # noqa: F401
|
||||
from ccxt.pro.krakenfutures import krakenfutures # noqa: F401
|
||||
from ccxt.pro.kucoin import kucoin # noqa: F401
|
||||
from ccxt.pro.kucoinfutures import kucoinfutures # noqa: F401
|
||||
from ccxt.pro.lbank import lbank # noqa: F401
|
||||
from ccxt.pro.lighter import lighter # noqa: F401
|
||||
from ccxt.pro.luno import luno # noqa: F401
|
||||
from ccxt.pro.mexc import mexc # noqa: F401
|
||||
from ccxt.pro.modetrade import modetrade # noqa: F401
|
||||
from ccxt.pro.myokx import myokx # noqa: F401
|
||||
from ccxt.pro.ndax import ndax # noqa: F401
|
||||
from ccxt.pro.okx import okx # noqa: F401
|
||||
from ccxt.pro.okxus import okxus # noqa: F401
|
||||
from ccxt.pro.onetrading import onetrading # noqa: F401
|
||||
from ccxt.pro.oxfun import oxfun # noqa: F401
|
||||
from ccxt.pro.p2b import p2b # noqa: F401
|
||||
from ccxt.pro.pacifica import pacifica # noqa: F401
|
||||
from ccxt.pro.paradex import paradex # noqa: F401
|
||||
from ccxt.pro.phemex import phemex # noqa: F401
|
||||
from ccxt.pro.poloniex import poloniex # noqa: F401
|
||||
from ccxt.pro.toobit import toobit # noqa: F401
|
||||
from ccxt.pro.upbit import upbit # noqa: F401
|
||||
from ccxt.pro.weex import weex # noqa: F401
|
||||
from ccxt.pro.whitebit import whitebit # noqa: F401
|
||||
from ccxt.pro.woo import woo # noqa: F401
|
||||
from ccxt.pro.woofipro import woofipro # noqa: F401
|
||||
from ccxt.pro.xt import xt # noqa: F401
|
||||
|
||||
exchanges = [
|
||||
'aftermath',
|
||||
'alpaca',
|
||||
'apex',
|
||||
'arkham',
|
||||
'ascendex',
|
||||
'aster',
|
||||
'backpack',
|
||||
'bequant',
|
||||
'binance',
|
||||
'binancecoinm',
|
||||
'binanceus',
|
||||
'binanceusdm',
|
||||
'bingx',
|
||||
'bitfinex',
|
||||
'bitget',
|
||||
'bithumb',
|
||||
'bitmart',
|
||||
'bitmex',
|
||||
'bitopro',
|
||||
'bitrue',
|
||||
'bitstamp',
|
||||
'bittrade',
|
||||
'bitvavo',
|
||||
'blockchaincom',
|
||||
'blofin',
|
||||
'bullish',
|
||||
'bybit',
|
||||
'bybiteu',
|
||||
'bydfi',
|
||||
'cex',
|
||||
'coinbase',
|
||||
'coinbaseadvanced',
|
||||
'coinbaseexchange',
|
||||
'coinbaseinternational',
|
||||
'coincheck',
|
||||
'coinex',
|
||||
'coinone',
|
||||
'cryptocom',
|
||||
'deepcoin',
|
||||
'deribit',
|
||||
'derive',
|
||||
'dydx',
|
||||
'exmo',
|
||||
'gate',
|
||||
'gateio',
|
||||
'gemini',
|
||||
'grvt',
|
||||
'hashkey',
|
||||
'hitbtc',
|
||||
'hollaex',
|
||||
'htx',
|
||||
'huobi',
|
||||
'hyperliquid',
|
||||
'independentreserve',
|
||||
'kraken',
|
||||
'krakenfutures',
|
||||
'kucoin',
|
||||
'kucoinfutures',
|
||||
'lbank',
|
||||
'lighter',
|
||||
'luno',
|
||||
'mexc',
|
||||
'modetrade',
|
||||
'myokx',
|
||||
'ndax',
|
||||
'okx',
|
||||
'okxus',
|
||||
'onetrading',
|
||||
'oxfun',
|
||||
'p2b',
|
||||
'pacifica',
|
||||
'paradex',
|
||||
'phemex',
|
||||
'poloniex',
|
||||
'toobit',
|
||||
'upbit',
|
||||
'weex',
|
||||
'whitebit',
|
||||
'woo',
|
||||
'woofipro',
|
||||
'xt',
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,347 @@
|
||||
# -*- 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, ArrayCacheBySymbolBySide
|
||||
from ccxt.base.types import Any, Int, OrderBook, Position, Strings, 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 AuthenticationError
|
||||
|
||||
|
||||
class aftermath(ccxt.async_support.aftermath):
|
||||
|
||||
def describe(self) -> Any:
|
||||
return self.deep_extend(super(aftermath, self).describe(), {
|
||||
'has': {
|
||||
'ws': True,
|
||||
'watchBalance': False,
|
||||
'watchMyTrades': False,
|
||||
'watchOHLCV': False,
|
||||
'watchOrderBook': True,
|
||||
'watchOrders': False,
|
||||
'watchTicker': False,
|
||||
'watchTickers': False,
|
||||
'watchBidsAsks': False,
|
||||
'watchTrades': True,
|
||||
'watchTradesForSymbols': False,
|
||||
'watchPositions': True,
|
||||
},
|
||||
'urls': {
|
||||
'api': {
|
||||
'ws': {
|
||||
'swap': 'wss://aftermath.finance/iperps-api/ccxt/stream',
|
||||
},
|
||||
},
|
||||
'test': {
|
||||
'ws': {
|
||||
'swap': 'wss://testnet.aftermath.finance/iperps-api/ccxt/stream',
|
||||
},
|
||||
},
|
||||
},
|
||||
'options': {
|
||||
'tradesLimit': 1000,
|
||||
'ordersLimit': 1000,
|
||||
'watchPositions': {
|
||||
'fetchPositionsSnapshot': True, # or False
|
||||
'awaitPositionsSnapshot': True, # whether to wait for the positions snapshot before providing updates
|
||||
},
|
||||
},
|
||||
'streaming': {
|
||||
'keepAlive': 59000,
|
||||
},
|
||||
'exceptions': {
|
||||
},
|
||||
})
|
||||
|
||||
async def watch_public(self, suffix, messageHash, message):
|
||||
url = self.urls['api']['ws']['swap'] + '/' + suffix
|
||||
return await self.watch(url, messageHash, self.json(message), messageHash, message)
|
||||
|
||||
async def watch_public_multiple(self, suffix, messageHashes, message):
|
||||
url = self.urls['api']['ws']['swap'] + '/' + suffix
|
||||
return await self.watch_multiple(url, messageHashes, self.json(message), messageHashes, message)
|
||||
|
||||
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
||||
"""
|
||||
watches information on multiple trades made in a market
|
||||
|
||||
https://testnet.aftermath.finance/docs/#/CCXT/service%3A%3Ahandlers%3A%3Accxt%3A%3Astream%3A%3Atrades
|
||||
|
||||
:param str symbol: unified market symbol of the market trades were made in
|
||||
:param int [since]: the earliest time in ms to fetch trades for
|
||||
:param int [limit]: the maximum number of trade structures to retrieve
|
||||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||||
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=trade-structure>`
|
||||
"""
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
topic = market['id'] + '@trade'
|
||||
request: dict = {
|
||||
'chId': market['id'],
|
||||
}
|
||||
message = self.extend(request, params)
|
||||
trades = await self.watch_public('trades', topic, message)
|
||||
if self.newUpdates:
|
||||
limit = trades.getLimit(market['symbol'], limit)
|
||||
return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
|
||||
|
||||
def handle_trade(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "amount": 0.1,
|
||||
# "cost": 0.1,
|
||||
# "datetime": "string",
|
||||
# "fee": null,
|
||||
# "id": "string",
|
||||
# "order": "string",
|
||||
# "price": 0.1,
|
||||
# "side": null,
|
||||
# "symbol": "string",
|
||||
# "takerOrMaker": null,
|
||||
# "timestamp": null,
|
||||
# "type": "string"
|
||||
# }
|
||||
#
|
||||
trade = self.parse_trade(message)
|
||||
symbol = trade['symbol']
|
||||
market = self.market(symbol)
|
||||
if not (symbol in self.trades):
|
||||
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
||||
stored = ArrayCache(limit)
|
||||
self.trades[symbol] = stored
|
||||
messageHash = market['id'] + '@trade'
|
||||
trades = self.trades[symbol]
|
||||
trades.append(trade)
|
||||
self.trades[symbol] = trades
|
||||
client.resolve(trades, messageHash)
|
||||
|
||||
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
||||
"""
|
||||
|
||||
https://testnet.aftermath.finance/docs/#/CCXT/service%3A%3Ahandlers%3A%3Accxt%3A%3Astream%3A%3Aorderbook
|
||||
|
||||
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
|
||||
: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)
|
||||
symbol = market['symbol']
|
||||
topic = market['id'] + '@orderbook'
|
||||
request: dict = {
|
||||
'chId': market['id'],
|
||||
}
|
||||
message = self.extend(request, params)
|
||||
orderbook = await self.watch_public('orderbook', topic, message)
|
||||
return orderbook.limit()
|
||||
|
||||
def handle_order_book(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "asks": [
|
||||
# []
|
||||
# ],
|
||||
# "bids": [
|
||||
# []
|
||||
# ],
|
||||
# "datetime": "string",
|
||||
# "nonce": 9007199254740991,
|
||||
# "symbol": "string",
|
||||
# "timestamp": 9007199254740991
|
||||
# }
|
||||
#
|
||||
symbol = self.safe_string(message, 'symbol')
|
||||
market = self.market(symbol)
|
||||
topic = market['id'] + '@orderbook'
|
||||
if not (symbol in self.orderbooks):
|
||||
defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
|
||||
subscription = client.subscriptions[topic]
|
||||
limit = self.safe_integer(subscription, 'limit', defaultLimit)
|
||||
self.orderbooks[symbol] = self.order_book({}, limit)
|
||||
subscription['limit'] = limit
|
||||
self.spawn(self.fetch_order_book_snapshot, client, message, subscription)
|
||||
else:
|
||||
orderbook = self.orderbooks[symbol]
|
||||
prevNonce = self.safe_integer(orderbook, 'nonce')
|
||||
nonce = self.safe_integer(message, 'nonce')
|
||||
if nonce == (prevNonce + 1):
|
||||
self.handle_order_book_message(client, message, orderbook)
|
||||
client.resolve(orderbook, topic)
|
||||
|
||||
async def fetch_order_book_snapshot(self, client, message, subscription):
|
||||
symbol = self.safe_string(message, 'symbol')
|
||||
market = self.market(symbol)
|
||||
messageHash = market['id'] + '@orderbook'
|
||||
try:
|
||||
defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
|
||||
limit = self.safe_integer(subscription, 'limit', defaultLimit)
|
||||
params = self.safe_dict(subscription, 'params')
|
||||
snapshot = await self.fetch_rest_order_book_safe(symbol, limit, params)
|
||||
if self.safe_value(self.orderbooks, symbol) is None:
|
||||
# if the orderbook is dropped before the snapshot is received
|
||||
return
|
||||
orderbook = self.orderbooks[symbol]
|
||||
orderbook.reset(snapshot)
|
||||
self.orderbooks[symbol] = orderbook
|
||||
client.resolve(orderbook, messageHash)
|
||||
except Exception as e:
|
||||
del client.subscriptions[messageHash]
|
||||
client.reject(e, messageHash)
|
||||
|
||||
def handle_order_book_message(self, client: Client, message, orderbook):
|
||||
self.handle_deltas(orderbook['asks'], self.safe_value(message, 'asks', []))
|
||||
self.handle_deltas(orderbook['bids'], self.safe_value(message, 'bids', []))
|
||||
timestamp = self.safe_integer(message, 'timestamp')
|
||||
nonce = self.safe_integer(message, 'nonce')
|
||||
orderbook['timestamp'] = timestamp
|
||||
orderbook['datetime'] = self.iso8601(timestamp)
|
||||
orderbook['nonce'] = nonce
|
||||
return orderbook
|
||||
|
||||
def handle_delta(self, bookside, delta):
|
||||
price = self.safe_float_2(delta, 'price', 0)
|
||||
amount = self.safe_float_2(delta, 'quantity', 1)
|
||||
bookside.store(price, amount)
|
||||
|
||||
def handle_deltas(self, bookside, deltas):
|
||||
for i in range(0, len(deltas)):
|
||||
self.handle_delta(bookside, deltas[i])
|
||||
|
||||
async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
|
||||
"""
|
||||
|
||||
https://testnet.aftermath.finance/docs/#/CCXT/service%3A%3Ahandlers%3A%3Accxt%3A%3Astream%3A%3Apositions
|
||||
|
||||
watch all open positions
|
||||
:param str[]|None symbols: list of unified market symbols
|
||||
:param int [since]: the earliest time in ms to fetch positions for
|
||||
:param int [limit]: the maximum number of position structures to retrieve
|
||||
:param dict params: extra parameters specific to the exchange API endpoint
|
||||
:param int [params.accountNumber]: account number to query orders for, required
|
||||
:returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
|
||||
"""
|
||||
await self.load_markets()
|
||||
messageHashes = []
|
||||
symbols = self.market_symbols(symbols)
|
||||
if not self.is_empty(symbols):
|
||||
for i in range(0, len(symbols)):
|
||||
symbol = symbols[i]
|
||||
messageHashes.append('positions::' + symbol)
|
||||
else:
|
||||
messageHashes.append('positions')
|
||||
accountNumber = self.safe_number(params, 'accountNumber')
|
||||
request = {
|
||||
'accountNumber': accountNumber,
|
||||
}
|
||||
message = self.extend(request, params)
|
||||
suffix = 'positions'
|
||||
url = self.urls['api']['ws']['swap'] + '/' + suffix
|
||||
client = self.client(url)
|
||||
self.set_positions_cache(client, symbols, params)
|
||||
fetchPositionsSnapshot = self.handle_option('watchPositions', 'fetchPositionsSnapshot', True)
|
||||
awaitPositionsSnapshot = self.handle_option('watchPositions', 'awaitPositionsSnapshot', True)
|
||||
if fetchPositionsSnapshot and awaitPositionsSnapshot and self.positions is None:
|
||||
snapshot = await client.future('fetchPositionsSnapshot')
|
||||
return self.filter_by_symbols_since_limit(snapshot, symbols, since, limit, True)
|
||||
newPositions = await self.watch_public_multiple(suffix, messageHashes, message)
|
||||
if self.newUpdates:
|
||||
return newPositions
|
||||
return self.filter_by_symbols_since_limit(self.positions, symbols, since, limit, True)
|
||||
|
||||
def set_positions_cache(self, client: Client, symbols: Strings = None, params: dict = {}):
|
||||
fetchPositionsSnapshot = self.handle_option('watchPositions', 'fetchPositionsSnapshot', False)
|
||||
if fetchPositionsSnapshot:
|
||||
messageHash = 'fetchPositionsSnapshot'
|
||||
if not (messageHash in client.futures):
|
||||
client.future(messageHash)
|
||||
self.spawn(self.load_positions_snapshot, client, messageHash, symbols, params)
|
||||
else:
|
||||
self.positions = ArrayCacheBySymbolBySide()
|
||||
|
||||
async def load_positions_snapshot(self, client, messageHash, symbols, params):
|
||||
positions = await self.fetch_positions(symbols, params)
|
||||
self.positions = ArrayCacheBySymbolBySide()
|
||||
cache = self.positions
|
||||
for i in range(0, len(positions)):
|
||||
position = positions[i]
|
||||
cache.append(position)
|
||||
# don't remove the future from the .futures cache
|
||||
future = client.futures[messageHash]
|
||||
future.resolve(cache)
|
||||
client.resolve(cache, 'positions')
|
||||
|
||||
def handle_positions(self, client, message):
|
||||
#
|
||||
# {
|
||||
# "collateral": 0.1,
|
||||
# "contractSize": 0.1,
|
||||
# "contracts": 0.1,
|
||||
# "datetime": "string",
|
||||
# "entryPrice": 0.1,
|
||||
# "id": "0x895037c09dd1025a136b5a5789c4ea2481176adf4c3b0c3521d5d2039bdfc3ba:123",
|
||||
# "initialMargin": 0.1,
|
||||
# "initialMarginPercentage": 0.1,
|
||||
# "leverage": 0.1,
|
||||
# "liquidationPrice": 0.1,
|
||||
# "maintenanceMargin": 0.1,
|
||||
# "maintenanceMarginPercentage": 0.1,
|
||||
# "marginMode": "cross",
|
||||
# "marginRatio": 0.1,
|
||||
# "notional": 0.1,
|
||||
# "side": "long",
|
||||
# "symbol": "BTC/USD:USDC",
|
||||
# "timestamp": 9007199254740991,
|
||||
# "unrealizedPnl": 0.1
|
||||
# }
|
||||
#
|
||||
if self.positions is None:
|
||||
self.positions = ArrayCacheBySymbolBySide()
|
||||
cache = self.positions
|
||||
symbol = self.safe_string(message, 'symbol')
|
||||
market = self.safe_market(symbol)
|
||||
position = self.parse_position(message, market)
|
||||
cache.append(position)
|
||||
messageHash = 'positions::' + market['symbol']
|
||||
client.resolve(position, messageHash)
|
||||
client.resolve([position], 'positions')
|
||||
|
||||
def handle_error_message(self, client: Client, message):
|
||||
#
|
||||
# User error: Expected Message::Text from client, got Ping(b\"\")
|
||||
#
|
||||
if isinstance(message, str):
|
||||
if message.find('error') >= 0:
|
||||
try:
|
||||
feedback = self.id + ' ' + message
|
||||
self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
|
||||
raise ExchangeError(message)
|
||||
except Exception as error:
|
||||
if isinstance(error, AuthenticationError):
|
||||
messageHash = 'authenticated'
|
||||
client.reject(error, messageHash)
|
||||
if messageHash in client.subscriptions:
|
||||
del client.subscriptions[messageHash]
|
||||
else:
|
||||
client.reject(error)
|
||||
return True
|
||||
return False
|
||||
|
||||
def handle_message(self, client: Client, message):
|
||||
if self.handle_error_message(client, message):
|
||||
return
|
||||
# methods: Dict = {
|
||||
# 'trade': self.handle_trade,
|
||||
# }
|
||||
if 'asks' in message:
|
||||
self.handle_order_book(client, message)
|
||||
elif 'notional' in message:
|
||||
self.handle_positions(client, message)
|
||||
else:
|
||||
self.handle_trade(client, message)
|
||||
716
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/alpaca.py
Normal file
716
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/alpaca.py
Normal file
@ -0,0 +1,716 @@
|
||||
# -*- 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
|
||||
from ccxt.base.types import Any, Bool, Int, Order, OrderBook, Str, Ticker, 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 AuthenticationError
|
||||
|
||||
|
||||
class alpaca(ccxt.async_support.alpaca):
|
||||
|
||||
def describe(self) -> Any:
|
||||
return self.deep_extend(super(alpaca, self).describe(), {
|
||||
'has': {
|
||||
'ws': True,
|
||||
'createOrderWithTakeProfitAndStopLossWs': False,
|
||||
'createReduceOnlyOrderWs': False,
|
||||
'createStopLossOrderWs': False,
|
||||
'createTakeProfitOrderWs': False,
|
||||
'fetchPositionForSymbolWs': False,
|
||||
'fetchPositionsForSymbolWs': False,
|
||||
'fetchPositionsWs': False,
|
||||
'fetchPositionWs': False,
|
||||
'unWatchPositions': False,
|
||||
'watchBalance': False,
|
||||
'watchLiquidations': False,
|
||||
'watchLiquidationsForSymbols': False,
|
||||
'watchMarkPrice': False,
|
||||
'watchMarkPrices': False,
|
||||
'watchMyLiquidations': False,
|
||||
'watchMyLiquidationsForSymbols': False,
|
||||
'watchMyTrades': True,
|
||||
'watchOHLCV': True,
|
||||
'watchOrderBook': True,
|
||||
'watchOrders': True,
|
||||
'watchPosition': False,
|
||||
'watchPositions': False,
|
||||
'watchTicker': True,
|
||||
'watchTickers': False, # for now
|
||||
'watchTrades': True,
|
||||
},
|
||||
'urls': {
|
||||
'api': {
|
||||
'ws': {
|
||||
'crypto': 'wss://stream.data.alpaca.markets/v1beta2/crypto',
|
||||
'trading': 'wss://api.alpaca.markets/stream',
|
||||
},
|
||||
},
|
||||
'test': {
|
||||
'ws': {
|
||||
'crypto': 'wss://stream.data.alpaca.markets/v1beta2/crypto',
|
||||
'trading': 'wss://paper-api.alpaca.markets/stream',
|
||||
},
|
||||
},
|
||||
},
|
||||
'options': {
|
||||
},
|
||||
'streaming': {},
|
||||
'exceptions': {
|
||||
'ws': {
|
||||
'exact': {
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
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.alpaca.markets/docs/real-time-crypto-pricing-data#quotes
|
||||
|
||||
: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>`
|
||||
"""
|
||||
url = self.urls['api']['ws']['crypto']
|
||||
await self.authenticate(url)
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
messageHash = 'ticker:' + market['symbol']
|
||||
request: dict = {
|
||||
'action': 'subscribe',
|
||||
'quotes': [market['id']],
|
||||
}
|
||||
return await self.watch(url, messageHash, self.extend(request, params), messageHash)
|
||||
|
||||
def handle_ticker(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "T": "q",
|
||||
# "S": "BTC/USDT",
|
||||
# "bp": 17394.44,
|
||||
# "bs": 0.021981,
|
||||
# "ap": 17397.99,
|
||||
# "as": 0.02,
|
||||
# "t": "2022-12-16T06:07:56.611063286Z"
|
||||
# ]
|
||||
#
|
||||
ticker = self.parse_ticker(message)
|
||||
symbol = ticker['symbol']
|
||||
messageHash = 'ticker:' + symbol
|
||||
self.tickers[symbol] = ticker
|
||||
client.resolve(self.tickers[symbol], messageHash)
|
||||
|
||||
def parse_ticker(self, ticker, market=None) -> Ticker:
|
||||
#
|
||||
# {
|
||||
# "T": "q",
|
||||
# "S": "BTC/USDT",
|
||||
# "bp": 17394.44,
|
||||
# "bs": 0.021981,
|
||||
# "ap": 17397.99,
|
||||
# "as": 0.02,
|
||||
# "t": "2022-12-16T06:07:56.611063286Z"
|
||||
# }
|
||||
#
|
||||
marketId = self.safe_string(ticker, 'S')
|
||||
datetime = self.safe_string(ticker, 't')
|
||||
return self.safe_ticker({
|
||||
'symbol': self.safe_symbol(marketId, market),
|
||||
'timestamp': self.parse8601(datetime),
|
||||
'datetime': datetime,
|
||||
'high': None,
|
||||
'low': None,
|
||||
'bid': self.safe_string(ticker, 'bp'),
|
||||
'bidVolume': self.safe_string(ticker, 'bs'),
|
||||
'ask': self.safe_string(ticker, 'ap'),
|
||||
'askVolume': self.safe_string(ticker, 'as'),
|
||||
'vwap': None,
|
||||
'open': None,
|
||||
'close': None,
|
||||
'last': None,
|
||||
'previousClose': None,
|
||||
'change': None,
|
||||
'percentage': None,
|
||||
'average': None,
|
||||
'baseVolume': None,
|
||||
'quoteVolume': None,
|
||||
'info': ticker,
|
||||
}, market)
|
||||
|
||||
async def watch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
||||
"""
|
||||
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
||||
|
||||
https://docs.alpaca.markets/docs/real-time-crypto-pricing-data#bars
|
||||
|
||||
: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
|
||||
"""
|
||||
url = self.urls['api']['ws']['crypto']
|
||||
await self.authenticate(url)
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
request: dict = {
|
||||
'action': 'subscribe',
|
||||
'bars': [market['id']],
|
||||
}
|
||||
messageHash = 'ohlcv:' + symbol
|
||||
ohlcv = await self.watch(url, messageHash, self.extend(request, params), messageHash)
|
||||
if self.newUpdates:
|
||||
limit = ohlcv.getLimit(symbol, limit)
|
||||
return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
|
||||
|
||||
def handle_ohlcv(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "T": "b",
|
||||
# "S": "BTC/USDT",
|
||||
# "o": 17416.39,
|
||||
# "h": 17424.82,
|
||||
# "l": 17416.39,
|
||||
# "c": 17424.82,
|
||||
# "v": 1.341054,
|
||||
# "t": "2022-12-16T06:53:00Z",
|
||||
# "n": 21,
|
||||
# "vw": 17421.9529234915
|
||||
# }
|
||||
#
|
||||
marketId = self.safe_string(message, 'S')
|
||||
symbol = self.safe_symbol(marketId)
|
||||
stored = self.safe_value(self.ohlcvs, symbol)
|
||||
if stored is None:
|
||||
limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
|
||||
stored = ArrayCacheByTimestamp(limit)
|
||||
self.ohlcvs[symbol] = stored
|
||||
parsed = self.parse_ohlcv(message)
|
||||
stored.append(parsed)
|
||||
messageHash = 'ohlcv:' + symbol
|
||||
client.resolve(stored, messageHash)
|
||||
|
||||
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.alpaca.markets/docs/real-time-crypto-pricing-data#orderbooks
|
||||
|
||||
: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
|
||||
"""
|
||||
url = self.urls['api']['ws']['crypto']
|
||||
await self.authenticate(url)
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
messageHash = 'orderbook' + ':' + symbol
|
||||
request: dict = {
|
||||
'action': 'subscribe',
|
||||
'orderbooks': [market['id']],
|
||||
}
|
||||
orderbook = await self.watch(url, messageHash, self.extend(request, params), messageHash)
|
||||
return orderbook.limit()
|
||||
|
||||
def handle_order_book(self, client: Client, message):
|
||||
#
|
||||
# snapshot
|
||||
# {
|
||||
# "T": "o",
|
||||
# "S": "BTC/USDT",
|
||||
# "t": "2022-12-16T06:35:31.585113205Z",
|
||||
# "b": [{
|
||||
# "p": 17394.37,
|
||||
# "s": 0.015499,
|
||||
# },
|
||||
# ...
|
||||
# ],
|
||||
# "a": [{
|
||||
# "p": 17398.8,
|
||||
# "s": 0.042919,
|
||||
# },
|
||||
# ...
|
||||
# ],
|
||||
# "r": True,
|
||||
# }
|
||||
#
|
||||
marketId = self.safe_string(message, 'S')
|
||||
symbol = self.safe_symbol(marketId)
|
||||
datetime = self.safe_string(message, 't')
|
||||
timestamp = self.parse8601(datetime)
|
||||
isSnapshot = self.safe_bool(message, 'r', False)
|
||||
if not (symbol in self.orderbooks):
|
||||
self.orderbooks[symbol] = self.order_book()
|
||||
orderbook = self.orderbooks[symbol]
|
||||
if isSnapshot:
|
||||
snapshot = self.parse_order_book(message, symbol, timestamp, 'b', 'a', 'p', 's')
|
||||
orderbook.reset(snapshot)
|
||||
else:
|
||||
asks = self.safe_list(message, 'a', [])
|
||||
bids = self.safe_list(message, 'b', [])
|
||||
self.handle_deltas(orderbook['asks'], asks)
|
||||
self.handle_deltas(orderbook['bids'], bids)
|
||||
orderbook['timestamp'] = timestamp
|
||||
orderbook['datetime'] = datetime
|
||||
messageHash = 'orderbook' + ':' + symbol
|
||||
self.orderbooks[symbol] = orderbook
|
||||
client.resolve(orderbook, messageHash)
|
||||
|
||||
def handle_delta(self, bookside, delta):
|
||||
bidAsk = self.parse_bid_ask(delta, 'p', 's')
|
||||
bookside.storeArray(bidAsk)
|
||||
|
||||
def handle_deltas(self, bookside, deltas):
|
||||
for i in range(0, len(deltas)):
|
||||
self.handle_delta(bookside, deltas[i])
|
||||
|
||||
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
||||
"""
|
||||
watches information on multiple trades made in a market
|
||||
|
||||
https://docs.alpaca.markets/docs/real-time-crypto-pricing-data#trades
|
||||
|
||||
:param str symbol: unified market symbol of the market trades were made in
|
||||
:param int [since]: the earliest time in ms to fetch orders for
|
||||
:param int [limit]: the maximum number of trade structures to retrieve
|
||||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||||
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=trade-structure>`
|
||||
"""
|
||||
url = self.urls['api']['ws']['crypto']
|
||||
await self.authenticate(url)
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
messageHash = 'trade:' + symbol
|
||||
request: dict = {
|
||||
'action': 'subscribe',
|
||||
'trades': [market['id']],
|
||||
}
|
||||
trades = await self.watch(url, messageHash, self.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_trades(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "T": "t",
|
||||
# "S": "BTC/USDT",
|
||||
# "p": 17408.8,
|
||||
# "s": 0.042919,
|
||||
# "t": "2022-12-16T06:43:18.327Z",
|
||||
# "i": 16585162,
|
||||
# "tks": "B"
|
||||
# ]
|
||||
#
|
||||
marketId = self.safe_string(message, 'S')
|
||||
symbol = self.safe_symbol(marketId)
|
||||
stored = self.safe_value(self.trades, symbol)
|
||||
if stored is None:
|
||||
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
||||
stored = ArrayCache(limit)
|
||||
self.trades[symbol] = stored
|
||||
parsed = self.parse_trade(message)
|
||||
stored.append(parsed)
|
||||
messageHash = 'trade' + ':' + symbol
|
||||
client.resolve(stored, messageHash)
|
||||
|
||||
async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
||||
"""
|
||||
watches information on multiple trades made by the user
|
||||
|
||||
https://docs.alpaca.markets/docs/websocket-streaming#trade-updates
|
||||
|
||||
:param str symbol: unified market symbol of the market trades were made in
|
||||
:param int [since]: the earliest time in ms to fetch trades for
|
||||
:param int [limit]: the maximum number of trade structures to retrieve
|
||||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||||
:param boolean [params.unifiedMargin]: use unified margin account
|
||||
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=trade-structure>`
|
||||
"""
|
||||
url = self.urls['api']['ws']['trading']
|
||||
await self.authenticate(url)
|
||||
messageHash = 'myTrades'
|
||||
await self.load_markets()
|
||||
if symbol is not None:
|
||||
symbol = self.symbol(symbol)
|
||||
messageHash += ':' + symbol
|
||||
request: dict = {
|
||||
'action': 'listen',
|
||||
'data': {
|
||||
'streams': ['trade_updates'],
|
||||
},
|
||||
}
|
||||
trades = await self.watch(url, messageHash, self.extend(request, params), messageHash)
|
||||
if self.newUpdates:
|
||||
limit = trades.getLimit(symbol, limit)
|
||||
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
|
||||
|
||||
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
|
||||
: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>`
|
||||
"""
|
||||
url = self.urls['api']['ws']['trading']
|
||||
await self.authenticate(url)
|
||||
await self.load_markets()
|
||||
messageHash = 'orders'
|
||||
if symbol is not None:
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
messageHash = 'orders:' + symbol
|
||||
request: dict = {
|
||||
'action': 'listen',
|
||||
'data': {
|
||||
'streams': ['trade_updates'],
|
||||
},
|
||||
}
|
||||
orders = await self.watch(url, messageHash, self.extend(request, params), messageHash)
|
||||
if self.newUpdates:
|
||||
limit = orders.getLimit(symbol, limit)
|
||||
return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True)
|
||||
|
||||
def handle_trade_update(self, client: Client, message):
|
||||
self.handle_order(client, message)
|
||||
self.handle_my_trade(client, message)
|
||||
|
||||
def handle_order(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "stream": "trade_updates",
|
||||
# "data": {
|
||||
# "event": "new",
|
||||
# "timestamp": "2022-12-16T07:28:51.67621869Z",
|
||||
# "order": {
|
||||
# "id": "c2470331-8993-4051-bf5d-428d5bdc9a48",
|
||||
# "client_order_id": "0f1f3764-107a-4d09-8b9a-d75a11738f5c",
|
||||
# "created_at": "2022-12-16T02:28:51.673531798-05:00",
|
||||
# "updated_at": "2022-12-16T02:28:51.678736847-05:00",
|
||||
# "submitted_at": "2022-12-16T02:28:51.673015558-05:00",
|
||||
# "filled_at": null,
|
||||
# "expired_at": null,
|
||||
# "cancel_requested_at": null,
|
||||
# "canceled_at": null,
|
||||
# "failed_at": null,
|
||||
# "replaced_at": null,
|
||||
# "replaced_by": null,
|
||||
# "replaces": null,
|
||||
# "asset_id": "276e2673-764b-4ab6-a611-caf665ca6340",
|
||||
# "symbol": "BTC/USD",
|
||||
# "asset_class": "crypto",
|
||||
# "notional": null,
|
||||
# "qty": "0.01",
|
||||
# "filled_qty": "0",
|
||||
# "filled_avg_price": null,
|
||||
# "order_class": '',
|
||||
# "order_type": "market",
|
||||
# "type": "market",
|
||||
# "side": "buy",
|
||||
# "time_in_force": "gtc",
|
||||
# "limit_price": null,
|
||||
# "stop_price": null,
|
||||
# "status": "new",
|
||||
# "extended_hours": False,
|
||||
# "legs": null,
|
||||
# "trail_percent": null,
|
||||
# "trail_price": null,
|
||||
# "hwm": null
|
||||
# },
|
||||
# "execution_id": "5f781a30-b9a3-4c86-b466-2175850cf340"
|
||||
# }
|
||||
# }
|
||||
#
|
||||
data = self.safe_value(message, 'data', {})
|
||||
rawOrder = self.safe_value(data, 'order', {})
|
||||
if self.orders is None:
|
||||
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
|
||||
self.orders = ArrayCacheBySymbolById(limit)
|
||||
orders = self.orders
|
||||
order = self.parse_order(rawOrder)
|
||||
orders.append(order)
|
||||
messageHash = 'orders'
|
||||
client.resolve(orders, messageHash)
|
||||
messageHash = 'orders:' + order['symbol']
|
||||
client.resolve(orders, messageHash)
|
||||
|
||||
def handle_my_trade(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "stream": "trade_updates",
|
||||
# "data": {
|
||||
# "event": "new",
|
||||
# "timestamp": "2022-12-16T07:28:51.67621869Z",
|
||||
# "order": {
|
||||
# "id": "c2470331-8993-4051-bf5d-428d5bdc9a48",
|
||||
# "client_order_id": "0f1f3764-107a-4d09-8b9a-d75a11738f5c",
|
||||
# "created_at": "2022-12-16T02:28:51.673531798-05:00",
|
||||
# "updated_at": "2022-12-16T02:28:51.678736847-05:00",
|
||||
# "submitted_at": "2022-12-16T02:28:51.673015558-05:00",
|
||||
# "filled_at": null,
|
||||
# "expired_at": null,
|
||||
# "cancel_requested_at": null,
|
||||
# "canceled_at": null,
|
||||
# "failed_at": null,
|
||||
# "replaced_at": null,
|
||||
# "replaced_by": null,
|
||||
# "replaces": null,
|
||||
# "asset_id": "276e2673-764b-4ab6-a611-caf665ca6340",
|
||||
# "symbol": "BTC/USD",
|
||||
# "asset_class": "crypto",
|
||||
# "notional": null,
|
||||
# "qty": "0.01",
|
||||
# "filled_qty": "0",
|
||||
# "filled_avg_price": null,
|
||||
# "order_class": '',
|
||||
# "order_type": "market",
|
||||
# "type": "market",
|
||||
# "side": "buy",
|
||||
# "time_in_force": "gtc",
|
||||
# "limit_price": null,
|
||||
# "stop_price": null,
|
||||
# "status": "new",
|
||||
# "extended_hours": False,
|
||||
# "legs": null,
|
||||
# "trail_percent": null,
|
||||
# "trail_price": null,
|
||||
# "hwm": null
|
||||
# },
|
||||
# "execution_id": "5f781a30-b9a3-4c86-b466-2175850cf340"
|
||||
# }
|
||||
# }
|
||||
#
|
||||
data = self.safe_value(message, 'data', {})
|
||||
event = self.safe_string(data, 'event')
|
||||
if event != 'fill' and event != 'partial_fill':
|
||||
return
|
||||
rawOrder = self.safe_value(data, 'order', {})
|
||||
myTrades = self.myTrades
|
||||
if myTrades is None:
|
||||
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
||||
myTrades = ArrayCacheBySymbolById(limit)
|
||||
trade = self.parse_my_trade(rawOrder)
|
||||
myTrades.append(trade)
|
||||
messageHash = 'myTrades:' + trade['symbol']
|
||||
client.resolve(myTrades, messageHash)
|
||||
messageHash = 'myTrades'
|
||||
client.resolve(myTrades, messageHash)
|
||||
|
||||
def parse_my_trade(self, trade, market=None):
|
||||
#
|
||||
# {
|
||||
# "id": "c2470331-8993-4051-bf5d-428d5bdc9a48",
|
||||
# "client_order_id": "0f1f3764-107a-4d09-8b9a-d75a11738f5c",
|
||||
# "created_at": "2022-12-16T02:28:51.673531798-05:00",
|
||||
# "updated_at": "2022-12-16T02:28:51.678736847-05:00",
|
||||
# "submitted_at": "2022-12-16T02:28:51.673015558-05:00",
|
||||
# "filled_at": null,
|
||||
# "expired_at": null,
|
||||
# "cancel_requested_at": null,
|
||||
# "canceled_at": null,
|
||||
# "failed_at": null,
|
||||
# "replaced_at": null,
|
||||
# "replaced_by": null,
|
||||
# "replaces": null,
|
||||
# "asset_id": "276e2673-764b-4ab6-a611-caf665ca6340",
|
||||
# "symbol": "BTC/USD",
|
||||
# "asset_class": "crypto",
|
||||
# "notional": null,
|
||||
# "qty": "0.01",
|
||||
# "filled_qty": "0",
|
||||
# "filled_avg_price": null,
|
||||
# "order_class": '',
|
||||
# "order_type": "market",
|
||||
# "type": "market",
|
||||
# "side": "buy",
|
||||
# "time_in_force": "gtc",
|
||||
# "limit_price": null,
|
||||
# "stop_price": null,
|
||||
# "status": "new",
|
||||
# "extended_hours": False,
|
||||
# "legs": null,
|
||||
# "trail_percent": null,
|
||||
# "trail_price": null,
|
||||
# "hwm": null
|
||||
# }
|
||||
#
|
||||
marketId = self.safe_string(trade, 'symbol')
|
||||
datetime = self.safe_string(trade, 'filled_at')
|
||||
type = self.safe_string(trade, 'type')
|
||||
if type.find('limit') >= 0:
|
||||
# might be limit or stop-limit
|
||||
type = 'limit'
|
||||
return self.safe_trade({
|
||||
'id': self.safe_string(trade, 'i'),
|
||||
'info': trade,
|
||||
'timestamp': self.parse8601(datetime),
|
||||
'datetime': datetime,
|
||||
'symbol': self.safe_symbol(marketId, None, '/'),
|
||||
'order': self.safe_string(trade, 'id'),
|
||||
'type': type,
|
||||
'side': self.safe_string(trade, 'side'),
|
||||
'takerOrMaker': 'taker' if (type == 'market') else 'maker',
|
||||
'price': self.safe_string(trade, 'filled_avg_price'),
|
||||
'amount': self.safe_string(trade, 'filled_qty'),
|
||||
'cost': None,
|
||||
'fee': None,
|
||||
}, market)
|
||||
|
||||
async def authenticate(self, url, params={}):
|
||||
self.check_required_credentials()
|
||||
messageHash = 'authenticated'
|
||||
client = self.client(url)
|
||||
future = client.reusableFuture(messageHash)
|
||||
authenticated = self.safe_value(client.subscriptions, messageHash)
|
||||
if authenticated is None:
|
||||
request = {
|
||||
'action': 'auth',
|
||||
'key': self.apiKey,
|
||||
'secret': self.secret,
|
||||
}
|
||||
if url == self.urls['api']['ws']['trading']:
|
||||
# self auth request is being deprecated in test environment
|
||||
request = {
|
||||
'action': 'authenticate',
|
||||
'data': {
|
||||
'key_id': self.apiKey,
|
||||
'secret_key': self.secret,
|
||||
},
|
||||
}
|
||||
self.watch(url, messageHash, request, messageHash, future)
|
||||
return await future
|
||||
|
||||
def handle_error_message(self, client: Client, message) -> Bool:
|
||||
#
|
||||
# {
|
||||
# "T": "error",
|
||||
# "code": 400,
|
||||
# "msg": "invalid syntax"
|
||||
# }
|
||||
#
|
||||
code = self.safe_string(message, 'code')
|
||||
msg = self.safe_value(message, 'msg', {})
|
||||
raise ExchangeError(self.id + ' code: ' + code + ' message: ' + msg)
|
||||
|
||||
def handle_connected(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "T": "success",
|
||||
# "msg": "connected"
|
||||
# }
|
||||
#
|
||||
return message
|
||||
|
||||
def handle_crypto_message(self, client: Client, message):
|
||||
for i in range(0, len(message)):
|
||||
data = message[i]
|
||||
T = self.safe_string(data, 'T')
|
||||
msg = self.safe_string(data, 'msg')
|
||||
if T == 'subscription':
|
||||
self.handle_subscription(client, data)
|
||||
return
|
||||
if T == 'success' and msg == 'connected':
|
||||
self.handle_connected(client, data)
|
||||
return
|
||||
if T == 'success' and msg == 'authenticated':
|
||||
self.handle_authenticate(client, data)
|
||||
return
|
||||
methods: dict = {
|
||||
'error': self.handle_error_message,
|
||||
'b': self.handle_ohlcv,
|
||||
'q': self.handle_ticker,
|
||||
't': self.handle_trades,
|
||||
'o': self.handle_order_book,
|
||||
}
|
||||
method = self.safe_value(methods, T)
|
||||
if method is not None:
|
||||
method(client, data)
|
||||
|
||||
def handle_trading_message(self, client: Client, message):
|
||||
stream = self.safe_string(message, 'stream')
|
||||
methods: dict = {
|
||||
'authorization': self.handle_authenticate,
|
||||
'listening': self.handle_subscription,
|
||||
'trade_updates': self.handle_trade_update,
|
||||
}
|
||||
method = self.safe_value(methods, stream)
|
||||
if method is not None:
|
||||
method(client, message)
|
||||
|
||||
def handle_message(self, client: Client, message):
|
||||
if isinstance(message, list):
|
||||
self.handle_crypto_message(client, message)
|
||||
return
|
||||
self.handle_trading_message(client, message)
|
||||
|
||||
def handle_authenticate(self, client: Client, message):
|
||||
#
|
||||
# crypto
|
||||
# {
|
||||
# "T": "success",
|
||||
# "msg": "connected"
|
||||
# ]
|
||||
#
|
||||
# trading
|
||||
# {
|
||||
# "stream": "authorization",
|
||||
# "data": {
|
||||
# "status": "authorized",
|
||||
# "action": "authenticate"
|
||||
# }
|
||||
# }
|
||||
# error
|
||||
# {
|
||||
# "stream": "authorization",
|
||||
# "data": {
|
||||
# "action": "authenticate",
|
||||
# "message": "access key verification failed",
|
||||
# "status": "unauthorized"
|
||||
# }
|
||||
# }
|
||||
#
|
||||
T = self.safe_string(message, 'T')
|
||||
data = self.safe_value(message, 'data', {})
|
||||
status = self.safe_string(data, 'status')
|
||||
if T == 'success' or status == 'authorized':
|
||||
promise = client.futures['authenticated']
|
||||
promise.resolve(message)
|
||||
return
|
||||
raise AuthenticationError(self.id + ' failed to authenticate.')
|
||||
|
||||
def handle_subscription(self, client: Client, message):
|
||||
#
|
||||
# crypto
|
||||
# {
|
||||
# "T": "subscription",
|
||||
# "trades": [],
|
||||
# "quotes": ["BTC/USDT"],
|
||||
# "orderbooks": [],
|
||||
# "bars": [],
|
||||
# "updatedBars": [],
|
||||
# "dailyBars": []
|
||||
# }
|
||||
# trading
|
||||
# {
|
||||
# "stream": "listening",
|
||||
# "data": {
|
||||
# "streams": ["trade_updates"]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
return message
|
||||
1040
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/apex.py
Normal file
1040
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/apex.py
Normal file
File diff suppressed because it is too large
Load Diff
686
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/arkham.py
Normal file
686
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/arkham.py
Normal file
@ -0,0 +1,686 @@
|
||||
# -*- 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, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
|
||||
import hashlib
|
||||
from ccxt.base.types import Any, Balances, Bool, Int, Order, OrderBook, Position, Str, Strings, Ticker, Trade
|
||||
from ccxt.async_support.base.ws.client import Client
|
||||
from typing import List
|
||||
from ccxt.base.errors import ExchangeError
|
||||
|
||||
|
||||
class arkham(ccxt.async_support.arkham):
|
||||
|
||||
def describe(self) -> Any:
|
||||
return self.deep_extend(super(arkham, self).describe(), {
|
||||
'has': {
|
||||
'ws': True,
|
||||
'watchTrades': True,
|
||||
'watchTradesForSymbols': False,
|
||||
'watchOrderBook': True,
|
||||
'watchOrderBookForSymbols': False,
|
||||
'watchOHLCV': True,
|
||||
'watchOHLCVForSymbols': False,
|
||||
'watchOrders': True,
|
||||
'watchMyTrades': False,
|
||||
'watchTicker': True,
|
||||
'watchTickers': False,
|
||||
'watchBalance': True,
|
||||
},
|
||||
'urls': {
|
||||
'api': {
|
||||
'ws': 'wss://arkm.com/ws',
|
||||
},
|
||||
},
|
||||
'options': {
|
||||
'watchOrderBook': {
|
||||
'depth': 100, # 5, 10, 20, 50, 100
|
||||
'interval': 500, # 100, 200, 500, 1000
|
||||
},
|
||||
},
|
||||
'streaming': {
|
||||
'keepAlive': 300000, # 5 minutes
|
||||
},
|
||||
})
|
||||
|
||||
def handle_message(self, client: Client, message):
|
||||
#
|
||||
# confirmation
|
||||
#
|
||||
# {channel: 'confirmations', confirmationId: 'myCustomId-123'}
|
||||
if self.handle_error_message(client, message):
|
||||
return
|
||||
methods: dict = {
|
||||
'ticker': self.handle_ticker,
|
||||
'candles': self.handle_ohlcv,
|
||||
'l2_updates': self.handle_order_book,
|
||||
'trades': self.handle_trades,
|
||||
'balances': self.handle_balance,
|
||||
'positions': self.handle_positions,
|
||||
'order_statuses': self.handle_order,
|
||||
'trigger_orders': self.handle_order,
|
||||
# 'confirmations': self.handle_ticker,
|
||||
}
|
||||
channel = self.safe_string(message, 'channel')
|
||||
if channel == 'confirmations':
|
||||
return
|
||||
# type = self.safe_string(message, 'type')
|
||||
# if type != 'update' and type != 'snapshot':
|
||||
# debugger
|
||||
# }
|
||||
method = self.safe_value(methods, channel)
|
||||
if method is not None:
|
||||
method(client, message)
|
||||
|
||||
async def subscribe(self, messageHash: str, rawChannel: str, params: dict) -> Any:
|
||||
subscriptionHash = messageHash
|
||||
request: dict = {
|
||||
'args': {
|
||||
'channel': rawChannel,
|
||||
'params': params,
|
||||
},
|
||||
'confirmationId': self.uuid(),
|
||||
'method': 'subscribe',
|
||||
}
|
||||
return await self.watch(self.urls['api']['ws'], messageHash, request, subscriptionHash)
|
||||
|
||||
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://arkm.com/docs#stream/ticker
|
||||
|
||||
: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()
|
||||
market = self.market(symbol)
|
||||
requestArg = {
|
||||
'symbol': market['id'],
|
||||
}
|
||||
messageHash = 'ticker::' + market['symbol']
|
||||
return await self.subscribe(messageHash, 'ticker', self.extend(params, requestArg))
|
||||
|
||||
def handle_ticker(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# channel: 'ticker',
|
||||
# type: 'update',
|
||||
# data: {
|
||||
# symbol: 'BTC_USDT',
|
||||
# baseSymbol: 'BTC',
|
||||
# quoteSymbol: 'USDT',
|
||||
# price: '118962.74',
|
||||
# price24hAgo: '118780.42',
|
||||
# high24h: '120327.96',
|
||||
# low24h: '118217.28',
|
||||
# volume24h: '32.89729',
|
||||
# quoteVolume24h: '3924438.7146048',
|
||||
# markPrice: '0',
|
||||
# indexPrice: '118963.080293502',
|
||||
# fundingRate: '0',
|
||||
# nextFundingRate: '0',
|
||||
# nextFundingTime: 0,
|
||||
# productType: 'spot',
|
||||
# openInterest: '0',
|
||||
# indexCurrency: 'USDT',
|
||||
# usdVolume24h: '3924438.7146048',
|
||||
# openInterestUSD: '0'
|
||||
# }
|
||||
# }
|
||||
#
|
||||
data = self.safe_dict(message, 'data', {})
|
||||
marketId = self.safe_string(data, 'symbol')
|
||||
market = self.safe_market(marketId, None)
|
||||
symbol = market['symbol']
|
||||
ticker = self.parse_ws_ticker(data, market)
|
||||
self.tickers[symbol] = ticker
|
||||
client.resolve(ticker, 'ticker::' + symbol)
|
||||
# if self.safe_string(message, 'dataType') == 'all@ticker':
|
||||
# client.resolve(ticker, self.getMessageHash('ticker'))
|
||||
# }
|
||||
|
||||
def parse_ws_ticker(self, message, market=None):
|
||||
# same dict api
|
||||
return self.parse_ticker(message, market)
|
||||
|
||||
async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
||||
"""
|
||||
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
||||
|
||||
https://arkm.com/docs#stream/candles
|
||||
|
||||
: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()
|
||||
market = self.market(symbol)
|
||||
rawTimeframe = self.safe_string(self.timeframes, timeframe, timeframe)
|
||||
requestArg = {
|
||||
'symbol': market['id'],
|
||||
'duration': rawTimeframe,
|
||||
}
|
||||
messageHash = 'ohlcv::' + market['symbol'] + '::' + rawTimeframe
|
||||
result = await self.subscribe(messageHash, 'candles', self.extend(requestArg, params))
|
||||
ohlcv = result
|
||||
if self.newUpdates:
|
||||
limit = ohlcv.getLimit(market['symbol'], limit)
|
||||
return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
|
||||
|
||||
def handle_ohlcv(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# channel: 'candles',
|
||||
# type: 'update',
|
||||
# data: {
|
||||
# symbol: 'BTC_USDT',
|
||||
# time: '1755076380000000',
|
||||
# duration: 60000000,
|
||||
# open: '120073.01',
|
||||
# high: '120073.01',
|
||||
# low: '120073.01',
|
||||
# close: '120073.01',
|
||||
# volume: '0',
|
||||
# quoteVolume: '0'
|
||||
# }
|
||||
# }
|
||||
#
|
||||
data = self.safe_dict(message, 'data', {})
|
||||
marketId = self.safe_string(data, 'symbol')
|
||||
market = self.safe_market(marketId, None)
|
||||
symbol = market['symbol']
|
||||
duration = self.safe_integer(data, 'duration')
|
||||
timeframe = self.findTimeframeByDuration(duration)
|
||||
messageHash = 'ohlcv::' + symbol + '::' + timeframe
|
||||
self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
|
||||
if not (timeframe in self.ohlcvs[symbol]):
|
||||
limit = self.handle_option('watchOHLCV', 'limit', 1000)
|
||||
self.ohlcvs[symbol][timeframe] = ArrayCacheByTimestamp(limit)
|
||||
stored = self.ohlcvs[symbol][timeframe]
|
||||
parsed = self.parse_ws_ohlcv(data, market)
|
||||
stored.append(parsed)
|
||||
client.resolve(stored, messageHash)
|
||||
return message
|
||||
|
||||
def parse_ws_ohlcv(self, ohlcv, market=None) -> list:
|
||||
# same api
|
||||
return self.parse_ohlcv(ohlcv, market)
|
||||
|
||||
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://arkm.com/docs#stream/l2_updates
|
||||
|
||||
: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)
|
||||
requestArg = {
|
||||
'symbol': market['id'],
|
||||
'snapshot': True,
|
||||
}
|
||||
messageHash = 'orderBook::' + market['symbol']
|
||||
orderbook = await self.subscribe(messageHash, 'l2_updates', self.extend(requestArg, params))
|
||||
return orderbook.limit()
|
||||
|
||||
def handle_order_book(self, client: Client, message):
|
||||
#
|
||||
# snapshot:
|
||||
#
|
||||
# {
|
||||
# channel: 'l2_updates',
|
||||
# type: 'snapshot',
|
||||
# data: {
|
||||
# symbol: 'BTC_USDT',
|
||||
# group: '0.01',
|
||||
# asks: [ [Object], [Object], ...],
|
||||
# bids: [ [Object], [Object], ...],
|
||||
# lastTime: 1755115180608299
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# update:
|
||||
#
|
||||
# {
|
||||
# channel: "l2_updates",
|
||||
# type: "update",
|
||||
# data: {
|
||||
# symbol: "BTC_USDT",
|
||||
# group: "0.01",
|
||||
# side: "sell",
|
||||
# size: "0.05295",
|
||||
# price: "122722.76",
|
||||
# revisionId: 2455511217,
|
||||
# time: 1755115736475207,
|
||||
# }
|
||||
# }
|
||||
#
|
||||
data = self.safe_dict(message, 'data')
|
||||
type = self.safe_string(message, 'type')
|
||||
marketId = self.safe_string(data, 'symbol')
|
||||
market = self.safe_market(marketId)
|
||||
symbol = market['symbol']
|
||||
messageHash = 'orderBook::' + symbol
|
||||
if not (symbol in self.orderbooks):
|
||||
ob = self.order_book({})
|
||||
ob['symbol'] = symbol
|
||||
self.orderbooks[symbol] = ob
|
||||
orderbook = self.orderbooks[symbol]
|
||||
if type == 'snapshot':
|
||||
timestamp = self.safe_integer_product(data, 'lastTime', 0.001)
|
||||
parsedOrderBook = self.parse_order_book(data, symbol, timestamp, 'bids', 'asks', 'price', 'size')
|
||||
orderbook.reset(parsedOrderBook)
|
||||
elif type == 'update':
|
||||
timestamp = self.safe_integer_product(data, 'time', 0.001)
|
||||
side = self.safe_string(data, 'side')
|
||||
bookside = orderbook['bids'] if (side == 'buy') else orderbook['asks']
|
||||
self.handle_delta(bookside, data)
|
||||
orderbook['timestamp'] = timestamp
|
||||
orderbook['datetime'] = self.iso8601(timestamp)
|
||||
self.orderbooks[symbol] = orderbook
|
||||
client.resolve(self.orderbooks[symbol], messageHash)
|
||||
|
||||
def handle_delta(self, bookside, delta):
|
||||
bidAsk = self.parse_bid_ask(delta, 'price', 'size')
|
||||
bookside.storeArray(bidAsk)
|
||||
|
||||
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
||||
"""
|
||||
watches information on multiple trades made in a market
|
||||
|
||||
https://arkm.com/docs#stream/trades
|
||||
|
||||
:param str symbol: unified market symbol of the market trades were made in
|
||||
:param int [since]: the earliest time in ms to fetch orders for
|
||||
:param int [limit]: the maximum number of trade structures to retrieve
|
||||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||||
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=trade-structure>`
|
||||
"""
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
requestArg = {
|
||||
'symbol': market['id'],
|
||||
}
|
||||
messageHash = 'trade::' + market['symbol']
|
||||
trades = await self.subscribe(messageHash, 'trades', self.extend(requestArg, params))
|
||||
if self.newUpdates:
|
||||
limit = trades.getLimit(market['symbol'], limit)
|
||||
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
|
||||
|
||||
def handle_trades(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# channel: 'trades',
|
||||
# type: 'update',
|
||||
# data: {
|
||||
# symbol: 'BTC_USDT',
|
||||
# revisionId: 2643896903,
|
||||
# size: '0.00261',
|
||||
# price: '118273.2',
|
||||
# takerSide: 'buy',
|
||||
# time: 1755200320146389
|
||||
# }
|
||||
# }
|
||||
#
|
||||
data = self.safe_dict(message, 'data')
|
||||
marketId = self.safe_string(data, 'symbol')
|
||||
symbol = self.safe_symbol(marketId)
|
||||
if not (symbol in self.trades):
|
||||
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
||||
self.trades[symbol] = ArrayCache(limit)
|
||||
parsed = self.parse_ws_trade(data)
|
||||
stored = self.trades[symbol]
|
||||
stored.append(parsed)
|
||||
client.resolve(stored, 'trade::' + symbol)
|
||||
|
||||
def parse_ws_trade(self, trade, market=None):
|
||||
# same api
|
||||
return self.parse_trade(trade, market)
|
||||
|
||||
async def authenticate(self, params={}):
|
||||
self.check_required_credentials()
|
||||
expires = (self.milliseconds() + self.safe_integer(self.options, 'requestExpiration', 5000)) * 1000 # need macroseconds
|
||||
wsOptions: dict = self.safe_dict(self.options, 'ws', {})
|
||||
authenticated = self.safe_string(wsOptions, 'token')
|
||||
if authenticated is None:
|
||||
method = 'GET'
|
||||
bodyStr = ''
|
||||
path = 'ws'
|
||||
payload = self.apiKey + str(expires) + method.upper() + '/' + path + bodyStr
|
||||
decodedSecret = self.base64_to_binary(self.secret)
|
||||
signature = self.hmac(self.encode(payload), decodedSecret, hashlib.sha256, 'base64')
|
||||
defaultOptions: dict = {
|
||||
'ws': {
|
||||
'options': {
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Arkham-Api-Key': self.apiKey,
|
||||
'Arkham-Expires': str(expires),
|
||||
'Arkham-Signature': signature,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
self.extend_exchange_options(defaultOptions)
|
||||
self.client(self.urls['api']['ws'])
|
||||
|
||||
async def watch_balance(self, params={}) -> Balances:
|
||||
"""
|
||||
watch balance and get the amount of funds available for trading or funds locked in orders
|
||||
|
||||
https://arkm.com/docs#stream/balances
|
||||
|
||||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||||
:param str [params.type]: spot or contract if not provided self.options['defaultType'] is used
|
||||
:returns dict: a `balance structure <https://docs.ccxt.com/?id=balance-structure>`
|
||||
"""
|
||||
await self.authenticate()
|
||||
await self.load_markets()
|
||||
requestArg = {
|
||||
'snapshot': True,
|
||||
}
|
||||
messageHash = 'balances'
|
||||
result = await self.subscribe(messageHash, 'balances', self.extend(requestArg, params))
|
||||
return result
|
||||
|
||||
def handle_balance(self, client: Client, message):
|
||||
#
|
||||
# snapshot:
|
||||
#
|
||||
# {
|
||||
# channel: 'balances',
|
||||
# type: 'snapshot',
|
||||
# data: [
|
||||
# {
|
||||
# subaccountId: 0,
|
||||
# symbol: 'USDT',
|
||||
# balance: '7.035335375',
|
||||
# free: '7.035335375',
|
||||
# priceUSDT: '1',
|
||||
# balanceUSDT: '7.035335375',
|
||||
# freeUSDT: '7.035335375',
|
||||
# lastUpdateReason: 'withdrawalFee',
|
||||
# lastUpdateTime: '1753905990432678',
|
||||
# lastUpdateId: 250483404,
|
||||
# lastUpdateAmount: '-2'
|
||||
# },
|
||||
# {
|
||||
# subaccountId: 0,
|
||||
# symbol: 'SOL',
|
||||
# balance: '0.03',
|
||||
# free: '0.03',
|
||||
# priceUSDT: '197.37823276',
|
||||
# balanceUSDT: '5.921346982',
|
||||
# freeUSDT: '5.921346982',
|
||||
# lastUpdateReason: 'orderFill',
|
||||
# lastUpdateTime: '1753777760560164',
|
||||
# lastUpdateId: 248588190,
|
||||
# lastUpdateAmount: '0.03'
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
#
|
||||
# update:
|
||||
#
|
||||
# {
|
||||
# channel: 'balances',
|
||||
# type: 'update',
|
||||
# data: {
|
||||
# subaccountId: 0,
|
||||
# symbol: 'USDT',
|
||||
# balance: '7.028357615',
|
||||
# free: '7.028357615',
|
||||
# priceUSDT: '1',
|
||||
# balanceUSDT: '7.028357615',
|
||||
# freeUSDT: '7.028357615',
|
||||
# lastUpdateReason: 'tradingFee',
|
||||
# lastUpdateTime: '1755240882544056',
|
||||
# lastUpdateId: 2697860787,
|
||||
# lastUpdateAmount: '-0.00697776'
|
||||
# }
|
||||
# }
|
||||
#
|
||||
type = self.safe_string(message, 'type')
|
||||
parsed = {}
|
||||
if type == 'snapshot':
|
||||
# response same api
|
||||
data = self.safe_list(message, 'data')
|
||||
parsed = self.parse_ws_balance(data)
|
||||
parsed['info'] = message
|
||||
self.balance = parsed
|
||||
else:
|
||||
data = self.safe_dict(message, 'data')
|
||||
balancesArray = [data]
|
||||
parsed = self.parse_ws_balance(balancesArray)
|
||||
currencyId = self.safe_string(data, 'symbol')
|
||||
code = self.safe_currency_code(currencyId)
|
||||
self.balance[code] = parsed[code]
|
||||
messageHash = 'balances'
|
||||
client.resolve(self.safe_balance(self.balance), messageHash)
|
||||
|
||||
def parse_ws_balance(self, balance):
|
||||
# same api
|
||||
return self.parse_balance(balance)
|
||||
|
||||
async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
|
||||
"""
|
||||
|
||||
https://arkm.com/docs#stream/positions
|
||||
|
||||
watch all open positions
|
||||
:param str[] [symbols]: list of unified market symbols
|
||||
:param int [since]: the earliest time in ms to fetch positions for
|
||||
:param int [limit]: the maximum number of positions to retrieve
|
||||
:param dict params: extra parameters specific to the exchange API endpoint
|
||||
:returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
|
||||
"""
|
||||
await self.authenticate()
|
||||
await self.load_markets()
|
||||
messageHash = 'positions'
|
||||
if not self.is_empty(symbols):
|
||||
symbols = self.market_symbols(symbols)
|
||||
messageHash += '::' + ','.join(symbols)
|
||||
self.positions = ArrayCacheBySymbolBySide()
|
||||
requestArg = {
|
||||
'snapshot': False, # no need for initial snapshot, it's done in REST api
|
||||
}
|
||||
newPositions = await self.subscribe(messageHash, 'positions', self.extend(requestArg, params))
|
||||
if self.newUpdates:
|
||||
return newPositions
|
||||
return self.filter_by_symbols_since_limit(self.positions, symbols, since, limit, True)
|
||||
|
||||
def handle_positions(self, client, message):
|
||||
#
|
||||
# snapshot:
|
||||
#
|
||||
# {
|
||||
# channel: 'positions',
|
||||
# type: 'snapshot',
|
||||
# data: [
|
||||
# {
|
||||
# subaccountId: 0,
|
||||
# symbol: 'SOL_USDT_PERP',
|
||||
# base: '0.059',
|
||||
# quote: '-11.50618',
|
||||
# openBuySize: '0',
|
||||
# openSellSize: '0',
|
||||
# openBuyNotional: '0',
|
||||
# openSellNotional: '0',
|
||||
# lastUpdateReason: 'orderFill',
|
||||
# lastUpdateTime: '1755251065621402',
|
||||
# lastUpdateId: 2709589783,
|
||||
# lastUpdateBaseDelta: '0.059',
|
||||
# lastUpdateQuoteDelta: '-11.50618',
|
||||
# breakEvenPrice: '195.02',
|
||||
# markPrice: '195',
|
||||
# value: '11.505',
|
||||
# pnl: '-0.00118',
|
||||
# initialMargin: '1.1505',
|
||||
# maintenanceMargin: '0.6903',
|
||||
# averageEntryPrice: '195.02'
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
#
|
||||
newPositions = []
|
||||
if self.positions is None:
|
||||
self.positions = {}
|
||||
type = self.safe_string(message, 'type')
|
||||
if type == 'snapshot':
|
||||
data = self.safe_list(message, 'data', [])
|
||||
for i in range(0, len(data)):
|
||||
position = self.parse_ws_position(data[i])
|
||||
if self.safe_integer(position, 'entryPrice') != 0:
|
||||
newPositions.append(position)
|
||||
symbol = self.safe_string(position, 'symbol')
|
||||
self.positions[symbol] = position
|
||||
else:
|
||||
data = self.safe_dict(message, 'data')
|
||||
position = self.parse_ws_position(data)
|
||||
symbol = self.safe_string(position, 'symbol')
|
||||
self.positions[symbol] = position
|
||||
newPositions.append(position)
|
||||
messageHashes = self.find_message_hashes(client, 'positions::')
|
||||
for i in range(0, len(messageHashes)):
|
||||
messageHash = messageHashes[i]
|
||||
parts = messageHash.split('::')
|
||||
symbolsString = parts[1]
|
||||
symbols = symbolsString.split(',')
|
||||
positions = self.filter_by_array(newPositions, 'symbol', symbols, False)
|
||||
if not self.is_empty(positions):
|
||||
client.resolve(positions, messageHash)
|
||||
length = len(newPositions)
|
||||
if length > 0:
|
||||
client.resolve(newPositions, 'positions')
|
||||
|
||||
def parse_ws_positions(self, positions: List[Any], symbols: List[str] = None, params={}) -> List[Position]:
|
||||
symbols = self.market_symbols(symbols)
|
||||
positions = self.to_array(positions)
|
||||
result = []
|
||||
for i in range(0, len(positions)):
|
||||
position = self.extend(self.parse_ws_position(positions[i], None), params)
|
||||
result.append(position)
|
||||
return self.filter_by_array_positions(result, 'symbol', symbols, False)
|
||||
|
||||
def parse_ws_position(self, position, market=None):
|
||||
# same api
|
||||
return self.parse_position(position, market)
|
||||
|
||||
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://arkm.com/docs#stream/order_statuses
|
||||
|
||||
: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.authenticate()
|
||||
await self.load_markets()
|
||||
market = None
|
||||
if symbol is not None:
|
||||
market = self.market(symbol)
|
||||
requestArg = {
|
||||
'snapshot': False,
|
||||
}
|
||||
isTriggerOrder = False
|
||||
isTriggerOrder, params = self.handle_option_and_params(params, 'watchOrders', 'trigger', False)
|
||||
rawChannel = 'trigger_orders' if isTriggerOrder else 'order_statuses'
|
||||
messageHash = 'orders'
|
||||
if symbol is not None:
|
||||
messageHash += '::' + market['symbol']
|
||||
messageHash += '::' + rawChannel
|
||||
orders = await self.subscribe(messageHash, rawChannel, self.extend(requestArg, params))
|
||||
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):
|
||||
#
|
||||
# {
|
||||
# channel: "order_statuses",
|
||||
# type: "update",
|
||||
# data: {
|
||||
# orderId: 4200775347657,
|
||||
# userId: 2959880,
|
||||
# subaccountId: 0,
|
||||
# symbol: "ARKM_USDT_PERP",
|
||||
# time: "1755253639782186",
|
||||
# side: "buy",
|
||||
# type: "limitGtc",
|
||||
# size: "10",
|
||||
# price: "0.5",
|
||||
# postOnly: False,
|
||||
# reduceOnly: False,
|
||||
# executedSize: "0",
|
||||
# status: "cancelled",
|
||||
# avgPrice: "0",
|
||||
# executedNotional: "0",
|
||||
# creditFeePaid: "0",
|
||||
# marginBonusFeePaid: "0",
|
||||
# quoteFeePaid: "0",
|
||||
# arkmFeePaid: "0",
|
||||
# revisionId: 2752963990,
|
||||
# lastTime: "1755272026403545",
|
||||
# clientOrderId: "",
|
||||
# lastSize: "0",
|
||||
# lastPrice: "0",
|
||||
# lastCreditFee: "0",
|
||||
# lastMarginBonusFee: "0",
|
||||
# lastQuoteFee: "0",
|
||||
# lastArkmFee: "0",
|
||||
# }
|
||||
# }
|
||||
#
|
||||
channel = self.safe_string(message, 'channel')
|
||||
data = self.safe_dict(message, 'data')
|
||||
if self.orders is None:
|
||||
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
|
||||
self.orders = ArrayCacheBySymbolById(limit)
|
||||
orders = self.orders
|
||||
order = self.parse_ws_order(data)
|
||||
orders.append(order)
|
||||
client.resolve(orders, 'orders')
|
||||
client.resolve(orders, 'orders::' + order['symbol'] + '::' + channel)
|
||||
client.resolve(orders, 'orders::' + channel)
|
||||
|
||||
def parse_ws_order(self, order, market=None) -> Order:
|
||||
# same api
|
||||
return self.parse_order(order, market)
|
||||
|
||||
def handle_error_message(self, client: Client, response) -> Bool:
|
||||
#
|
||||
# error example:
|
||||
#
|
||||
# {
|
||||
# "id": "30005",
|
||||
# "name": "InvalidNotional",
|
||||
# "message": "order validation failed: invalid notional: notional 0.25 is less than min notional 1"
|
||||
# }
|
||||
#
|
||||
message = self.safe_string(response, 'message')
|
||||
if message is not None:
|
||||
body = self.json(response)
|
||||
errorCode = self.safe_string(response, 'id')
|
||||
feedback = self.id + ' ' + body
|
||||
self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
|
||||
self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
|
||||
self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
|
||||
raise ExchangeError(self.id + ' ' + body)
|
||||
return False
|
||||
964
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/ascendex.py
Normal file
964
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/ascendex.py
Normal file
@ -0,0 +1,964 @@
|
||||
# -*- 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, Bool, Int, Order, OrderBook, Str, Trade
|
||||
from ccxt.async_support.base.ws.client import Client
|
||||
from typing import List
|
||||
from ccxt.base.errors import AuthenticationError
|
||||
from ccxt.base.errors import NetworkError
|
||||
|
||||
|
||||
class ascendex(ccxt.async_support.ascendex):
|
||||
|
||||
def describe(self) -> Any:
|
||||
return self.deep_extend(super(ascendex, self).describe(), {
|
||||
'has': {
|
||||
'ws': True,
|
||||
'watchBalance': True,
|
||||
'watchOHLCV': True,
|
||||
'watchOrderBook': True,
|
||||
'watchOrders': True,
|
||||
'watchTicker': False,
|
||||
'watchTrades': True,
|
||||
'watchTradesForSymbols': True,
|
||||
},
|
||||
'urls': {
|
||||
'api': {
|
||||
'ws': {
|
||||
'public': 'wss://ascendex.com:443/api/pro/v2/stream',
|
||||
'private': 'wss://ascendex.com:443/{accountGroup}/api/pro/v2/stream',
|
||||
},
|
||||
},
|
||||
'test': {
|
||||
'ws': {
|
||||
'public': 'wss://api-test.ascendex-sandbox.com:443/api/pro/v2/stream',
|
||||
'private': 'wss://api-test.ascendex-sandbox.com:443/{accountGroup}/api/pro/v2/stream',
|
||||
},
|
||||
},
|
||||
},
|
||||
'options': {
|
||||
'tradesLimit': 1000,
|
||||
'ordersLimit': 1000,
|
||||
'OHLCVLimit': 1000,
|
||||
'categoriesAccount': {
|
||||
'cash': 'spot',
|
||||
'futures': 'swap',
|
||||
'margin': 'margin',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
async def watch_public(self, messageHash, params={}):
|
||||
url = self.urls['api']['ws']['public']
|
||||
id = self.nonce()
|
||||
request: dict = {
|
||||
'id': str(id),
|
||||
'op': 'sub',
|
||||
}
|
||||
message = self.extend(request, params)
|
||||
return await self.watch(url, messageHash, message, messageHash)
|
||||
|
||||
async def watch_public_multiple(self, messageHashes, params={}):
|
||||
url = self.urls['api']['ws']['public']
|
||||
id = self.nonce()
|
||||
request: dict = {
|
||||
'id': str(id),
|
||||
'op': 'sub',
|
||||
}
|
||||
message = self.extend(request, params)
|
||||
return await self.watch_multiple(url, messageHashes, message, messageHashes)
|
||||
|
||||
async def watch_private(self, channel, messageHash, params={}):
|
||||
await self.load_accounts()
|
||||
accountGroup = self.safe_string(self.options, 'account-group')
|
||||
url = self.urls['api']['ws']['private']
|
||||
url = self.implode_params(url, {'accountGroup': accountGroup})
|
||||
id = self.nonce()
|
||||
request: dict = {
|
||||
'id': str(id),
|
||||
'op': 'sub',
|
||||
'ch': channel,
|
||||
}
|
||||
message = self.extend(request, params)
|
||||
await self.authenticate(url, params)
|
||||
return await self.watch(url, messageHash, message, channel)
|
||||
|
||||
async def watch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
||||
"""
|
||||
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
||||
|
||||
https://ascendex.github.io/ascendex-pro-api/#channel-bar-data
|
||||
|
||||
: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()
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
if (limit is None) or (limit > 1440):
|
||||
limit = 100
|
||||
interval = self.safe_string(self.timeframes, timeframe, timeframe)
|
||||
channel = 'bar' + ':' + interval + ':' + market['id']
|
||||
params = {
|
||||
'ch': channel,
|
||||
}
|
||||
ohlcv = await self.watch_public(channel, params)
|
||||
if self.newUpdates:
|
||||
limit = ohlcv.getLimit(symbol, limit)
|
||||
return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
|
||||
|
||||
def handle_ohlcv(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "m": "bar",
|
||||
# "s": "ASD/USDT",
|
||||
# "data": {
|
||||
# "i": "1",
|
||||
# "ts": 1575398940000,
|
||||
# "o": "0.04993",
|
||||
# "c": "0.04970",
|
||||
# "h": "0.04993",
|
||||
# "l": "0.04970",
|
||||
# "v": "8052"
|
||||
# }
|
||||
# }
|
||||
#
|
||||
marketId = self.safe_string(message, 's')
|
||||
symbol = self.safe_symbol(marketId)
|
||||
channel = self.safe_string(message, 'm')
|
||||
data = self.safe_value(message, 'data', {})
|
||||
interval = self.safe_string(data, 'i')
|
||||
messageHash = channel + ':' + interval + ':' + marketId
|
||||
timeframe = self.find_timeframe(interval)
|
||||
market = self.market(symbol)
|
||||
parsed = self.parse_ohlcv(message, market)
|
||||
self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
|
||||
stored = self.safe_value(self.ohlcvs[symbol], timeframe)
|
||||
if stored is None:
|
||||
limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
|
||||
stored = ArrayCacheByTimestamp(limit)
|
||||
self.ohlcvs[symbol][timeframe] = stored
|
||||
stored.append(parsed)
|
||||
client.resolve(stored, messageHash)
|
||||
return message
|
||||
|
||||
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://ascendex.github.io/ascendex-pro-api/#channel-market-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>`
|
||||
"""
|
||||
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://ascendex.github.io/ascendex-pro-api/#channel-market-trades
|
||||
|
||||
: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
|
||||
:param str [params.name]: the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
|
||||
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=public-trades>`
|
||||
"""
|
||||
await self.load_markets()
|
||||
symbols = self.market_symbols(symbols, None, False, True, True)
|
||||
marketIds = []
|
||||
messageHashes = []
|
||||
if symbols is not None:
|
||||
for i in range(0, len(symbols)):
|
||||
market = self.market(symbols[i])
|
||||
marketIds.append(market['id'])
|
||||
messageHashes.append('trades:' + market['id'])
|
||||
channel = 'trades:' + ','.join(marketIds)
|
||||
params = self.extend(params, {
|
||||
'ch': channel,
|
||||
})
|
||||
trades = await self.watch_public_multiple(messageHashes, params)
|
||||
if self.newUpdates:
|
||||
first = self.safe_value(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):
|
||||
#
|
||||
# {
|
||||
# "m": "trades",
|
||||
# "symbol": "BTC/USDT",
|
||||
# "data": [
|
||||
# {
|
||||
# "p": "40744.28",
|
||||
# "q": "0.00150",
|
||||
# "ts": 1647514330758,
|
||||
# "bm": True,
|
||||
# "seqnum": 72057633465800320
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
#
|
||||
marketId = self.safe_string(message, 'symbol')
|
||||
symbol = self.safe_symbol(marketId)
|
||||
channel = self.safe_string(message, 'm')
|
||||
messageHash = channel + ':' + marketId
|
||||
market = self.market(symbol)
|
||||
rawData = self.safe_value(message, 'data')
|
||||
if rawData is None:
|
||||
rawData = []
|
||||
trades = self.parse_trades(rawData, market)
|
||||
tradesArray = self.safe_value(self.trades, symbol)
|
||||
if tradesArray is None:
|
||||
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
||||
tradesArray = ArrayCache(limit)
|
||||
for i in range(0, len(trades)):
|
||||
tradesArray.append(trades[i])
|
||||
self.trades[symbol] = tradesArray
|
||||
client.resolve(tradesArray, messageHash)
|
||||
|
||||
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://ascendex.github.io/ascendex-pro-api/#channel-level-2-order-book-updates
|
||||
|
||||
: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)
|
||||
channel = 'depth' + ':' + market['id']
|
||||
params = self.extend(params, {
|
||||
'ch': channel,
|
||||
})
|
||||
orderbook = await self.watch_public(channel, params)
|
||||
return orderbook.limit()
|
||||
|
||||
async def watch_order_book_snapshot(self, symbol: str, limit: Int = None, params={}):
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
action = 'depth-snapshot'
|
||||
channel = action + ':' + market['id']
|
||||
params = self.extend(params, {
|
||||
'action': action,
|
||||
'args': {
|
||||
'symbol': market['id'],
|
||||
},
|
||||
'op': 'req',
|
||||
})
|
||||
orderbook = await self.watch_public(channel, params)
|
||||
return orderbook.limit()
|
||||
|
||||
async def fetch_order_book_snapshot_custom(self, symbol: str, limit: Int = None, params={}):
|
||||
restOrderBook = await self.fetch_rest_order_book_safe(symbol, limit, params)
|
||||
if not (symbol in self.orderbooks):
|
||||
self.orderbooks[symbol] = self.order_book()
|
||||
orderbook = self.orderbooks[symbol]
|
||||
orderbook.reset(restOrderBook)
|
||||
return orderbook
|
||||
|
||||
def handle_order_book_snapshot(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "m": "depth",
|
||||
# "symbol": "BTC/USDT",
|
||||
# "data": {
|
||||
# "ts": 1647520500149,
|
||||
# "seqnum": 28590487626,
|
||||
# "asks": [
|
||||
# [Array], [Array], [Array],
|
||||
# [Array], [Array], [Array],
|
||||
# ],
|
||||
# "bids": [
|
||||
# [Array], [Array], [Array],
|
||||
# [Array], [Array], [Array],
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
marketId = self.safe_string(message, 'symbol')
|
||||
symbol = self.safe_symbol(marketId)
|
||||
channel = self.safe_string(message, 'm')
|
||||
messageHash = channel + ':' + symbol
|
||||
orderbook = self.orderbooks[symbol]
|
||||
data = self.safe_value(message, 'data')
|
||||
snapshot = self.parse_order_book(data, symbol)
|
||||
snapshot['nonce'] = self.safe_integer(data, 'seqnum')
|
||||
orderbook.reset(snapshot)
|
||||
# unroll the accumulated deltas
|
||||
messages = orderbook.cache
|
||||
for i in range(0, len(messages)):
|
||||
messageItem = messages[i]
|
||||
self.handle_order_book_message(client, messageItem, orderbook)
|
||||
self.orderbooks[symbol] = orderbook
|
||||
client.resolve(orderbook, messageHash)
|
||||
|
||||
def handle_order_book(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "m": "depth",
|
||||
# "symbol": "BTC/USDT",
|
||||
# "data": {
|
||||
# "ts": 1647515136144,
|
||||
# "seqnum": 28590470736,
|
||||
# "asks": [[Array], [Array]],
|
||||
# "bids": [[Array], [Array], [Array], [Array], [Array], [Array]]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
channel = self.safe_string(message, 'm')
|
||||
marketId = self.safe_string(message, 'symbol')
|
||||
symbol = self.safe_symbol(marketId)
|
||||
messageHash = channel + ':' + marketId
|
||||
if not (symbol in self.orderbooks):
|
||||
self.orderbooks[symbol] = self.order_book({})
|
||||
orderbook = self.orderbooks[symbol]
|
||||
if orderbook['nonce'] is None:
|
||||
orderbook.cache.append(message)
|
||||
else:
|
||||
self.handle_order_book_message(client, message, orderbook)
|
||||
client.resolve(orderbook, messageHash)
|
||||
|
||||
def handle_delta(self, bookside, delta):
|
||||
#
|
||||
# ["40990.47","0.01619"],
|
||||
#
|
||||
price = self.safe_float(delta, 0)
|
||||
amount = self.safe_float(delta, 1)
|
||||
bookside.store(price, amount)
|
||||
|
||||
def handle_deltas(self, bookside, deltas):
|
||||
for i in range(0, len(deltas)):
|
||||
self.handle_delta(bookside, deltas[i])
|
||||
|
||||
def handle_order_book_message(self, client: Client, message, orderbook):
|
||||
#
|
||||
# {
|
||||
# "m":"depth",
|
||||
# "symbol":"BTC/USDT",
|
||||
# "data":{
|
||||
# "ts":1647527417715,
|
||||
# "seqnum":28590257013,
|
||||
# "asks":[
|
||||
# ["40990.47","0.01619"],
|
||||
# ["41021.21","0"],
|
||||
# ["41031.59","0.06096"]
|
||||
# ],
|
||||
# "bids":[
|
||||
# ["40990.46","0.76114"],
|
||||
# ["40985.18","0"]
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
data = self.safe_value(message, 'data', {})
|
||||
seqNum = self.safe_integer(data, 'seqnum')
|
||||
if seqNum > orderbook['nonce']:
|
||||
asks = self.safe_value(data, 'asks', [])
|
||||
bids = self.safe_value(data, 'bids', [])
|
||||
self.handle_deltas(orderbook['asks'], asks)
|
||||
self.handle_deltas(orderbook['bids'], bids)
|
||||
orderbook['nonce'] = seqNum
|
||||
timestamp = self.safe_integer(data, 'ts')
|
||||
orderbook['timestamp'] = timestamp
|
||||
orderbook['datetime'] = self.iso8601(timestamp)
|
||||
return orderbook
|
||||
|
||||
async def watch_balance(self, params={}) -> Balances:
|
||||
"""
|
||||
watch balance and get the amount of funds available for trading or funds locked in orders
|
||||
|
||||
https://ascendex.github.io/ascendex-pro-api/#channel-order-and-balance
|
||||
|
||||
: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.load_markets()
|
||||
type, query = self.handle_market_type_and_params('watchBalance', None, params)
|
||||
channel = None
|
||||
messageHash = None
|
||||
if (type == 'spot') or (type == 'margin'):
|
||||
accountCategories = self.safe_value(self.options, 'accountCategories', {})
|
||||
accountCategory = self.safe_string(accountCategories, type, 'cash') # cash, margin,
|
||||
accountCategory = accountCategory.upper()
|
||||
channel = 'order:' + accountCategory # order and balance share the same channel
|
||||
messageHash = 'balance:' + type
|
||||
else:
|
||||
channel = 'futures-account-update'
|
||||
messageHash = 'balance:swap'
|
||||
return await self.watch_private(channel, messageHash, query)
|
||||
|
||||
def handle_balance(self, client: Client, message):
|
||||
#
|
||||
# cash account
|
||||
#
|
||||
# {
|
||||
# "m": "balance",
|
||||
# "accountId": "cshQtyfq8XLAA9kcf19h8bXHbAwwoqEo",
|
||||
# "ac": "CASH",
|
||||
# "data": {
|
||||
# "a" : "USDT",
|
||||
# "sn": 8159798,
|
||||
# "tb": "600",
|
||||
# "ab": "600"
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# margin account
|
||||
#
|
||||
# {
|
||||
# "m": "balance",
|
||||
# "accountId": "marOxpKJV83dxTRx0Eyxpa0gxc4Txt0P",
|
||||
# "ac": "MARGIN",
|
||||
# "data": {
|
||||
# "a" : "USDT",
|
||||
# "sn" : 8159802,
|
||||
# "tb" : "400", # total Balance
|
||||
# "ab" : "400", # available balance
|
||||
# "brw": "0", # borrowws
|
||||
# "int": "0" # interest
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# futures
|
||||
# {
|
||||
# "m" : "futures-account-update", # message
|
||||
# "e" : "ExecutionReport", # event type
|
||||
# "t" : 1612508562129, # time
|
||||
# "acc" : "futures-account-id", # account ID
|
||||
# "at" : "FUTURES", # account type
|
||||
# "sn" : 23128, # sequence number,
|
||||
# "id" : "r177710001cbU3813942147C5kbFGOan",
|
||||
# "col": [
|
||||
# {
|
||||
# "a": "USDT", # asset code
|
||||
# "b": "1000000", # balance
|
||||
# "f": "1" # discount factor
|
||||
# }
|
||||
# ],
|
||||
# (...)
|
||||
#
|
||||
channel = self.safe_string(message, 'm')
|
||||
result = None
|
||||
type = None
|
||||
if (channel == 'order') or (channel == 'futures-order'):
|
||||
data = self.safe_value(message, 'data')
|
||||
marketId = self.safe_string(data, 's')
|
||||
market = self.safe_market(marketId)
|
||||
baseAccount = self.account()
|
||||
baseAccount['free'] = self.safe_string(data, 'bab')
|
||||
baseAccount['total'] = self.safe_string(data, 'btb')
|
||||
quoteAccount = self.account()
|
||||
quoteAccount['free'] = self.safe_string(data, 'qab')
|
||||
quoteAccount['total'] = self.safe_string(data, 'qtb')
|
||||
if market['contract']:
|
||||
type = 'swap'
|
||||
result = self.safe_value(self.balance, type, {})
|
||||
else:
|
||||
type = market['type']
|
||||
result = self.safe_value(self.balance, type, {})
|
||||
result[market['base']] = baseAccount
|
||||
result[market['quote']] = quoteAccount
|
||||
else:
|
||||
accountType = self.safe_string_lower_2(message, 'ac', 'at')
|
||||
categoriesAccounts = self.safe_value(self.options, 'categoriesAccount')
|
||||
type = self.safe_string(categoriesAccounts, accountType, 'spot')
|
||||
result = self.safe_value(self.balance, type, {})
|
||||
data = self.safe_value(message, 'data')
|
||||
balances = None
|
||||
if data is None:
|
||||
balances = self.safe_value(message, 'col')
|
||||
else:
|
||||
balances = [data]
|
||||
for i in range(0, len(balances)):
|
||||
balance = balances[i]
|
||||
code = self.safe_currency_code(self.safe_string(balance, 'a'))
|
||||
account = self.account()
|
||||
account['free'] = self.safe_string(balance, 'ab')
|
||||
account['total'] = self.safe_string_2(balance, 'tb', 'b')
|
||||
result[code] = account
|
||||
messageHash = 'balance' + ':' + type
|
||||
client.resolve(self.safe_balance(result), messageHash)
|
||||
|
||||
async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
||||
"""
|
||||
|
||||
https://ascendex.github.io/ascendex-pro-api/#channel-order-and-balance
|
||||
|
||||
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()
|
||||
market = None
|
||||
if symbol is not None:
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
type, query = self.handle_market_type_and_params('watchOrders', market, params)
|
||||
messageHash = None
|
||||
channel = None
|
||||
if type != 'spot' and type != 'margin':
|
||||
channel = 'futures-order'
|
||||
messageHash = 'order:FUTURES'
|
||||
else:
|
||||
accountCategories = self.safe_value(self.options, 'accountCategories', {})
|
||||
accountCategory = self.safe_string(accountCategories, type, 'cash') # cash, margin
|
||||
accountCategory = accountCategory.upper()
|
||||
messageHash = 'order' + ':' + accountCategory
|
||||
channel = messageHash
|
||||
if symbol is not None:
|
||||
messageHash = messageHash + ':' + symbol
|
||||
orders = await self.watch_private(channel, messageHash, query)
|
||||
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):
|
||||
#
|
||||
# spot order
|
||||
# {
|
||||
# "m": "order",
|
||||
# "accountId": "cshF5SlR9ukAXoDOuXbND4dVpBMw9gzH",
|
||||
# "ac": "CASH",
|
||||
# "data": {
|
||||
# "sn": 19399016185,
|
||||
# "orderId": "r17f9d7983faU7223046196CMlrj3bfC",
|
||||
# "s": "LTC/USDT",
|
||||
# "ot": "Limit",
|
||||
# "t": 1647614461160,
|
||||
# "p": "50",
|
||||
# "q": "0.1",
|
||||
# "sd": "Buy",
|
||||
# "st": "New",
|
||||
# "ap": "0",
|
||||
# "cfq": "0",
|
||||
# "sp": '',
|
||||
# "err": '',
|
||||
# "btb": "0",
|
||||
# "bab": "0",
|
||||
# "qtb": "8",
|
||||
# "qab": "2.995",
|
||||
# "cf": "0",
|
||||
# "fa": "USDT",
|
||||
# "ei": "NULL_VAL"
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# futures order
|
||||
# {
|
||||
# "m": "futures-order",
|
||||
# "sn": 19399927636,
|
||||
# "e": "ExecutionReport",
|
||||
# "a": "futF5SlR9ukAXoDOuXbND4dVpBMw9gzH", # account id
|
||||
# "ac": "FUTURES",
|
||||
# "t": 1647622515434, # last execution time
|
||||
# (...)
|
||||
# }
|
||||
#
|
||||
accountType = self.safe_string(message, 'ac')
|
||||
messageHash = 'order:' + accountType
|
||||
data = self.safe_value(message, 'data', message)
|
||||
order = self.parse_ws_order(data)
|
||||
if self.orders is None:
|
||||
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
|
||||
self.orders = ArrayCacheBySymbolById(limit)
|
||||
orders = self.orders
|
||||
orders.append(order)
|
||||
symbolMessageHash = messageHash + ':' + order['symbol']
|
||||
client.resolve(orders, symbolMessageHash)
|
||||
client.resolve(orders, messageHash)
|
||||
|
||||
def parse_ws_order(self, order, market=None):
|
||||
#
|
||||
# spot order
|
||||
# {
|
||||
# "sn": 19399016185, #sequence number
|
||||
# "orderId": "r17f9d7983faU7223046196CMlrj3bfC",
|
||||
# "s": "LTC/USDT",
|
||||
# "ot": "Limit", # order type
|
||||
# "t": 1647614461160, # last execution timestamp
|
||||
# "p": "50", # price
|
||||
# "q": "0.1", # quantity
|
||||
# "sd": "Buy", # side
|
||||
# "st": "New", # status
|
||||
# "ap": "0", # average fill price
|
||||
# "cfq": "0", # cumulated fill quantity
|
||||
# "sp": '', # stop price
|
||||
# "err": '',
|
||||
# "btb": "0", # base asset total balance
|
||||
# "bab": "0", # base asset available balance
|
||||
# "qtb": "8", # quote asset total balance
|
||||
# "qab": "2.995", # quote asset available balance
|
||||
# "cf": "0", # cumulated commission
|
||||
# "fa": "USDT", # fee asset
|
||||
# "ei": "NULL_VAL"
|
||||
# }
|
||||
#
|
||||
# futures order
|
||||
# {
|
||||
# "m": "futures-order",
|
||||
# "sn": 19399927636,
|
||||
# "e": "ExecutionReport",
|
||||
# "a": "futF5SlR9ukAXoDOuXbND4dVpBMw9gzH", # account id
|
||||
# "ac": "FUTURES",
|
||||
# "t": 1647622515434, # last execution time
|
||||
# "ct": 1647622515413, # order creation time
|
||||
# "orderId": "r17f9df469b1U7223046196Okf5Kbmd",
|
||||
# "sd": "Buy", # side
|
||||
# "ot": "Limit", # order type
|
||||
# "ei": "NULL_VAL",
|
||||
# "q": "1", # quantity
|
||||
# "p": "50", #price
|
||||
# "sp": "0", # stopPrice
|
||||
# "spb": '', # stopTrigger
|
||||
# "s": "LTC-PERP", # symbol
|
||||
# "st": "New", # state
|
||||
# "err": '',
|
||||
# "lp": "0", # last filled price
|
||||
# "lq": "0", # last filled quantity(base asset)
|
||||
# "ap": "0", # average filled price
|
||||
# "cfq": "0", # cummulative filled quantity(base asset)
|
||||
# "f": "0", # commission fee of the current execution
|
||||
# "cf": "0", # cumulative commission fee
|
||||
# "fa": "USDT", # fee asset
|
||||
# "psl": "0",
|
||||
# "pslt": "market",
|
||||
# "ptp": "0",
|
||||
# "ptpt": "market"
|
||||
# }
|
||||
#
|
||||
status = self.parse_order_status(self.safe_string(order, 'st'))
|
||||
marketId = self.safe_string(order, 's')
|
||||
timestamp = self.safe_integer(order, 't')
|
||||
symbol = self.safe_symbol(marketId, market, '/')
|
||||
lastTradeTimestamp = self.safe_integer(order, 't')
|
||||
price = self.safe_string(order, 'p')
|
||||
amount = self.safe_string(order, 'q')
|
||||
average = self.safe_string(order, 'ap')
|
||||
filled = self.safe_string(order, 'cfq')
|
||||
id = self.safe_string(order, 'orderId')
|
||||
type = self.safe_string_lower(order, 'ot')
|
||||
side = self.safe_string_lower(order, 'sd')
|
||||
feeCost = self.safe_number(order, 'cf')
|
||||
fee = None
|
||||
if feeCost is not None:
|
||||
feeCurrencyId = self.safe_string(order, 'fa')
|
||||
feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
|
||||
fee = {
|
||||
'cost': feeCost,
|
||||
'currency': feeCurrencyCode,
|
||||
}
|
||||
stopPrice = self.parse_number(self.omit_zero(self.safe_string(order, 'sp')))
|
||||
return self.safe_order({
|
||||
'info': order,
|
||||
'id': id,
|
||||
'clientOrderId': None,
|
||||
'timestamp': timestamp,
|
||||
'datetime': self.iso8601(timestamp),
|
||||
'lastTradeTimestamp': lastTradeTimestamp,
|
||||
'symbol': symbol,
|
||||
'type': type,
|
||||
'timeInForce': None,
|
||||
'postOnly': None,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'stopPrice': stopPrice,
|
||||
'triggerPrice': stopPrice,
|
||||
'amount': amount,
|
||||
'cost': None,
|
||||
'average': average,
|
||||
'filled': filled,
|
||||
'remaining': None,
|
||||
'status': status,
|
||||
'fee': fee,
|
||||
'trades': None,
|
||||
}, market)
|
||||
|
||||
def handle_error_message(self, client: Client, message) -> Bool:
|
||||
#
|
||||
# {
|
||||
# "m": "disconnected",
|
||||
# "code": 100005,
|
||||
# "reason": "INVALID_WS_REQUEST_DATA",
|
||||
# "info": "Session is disconnected due to missing pong message from the client"
|
||||
# }
|
||||
#
|
||||
errorCode = self.safe_integer(message, 'code')
|
||||
try:
|
||||
if errorCode is not None:
|
||||
feedback = self.id + ' ' + self.json(message)
|
||||
self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
|
||||
messageString = self.safe_value(message, 'message')
|
||||
if messageString is not None:
|
||||
self.throw_broadly_matched_exception(self.exceptions['broad'], messageString, feedback)
|
||||
return False
|
||||
except Exception as e:
|
||||
if isinstance(e, AuthenticationError):
|
||||
messageHash = 'authenticated'
|
||||
client.reject(e, messageHash)
|
||||
if messageHash in client.subscriptions:
|
||||
del client.subscriptions[messageHash]
|
||||
else:
|
||||
client.reject(e)
|
||||
return True
|
||||
|
||||
def handle_authenticate(self, client: Client, message):
|
||||
#
|
||||
# {m: "auth", id: "1647605234", code: 0}
|
||||
#
|
||||
messageHash = 'authenticated'
|
||||
client.resolve(message, messageHash)
|
||||
|
||||
def handle_message(self, client: Client, message):
|
||||
if self.handle_error_message(client, message):
|
||||
return
|
||||
#
|
||||
# {m: "ping", hp: 3}
|
||||
#
|
||||
# {m: "sub", ch: "bar:BTC/USDT", code: 0}
|
||||
#
|
||||
# {m: 'sub', id: "1647515701", ch: "depth:BTC/USDT", code: 0}
|
||||
#
|
||||
# {m: "connected", type: "unauth"}
|
||||
#
|
||||
# {m: "auth", id: "1647605234", code: 0}
|
||||
#
|
||||
# order or balance sub
|
||||
# {
|
||||
# "m": "sub",
|
||||
# "id": "1647605952",
|
||||
# "ch": "order:cshF5SlR9ukAXoDOuXbND4dVpBMw9gzH", or futures-order
|
||||
# "code": 0
|
||||
# }
|
||||
#
|
||||
# ohlcv
|
||||
# {
|
||||
# "m": "bar",
|
||||
# "s": "BTC/USDT",
|
||||
# "data": {
|
||||
# "i": "1",
|
||||
# "ts": 1647510060000,
|
||||
# "o": "40813.93",
|
||||
# "c": "40804.57",
|
||||
# "h": "40814.21",
|
||||
# "l": "40804.56",
|
||||
# "v": "0.01537"
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# trades
|
||||
#
|
||||
# {
|
||||
# "m": "trades",
|
||||
# "symbol": "BTC/USDT",
|
||||
# "data": [
|
||||
# {
|
||||
# "p": "40762.26",
|
||||
# "q": "0.01500",
|
||||
# "ts": 1647514306759,
|
||||
# "bm": True,
|
||||
# "seqnum": 72057633465795180
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
#
|
||||
# orderbook deltas
|
||||
#
|
||||
# {
|
||||
# "m":"depth",
|
||||
# "symbol":"BTC/USDT",
|
||||
# "data":{
|
||||
# "ts":1647527417715,
|
||||
# "seqnum":28590257013,
|
||||
# "asks":[
|
||||
# ["40990.47","0.01619"],
|
||||
# ["41021.21","0"],
|
||||
# ["41031.59","0.06096"]
|
||||
# ],
|
||||
# "bids":[
|
||||
# ["40990.46","0.76114"],
|
||||
# ["40985.18","0"]
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# orderbook snapshot
|
||||
# {
|
||||
# "m": "depth-snapshot",
|
||||
# "symbol": "BTC/USDT",
|
||||
# "data": {
|
||||
# "ts": 1647525938513,
|
||||
# "seqnum": 28590504772,
|
||||
# "asks": [
|
||||
# [Array], [Array], [Array], [Array], [Array], [Array], [Array],
|
||||
# [Array], [Array], [Array], [Array], [Array], [Array], [Array],
|
||||
# [Array], [Array], [Array], [Array], [Array], [Array], [Array],
|
||||
# (...)
|
||||
# ]
|
||||
# }
|
||||
#
|
||||
# spot order update
|
||||
# {
|
||||
# "m": "order",
|
||||
# "accountId": "cshQtyfq8XLAA9kcf19h8bXHbAwwoqDo",
|
||||
# "ac": "CASH",
|
||||
# "data": {
|
||||
# "s": "BTC/USDT",
|
||||
# "sn": 8159711,
|
||||
# "sd": "Buy",
|
||||
# "ap": "0",
|
||||
# "bab": "2006.5974027",
|
||||
# "btb": "2006.5974027",
|
||||
# "cf": "0",
|
||||
# "cfq": "0",
|
||||
# (...)
|
||||
# }
|
||||
# }
|
||||
# future order update
|
||||
# {
|
||||
# "m": "futures-order",
|
||||
# "sn": 19404258063,
|
||||
# "e": "ExecutionReport",
|
||||
# "a": "futF5SlR9ukAXoDOuXbND4dVpBMw9gzH",
|
||||
# "ac": "FUTURES",
|
||||
# "t": 1647681792543,
|
||||
# "ct": 1647622515413,
|
||||
# "orderId": "r17f9df469b1U7223046196Okf5KbmdL",
|
||||
# (...)
|
||||
# "ptpt": "None"
|
||||
# }
|
||||
#
|
||||
# balance update cash
|
||||
# {
|
||||
# "m": "balance",
|
||||
# "accountId": "cshQtyfq8XLAA9kcf19h8bXHbAwwoqDo",
|
||||
# "ac": "CASH",
|
||||
# "data": {
|
||||
# "a" : "USDT",
|
||||
# "sn": 8159798,
|
||||
# "tb": "600",
|
||||
# "ab": "600"
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# balance update margin
|
||||
# {
|
||||
# "m": "balance",
|
||||
# "accountId": "marOxpKJV83dxTRx0Eyxpa0gxc4Txt0P",
|
||||
# "ac": "MARGIN",
|
||||
# "data": {
|
||||
# "a" : "USDT",
|
||||
# "sn" : 8159802,
|
||||
# "tb" : "400",
|
||||
# "ab" : "400",
|
||||
# "brw": "0",
|
||||
# "int": "0"
|
||||
# }
|
||||
# }
|
||||
#
|
||||
subject = self.safe_string(message, 'm')
|
||||
methods: dict = {
|
||||
'ping': self.handle_ping,
|
||||
'auth': self.handle_authenticate,
|
||||
'sub': self.handle_subscription_status,
|
||||
'depth': self.handle_order_book,
|
||||
'depth-snapshot': self.handle_order_book_snapshot,
|
||||
'trades': self.handle_trades,
|
||||
'bar': self.handle_ohlcv,
|
||||
'balance': self.handle_balance,
|
||||
'futures-account-update': self.handle_balance,
|
||||
}
|
||||
method = self.safe_value(methods, subject)
|
||||
if method is not None:
|
||||
method(client, message)
|
||||
if (subject == 'order') or (subject == 'futures-order'):
|
||||
# self.handle_order(client, message)
|
||||
# balance updates may be in the order structure
|
||||
# they may also be standalone balance updates related to account transfers
|
||||
self.handle_order(client, message)
|
||||
if subject == 'order':
|
||||
self.handle_balance(client, message)
|
||||
|
||||
def handle_subscription_status(self, client: Client, message):
|
||||
#
|
||||
# {m: "sub", ch: "bar:BTC/USDT", code: 0}
|
||||
#
|
||||
# {m: 'sub', id: "1647515701", ch: "depth:BTC/USDT", code: 0}
|
||||
#
|
||||
channel = self.safe_string(message, 'ch', '')
|
||||
if channel.find('depth') > -1 and not (channel.find('depth-snapshot') > -1):
|
||||
self.handle_order_book_subscription(client, message)
|
||||
return message
|
||||
|
||||
def handle_order_book_subscription(self, client: Client, message):
|
||||
channel = self.safe_string(message, 'ch')
|
||||
parts = channel.split(':')
|
||||
marketId = parts[1]
|
||||
market = self.safe_market(marketId)
|
||||
symbol = market['symbol']
|
||||
if symbol in self.orderbooks:
|
||||
del self.orderbooks[symbol]
|
||||
self.orderbooks[symbol] = self.order_book({})
|
||||
if self.options['defaultType'] == 'swap' or market['contract']:
|
||||
self.spawn(self.fetch_order_book_snapshot_custom, symbol)
|
||||
else:
|
||||
self.spawn(self.watch_order_book_snapshot, symbol)
|
||||
|
||||
async def pong(self, client, message):
|
||||
#
|
||||
# {m: "ping", hp: 3}
|
||||
#
|
||||
try:
|
||||
await client.send({'op': 'pong', 'hp': self.safe_integer(message, 'hp')})
|
||||
except Exception as e:
|
||||
error = NetworkError(self.id + ' handlePing failed with error ' + self.exception_message(e))
|
||||
client.reset(error)
|
||||
|
||||
def handle_ping(self, client: Client, message):
|
||||
self.spawn(self.pong, client, message)
|
||||
|
||||
async def authenticate(self, url, params={}):
|
||||
self.check_required_credentials()
|
||||
messageHash = 'authenticated'
|
||||
client = self.client(url)
|
||||
future = self.safe_value(client.subscriptions, messageHash)
|
||||
if future is None:
|
||||
timestamp = str(self.milliseconds())
|
||||
urlParts = url.split('/')
|
||||
partsLength = len(urlParts)
|
||||
path = self.safe_string(urlParts, partsLength - 1)
|
||||
version = self.safe_string(urlParts, partsLength - 2)
|
||||
auth = timestamp + '+' + version + '/' + path
|
||||
secret = self.base64_to_binary(self.secret)
|
||||
signature = self.hmac(self.encode(auth), secret, hashlib.sha256, 'base64')
|
||||
request: dict = {
|
||||
'op': 'auth',
|
||||
'id': str(self.nonce()),
|
||||
't': timestamp,
|
||||
'key': self.apiKey,
|
||||
'sig': signature,
|
||||
}
|
||||
future = await self.watch(url, messageHash, self.extend(request, params), messageHash)
|
||||
client.subscriptions[messageHash] = future
|
||||
return future
|
||||
1793
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/aster.py
Normal file
1793
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/aster.py
Normal file
File diff suppressed because it is too large
Load Diff
1247
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/backpack.py
Normal file
1247
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/backpack.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,43 @@
|
||||
# -*- 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
|
||||
|
||||
from ccxt.pro.hitbtc import hitbtc
|
||||
from ccxt.base.types import Any
|
||||
|
||||
import ccxt.async_support.bequant as bequantRest
|
||||
|
||||
|
||||
class bequant(hitbtc):
|
||||
|
||||
def describe(self) -> Any:
|
||||
# eslint-disable-next-line new-cap
|
||||
restInstance = bequantRest()
|
||||
restDescribe = restInstance.describe()
|
||||
extended = self.deep_extend(super(bequant, self).describe(), restDescribe)
|
||||
return self.deep_extend(extended, {
|
||||
'id': 'bequant',
|
||||
'name': 'Bequant',
|
||||
'countries': ['MT'], # Malta
|
||||
'pro': True,
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/55248342-a75dfe00-525a-11e9-8aa2-05e9dca943c6.jpg',
|
||||
'api': {
|
||||
'public': 'https://api.bequant.io/api/3',
|
||||
'private': 'https://api.bequant.io/api/3',
|
||||
'ws': {
|
||||
'public': 'wss://api.bequant.io/api/3/ws/public',
|
||||
'private': 'wss://api.bequant.io/api/3/ws/trading',
|
||||
},
|
||||
},
|
||||
'www': 'https://bequant.io',
|
||||
'doc': [
|
||||
'https://api.bequant.io/',
|
||||
],
|
||||
'fees': [
|
||||
'https://bequant.io/fees-and-limits',
|
||||
],
|
||||
'referral': 'https://bequant.io',
|
||||
},
|
||||
})
|
||||
4413
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/binance.py
Normal file
4413
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/binance.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,32 @@
|
||||
# -*- 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
|
||||
|
||||
from ccxt.pro.binance import binance
|
||||
from ccxt.base.types import Any
|
||||
|
||||
import ccxt.async_support.binancecoinm as binancecoinmRest
|
||||
|
||||
|
||||
class binancecoinm(binance):
|
||||
|
||||
def describe(self) -> Any:
|
||||
# eslint-disable-next-line new-cap
|
||||
restInstance = binancecoinmRest()
|
||||
restDescribe = restInstance.describe()
|
||||
extended = self.deep_extend(super(binancecoinm, self).describe(), restDescribe)
|
||||
return self.deep_extend(extended, {
|
||||
'id': 'binancecoinm',
|
||||
'name': 'Binance COIN-M',
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/117738721-668c8d80-b205-11eb-8c49-3fad84c4a07f.jpg',
|
||||
'doc': 'https://developers.binance.com/en',
|
||||
},
|
||||
'options': {
|
||||
'fetchMarkets': {
|
||||
'types': ['inverse'],
|
||||
},
|
||||
'defaultSubType': 'inverse',
|
||||
},
|
||||
})
|
||||
@ -0,0 +1,70 @@
|
||||
# -*- 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
|
||||
|
||||
from ccxt.pro.binance import binance
|
||||
from ccxt.base.types import Any
|
||||
|
||||
import ccxt.async_support.binanceus as binanceusRest
|
||||
|
||||
|
||||
class binanceus(binance):
|
||||
|
||||
def describe(self) -> Any:
|
||||
# eslint-disable-next-line new-cap
|
||||
restInstance = binanceusRest()
|
||||
restDescribe = restInstance.describe()
|
||||
parentWsDescribe = super(binanceus, self).describe_data()
|
||||
extended = self.deep_extend(restDescribe, parentWsDescribe)
|
||||
return self.deep_extend(extended, {
|
||||
'id': 'binanceus',
|
||||
'name': 'Binance US',
|
||||
'countries': ['US'], # US
|
||||
'certified': False,
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/65177307-217b7c80-da5f-11e9-876e-0b748ba0a358.jpg',
|
||||
'api': {
|
||||
'ws': {
|
||||
'spot': 'wss://stream.binance.us:9443/ws',
|
||||
},
|
||||
'web': 'https://www.binance.us',
|
||||
'sapi': 'https://api.binance.us/sapi/v1',
|
||||
'wapi': 'https://api.binance.us/wapi/v3',
|
||||
'public': 'https://api.binance.us/api/v3',
|
||||
'private': 'https://api.binance.us/api/v3',
|
||||
'v3': 'https://api.binance.us/api/v3',
|
||||
'v1': 'https://api.binance.us/api/v1',
|
||||
},
|
||||
'www': 'https://www.binance.us',
|
||||
'referral': 'https://www.binance.us/?ref=35005074',
|
||||
'doc': 'https://github.com/binance-us/binance-official-api-docs',
|
||||
'fees': 'https://www.binance.us/en/fee/schedule',
|
||||
},
|
||||
'has': {
|
||||
'createOrderWithTakeProfitAndStopLossWs': False,
|
||||
'createReduceOnlyOrderWs': False,
|
||||
'createStopLossOrderWs': False,
|
||||
'createTakeProfitOrderWs': False,
|
||||
'fetchPositionForSymbolWs': False,
|
||||
'fetchPositionsForSymbolWs': False,
|
||||
'fetchPositionsWs': False,
|
||||
'fetchPositionWs': False,
|
||||
'unWatchPositions': False,
|
||||
'watchLiquidations': False,
|
||||
'watchLiquidationsForSymbols': False,
|
||||
'watchMarkPrice': False,
|
||||
'watchMarkPrices': False,
|
||||
'watchMyLiquidations': False,
|
||||
'watchMyLiquidationsForSymbols': False,
|
||||
'watchPosition': False,
|
||||
'watchPositions': False,
|
||||
},
|
||||
'options': {
|
||||
'quoteOrderQty': False,
|
||||
'defaultType': 'spot',
|
||||
'fetchMarkets': {
|
||||
'types': ['spot'],
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -0,0 +1,42 @@
|
||||
# -*- 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
|
||||
|
||||
from ccxt.pro.binance import binance
|
||||
from ccxt.base.types import Any
|
||||
from ccxt.base.errors import InvalidOrder
|
||||
|
||||
import ccxt.async_support.binanceusdm as binanceusdmRest
|
||||
|
||||
|
||||
class binanceusdm(binance):
|
||||
|
||||
def describe(self) -> Any:
|
||||
# eslint-disable-next-line new-cap
|
||||
restInstance = binanceusdmRest()
|
||||
restDescribe = restInstance.describe()
|
||||
extended = self.deep_extend(super(binanceusdm, self).describe(), restDescribe)
|
||||
return self.deep_extend(extended, {
|
||||
'id': 'binanceusdm',
|
||||
'name': 'Binance USDⓈ-M',
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/117738721-668c8d80-b205-11eb-8c49-3fad84c4a07f.jpg',
|
||||
'doc': 'https://developers.binance.com/en',
|
||||
},
|
||||
'options': {
|
||||
'fetchMarkets': {
|
||||
'types': ['linear'],
|
||||
},
|
||||
'defaultSubType': 'linear',
|
||||
},
|
||||
# https://binance-docs.github.io/apidocs/futures/en/#error-codes
|
||||
# https://developers.binance.com/docs/derivatives/usds-margined-futures/error-code
|
||||
'exceptions': {
|
||||
'exact': {
|
||||
'-5021': InvalidOrder, # {"code":-5021,"msg":"Due to the order could not be filled immediately, the FOK order has been rejected."}
|
||||
'-5022': InvalidOrder, # {"code":-5022,"msg":"Due to the order could not be executed, the Post Only order will be rejected."}
|
||||
'-5028': InvalidOrder, # {"code":-5028,"msg":"Timestamp for self request is outside of the ME recvWindow."}
|
||||
},
|
||||
},
|
||||
})
|
||||
1660
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/bingx.py
Normal file
1660
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/bingx.py
Normal file
File diff suppressed because it is too large
Load Diff
1218
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/bitfinex.py
Normal file
1218
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/bitfinex.py
Normal file
File diff suppressed because it is too large
Load Diff
2691
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/bitget.py
Normal file
2691
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/bitget.py
Normal file
File diff suppressed because it is too large
Load Diff
622
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/bithumb.py
Normal file
622
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/bithumb.py
Normal file
@ -0,0 +1,622 @@
|
||||
# -*- 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, Balances, Bool, 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
|
||||
|
||||
|
||||
class bithumb(ccxt.async_support.bithumb):
|
||||
|
||||
def describe(self) -> Any:
|
||||
return self.deep_extend(super(bithumb, self).describe(), {
|
||||
'has': {
|
||||
'ws': True,
|
||||
'watchBalance': True,
|
||||
'watchOrders': True,
|
||||
'watchTicker': True,
|
||||
'watchTickers': True,
|
||||
'watchTrades': True,
|
||||
'watchOrderBook': True,
|
||||
'watchOHLCV': False,
|
||||
},
|
||||
'urls': {
|
||||
'api': {
|
||||
'ws': {
|
||||
'public': 'wss://pubwss.bithumb.com/pub/ws', # v1.2.0
|
||||
'publicV2': 'wss://ws-api.bithumb.com/websocket/v1', # v2.1.5
|
||||
'privateV2': 'wss://ws-api.bithumb.com/websocket/v1/private', # v2.1.5
|
||||
},
|
||||
},
|
||||
},
|
||||
'options': {},
|
||||
'streaming': {},
|
||||
'exceptions': {},
|
||||
})
|
||||
|
||||
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://apidocs.bithumb.com/v1.2.0/reference/%EB%B9%97%EC%8D%B8-%EA%B1%B0%EB%9E%98%EC%86%8C-%EC%A0%95%EB%B3%B4-%EC%88%98%EC%8B%A0
|
||||
|
||||
: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.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
|
||||
:returns dict: a `ticker structure <https://github.com/ccxt/ccxt/wiki/Manual#ticker-structure>`
|
||||
"""
|
||||
url = self.urls['api']['ws']['public']
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
messageHash = 'ticker:' + market['symbol']
|
||||
request: dict = {
|
||||
'type': 'ticker',
|
||||
'symbols': [market['base'] + '_' + market['quote']],
|
||||
'tickTypes': [self.safe_string(params, 'tickTypes', '24H')],
|
||||
}
|
||||
return await self.watch(url, messageHash, self.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://apidocs.bithumb.com/v1.2.0/reference/%EB%B9%97%EC%8D%B8-%EA%B1%B0%EB%9E%98%EC%86%8C-%EC%A0%95%EB%B3%B4-%EC%88%98%EC%8B%A0
|
||||
|
||||
: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()
|
||||
url = self.urls['api']['ws']['public']
|
||||
marketIds = []
|
||||
messageHashes = []
|
||||
symbols = self.market_symbols(symbols, None, False, True, True)
|
||||
for i in range(0, len(symbols)):
|
||||
symbol = symbols[i]
|
||||
market = self.market(symbol)
|
||||
marketIds.append(market['base'] + '_' + market['quote'])
|
||||
messageHashes.append('ticker:' + market['symbol'])
|
||||
request: dict = {
|
||||
'type': 'ticker',
|
||||
'symbols': marketIds,
|
||||
'tickTypes': [self.safe_string(params, 'tickTypes', '24H')],
|
||||
}
|
||||
message = self.extend(request, params)
|
||||
newTicker = await self.watch_multiple(url, messageHashes, message, messageHashes)
|
||||
if self.newUpdates:
|
||||
result: dict = {}
|
||||
result[newTicker['symbol']] = newTicker
|
||||
return result
|
||||
return self.filter_by_array(self.tickers, 'symbol', symbols)
|
||||
|
||||
def handle_ticker(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "type" : "ticker",
|
||||
# "content" : {
|
||||
# "symbol" : "BTC_KRW", # 통화코드
|
||||
# "tickType" : "24H", # 변동 기준시간- 30M, 1H, 12H, 24H, MID
|
||||
# "date" : "20200129", # 일자
|
||||
# "time" : "121844", # 시간
|
||||
# "openPrice" : "2302", # 시가
|
||||
# "closePrice" : "2317", # 종가
|
||||
# "lowPrice" : "2272", # 저가
|
||||
# "highPrice" : "2344", # 고가
|
||||
# "value" : "2831915078.07065789", # 누적거래금액
|
||||
# "volume" : "1222314.51355788", # 누적거래량
|
||||
# "sellVolume" : "760129.34079004", # 매도누적거래량
|
||||
# "buyVolume" : "462185.17276784", # 매수누적거래량
|
||||
# "prevClosePrice" : "2326", # 전일종가
|
||||
# "chgRate" : "0.65", # 변동률
|
||||
# "chgAmt" : "15", # 변동금액
|
||||
# "volumePower" : "60.80" # 체결강도
|
||||
# }
|
||||
# }
|
||||
#
|
||||
content = self.safe_dict(message, 'content', {})
|
||||
marketId = self.safe_string(content, 'symbol')
|
||||
symbol = self.safe_symbol(marketId, None, '_')
|
||||
ticker = self.parse_ws_ticker(content)
|
||||
messageHash = 'ticker:' + symbol
|
||||
self.tickers[symbol] = ticker
|
||||
client.resolve(self.tickers[symbol], messageHash)
|
||||
|
||||
def parse_ws_ticker(self, ticker, market=None):
|
||||
#
|
||||
# {
|
||||
# "symbol" : "BTC_KRW", # 통화코드
|
||||
# "tickType" : "24H", # 변동 기준시간- 30M, 1H, 12H, 24H, MID
|
||||
# "date" : "20200129", # 일자
|
||||
# "time" : "121844", # 시간
|
||||
# "openPrice" : "2302", # 시가
|
||||
# "closePrice" : "2317", # 종가
|
||||
# "lowPrice" : "2272", # 저가
|
||||
# "highPrice" : "2344", # 고가
|
||||
# "value" : "2831915078.07065789", # 누적거래금액
|
||||
# "volume" : "1222314.51355788", # 누적거래량
|
||||
# "sellVolume" : "760129.34079004", # 매도누적거래량
|
||||
# "buyVolume" : "462185.17276784", # 매수누적거래량
|
||||
# "prevClosePrice" : "2326", # 전일종가
|
||||
# "chgRate" : "0.65", # 변동률
|
||||
# "chgAmt" : "15", # 변동금액
|
||||
# "volumePower" : "60.80" # 체결강도
|
||||
# }
|
||||
#
|
||||
date = self.safe_string(ticker, 'date', '')
|
||||
time = self.safe_string(ticker, 'time', '')
|
||||
datetime = date[0:4] + '-' + date[4:6] + '-' + date[6:8] + 'T' + time[0:2] + ':' + time[2:4] + ':' + time[4:6]
|
||||
marketId = self.safe_string(ticker, 'symbol')
|
||||
return self.safe_ticker({
|
||||
'symbol': self.safe_symbol(marketId, market, '_'),
|
||||
'timestamp': self.parse8601(datetime),
|
||||
'datetime': datetime,
|
||||
'high': self.safe_string(ticker, 'highPrice'),
|
||||
'low': self.safe_string(ticker, 'lowPrice'),
|
||||
'bid': None,
|
||||
'bidVolume': self.safe_string(ticker, 'buyVolume'),
|
||||
'ask': None,
|
||||
'askVolume': self.safe_string(ticker, 'sellVolume'),
|
||||
'vwap': None,
|
||||
'open': self.safe_string(ticker, 'openPrice'),
|
||||
'close': self.safe_string(ticker, 'closePrice'),
|
||||
'last': None,
|
||||
'previousClose': self.safe_string(ticker, 'prevClosePrice'),
|
||||
'change': self.safe_string(ticker, 'chgAmt'),
|
||||
'percentage': self.safe_string(ticker, 'chgRate'),
|
||||
'average': None,
|
||||
'baseVolume': self.safe_string(ticker, 'volume'),
|
||||
'quoteVolume': self.safe_string(ticker, 'value'),
|
||||
'info': ticker,
|
||||
}, market)
|
||||
|
||||
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
||||
"""
|
||||
|
||||
https://apidocs.bithumb.com/v1.2.0/reference/%EB%B9%97%EC%8D%B8-%EA%B1%B0%EB%9E%98%EC%86%8C-%EC%A0%95%EB%B3%B4-%EC%88%98%EC%8B%A0
|
||||
|
||||
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
|
||||
:returns dict: A dictionary of `order book structures <https://github.com/ccxt/ccxt/wiki/Manual#order-book-structure>` indexed by market symbols
|
||||
"""
|
||||
await self.load_markets()
|
||||
url = self.urls['api']['ws']['public']
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
messageHash = 'orderbook' + ':' + symbol
|
||||
request: dict = {
|
||||
'type': 'orderbookdepth',
|
||||
'symbols': [market['base'] + '_' + market['quote']],
|
||||
}
|
||||
orderbook = await self.watch(url, messageHash, self.extend(request, params), messageHash)
|
||||
return orderbook.limit()
|
||||
|
||||
def handle_order_book(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "type" : "orderbookdepth",
|
||||
# "content" : {
|
||||
# "list" : [
|
||||
# {
|
||||
# "symbol" : "BTC_KRW",
|
||||
# "orderType" : "ask", # 주문타입 – bid / ask
|
||||
# "price" : "10593000", # 호가
|
||||
# "quantity" : "1.11223318", # 잔량
|
||||
# "total" : "3" # 건수
|
||||
# },
|
||||
# {"symbol" : "BTC_KRW", "orderType" : "ask", "price" : "10596000", "quantity" : "0.5495", "total" : "8"},
|
||||
# {"symbol" : "BTC_KRW", "orderType" : "ask", "price" : "10598000", "quantity" : "18.2085", "total" : "10"},
|
||||
# {"symbol" : "BTC_KRW", "orderType" : "bid", "price" : "10532000", "quantity" : "0", "total" : "0"},
|
||||
# {"symbol" : "BTC_KRW", "orderType" : "bid", "price" : "10572000", "quantity" : "2.3324", "total" : "4"},
|
||||
# {"symbol" : "BTC_KRW", "orderType" : "bid", "price" : "10571000", "quantity" : "1.469", "total" : "3"},
|
||||
# {"symbol" : "BTC_KRW", "orderType" : "bid", "price" : "10569000", "quantity" : "0.5152", "total" : "2"}
|
||||
# ],
|
||||
# "datetime":1580268255864325 # 일시
|
||||
# }
|
||||
# }
|
||||
#
|
||||
content = self.safe_dict(message, 'content', {})
|
||||
list = self.safe_list(content, 'list', [])
|
||||
first = self.safe_dict(list, 0, {})
|
||||
marketId = self.safe_string(first, 'symbol')
|
||||
symbol = self.safe_symbol(marketId, None, '_')
|
||||
timestampStr = self.safe_string(content, 'datetime')
|
||||
timestamp = self.parse_to_int(timestampStr[0:13])
|
||||
if not (symbol in self.orderbooks):
|
||||
ob = self.order_book()
|
||||
ob['symbol'] = symbol
|
||||
self.orderbooks[symbol] = ob
|
||||
orderbook = self.orderbooks[symbol]
|
||||
self.handle_deltas(orderbook, list)
|
||||
orderbook['timestamp'] = timestamp
|
||||
orderbook['datetime'] = self.iso8601(timestamp)
|
||||
messageHash = 'orderbook' + ':' + symbol
|
||||
client.resolve(orderbook, messageHash)
|
||||
|
||||
def handle_delta(self, orderbook, delta):
|
||||
#
|
||||
# {
|
||||
# symbol: "ETH_BTC",
|
||||
# orderType: "bid",
|
||||
# price: "0.07349517",
|
||||
# quantity: "0",
|
||||
# total: "0",
|
||||
# }
|
||||
#
|
||||
sideId = self.safe_string(delta, 'orderType')
|
||||
side = 'bids' if (sideId == 'bid') else 'asks'
|
||||
bidAsk = self.parse_bid_ask(delta, 'price', 'quantity')
|
||||
orderbookSide = orderbook[side]
|
||||
orderbookSide.storeArray(bidAsk)
|
||||
|
||||
def handle_deltas(self, orderbook, deltas):
|
||||
for i in range(0, len(deltas)):
|
||||
self.handle_delta(orderbook, deltas[i])
|
||||
|
||||
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://apidocs.bithumb.com/v1.2.0/reference/%EB%B9%97%EC%8D%B8-%EA%B1%B0%EB%9E%98%EC%86%8C-%EC%A0%95%EB%B3%B4-%EC%88%98%EC%8B%A0
|
||||
|
||||
: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://github.com/ccxt/ccxt/wiki/Manual#public-trades>`
|
||||
"""
|
||||
await self.load_markets()
|
||||
url = self.urls['api']['ws']['public']
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
messageHash = 'trade:' + symbol
|
||||
request: dict = {
|
||||
'type': 'transaction',
|
||||
'symbols': [market['base'] + '_' + market['quote']],
|
||||
}
|
||||
trades = await self.watch(url, messageHash, self.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_trades(self, client, message):
|
||||
#
|
||||
# {
|
||||
# "type" : "transaction",
|
||||
# "content" : {
|
||||
# "list" : [
|
||||
# {
|
||||
# "symbol" : "BTC_KRW",
|
||||
# "buySellGb" : "1",
|
||||
# "contPrice" : "10579000",
|
||||
# "contQty" : "0.01",
|
||||
# "contAmt" : "105790.00",
|
||||
# "contDtm" : "2020-01-29 12:24:18.830039",
|
||||
# "updn" : "dn"
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
content = self.safe_dict(message, 'content', {})
|
||||
rawTrades = self.safe_list(content, 'list', [])
|
||||
for i in range(0, len(rawTrades)):
|
||||
rawTrade = rawTrades[i]
|
||||
marketId = self.safe_string(rawTrade, 'symbol')
|
||||
symbol = self.safe_symbol(marketId, None, '_')
|
||||
if not (symbol in self.trades):
|
||||
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
||||
stored = ArrayCache(limit)
|
||||
self.trades[symbol] = stored
|
||||
trades = self.trades[symbol]
|
||||
parsed = self.parse_ws_trade(rawTrade)
|
||||
trades.append(parsed)
|
||||
messageHash = 'trade' + ':' + symbol
|
||||
client.resolve(trades, messageHash)
|
||||
|
||||
def parse_ws_trade(self, trade, market=None):
|
||||
#
|
||||
# {
|
||||
# "symbol" : "BTC_KRW",
|
||||
# "buySellGb" : "1",
|
||||
# "contPrice" : "10579000",
|
||||
# "contQty" : "0.01",
|
||||
# "contAmt" : "105790.00",
|
||||
# "contDtm" : "2020-01-29 12:24:18.830038",
|
||||
# "updn" : "dn"
|
||||
# }
|
||||
#
|
||||
marketId = self.safe_string(trade, 'symbol')
|
||||
datetime = self.safe_string(trade, 'contDtm')
|
||||
# that date is not UTC iso8601, but exchange's local time, -9hr difference
|
||||
timestamp = self.parse8601(datetime) - 32400000
|
||||
sideId = self.safe_string(trade, 'buySellGb')
|
||||
return self.safe_trade({
|
||||
'id': None,
|
||||
'info': trade,
|
||||
'timestamp': timestamp,
|
||||
'datetime': self.iso8601(timestamp),
|
||||
'symbol': self.safe_symbol(marketId, market, '_'),
|
||||
'order': None,
|
||||
'type': None,
|
||||
'side': 'buy' if (sideId == '1') else 'sell',
|
||||
'takerOrMaker': None,
|
||||
'price': self.safe_string(trade, 'contPrice'),
|
||||
'amount': self.safe_string(trade, 'contQty'),
|
||||
'cost': self.safe_string(trade, 'contAmt'),
|
||||
'fee': None,
|
||||
}, market)
|
||||
|
||||
def handle_error_message(self, client: Client, message) -> Bool:
|
||||
#
|
||||
# {
|
||||
# "status" : "5100",
|
||||
# "resmsg" : "Invalid Filter Syntax"
|
||||
# }
|
||||
#
|
||||
if not ('status' in message):
|
||||
return True
|
||||
errorCode = self.safe_string(message, 'status')
|
||||
try:
|
||||
if errorCode != '0000':
|
||||
msg = self.safe_string(message, 'resmsg')
|
||||
raise ExchangeError(self.id + ' ' + msg)
|
||||
return True
|
||||
except Exception as e:
|
||||
client.reject(e)
|
||||
return True
|
||||
|
||||
async def watch_balance(self, params={}) -> Balances:
|
||||
"""
|
||||
watch balance and get the amount of funds available for trading or funds locked in orders
|
||||
|
||||
https://apidocs.bithumb.com/v2.1.5/reference/%EB%82%B4-%EC%9E%90%EC%82%B0-myasset
|
||||
|
||||
: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.load_markets()
|
||||
await self.authenticate()
|
||||
url = self.urls['api']['ws']['privateV2']
|
||||
messageHash = 'myAsset'
|
||||
request = [
|
||||
{'ticket': 'ccxt'},
|
||||
{'type': messageHash},
|
||||
]
|
||||
balance = await self.watch(url, messageHash, request, messageHash)
|
||||
return balance
|
||||
|
||||
def handle_balance(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "type": "myAsset",
|
||||
# "assets": [
|
||||
# {
|
||||
# "currency": "KRW",
|
||||
# "balance": "2061832.35",
|
||||
# "locked": "3824127.3"
|
||||
# }
|
||||
# ],
|
||||
# "asset_timestamp": 1727052537592,
|
||||
# "timestamp": 1727052537687,
|
||||
# "stream_type": "REALTIME"
|
||||
# }
|
||||
#
|
||||
messageHash = 'myAsset'
|
||||
assets = self.safe_list(message, 'assets', [])
|
||||
if self.balance is None:
|
||||
self.balance = {}
|
||||
for i in range(0, len(assets)):
|
||||
asset = assets[i]
|
||||
currencyId = self.safe_string(asset, 'currency')
|
||||
code = self.safe_currency_code(currencyId)
|
||||
account = self.account()
|
||||
account['free'] = self.safe_string(asset, 'balance')
|
||||
account['used'] = self.safe_string(asset, 'locked')
|
||||
self.balance[code] = account
|
||||
self.balance['info'] = message
|
||||
timestamp = self.safe_integer(message, 'timestamp')
|
||||
self.balance['timestamp'] = timestamp
|
||||
self.balance['datetime'] = self.iso8601(timestamp)
|
||||
self.balance = self.safe_balance(self.balance)
|
||||
client.resolve(self.balance, messageHash)
|
||||
|
||||
async def authenticate(self, params={}):
|
||||
self.check_required_credentials()
|
||||
wsOptions: dict = self.safe_dict(self.options, 'ws', {})
|
||||
authenticated = self.safe_string(wsOptions, 'token')
|
||||
if authenticated is None:
|
||||
payload: dict = {
|
||||
'access_key': self.apiKey,
|
||||
'nonce': self.uuid(),
|
||||
'timestamp': self.milliseconds(),
|
||||
}
|
||||
jwtToken = self.jwt(payload, self.encode(self.secret), 'sha256')
|
||||
wsOptions['token'] = jwtToken
|
||||
wsOptions['options'] = {
|
||||
'headers': {
|
||||
'authorization': 'Bearer ' + jwtToken,
|
||||
},
|
||||
}
|
||||
self.options['ws'] = wsOptions
|
||||
url = self.urls['api']['ws']['privateV2']
|
||||
client = self.client(url)
|
||||
return client
|
||||
|
||||
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://apidocs.bithumb.com/v2.1.5/reference/%EB%82%B4-%EC%A3%BC%EB%AC%B8-%EB%B0%8F-%EC%B2%B4%EA%B2%B0-myorder
|
||||
|
||||
: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
|
||||
:param str[] [params.codes]: market codes to filter orders
|
||||
:returns dict[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
|
||||
"""
|
||||
await self.load_markets()
|
||||
await self.authenticate()
|
||||
url = self.urls['api']['ws']['privateV2']
|
||||
messageHash = 'myOrder'
|
||||
codes = self.safe_list(params, 'codes', [])
|
||||
request = [
|
||||
{'ticket': 'ccxt'},
|
||||
{'type': messageHash, 'codes': codes},
|
||||
]
|
||||
if symbol is not None:
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
messageHash = messageHash + ':' + symbol
|
||||
orders = await self.watch(url, messageHash, request, messageHash)
|
||||
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):
|
||||
#
|
||||
# {
|
||||
# "type": "myOrder",
|
||||
# "code": "KRW-BTC",
|
||||
# "uuid": "C0101000000001818113",
|
||||
# "ask_bid": "BID",
|
||||
# "order_type": "limit",
|
||||
# "state": "trade",
|
||||
# "trade_uuid": "C0101000000001744207",
|
||||
# "price": 1927000,
|
||||
# "volume": 0.4697,
|
||||
# "remaining_volume": 0.0803,
|
||||
# "executed_volume": 0.4697,
|
||||
# "trades_count": 1,
|
||||
# "reserved_fee": 0,
|
||||
# "remaining_fee": 0,
|
||||
# "paid_fee": 0,
|
||||
# "executed_funds": 905111.9000,
|
||||
# "trade_timestamp": 1727052318148,
|
||||
# "order_timestamp": 1727052318074,
|
||||
# "timestamp": 1727052318369,
|
||||
# "stream_type": "REALTIME"
|
||||
# }
|
||||
#
|
||||
messageHash = 'myOrder'
|
||||
parsed = self.parse_ws_order(message)
|
||||
symbol = self.safe_string(parsed, 'symbol')
|
||||
# orderId = self.safe_string(parsed, 'id')
|
||||
if self.orders is None:
|
||||
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
|
||||
self.orders = ArrayCacheBySymbolById(limit)
|
||||
cachedOrders = self.orders
|
||||
cachedOrders.append(parsed)
|
||||
client.resolve(cachedOrders, messageHash)
|
||||
symbolSpecificMessageHash = messageHash + ':' + symbol
|
||||
client.resolve(cachedOrders, symbolSpecificMessageHash)
|
||||
|
||||
def parse_ws_order(self, order, market=None):
|
||||
#
|
||||
# {
|
||||
# "type": "myOrder",
|
||||
# "code": "KRW-BTC",
|
||||
# "uuid": "C0101000000001818113",
|
||||
# "ask_bid": "BID",
|
||||
# "order_type": "limit",
|
||||
# "state": "trade",
|
||||
# "trade_uuid": "C0101000000001744207",
|
||||
# "price": 1927000,
|
||||
# "volume": 0.4697,
|
||||
# "remaining_volume": 0.0803,
|
||||
# "executed_volume": 0.4697,
|
||||
# "trades_count": 1,
|
||||
# "reserved_fee": 0,
|
||||
# "remaining_fee": 0,
|
||||
# "paid_fee": 0,
|
||||
# "executed_funds": 905111.9000,
|
||||
# "trade_timestamp": 1727052318148,
|
||||
# "order_timestamp": 1727052318074,
|
||||
# "timestamp": 1727052318369,
|
||||
# "stream_type": "REALTIME"
|
||||
# }
|
||||
#
|
||||
marketId = self.safe_string(order, 'code')
|
||||
symbol = self.safe_symbol(marketId, market, '-')
|
||||
timestamp = self.safe_integer(order, 'order_timestamp')
|
||||
sideId = self.safe_string(order, 'ask_bid')
|
||||
side = ('buy') if (sideId == 'BID') else ('sell')
|
||||
typeId = self.safe_string(order, 'order_type')
|
||||
type = None
|
||||
if typeId == 'limit':
|
||||
type = 'limit'
|
||||
elif typeId == 'price':
|
||||
type = 'market'
|
||||
elif typeId == 'market':
|
||||
type = 'market'
|
||||
stateId = self.safe_string(order, 'state')
|
||||
status = None
|
||||
if stateId == 'wait':
|
||||
status = 'open'
|
||||
elif stateId == 'trade':
|
||||
status = 'open'
|
||||
elif stateId == 'done':
|
||||
status = 'closed'
|
||||
elif stateId == 'cancel':
|
||||
status = 'canceled'
|
||||
price = self.safe_string(order, 'price')
|
||||
amount = self.safe_string(order, 'volume')
|
||||
remaining = self.safe_string(order, 'remaining_volume')
|
||||
filled = self.safe_string(order, 'executed_volume')
|
||||
cost = self.safe_string(order, 'executed_funds')
|
||||
feeCost = self.safe_string(order, 'paid_fee')
|
||||
fee = None
|
||||
if feeCost is not None:
|
||||
marketForFee = self.safe_market(marketId, market)
|
||||
feeCurrency = self.safe_string(marketForFee, 'quote')
|
||||
fee = {
|
||||
'cost': feeCost,
|
||||
'currency': feeCurrency,
|
||||
}
|
||||
return self.safe_order({
|
||||
'info': order,
|
||||
'id': self.safe_string(order, 'uuid'),
|
||||
'clientOrderId': None,
|
||||
'timestamp': timestamp,
|
||||
'datetime': self.iso8601(timestamp),
|
||||
'lastTradeTimestamp': self.safe_integer(order, 'trade_timestamp'),
|
||||
'symbol': symbol,
|
||||
'type': type,
|
||||
'timeInForce': None,
|
||||
'postOnly': None,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'stopPrice': None,
|
||||
'triggerPrice': None,
|
||||
'amount': amount,
|
||||
'cost': cost,
|
||||
'average': None,
|
||||
'filled': filled,
|
||||
'remaining': remaining,
|
||||
'status': status,
|
||||
'fee': fee,
|
||||
'trades': None,
|
||||
}, market)
|
||||
|
||||
def handle_message(self, client: Client, message):
|
||||
if not self.handle_error_message(client, message):
|
||||
return
|
||||
topic = self.safe_string(message, 'type')
|
||||
if topic is not None:
|
||||
methods: dict = {
|
||||
'ticker': self.handle_ticker,
|
||||
'orderbookdepth': self.handle_order_book,
|
||||
'transaction': self.handle_trades,
|
||||
'myAsset': self.handle_balance,
|
||||
'myOrder': self.handle_orders,
|
||||
}
|
||||
method = self.safe_value(methods, topic)
|
||||
if method is not None:
|
||||
method(client, message)
|
||||
1988
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/bitmart.py
Normal file
1988
dashboard/venv/lib/python3.12/site-packages/ccxt/pro/bitmart.py
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user