11import asyncio
2- from collections import defaultdict
32from datetime import datetime
43from decimal import Decimal
54from typing import Annotated
65
6+ from anyio import move_on_after
77from rich .console import Console
88from rich .table import Table
99from tastytrade import DXLinkStreamer
10- from tastytrade .dxfeed import Greeks , Summary , Quote , Trade
10+ from tastytrade .dxfeed import Greeks , Quote , Summary , Trade
1111from tastytrade .instruments import (
1212 Future ,
1313 FutureOption ,
1414 NestedFutureOptionChain ,
1515 NestedFutureOptionChainExpiration ,
1616 NestedOptionChain ,
1717 NestedOptionChainExpiration ,
18+ )
19+ from tastytrade .instruments import (
1820 Option as TastytradeOption ,
1921)
20- from tastytrade .market_data import get_market_data , get_market_data_by_type
22+ from tastytrade .market_data import get_market_data_by_type
2123from tastytrade .order import (
22- InstrumentType ,
2324 NewOrder ,
2425 OrderAction ,
2526 OrderTimeInForce ,
@@ -217,11 +218,11 @@ async def call(
217218 bid = data_dict [strike_symbol ].bid - data_dict [spread_strike .call ].ask # type: ignore
218219 ask = data_dict [strike_symbol ].ask - data_dict [spread_strike .call ].bid # type: ignore
219220 else :
220- data = get_market_data (
221+ data = get_market_data_by_type (
221222 sesh ,
222- strike_symbol ,
223- InstrumentType . FUTURE_OPTION if is_future else InstrumentType . EQUITY_OPTION ,
224- )
223+ future_options = [ strike_symbol ] if is_future else None ,
224+ options = [ strike_symbol ] if not is_future else None ,
225+ )[ 0 ]
225226 bid = data .bid or 0
226227 ask = data .ask or 0
227228 mid = fmt ((bid + ask ) / Decimal (2 ))
@@ -453,11 +454,11 @@ async def put(
453454 bid = data_dict [strike_symbol ].bid - data_dict [spread_strike .call ].ask # type: ignore
454455 ask = data_dict [strike_symbol ].ask - data_dict [spread_strike .call ].bid # type: ignore
455456 else :
456- data = get_market_data (
457+ data = get_market_data_by_type (
457458 sesh ,
458- strike_symbol ,
459- InstrumentType . FUTURE_OPTION if is_future else InstrumentType . EQUITY_OPTION ,
460- )
459+ future_options = [ strike_symbol ] if is_future else None ,
460+ options = [ strike_symbol ] if not is_future else None ,
461+ )[ 0 ]
461462 bid = data .bid or 0
462463 ask = data .ask or 0
463464 mid = fmt ((bid + ask ) / Decimal (2 ))
@@ -640,7 +641,7 @@ async def strangle(
640641 if (call is not None or put is not None ) and delta is not None :
641642 print_error ("Must specify either delta or strike, but not both." )
642643 return
643- elif delta is not None and (call is not None or put is not None ):
644+ elif delta is None and (call is None or put is None ):
644645 print_error ("Please specify either delta, or strikes for both options." )
645646 return
646647 elif delta is not None and abs (delta ) > 99 :
@@ -901,7 +902,7 @@ async def strangle(
901902 if data .warnings :
902903 for warning in data .warnings :
903904 print_warning (warning .message )
904- warn_percent = sesh .config .getint (
905+ warn_percent = sesh .config .getfloat (
905906 "portfolio" , "bp-max-percent-per-position" , fallback = None
906907 )
907908 if warn_percent and percent > warn_percent :
@@ -986,7 +987,10 @@ async def chain(
986987 await streamer .subscribe (Trade , [future .streamer_symbol ])
987988 else :
988989 await streamer .subscribe (Trade , [symbol ])
989- trade = await streamer .get_event (Trade )
990+ with move_on_after (3 ) as scope :
991+ trade = await streamer .get_event (Trade )
992+ if scope .cancelled_caught :
993+ raise Exception ("Timed out listening for quote, is symbol active?" )
990994
991995 subchain .strikes .sort (key = lambda s : s .strike_price )
992996 mid_index = 0
@@ -1007,8 +1011,8 @@ async def chain(
10071011
10081012 # take into account the symbol we subscribed to
10091013 streamer_symbol = symbol if symbol [0 ] != "/" else future .streamer_symbol # type: ignore
1010- trade_dict = defaultdict ( lambda : 0 )
1011- trade_dict [streamer_symbol ] = trade . day_volume or 0
1014+ trade_dict : dict [ str , Trade | None ] = {}
1015+ trade_dict [streamer_symbol ] = trade
10121016
10131017 greeks_task = asyncio .create_task (listen_events (dxfeeds , Greeks , streamer ))
10141018 quote_task = asyncio .create_task (listen_events (dxfeeds , Quote , streamer ))
@@ -1029,38 +1033,37 @@ async def chain(
10291033 if show_oi :
10301034 summary_dict = summary_task .result () # type: ignore
10311035 if show_volume :
1032- trade_dict = trade_task .result () # type: ignore
1036+ trade_dict . update ( trade_task .result () ) # type: ignore
10331037
10341038 for i , strike in enumerate (all_strikes ):
1035- put_bid = quote_dict [strike .put_streamer_symbol ].bid_price
1036- put_ask = quote_dict [strike .put_streamer_symbol ].ask_price
1037- call_bid = quote_dict [strike .call_streamer_symbol ].bid_price
1038- call_ask = quote_dict [strike .call_streamer_symbol ].ask_price
1039+ put = quote_dict [strike .put_streamer_symbol ]
1040+ call = quote_dict [strike .call_streamer_symbol ]
10391041 row = [
1040- f"{ fmt (call_bid ) } " ,
1041- f"{ fmt (call_ask ) } " ,
1042+ f"{ fmt (call . bid_price ) } " if call else " " ,
1043+ f"{ fmt (call . ask_price ) } " if call else " " ,
10421044 f"{ fmt (strike .strike_price )} " ,
1043- f"{ fmt (put_bid ) } " ,
1044- f"{ fmt (put_ask ) } " ,
1045+ f"{ fmt (put . bid_price ) } " if put else " " ,
1046+ f"{ fmt (put . ask_price ) } " if put else " " ,
10451047 ]
10461048 prepend = []
1049+ put_greek = greeks_dict [strike .put_streamer_symbol ]
1050+ call_greek = greeks_dict [strike .call_streamer_symbol ]
10471051 if show_delta :
1048- put_delta = int (greeks_dict [strike .put_streamer_symbol ].delta * 100 )
1049- call_delta = int (greeks_dict [strike .call_streamer_symbol ].delta * 100 )
1050- prepend .append (f"{ call_delta :g} " )
1051- row .append (f"{ put_delta :g} " )
1052-
1052+ prepend .append (f"{ int (call_greek .delta * 100 ):g} " if call_greek else "" )
1053+ row .append (f"{ int (put_greek .delta * 100 ):g} " if put_greek else "" )
10531054 if show_theta :
1054- prepend .append (f"{ abs (greeks_dict [ strike . call_streamer_symbol ]. theta ):.2f} " )
1055- row .append (f"{ abs (greeks_dict [ strike . put_streamer_symbol ]. theta ):.2f} " )
1055+ prepend .append (f"{ abs (call_greek . theta ):.2f} " if call_greek else " " )
1056+ row .append (f"{ abs (put_greek . theta ):.2f} " if put_greek else " " )
10561057 if show_oi :
1057- prepend . append (
1058- f" { summary_dict [strike .call_streamer_symbol ]. open_interest } " # type: ignore
1059- )
1060- row .append (f"{ summary_dict [ strike . put_streamer_symbol ]. open_interest } " ) # type: ignore
1058+ call_summary = summary_dict [ strike . call_streamer_symbol ] # type: ignore
1059+ put_summary = summary_dict [strike .put_streamer_symbol ] # type: ignore
1060+ prepend . append ( f" { call_summary . open_interest } " if call_summary else "" )
1061+ row .append (f"{ put_summary . open_interest } " if put_summary else "" )
10611062 if show_volume :
1062- prepend .append (f"{ trade_dict [strike .call_streamer_symbol ].day_volume } " ) # type: ignore
1063- row .append (f"{ trade_dict [strike .put_streamer_symbol ].day_volume } " ) # type: ignore
1063+ call_trade = trade_dict [strike .call_streamer_symbol ]
1064+ put_trade = trade_dict [strike .put_streamer_symbol ]
1065+ prepend .append (f"{ call_trade .day_volume or 0 } " if call_trade else "" )
1066+ row .append (f"{ put_trade .day_volume or 0 } " if put_trade else "" )
10641067
10651068 prepend .reverse ()
10661069 table .add_row (* (prepend + row ), end_section = (i == mid_index - 1 ))
0 commit comments