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

559 lines
23 KiB
Python

# -*- coding: utf-8 -*-
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
import ccxt.async_support
from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById
from ccxt.base.types import Any, Balances, 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 NotSupported
class probit(ccxt.async_support.probit):
def describe(self) -> Any:
return self.deep_extend(super(probit, self).describe(), {
'has': {
'ws': True,
'watchBalance': True,
'watchTicker': True,
'watchTickers': False,
'watchTrades': True,
'watchTradesForSymbols': False,
'watchMyTrades': True,
'watchOrders': True,
'watchOrderBook': True,
'watchOHLCV': False,
},
'urls': {
'api': {
'ws': 'wss://api.probit.com/api/exchange/v1/ws',
},
'test': {
'ws': 'wss://demo-api.probit.com/api/exchange/v1/ws',
},
},
'options': {
'watchOrderBook': {
'filter': 'order_books_l2',
'interval': 100, # or 500
},
},
'streaming': {
},
})
async def watch_balance(self, params={}) -> Balances:
"""
watch balance and get the amount of funds available for trading or funds locked in orders
https://docs-en.probit.com/reference/balance-1
:param dict [params]: extra parameters specific to the exchange API endpoint
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
"""
await self.authenticate(params)
messageHash = 'balance'
return await self.subscribe_private(messageHash, 'balance', params)
def handle_balance(self, client: Client, message):
#
# {
# "channel": "balance",
# "reset": False,
# "data": {
# "USDT": {
# "available": "15",
# "total": "15"
# }
# }
# }
#
messageHash = 'balance'
self.parse_ws_balance(message)
client.resolve(self.balance, messageHash)
def parse_ws_balance(self, message):
#
# {
# "channel": "balance",
# "reset": False,
# "data": {
# "USDT": {
# "available": "15",
# "total": "15"
# }
# }
# }
#
reset = self.safe_bool(message, 'reset', False)
data = self.safe_value(message, 'data', {})
currencyIds = list(data.keys())
if reset:
self.balance = {}
for i in range(0, len(currencyIds)):
currencyId = currencyIds[i]
entry = data[currencyId]
code = self.safe_currency_code(currencyId)
account = self.account()
account['free'] = self.safe_string(entry, 'available')
account['total'] = self.safe_string(entry, 'total')
self.balance[code] = account
self.balance = self.safe_balance(self.balance)
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-en.probit.com/reference/marketdata
: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 int [params.interval]: Unit time to synchronize market information(ms). Available units: 100, 500
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
"""
channel = 'ticker'
return await self.subscribe_public('watchTicker', symbol, 'ticker', channel, params)
def handle_ticker(self, client: Client, message):
#
# {
# "channel": "marketdata",
# "market_id": "BTC-USDT",
# "status": "ok",
# "lag": 0,
# "ticker": {
# "time": "2022-07-21T14:18:04.000Z",
# "last": "22591.3",
# "low": "22500.1",
# "high": "39790.7",
# "change": "-1224",
# "base_volume": "1002.32005445",
# "quote_volume": "23304489.385351021"
# },
# "reset": True
# }
#
marketId = self.safe_string(message, 'market_id')
symbol = self.safe_symbol(marketId)
ticker = self.safe_value(message, 'ticker', {})
market = self.safe_market(marketId)
parsedTicker = self.parse_ticker(ticker, market)
messageHash = 'ticker:' + symbol
self.tickers[symbol] = parsedTicker
client.resolve(parsedTicker, messageHash)
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
"""
get the list of most recent trades for a particular symbol
https://docs-en.probit.com/reference/trade_history
:param str symbol: unified symbol of the market to fetch trades for
:param int [since]: timestamp in ms of the earliest trade to fetch
:param int [limit]: the maximum amount of trades to fetch
:param dict [params]: extra parameters specific to the exchange API endpoint
:param int [params.interval]: Unit time to synchronize market information(ms). Available units: 100, 500
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
"""
channel = 'recent_trades'
symbol = self.safe_symbol(symbol)
trades = await self.subscribe_public('watchTrades', symbol, 'trades', channel, params)
if self.newUpdates:
limit = trades.getLimit(symbol, limit)
return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
def handle_trades(self, client: Client, message):
#
# {
# "channel": "marketdata",
# "market_id": "BTC-USDT",
# "status": "ok",
# "lag": 0,
# "recent_trades": [
# {
# "id": "BTC-USDT:8010233",
# "price": "22701.4",
# "quantity": "0.011011",
# "time": "2022-07-21T13:40:40.983Z",
# "side": "buy",
# "tick_direction": "up"
# }
# ...
# ]
# "reset": True
# }
#
marketId = self.safe_string(message, 'market_id')
symbol = self.safe_symbol(marketId)
market = self.safe_market(marketId)
trades = self.safe_value(message, 'recent_trades', [])
if self.safe_bool(message, 'reset', False):
return # see comment in handleMessage
messageHash = 'trades:' + symbol
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
for i in range(0, len(trades)):
trade = trades[i]
parsed = self.parse_trade(trade, market)
stored.append(parsed)
self.trades[symbol] = stored
client.resolve(self.trades[symbol], messageHash)
async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
"""
get the list of trades associated with the user
https://docs-en.probit.com/reference/trade_history
:param str symbol: unified symbol of the market to fetch trades for
:param int [since]: timestamp in ms of the earliest trade to fetch
:param int [limit]: the maximum amount of trades to fetch
:param dict [params]: extra parameters specific to the exchange API endpoint
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
"""
await self.load_markets()
await self.authenticate(params)
messageHash = 'trades'
if symbol is not None:
symbol = self.safe_symbol(symbol)
messageHash = messageHash + ':' + symbol
trades = await self.subscribe_private(messageHash, 'trade_history', params)
if self.newUpdates:
limit = trades.getLimit(symbol, limit)
return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
def handle_my_trades(self, client: Client, message):
#
# {
# "channel": "trade_history",
# "reset": False,
# "data": [{
# "id": "BTC-USDT:8010722",
# "order_id": "4124999207",
# "side": "buy",
# "fee_amount": "0.0134999868096",
# "fee_currency_id": "USDT",
# "status": "settled",
# "price": "23136.7",
# "quantity": "0.00032416",
# "cost": "7.499992672",
# "time": "2022-07-21T17:09:33.056Z",
# "market_id": "BTC-USDT"
# }]
# }
#
rawTrades = self.safe_value(message, 'data', [])
length = len(rawTrades)
if length == 0:
return
if self.safe_bool(message, 'reset', False):
return # see comment in handleMessage
messageHash = 'trades'
stored = self.myTrades
if stored is None:
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
stored = ArrayCacheBySymbolById(limit)
self.myTrades = stored
trades = self.parse_trades(rawTrades)
tradeSymbols: dict = {}
for j in range(0, len(trades)):
trade = trades[j]
# don't include 'executed' state, because it's just blanket state of the trade, emited before actual trade event
if self.safe_string(trade['info'], 'status') == 'executed':
continue
tradeSymbols[trade['symbol']] = True
stored.append(trade)
unique = list(tradeSymbols.keys())
uniqueLength = len(unique)
if uniqueLength == 0:
return
for i in range(0, len(unique)):
symbol = unique[i]
symbolSpecificMessageHash = messageHash + ':' + symbol
client.resolve(stored, symbolSpecificMessageHash)
client.resolve(stored, messageHash)
async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
"""
watches information on an order made by the user
https://docs-en.probit.com/reference/open_order
:param str symbol: unified symbol of the market the order was made in
:param int [since]: timestamp in ms of the earliest order to watch
:param int [limit]: the maximum amount of orders to watch
:param dict [params]: extra parameters specific to the exchange API endpoint
:param str [params.channel]: choose what channel to use. Can open_order or order_history.
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
"""
await self.authenticate(params)
messageHash = 'orders'
if symbol is not None:
symbol = self.safe_symbol(symbol)
messageHash = messageHash + ':' + symbol
orders = await self.subscribe_private(messageHash, 'open_order', params)
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):
#
# {
# "channel": "order_history",
# "reset": True,
# "data": [{
# "id": "4124999207",
# "user_id": "633dc56a-621b-4680-8a4e-85a823499b6d",
# "market_id": "BTC-USDT",
# "type": "market",
# "side": "buy",
# "limit_price": "0",
# "time_in_force": "ioc",
# "filled_cost": "7.499992672",
# "filled_quantity": "0.00032416",
# "open_quantity": "0",
# "status": "filled",
# "time": "2022-07-21T17:09:33.056Z",
# "client_order_id": '',
# "cost": "7.5"
# },
# ...
# ]
# }
#
rawOrders = self.safe_value(message, 'data', [])
length = len(rawOrders)
if length == 0:
return
messageHash = 'orders'
reset = self.safe_bool(message, 'reset', False)
stored = self.orders
if stored is None or reset:
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
stored = ArrayCacheBySymbolById(limit)
self.orders = stored
orderSymbols: dict = {}
for i in range(0, len(rawOrders)):
rawOrder = rawOrders[i]
order = self.parse_order(rawOrder)
orderSymbols[order['symbol']] = True
stored.append(order)
unique = list(orderSymbols.keys())
for i in range(0, len(unique)):
symbol = unique[i]
symbolSpecificMessageHash = messageHash + ':' + symbol
client.resolve(stored, symbolSpecificMessageHash)
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-en.probit.com/reference/marketdata
: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
"""
channel = None
channel, params = self.handle_option_and_params(params, 'watchOrderBook', 'filter', 'order_books')
orderbook = await self.subscribe_public('watchOrderBook', symbol, 'orderbook', channel, params)
return orderbook.limit()
async def subscribe_private(self, messageHash, channel, params):
url = self.urls['api']['ws']
subscribe: dict = {
'type': 'subscribe',
'channel': channel,
}
request = self.extend(subscribe, params)
subscribeHash = messageHash
return await self.watch(url, messageHash, request, subscribeHash)
async def subscribe_public(self, methodName: str, symbol: str, dataType, filter, params={}):
await self.load_markets()
market = self.market(symbol)
symbol = market['symbol']
url = self.urls['api']['ws']
client = self.client(url)
subscribeHash = 'marketdata:' + symbol
messageHash = dataType + ':' + symbol
filters = {}
if subscribeHash in client.subscriptions:
# already subscribed
filters = client.subscriptions[subscribeHash]
if not (filter in filters):
# resubscribe
del client.subscriptions[subscribeHash]
filters[filter] = True
keys = list(filters.keys())
interval = None
interval, params = self.handle_option_and_params(params, methodName, 'interval', 100)
request: dict = {
'type': 'subscribe',
'channel': 'marketdata',
'market_id': market['id'],
'filter': keys,
'interval': interval,
}
request = self.extend(request, params)
return await self.watch(url, messageHash, request, subscribeHash, filters)
def handle_order_book(self, client: Client, message, orderBook):
#
# {
# "channel": "marketdata",
# "market_id": "BTC-USDT",
# "status": "ok",
# "lag": 0,
# "order_books": [
# {side: "buy", price: '1420.7', quantity: "0.057"},
# ...
# ],
# "reset": True
# }
#
marketId = self.safe_string(message, 'market_id')
symbol = self.safe_symbol(marketId)
dataBySide = self.group_by(orderBook, 'side')
messageHash = 'orderbook:' + symbol
# orderbook = self.safe_value(self.orderbooks, symbol)
if not (symbol in self.orderbooks):
self.orderbooks[symbol] = self.order_book({})
orderbook = self.orderbooks[symbol]
reset = self.safe_bool(message, 'reset', False)
if reset:
snapshot = self.parse_order_book(dataBySide, symbol, None, 'buy', 'sell', 'price', 'quantity')
orderbook.reset(snapshot)
else:
self.handle_delta(orderbook, dataBySide)
client.resolve(orderbook, messageHash)
def handle_bid_asks(self, bookSide, bidAsks):
for i in range(0, len(bidAsks)):
bidAsk = bidAsks[i]
parsed = self.parse_bid_ask(bidAsk, 'price', 'quantity')
bookSide.storeArray(parsed)
def handle_delta(self, orderbook, delta):
storedBids = orderbook['bids']
storedAsks = orderbook['asks']
asks = self.safe_value(delta, 'sell', [])
bids = self.safe_value(delta, 'buy', [])
self.handle_bid_asks(storedBids, bids)
self.handle_bid_asks(storedAsks, asks)
def handle_error_message(self, client: Client, message) -> Bool:
#
# {
# "errorCode": "INVALID_ARGUMENT",
# "message": '',
# "details": {
# "interval": "invalid"
# }
# }
#
code = self.safe_string(message, 'errorCode')
errMessage = self.safe_string(message, 'message', '')
details = self.safe_value(message, 'details')
feedback = self.id + ' ' + code + ' ' + errMessage + ' ' + self.json(details)
if 'exact' in self.exceptions:
self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
if 'broad' in self.exceptions:
self.throw_broadly_matched_exception(self.exceptions['broad'], errMessage, feedback)
raise ExchangeError(feedback)
def handle_authenticate(self, client: Client, message):
#
# {type: "authorization", result: "ok"}
#
result = self.safe_string(message, 'result')
future = client.subscriptions['authenticated']
if result == 'ok':
messageHash = 'authenticated'
client.resolve(message, messageHash)
else:
future.reject(message)
del client.subscriptions['authenticated']
def handle_market_data(self, client: Client, message):
ticker = self.safe_value(message, 'ticker')
if ticker is not None:
self.handle_ticker(client, message)
trades = self.safe_value(message, 'recent_trades', [])
tradesLength = len(trades)
if tradesLength:
self.handle_trades(client, message)
orderBook = self.safe_value_n(message, ['order_books', 'order_books_l1', 'order_books_l2', 'order_books_l3', 'order_books_l4'], [])
orderBookLength = len(orderBook)
if orderBookLength:
self.handle_order_book(client, message, orderBook)
def handle_message(self, client: Client, message):
#
# {
# "errorCode": "INVALID_ARGUMENT",
# "message": '',
# "details": {
# "interval": "invalid"
# }
# }
#
# Note about 'reset' field
# 'reset': True field - it happens once after initial subscription, which just returns old items by the moment of subscription(like "fetchMyTrades" does)
#
errorCode = self.safe_string(message, 'errorCode')
if errorCode is not None:
self.handle_error_message(client, message)
return
type = self.safe_string(message, 'type')
if type == 'authorization':
self.handle_authenticate(client, message)
return
handlers: dict = {
'marketdata': self.handle_market_data,
'balance': self.handle_balance,
'trade_history': self.handle_my_trades,
'open_order': self.handle_orders,
'order_history': self.handle_orders,
}
channel = self.safe_string(message, 'channel')
handler = self.safe_value(handlers, channel)
if handler is not None:
handler(client, message)
return
error = NotSupported(self.id + ' handleMessage: unknown message: ' + self.json(message))
client.reject(error)
async def authenticate(self, params={}):
url = self.urls['api']['ws']
client = self.client(url)
messageHash = 'authenticated'
expires = self.safe_integer(self.options, 'expires', 0)
future = self.safe_value(client.subscriptions, messageHash)
if (future is None) or (self.milliseconds() > expires):
response = await self.sign_in()
#
# {
# "access_token": "0ttDv/2hTTn3bLi8GP1gKaneiEQ6+0hOBenPrxNQt2s=",
# "token_type": "bearer",
# "expires_in": 900
# }
#
accessToken = self.safe_string(response, 'access_token')
request: dict = {
'type': 'authorization',
'token': accessToken,
}
future = await self.watch(url, messageHash, self.extend(request, params), messageHash)
client.subscriptions[messageHash] = future
return future