From bf86772d4b3e1b9825d65ddd191cee493dd3f43e Mon Sep 17 00:00:00 2001 From: Nelson Torres Date: Thu, 5 Dec 2024 23:31:36 -0300 Subject: [PATCH] oi max_pain major refactor Now the max_pain is calculated taking into account all strike prices and all close prices to find the intrinsic value as deribit. --- piker/brokers/deribit/api.py | 125 ++++++++++++----------------------- 1 file changed, 44 insertions(+), 81 deletions(-) diff --git a/piker/brokers/deribit/api.py b/piker/brokers/deribit/api.py index 336a3636..57861368 100644 --- a/piker/brokers/deribit/api.py +++ b/piker/brokers/deribit/api.py @@ -827,13 +827,15 @@ async def maybe_open_price_feed( async def aio_open_interest_feed_relay( fh: FeedHandler, 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, + intrinsic_values: dict[str, Decimal], + oi_by_strikes: dict[str, dict[str, Decimal]], from_trio: asyncio.Queue, to_trio: trio.abc.SendChannel, ) -> None: + + max_losses: Decimal = Decimal('Infinity') + max_pain: Decimal = Decimal(0) + async def _trade( trade: Trade, # cryptofeed, NOT ours from `.venues`! receipt_timestamp: int, @@ -855,13 +857,13 @@ async def aio_open_interest_feed_relay( Proxy-thru `cryptofeed.FeedHandler` "oi" to `piker`-side. ''' - nonlocal losses_cache + nonlocal intrinsic_values + nonlocal oi_by_strikes nonlocal max_losses nonlocal max_pain - nonlocal open_interests + symbol: Symbol = str_to_cb_sym(oi.symbol) piker_sym: str = cb_sym_to_deribit_inst(symbol) - data: dict = oi.raw['params']['data'] ( base, expiry_date, @@ -870,69 +872,37 @@ async def aio_open_interest_feed_relay( ) = 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'] open_interest: Decimal = oi.open_interest - price_delta: Decimal - if f'{option_type}' == 'C': - price_delta = index_price - Decimal(strike_price) + oi_by_strikes[f'{strike_price}'][f'{option_type}'] = open_interest - elif f'{option_type}' == 'P': - price_delta = Decimal(strike_price) - index_price + is_ready = check_if_complete(oi_by_strikes) + if is_ready: + for strike in oi_by_strikes: + s: Decimal = Decimal(f'{strike}') + close_losses = Decimal(0) + closes: list[str] = sorted(oi_by_strikes.keys()) + call_cash: Decimal = Decimal(0) + put_cash: Decimal = Decimal(0) + for close in closes: + c: Decimal = Decimal(f'{close}') + call_cash += max(0, (s - c) * oi_by_strikes[f'{close}']['C']) + put_cash += max(0, (c - s) * oi_by_strikes[f'{close}']['P']) + + intrinsic_values[f'{strike}'] = {} + intrinsic_values[f'{strike}']['C'] = call_cash + intrinsic_values[f'{strike}']['P'] = put_cash + intrinsic_values[f'{strike}']['total'] = (call_cash + put_cash) - notional_value = open_interest * index_price - losses: Decimal = max(0, 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'] - ) - - 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, - 'notional_value': notional_value, - '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 strike_losses > max_losses: - max_losses = open_interests[f'{expiry_date}'][strike]['strike_losses'] - max_pain = strike - print(f'>>>> Open Interest...') - print(f'expiration: {expiry_date}') - print(f'>>>> max_pain: {max_pain}') - print(f'max_losses: {max_losses}') - print(f'{pformat(open_interests[f'{expiry_date}'][max_pain])}') - print('-----------------------------------------------') + for strike in intrinsic_values: + if intrinsic_values[f'{strike}']['total'] < max_losses: + max_losses = intrinsic_values[f'{strike}']['total'] + max_pain = strike + + print('-----------------------------------------------') + print(f'time: {oi.timestamp}') + print(f'max_pain: {max_pain}') + print(f'max_losses: {max_losses}') + print('-----------------------------------------------') channels = [TRADES, OPEN_INTEREST] @@ -964,21 +934,16 @@ async def open_oi_feed( expiry_date: str = '20DEC24' instruments: list[Symbol] = [] + intrinsic_values: dict[str, dict[str, Decimal]] = {} + oi_by_strikes: dict[str, dict[str, Decimal]] + 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] = { # {'': } - 'C': Decimal(0), - 'P': Decimal(0), - } -# max_losses: Decimal = Decimal('Infinity') - max_losses: Decimal = Decimal(0) - max_pain: Decimal = Decimal(0) - open_interests: dict[str, dict[str, dict[str, list[dict]]]] = {} + oi_by_strikes = client.get_strikes_dict(instruments) + fh: FeedHandler first: None chan: to_asyncio.LinkedTaskChannel @@ -989,10 +954,8 @@ async def open_oi_feed( aio_open_interest_feed_relay, fh, instruments, - open_interests, - losses_cache, - max_losses, - max_pain, + intrinsic_values, + oi_by_strikes, ) ) as (first, chan) ):