Initial commit: 首次建仓,建立目录结构
This commit is contained in:
@ -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)
|
||||
Reference in New Issue
Block a user