Compare commits

..

10 Commits

Author SHA1 Message Date
Nelson Torres 5de14bc3f9 added first calcs for max_pain 2024-12-03 14:35:57 -03:00
Nelson Torres 6e08c60d53 deactivate cryptofeed debug log 2024-12-03 14:29:35 -03:00
Nelson Torres a9110b1196 Update api.py
Now we get the data for an e specific expiration date if and expiry_date is passed, or for all expiration if expiry_date is None
2024-12-02 09:47:20 -03:00
Nelson Torres 9abbd8ca1e Update api.py
open_interest for each strike price and each expiry date
2024-11-29 10:49:07 -03:00
Nelson Torres 24bf19c1fb Update api.py
get_currencies function
2024-11-29 10:41:20 -03:00
Nelson Torres 4ccda643b8 Update api.py
covers cases when option_type comes as PUT or P, same with calls CALL or C
2024-11-29 10:39:48 -03:00
Nelson Torres 7b02df9b49 Update api.py
This function receive a date in this format DDMMMYY and returns timestamps int
2024-11-29 10:38:17 -03:00
Nelson Torres f2f7855e54 max_pain_daemon 2024-11-26 15:16:21 -03:00
Nelson Torres 16480bd2d4 maybe_open_oi_feed 2024-11-26 15:15:59 -03:00
Nelson Torres 733b58250f open_oi_feed 2024-11-26 15:15:51 -03:00
2 changed files with 197 additions and 28 deletions

27
max_pain.py 100644
View File

@ -0,0 +1,27 @@
#!/usr/bin/env python
import trio
import tractor
from piker.brokers.deribit.api import (
maybe_open_oi_feed,
)
async def max_pain_daemon(
) -> None:
async with maybe_open_oi_feed() as oi_feed:
print('Im in...')
async def main():
async with tractor.open_nursery() as n:
p: tractor.Portal = await n.start_actor(
'max_pain_daemon',
enable_modules=[__name__],
infect_asyncio=True,
)
await p.run(max_pain_daemon)
if __name__ == '__main__':
trio.run(main)

View File

@ -112,6 +112,10 @@ def deribit_timestamp(when: datetime) -> int:
) )
def get_timestamp_int(expiry_date: str) -> int:
return int(time.mktime(time.strptime(expiry_date, '%d%b%y')))
def str_to_cb_sym(name: str) -> Symbol: def str_to_cb_sym(name: str) -> Symbol:
base, strike_price, expiry_date, option_type = name.split('-') base, strike_price, expiry_date, option_type = name.split('-')
@ -124,8 +128,9 @@ def str_to_cb_sym(name: str) -> Symbol:
else: else:
raise Exception("Couldn\'t parse option type") raise Exception("Couldn\'t parse option type")
new_expiry_date = get_values_from_cb_normalized_date(expiry_date) new_expiry_date: int = get_timestamp_int(
get_values_from_cb_normalized_date(expiry_date)
)
return Symbol( return Symbol(
base=base, base=base,
quote=quote, quote=quote,
@ -145,11 +150,12 @@ def piker_sym_to_cb_sym(name: str) -> Symbol:
)= tuple( )= tuple(
name.upper().split('-')) name.upper().split('-'))
new_expiry_date = get_timestamp_int(expiry_date)
quote: str = base quote: str = base
if option_type == 'P': if option_type == 'P' or option_type == 'PUT':
option_type = PUT option_type = PUT
elif option_type == 'C': elif option_type == 'C' or option_type == 'CALL':
option_type = CALL option_type = CALL
else: else:
raise Exception("Couldn\'t parse option type") raise Exception("Couldn\'t parse option type")
@ -160,7 +166,7 @@ def piker_sym_to_cb_sym(name: str) -> Symbol:
type=OPTION, type=OPTION,
strike_price=strike_price, strike_price=strike_price,
option_type=option_type, option_type=option_type,
expiry_date=expiry_date expiry_date=new_expiry_date
) )
@ -236,6 +242,7 @@ def get_config() -> dict[str, Any]:
section['log'] = {} section['log'] = {}
section['log']['filename'] = 'feedhandler.log' section['log']['filename'] = 'feedhandler.log'
section['log']['level'] = 'DEBUG' section['log']['level'] = 'DEBUG'
section['log']['disabled'] = True
return section return section
@ -313,6 +320,20 @@ class Client:
return balances return balances
async def get_currencies(
self,
) -> list[dict]:
'''
Return the set of currencies for deribit.
'''
assets = {}
resp = await self._json_rpc_auth_wrapper(
'public/get_currencies',
params={}
)
return resp.result
async def get_assets( async def get_assets(
self, self,
venue: str | None = None, venue: str | None = None,
@ -325,11 +346,7 @@ class Client:
''' '''
assets = {} assets = {}
resp = await self._json_rpc_auth_wrapper( currencies = await self.get_currencies()
'public/get_currencies',
params={}
)
currencies: list[dict] = resp.result
for currency in currencies: for currency in currencies:
name: str = currency['currency'] name: str = currency['currency']
tx_tick: Decimal = digits_to_dec(currency['fee_precision']) tx_tick: Decimal = digits_to_dec(currency['fee_precision'])
@ -366,6 +383,7 @@ class Client:
currency: str = 'btc', currency: str = 'btc',
kind: str = 'option', kind: str = 'option',
expired: bool = False, expired: bool = False,
expiry_date: str = None,
) -> list[Symbol]: ) -> list[Symbol]:
""" """
@ -384,8 +402,10 @@ class Client:
resp = r.result resp = r.result
response_list = [] response_list = []
for i in range(len(resp) // 10): for i in range(len(resp)):
element = resp[i] element = resp[i]
name = f'{element["instrument_name"].split("-")[1]}'
if not expiry_date or name == expiry_date.upper():
response_list.append(piker_sym_to_cb_sym(element['instrument_name'])) response_list.append(piker_sym_to_cb_sym(element['instrument_name']))
return response_list return response_list
@ -772,6 +792,10 @@ async def maybe_open_price_feed(
async def aio_open_interest_feed_relay( async def aio_open_interest_feed_relay(
fh: FeedHandler, fh: FeedHandler,
instruments: list, instruments: list,
open_interests: dict[str, dict[str, dict[str, list[dict[str, Decimal]]]]],
losses_cache: dict[str, Decimal],
max_losses: Decimal,
max_pain: Decimal,
from_trio: asyncio.Queue, from_trio: asyncio.Queue,
to_trio: trio.abc.SendChannel, to_trio: trio.abc.SendChannel,
) -> None: ) -> None:
@ -783,12 +807,6 @@ async def aio_open_interest_feed_relay(
Proxy-thru `cryptofeed.FeedHandler` "trades" to `piker`-side. Proxy-thru `cryptofeed.FeedHandler` "trades" to `piker`-side.
''' '''
# Get timestamp and convert it to isoformat
date = (datetime.utcfromtimestamp(trade.timestamp)).isoformat()
print('Trade...')
print(date)
print(trade)
print('=======================')
to_trio.send_nowait(('trade', trade)) to_trio.send_nowait(('trade', trade))
# trade and oi are user defined functions that # trade and oi are user defined functions that
@ -802,18 +820,88 @@ async def aio_open_interest_feed_relay(
Proxy-thru `cryptofeed.FeedHandler` "oi" to `piker`-side. Proxy-thru `cryptofeed.FeedHandler` "oi" to `piker`-side.
''' '''
# Get timestamp and convert it to isoformat nonlocal losses_cache
date = (datetime.utcfromtimestamp(oi.timestamp)).isoformat() nonlocal max_losses
print('>>>> Open Interest...') nonlocal max_pain
print(date) nonlocal open_interests
print(oi) symbol: Symbol = str_to_cb_sym(oi.symbol)
print('==========================') piker_sym: str = cb_sym_to_deribit_inst(symbol)
to_trio.send_nowait(('oi', oi)) data: dict = oi.raw['params']['data']
(
base,
expiry_date,
strike_price,
option_type
) = tuple(
piker_sym.split('-')
)
if not f'{expiry_date}' in open_interests:
open_interests[f'{expiry_date}'] = {
f'{strike_price}': {
'C': [],
'P': [],
'strike_losses': Decimal(0),
}
}
if not f'{strike_price}' in open_interests[f'{expiry_date}']:
open_interests[f'{expiry_date}'][f'{strike_price}'] = {
'C': [],
'P': [],
'strike_losses': Decimal(0),
}
index_price: Decimal = data['index_price']
price_delta: Decimal = abs(index_price - Decimal(strike_price))
open_interest: Decimal = oi.open_interest
losses: Decimal = price_delta * open_interest
if not f'{strike_price}' in losses_cache:
losses_cache[f'{strike_price}'] = {
'C': Decimal(0),
'P': Decimal(0),
}
losses_cache[f'{strike_price}'][f'{option_type}'] = losses
strike_losses: Decimal = (
losses_cache[f'{strike_price}']['C']
+
losses_cache[f'{strike_price}']['P']
)
print(f'>>>> Open Interest...')
print(f'max_losses: {max_losses}\n')
print(f'max_pain: {max_pain}')
print('-----------------------------------------------')
open_interests[f'{expiry_date}'][f'{strike_price}'][f'{option_type}'] = {
'date': oi.timestamp,
'open_interest': open_interest,
'index_price': index_price,
'strike_price': strike_price,
'price_delta': price_delta,
'losses': losses, # this updates the global value call_losses and put_losses
}
# calculate with latest values stored in call_losses and put_losses global cache.
open_interests[f'{expiry_date}'][f'{strike_price}']['strike_losses'] = strike_losses
for strike in open_interests[f'{expiry_date}']:
if open_interests[f'{expiry_date}'][strike]['strike_losses'] > max_losses:
max_losses = open_interests[f'{expiry_date}'][strike]['strike_losses']
max_pain = strike
print('-----------------------------------------------')
print(f'strike_price: {strike_price}')
print(f'strike_losses: {open_interests[f'{expiry_date}'][strike]['strike_losses']}')
print(f'{pformat(open_interests[f'{expiry_date}'][strike])}')
print('-----------------------------------------------')
channels = [TRADES, OPEN_INTEREST]
callbacks={TRADES: _trade, OPEN_INTEREST: _oi} callbacks={TRADES: _trade, OPEN_INTEREST: _oi}
fh.add_feed( fh.add_feed(
DERIBIT, DERIBIT,
channels=[TRADES, OPEN_INTEREST], channels=channels,
symbols=instruments, symbols=instruments,
callbacks=callbacks callbacks=callbacks
) )
@ -831,6 +919,60 @@ async def aio_open_interest_feed_relay(
await asyncio.sleep(float('inf')) await asyncio.sleep(float('inf'))
@acm
async def open_oi_feed(
) -> to_asyncio.LinkedTaskChannel:
expiry_date: str = '6DEC24' # '6DEC24' '26SEP25' '27JUN25' '13DEC24'
instruments: list[Symbol] = []
async with get_client(
) as client:
# to get all currencies available in deribit
# currencies = await client.get_currencies()
instruments = await client.get_instruments(
expiry_date=expiry_date,
)
losses_cache: dict[str, Decimal] = { # {'<strike_price>': <value>}
'C': Decimal(0),
'P': Decimal(0),
}
max_losses: Decimal = Decimal(0)
max_pain: Decimal = Decimal(0)
open_interests: dict[str, dict[str, dict[str, list[dict]]]] = {}
fh: FeedHandler
first: None
chan: to_asyncio.LinkedTaskChannel
async with (
maybe_open_feed_handler() as fh,
to_asyncio.open_channel_from(
partial(
aio_open_interest_feed_relay,
fh,
instruments,
open_interests,
losses_cache,
max_losses,
max_pain,
)
) as (first, chan)
):
yield chan
@acm
async def maybe_open_oi_feed(
) -> trio.abc.ReceiveStream:
# TODO: add a predicate to maybe_open_context
feed: to_asyncio.LinkedTaskChannel
async with maybe_open_context(
acm_func=open_oi_feed,
) as (cache_hit, feed):
if cache_hit:
yield broadcast_receiver(feed, 10)
else:
yield feed
# TODO, move all to `.broker` submod! # TODO, move all to `.broker` submod!
# async def aio_order_feed_relay( # async def aio_order_feed_relay(