Compare commits
6 Commits
45788b0b53
...
a117177759
Author | SHA1 | Date |
---|---|---|
Nelson Torres | a117177759 | |
Nelson Torres | 30060a83c9 | |
Nelson Torres | 156a35b606 | |
Nelson Torres | 89e241c132 | |
Nelson Torres | df8d1274ae | |
Nelson Torres | 0916b707e2 |
|
@ -62,6 +62,11 @@ from .venues import (
|
||||||
PAIRTYPES,
|
PAIRTYPES,
|
||||||
Pair,
|
Pair,
|
||||||
OptionPair,
|
OptionPair,
|
||||||
|
JSONRPCResult,
|
||||||
|
JSONRPCChannel,
|
||||||
|
KLinesResult,
|
||||||
|
Trade,
|
||||||
|
LastTradesResult,
|
||||||
)
|
)
|
||||||
from piker.accounting import (
|
from piker.accounting import (
|
||||||
Asset,
|
Asset,
|
||||||
|
@ -95,55 +100,6 @@ _ws_url = 'wss://www.deribit.com/ws/api/v2'
|
||||||
_testnet_ws_url = 'wss://test.deribit.com/ws/api/v2'
|
_testnet_ws_url = 'wss://test.deribit.com/ws/api/v2'
|
||||||
|
|
||||||
|
|
||||||
class JSONRPCResult(Struct):
|
|
||||||
id: int
|
|
||||||
usIn: int
|
|
||||||
usOut: int
|
|
||||||
usDiff: int
|
|
||||||
testnet: bool
|
|
||||||
jsonrpc: str = '2.0'
|
|
||||||
result: Optional[list[dict]] = None
|
|
||||||
error: Optional[dict] = None
|
|
||||||
|
|
||||||
class JSONRPCChannel(Struct):
|
|
||||||
method: str
|
|
||||||
params: dict
|
|
||||||
jsonrpc: str = '2.0'
|
|
||||||
|
|
||||||
|
|
||||||
class KLinesResult(Struct):
|
|
||||||
close: list[float]
|
|
||||||
cost: list[float]
|
|
||||||
high: list[float]
|
|
||||||
low: list[float]
|
|
||||||
open: list[float]
|
|
||||||
status: str
|
|
||||||
ticks: list[int]
|
|
||||||
volume: list[float]
|
|
||||||
|
|
||||||
class Trade(Struct):
|
|
||||||
trade_seq: int
|
|
||||||
trade_id: str
|
|
||||||
timestamp: int
|
|
||||||
tick_direction: int
|
|
||||||
price: float
|
|
||||||
mark_price: float
|
|
||||||
iv: float
|
|
||||||
instrument_name: str
|
|
||||||
index_price: float
|
|
||||||
direction: str
|
|
||||||
contracts: float
|
|
||||||
amount: float
|
|
||||||
combo_trade_id: Optional[int] = 0,
|
|
||||||
combo_id: Optional[str] = '',
|
|
||||||
block_trade_leg_count: Optional[int] = 0,
|
|
||||||
block_trade_id: Optional[str] = '',
|
|
||||||
|
|
||||||
class LastTradesResult(Struct):
|
|
||||||
trades: list[Trade]
|
|
||||||
has_more: bool
|
|
||||||
|
|
||||||
|
|
||||||
# convert datetime obj timestamp to unixtime in milliseconds
|
# convert datetime obj timestamp to unixtime in milliseconds
|
||||||
def deribit_timestamp(when):
|
def deribit_timestamp(when):
|
||||||
return int((when.timestamp() * 1000) + (when.microsecond / 1000))
|
return int((when.timestamp() * 1000) + (when.microsecond / 1000))
|
||||||
|
@ -280,6 +236,39 @@ class Client:
|
||||||
|
|
||||||
self.json_rpc = json_rpc
|
self.json_rpc = json_rpc
|
||||||
|
|
||||||
|
self._auth_ts = None
|
||||||
|
self._auth_renew_ts = 5 # seconds to renew auth
|
||||||
|
|
||||||
|
async def _json_rpc_auth_wrapper(self, *args, **kwargs) -> JSONRPCResult:
|
||||||
|
|
||||||
|
"""Background task that adquires a first access token and then will
|
||||||
|
refresh the access token.
|
||||||
|
|
||||||
|
https://docs.deribit.com/?python#authentication-2
|
||||||
|
"""
|
||||||
|
access_scope = 'trade:read_write'
|
||||||
|
current_ts = time.time()
|
||||||
|
|
||||||
|
if not self._auth_ts or current_ts - self._auth_ts < self._auth_renew_ts:
|
||||||
|
# if we are close to token expiry time
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'grant_type': 'client_credentials',
|
||||||
|
'client_id': self._key_id,
|
||||||
|
'client_secret': self._key_secret,
|
||||||
|
'scope': access_scope
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = await self.json_rpc('public/auth', params)
|
||||||
|
result = resp.result
|
||||||
|
|
||||||
|
self._auth_ts = time.time() + result['expires_in']
|
||||||
|
|
||||||
|
return await self.json_rpc(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def get_balances(
|
async def get_balances(
|
||||||
self,
|
self,
|
||||||
kind: str = 'option'
|
kind: str = 'option'
|
||||||
|
@ -290,7 +279,7 @@ class Client:
|
||||||
balances = {}
|
balances = {}
|
||||||
|
|
||||||
for currency in self.currencies:
|
for currency in self.currencies:
|
||||||
resp = await self.json_rpc(
|
resp = await self._json_rpc_auth_wrapper(
|
||||||
'private/get_positions', params={
|
'private/get_positions', params={
|
||||||
'currency': currency.upper(),
|
'currency': currency.upper(),
|
||||||
'kind': kind})
|
'kind': kind})
|
||||||
|
@ -308,7 +297,7 @@ class Client:
|
||||||
by symbol.
|
by symbol.
|
||||||
"""
|
"""
|
||||||
assets = {}
|
assets = {}
|
||||||
resp = await self.json_rpc(
|
resp = await self._json_rpc_auth_wrapper(
|
||||||
'public/get_currencies',
|
'public/get_currencies',
|
||||||
params={}
|
params={}
|
||||||
)
|
)
|
||||||
|
@ -321,6 +310,15 @@ class Client:
|
||||||
name=name,
|
name=name,
|
||||||
atype=atype,
|
atype=atype,
|
||||||
tx_tick=tx_tick)
|
tx_tick=tx_tick)
|
||||||
|
|
||||||
|
instruments = await self.symbol_info(currency=name)
|
||||||
|
for instrument in instruments:
|
||||||
|
pair = instruments[instrument]
|
||||||
|
assets[pair.symbol] = Asset(
|
||||||
|
name=pair.symbol,
|
||||||
|
atype=pair.venue,
|
||||||
|
tx_tick=pair.size_tick)
|
||||||
|
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
async def get_mkt_pairs(self) -> dict[str, Pair]:
|
async def get_mkt_pairs(self) -> dict[str, Pair]:
|
||||||
|
@ -346,7 +344,7 @@ class Client:
|
||||||
'type': 'limit',
|
'type': 'limit',
|
||||||
'price': price,
|
'price': price,
|
||||||
}
|
}
|
||||||
resp = await self.json_rpc(
|
resp = await self._json_rpc_auth_wrapper(
|
||||||
f'private/{action}', params)
|
f'private/{action}', params)
|
||||||
|
|
||||||
return resp.result
|
return resp.result
|
||||||
|
@ -354,7 +352,7 @@ class Client:
|
||||||
async def submit_cancel(self, oid: str):
|
async def submit_cancel(self, oid: str):
|
||||||
"""Send cancel request for order id
|
"""Send cancel request for order id
|
||||||
"""
|
"""
|
||||||
resp = await self.json_rpc(
|
resp = await self._json_rpc_auth_wrapper(
|
||||||
'private/cancel', {'order_id': oid})
|
'private/cancel', {'order_id': oid})
|
||||||
return resp.result
|
return resp.result
|
||||||
|
|
||||||
|
@ -362,7 +360,7 @@ class Client:
|
||||||
self,
|
self,
|
||||||
sym: str | None = None,
|
sym: str | None = None,
|
||||||
|
|
||||||
venue: MarketType | None = None,
|
venue: MarketType = 'option',
|
||||||
expiry: str | None = None,
|
expiry: str | None = None,
|
||||||
|
|
||||||
) -> dict[str, Pair] | Pair:
|
) -> dict[str, Pair] | Pair:
|
||||||
|
@ -376,7 +374,7 @@ class Client:
|
||||||
return cached_pair
|
return cached_pair
|
||||||
|
|
||||||
if sym:
|
if sym:
|
||||||
return pair_table[sym.lower()]
|
return pair_table[sym]
|
||||||
else:
|
else:
|
||||||
return self._pairs
|
return self._pairs
|
||||||
|
|
||||||
|
@ -402,7 +400,7 @@ class Client:
|
||||||
'expired': str(expired).lower()
|
'expired': str(expired).lower()
|
||||||
}
|
}
|
||||||
|
|
||||||
resp: JSONRPCResult = await self.json_rpc(
|
resp: JSONRPCResult = await self._json_rpc_auth_wrapper(
|
||||||
'public/get_instruments',
|
'public/get_instruments',
|
||||||
params,
|
params,
|
||||||
)
|
)
|
||||||
|
@ -435,10 +433,38 @@ class Client:
|
||||||
|
|
||||||
async def cache_symbols(
|
async def cache_symbols(
|
||||||
self,
|
self,
|
||||||
) -> dict:
|
venue: MarketType = 'option',
|
||||||
|
|
||||||
if not self._pairs:
|
) -> None:
|
||||||
self._pairs = await self.symbol_info()
|
# lookup internal mkt-specific pair table to update
|
||||||
|
pair_table: dict[str, Pair] = self._pairs
|
||||||
|
|
||||||
|
# make API request(s)
|
||||||
|
mkt_pairs = await self.symbol_info()
|
||||||
|
|
||||||
|
if not mkt_pairs:
|
||||||
|
raise SymbolNotFound(f'No market pairs found!?:\n{resp}')
|
||||||
|
|
||||||
|
pairs_view_subtable: dict[str, Pair] = {}
|
||||||
|
|
||||||
|
for instrument in mkt_pairs:
|
||||||
|
pair_type: Type = PAIRTYPES[venue]
|
||||||
|
try:
|
||||||
|
pair: Pair = pair_type(**mkt_pairs[instrument].to_dict())
|
||||||
|
except Exception as e:
|
||||||
|
e.add_note(
|
||||||
|
"\nDon't panic, prolly stupid binance changed their symbology schema again..\n"
|
||||||
|
'Check out their API docs here:\n\n'
|
||||||
|
'https://binance-docs.github.io/apidocs/spot/en/#exchange-information'
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
pair_table[pair.symbol.upper()] = pair
|
||||||
|
|
||||||
|
# update an additional top-level-cross-venue-table
|
||||||
|
# `._pairs: ChainMap` for search B0
|
||||||
|
pairs_view_subtable[pair.bs_fqme] = pair
|
||||||
|
|
||||||
|
self._pairs.maps.append(pairs_view_subtable)
|
||||||
|
|
||||||
return self._pairs
|
return self._pairs
|
||||||
|
|
||||||
|
@ -471,7 +497,7 @@ class Client:
|
||||||
as_np: bool = True,
|
as_np: bool = True,
|
||||||
|
|
||||||
) -> list[tuple] | np.ndarray:
|
) -> list[tuple] | np.ndarray:
|
||||||
instrument: str = mkt.bs_mktid
|
instrument: str = mkt.bs_fqme.split('.')[0]
|
||||||
|
|
||||||
if end_dt is None:
|
if end_dt is None:
|
||||||
end_dt = now('UTC')
|
end_dt = now('UTC')
|
||||||
|
@ -484,7 +510,7 @@ class Client:
|
||||||
end_time = deribit_timestamp(end_dt)
|
end_time = deribit_timestamp(end_dt)
|
||||||
|
|
||||||
# https://docs.deribit.com/#public-get_tradingview_chart_data
|
# https://docs.deribit.com/#public-get_tradingview_chart_data
|
||||||
resp = await self.json_rpc(
|
resp = await self._json_rpc_auth_wrapper(
|
||||||
'public/get_tradingview_chart_data',
|
'public/get_tradingview_chart_data',
|
||||||
params={
|
params={
|
||||||
'instrument_name': instrument.upper(),
|
'instrument_name': instrument.upper(),
|
||||||
|
@ -521,7 +547,7 @@ class Client:
|
||||||
instrument: str,
|
instrument: str,
|
||||||
count: int = 10
|
count: int = 10
|
||||||
):
|
):
|
||||||
resp = await self.json_rpc(
|
resp = await self._json_rpc_auth_wrapper(
|
||||||
'public/get_last_trades_by_instrument',
|
'public/get_last_trades_by_instrument',
|
||||||
params={
|
params={
|
||||||
'instrument_name': instrument,
|
'instrument_name': instrument,
|
||||||
|
@ -533,7 +559,8 @@ class Client:
|
||||||
|
|
||||||
@acm
|
@acm
|
||||||
async def get_client(
|
async def get_client(
|
||||||
is_brokercheck: bool = False
|
is_brokercheck: bool = False,
|
||||||
|
venue: MarketType = 'option',
|
||||||
) -> Client:
|
) -> Client:
|
||||||
|
|
||||||
async with (
|
async with (
|
||||||
|
@ -543,68 +570,6 @@ async def get_client(
|
||||||
) as json_rpc
|
) as json_rpc
|
||||||
):
|
):
|
||||||
client = Client(json_rpc)
|
client = Client(json_rpc)
|
||||||
_refresh_token: Optional[str] = None
|
|
||||||
_access_token: Optional[str] = None
|
|
||||||
|
|
||||||
async def _auth_loop(
|
|
||||||
task_status: TaskStatus = trio.TASK_STATUS_IGNORED
|
|
||||||
):
|
|
||||||
"""Background task that adquires a first access token and then will
|
|
||||||
refresh the access token while the nursery isn't cancelled.
|
|
||||||
|
|
||||||
https://docs.deribit.com/?python#authentication-2
|
|
||||||
"""
|
|
||||||
renew_time = 10
|
|
||||||
access_scope = 'trade:read_write'
|
|
||||||
_expiry_time = time.time()
|
|
||||||
got_access = False
|
|
||||||
nonlocal _refresh_token
|
|
||||||
nonlocal _access_token
|
|
||||||
|
|
||||||
while True:
|
|
||||||
if time.time() - _expiry_time < renew_time:
|
|
||||||
# if we are close to token expiry time
|
|
||||||
|
|
||||||
if _refresh_token != None:
|
|
||||||
# if we have a refresh token already dont need to send
|
|
||||||
# secret
|
|
||||||
params = {
|
|
||||||
'grant_type': 'refresh_token',
|
|
||||||
'refresh_token': _refresh_token,
|
|
||||||
'scope': access_scope
|
|
||||||
}
|
|
||||||
|
|
||||||
else:
|
|
||||||
# we don't have refresh token, send secret to initialize
|
|
||||||
params = {
|
|
||||||
'grant_type': 'client_credentials',
|
|
||||||
'client_id': client._key_id,
|
|
||||||
'client_secret': client._key_secret,
|
|
||||||
'scope': access_scope
|
|
||||||
}
|
|
||||||
|
|
||||||
resp = await json_rpc('public/auth', params)
|
|
||||||
result = resp.result
|
|
||||||
|
|
||||||
_expiry_time = time.time() + result['expires_in']
|
|
||||||
_refresh_token = result['refresh_token']
|
|
||||||
|
|
||||||
if 'access_token' in result:
|
|
||||||
_access_token = result['access_token']
|
|
||||||
|
|
||||||
if not got_access:
|
|
||||||
# first time this loop runs we must indicate task is
|
|
||||||
# started, we have auth
|
|
||||||
got_access = True
|
|
||||||
task_status.started()
|
|
||||||
|
|
||||||
else:
|
|
||||||
await trio.sleep(renew_time / 2)
|
|
||||||
|
|
||||||
# if we have client creds launch auth loop
|
|
||||||
if client._key_id is not None:
|
|
||||||
await n.start(_auth_loop)
|
|
||||||
|
|
||||||
await client.cache_symbols()
|
await client.cache_symbols()
|
||||||
yield client
|
yield client
|
||||||
n.cancel_scope.cancel()
|
n.cancel_scope.cancel()
|
||||||
|
|
|
@ -160,38 +160,26 @@ async def get_mkt_info(
|
||||||
assets: dict[str, Asset] = await client.get_assets()
|
assets: dict[str, Asset] = await client.get_assets()
|
||||||
pair_str: str = mkt_ep.lower()
|
pair_str: str = mkt_ep.lower()
|
||||||
|
|
||||||
# switch venue-mode depending on input pattern parsing
|
|
||||||
# since we want to use a particular endpoint (set) for
|
|
||||||
# pair info lookup!
|
|
||||||
client.mkt_mode = mkt_mode
|
|
||||||
|
|
||||||
pair: Pair = await client.exch_info(
|
pair: Pair = await client.exch_info(
|
||||||
sym=pair_str,
|
sym=pair_str,
|
||||||
)
|
)
|
||||||
dst: Asset | None = assets.get(pair.bs_dst_asset)
|
mkt_mode = pair.venue
|
||||||
if (
|
client.mkt_mode = mkt_mode
|
||||||
not dst
|
|
||||||
# TODO: a known asset DNE list?
|
|
||||||
# and pair.baseAsset == 'DEFI'
|
|
||||||
):
|
|
||||||
log.warning(
|
|
||||||
f'UNKNOWN {venue} asset {pair.base_currency} from,\n'
|
|
||||||
f'{pformat(pair.to_dict())}'
|
|
||||||
)
|
|
||||||
|
|
||||||
# XXX UNKNOWN missing "asset", though no idea why?
|
dst: Asset | None = assets.get(pair.bs_dst_asset)
|
||||||
# maybe it's only avail in the margin venue(s): /dapi/ ?
|
src: Asset | None = assets.get(pair.bs_src_asset)
|
||||||
return None
|
|
||||||
|
|
||||||
mkt = MktPair(
|
mkt = MktPair(
|
||||||
dst=dst,
|
dst=dst,
|
||||||
src=assets.get(pair.bs_src_asset),
|
src=src,
|
||||||
price_tick=pair.price_tick,
|
price_tick=pair.price_tick,
|
||||||
size_tick=pair.size_tick,
|
size_tick=pair.size_tick,
|
||||||
bs_mktid=pair.symbol,
|
bs_mktid=pair.symbol,
|
||||||
expiry=expiry,
|
expiry=pair.expiry,
|
||||||
venue=venue,
|
venue=mkt_mode,
|
||||||
broker='deribit',
|
broker='deribit',
|
||||||
|
_atype=mkt_mode,
|
||||||
|
_fqme_without_src=True,
|
||||||
)
|
)
|
||||||
return mkt, pair
|
return mkt, pair
|
||||||
|
|
||||||
|
@ -210,7 +198,7 @@ async def stream_quotes(
|
||||||
# XXX: required to propagate ``tractor`` loglevel to piker logging
|
# XXX: required to propagate ``tractor`` loglevel to piker logging
|
||||||
get_console_log(loglevel or tractor.current_actor().loglevel)
|
get_console_log(loglevel or tractor.current_actor().loglevel)
|
||||||
|
|
||||||
sym = symbols[0]
|
sym = symbols[0].split('.')[0]
|
||||||
|
|
||||||
init_msgs: list[FeedInit] = []
|
init_msgs: list[FeedInit] = []
|
||||||
|
|
||||||
|
@ -225,11 +213,11 @@ async def stream_quotes(
|
||||||
init_msgs.append(
|
init_msgs.append(
|
||||||
FeedInit(mkt_info=mkt)
|
FeedInit(mkt_info=mkt)
|
||||||
)
|
)
|
||||||
nsym = piker_sym_to_cb_sym(sym.split('.')[0])
|
nsym = piker_sym_to_cb_sym(sym)
|
||||||
|
|
||||||
async with maybe_open_price_feed(sym) as stream:
|
async with maybe_open_price_feed(sym) as stream:
|
||||||
|
|
||||||
cache = await client.cache_symbols()
|
cache = client._pairs
|
||||||
|
|
||||||
last_trades = (await client.last_trades(
|
last_trades = (await client.last_trades(
|
||||||
cb_sym_to_deribit_inst(nsym), count=1)).trades
|
cb_sym_to_deribit_inst(nsym), count=1)).trades
|
||||||
|
@ -271,7 +259,7 @@ async def open_symbol_search(
|
||||||
async with open_cached_client('deribit') as client:
|
async with open_cached_client('deribit') as client:
|
||||||
|
|
||||||
# load all symbols locally for fast search
|
# load all symbols locally for fast search
|
||||||
cache = await client.cache_symbols()
|
cache = client._pairs
|
||||||
await ctx.started()
|
await ctx.started()
|
||||||
|
|
||||||
async with ctx.open_stream() as stream:
|
async with ctx.open_stream() as stream:
|
||||||
|
|
|
@ -19,6 +19,7 @@ Per market data-type definitions and schemas types.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import pendulum
|
||||||
from typing import (
|
from typing import (
|
||||||
Literal,
|
Literal,
|
||||||
)
|
)
|
||||||
|
@ -66,29 +67,27 @@ class Pair(Struct, frozen=True, kw_only=True):
|
||||||
# dst
|
# dst
|
||||||
base_currency: str # "BTC",
|
base_currency: str # "BTC",
|
||||||
|
|
||||||
tick_size: float # 0.0001
|
tick_size: float # 0.0001 # [{'above_price': 0.005, 'tick_size': 0.0005}]
|
||||||
tick_size_steps: list[dict[str, str | int | float]] # [{'above_price': 0.005, 'tick_size': 0.0005}]
|
tick_size_steps: list[dict[str, float]]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def price_tick(self) -> Decimal:
|
def price_tick(self) -> Decimal:
|
||||||
step_size: float = self.tick_size_steps[0].get('above_price')
|
return Decimal(str(self.tick_size_steps[0]['above_price']))
|
||||||
return Decimal(step_size)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def size_tick(self) -> Decimal:
|
def size_tick(self) -> Decimal:
|
||||||
step_size: float = self.tick_size_steps[0].get('tick_size')
|
return Decimal(str(self.tick_size))
|
||||||
return Decimal(step_size)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bs_fqme(self) -> str:
|
def bs_fqme(self) -> str:
|
||||||
return self.symbol
|
return f'{self.symbol}'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bs_mktid(self) -> str:
|
def bs_mktid(self) -> str:
|
||||||
return f'{self.symbol}'
|
return f'{self.symbol}.{self.venue}'
|
||||||
|
|
||||||
|
|
||||||
class OptionPair(Pair, frozen=True, kw_only=True):
|
class OptionPair(Pair, frozen=True):
|
||||||
|
|
||||||
taker_commission: float # 0.0003
|
taker_commission: float # 0.0003
|
||||||
strike: float # 5000.0
|
strike: float # 5000.0
|
||||||
|
@ -118,17 +117,16 @@ class OptionPair(Pair, frozen=True, kw_only=True):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def expiry(self) -> str:
|
def expiry(self) -> str:
|
||||||
symbol: str = self.instrument_name.lower()
|
iso_date = pendulum.from_timestamp(self.expiration_timestamp / 1000).isoformat()
|
||||||
pair, expiry, strike_price, otype = symbol.split('-')
|
return iso_date
|
||||||
return f'{expiry}'
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def venue(self) -> str:
|
def venue(self) -> str:
|
||||||
return 'OPTION'
|
return 'option'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bs_fqme(self) -> str:
|
def bs_fqme(self) -> str:
|
||||||
return f'{self.symbol}.{self.venue}.{self.expiry}'
|
return f'{self.symbol}'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bs_src_asset(self) -> str:
|
def bs_src_asset(self) -> str:
|
||||||
|
@ -136,13 +134,58 @@ class OptionPair(Pair, frozen=True, kw_only=True):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bs_dst_asset(self) -> str:
|
def bs_dst_asset(self) -> str:
|
||||||
return f'{self.base_currency}'
|
|
||||||
|
|
||||||
@property
|
|
||||||
def bs_mktid(self) -> str:
|
|
||||||
return f'{self.symbol}'
|
return f'{self.symbol}'
|
||||||
|
|
||||||
|
|
||||||
PAIRTYPES: dict[MarketType, Pair] = {
|
PAIRTYPES: dict[MarketType, Pair] = {
|
||||||
'option': OptionPair,
|
'option': OptionPair,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class JSONRPCResult(Struct):
|
||||||
|
id: int
|
||||||
|
usIn: int
|
||||||
|
usOut: int
|
||||||
|
usDiff: int
|
||||||
|
testnet: bool
|
||||||
|
jsonrpc: str = '2.0'
|
||||||
|
error: Optional[dict] = None
|
||||||
|
result: Optional[list[dict]] = None
|
||||||
|
|
||||||
|
class JSONRPCChannel(Struct):
|
||||||
|
method: str
|
||||||
|
params: dict
|
||||||
|
jsonrpc: str = '2.0'
|
||||||
|
|
||||||
|
|
||||||
|
class KLinesResult(Struct):
|
||||||
|
low: list[float]
|
||||||
|
cost: list[float]
|
||||||
|
high: list[float]
|
||||||
|
open: list[float]
|
||||||
|
close: list[float]
|
||||||
|
ticks: list[int]
|
||||||
|
status: str
|
||||||
|
volume: list[float]
|
||||||
|
|
||||||
|
class Trade(Struct):
|
||||||
|
iv: float
|
||||||
|
price: float
|
||||||
|
amount: float
|
||||||
|
trade_id: str
|
||||||
|
contracts: float
|
||||||
|
direction: str
|
||||||
|
trade_seq: int
|
||||||
|
timestamp: int
|
||||||
|
mark_price: float
|
||||||
|
index_price: float
|
||||||
|
tick_direction: int
|
||||||
|
instrument_name: str
|
||||||
|
combo_id: Optional[str] = '',
|
||||||
|
combo_trade_id: Optional[int] = 0,
|
||||||
|
block_trade_id: Optional[str] = '',
|
||||||
|
block_trade_leg_count: Optional[int] = 0,
|
||||||
|
|
||||||
|
class LastTradesResult(Struct):
|
||||||
|
trades: list[Trade]
|
||||||
|
has_more: bool
|
||||||
|
|
Loading…
Reference in New Issue