@@ -103,6 +103,7 @@ def __init__(
103103 self .qualified_contracts : Dict [int , Contract ] = {}
104104 self .dry_run = dry_run
105105 self .regime_rebalance_order_ref_prefix = "tg:regime-rebalance"
106+ self .last_untracked_positions : Dict [str , List [PortfolioItem ]] = {}
106107
107108 def get_short_calls (
108109 self , portfolio_positions : Dict [str , List [PortfolioItem ]]
@@ -361,22 +362,32 @@ def get_symbols(self) -> List[str]:
361362 def filter_positions (
362363 self , portfolio_positions : List [PortfolioItem ]
363364 ) -> List [PortfolioItem ]:
365+ filtered_positions , _ = self .partition_positions (portfolio_positions )
366+ return filtered_positions
367+
368+ def partition_positions (
369+ self , portfolio_positions : List [PortfolioItem ]
370+ ) -> Tuple [List [PortfolioItem ], List [PortfolioItem ]]:
364371 symbols = self .get_symbols ()
365- return [
366- item
367- for item in portfolio_positions
368- if item .account == self .account_number
369- and (
372+ tracked_positions : List [PortfolioItem ] = []
373+ untracked_positions : List [PortfolioItem ] = []
374+ for item in portfolio_positions :
375+ if item .account != self .account_number or item .position == 0 :
376+ continue
377+ if (
370378 item .contract .symbol in symbols
371379 or item .contract .symbol == "VIX"
372380 or item .contract .symbol == self .config .cash_management .cash_fund
373- )
374- and item .position != 0
375- ]
381+ ):
382+ tracked_positions .append (item )
383+ else :
384+ untracked_positions .append (item )
385+ return (tracked_positions , untracked_positions )
376386
377387 async def get_portfolio_positions (self ) -> Dict [str , List [PortfolioItem ]]:
378388 attempts = 3
379389 symbols = set (self .get_symbols ())
390+ self .last_untracked_positions = {}
380391
381392 for attempt in range (1 , attempts + 1 ):
382393 try :
@@ -397,8 +408,13 @@ async def get_portfolio_positions(self) -> Dict[str, List[PortfolioItem]]:
397408 continue
398409
399410 portfolio_positions = self .ibkr .portfolio (account = self .account_number )
400- filtered_positions = self .filter_positions (portfolio_positions )
411+ filtered_positions , untracked_positions = self .partition_positions (
412+ portfolio_positions
413+ )
401414 portfolio_by_symbol = portfolio_positions_to_dict (filtered_positions )
415+ self .last_untracked_positions = portfolio_positions_to_dict (
416+ untracked_positions
417+ )
402418 filtered_conids = {item .contract .conId for item in filtered_positions }
403419
404420 if portfolio_by_symbol :
@@ -549,9 +565,18 @@ async def summarize_account(
549565 log .print (Panel (table ))
550566
551567 portfolio_positions = await self .get_portfolio_positions ()
568+ untracked_positions = self .last_untracked_positions
552569 if self .data_store :
553570 self .data_store .record_account_snapshot (account_summary )
554- self .data_store .record_positions_snapshot (portfolio_positions )
571+ combined_positions : Dict [str , List [PortfolioItem ]] = dict (
572+ portfolio_positions
573+ )
574+ for symbol , positions in untracked_positions .items ():
575+ if symbol in combined_positions :
576+ combined_positions [symbol ].extend (positions )
577+ else :
578+ combined_positions [symbol ] = positions
579+ self .data_store .record_positions_snapshot (combined_positions )
555580
556581 position_values : Dict [int , Dict [str , str ]] = {}
557582
@@ -597,11 +622,13 @@ async def load_position_task(pos: PortfolioItem) -> None:
597622 pos .contract .lastTradeDateOrContractMonth
598623 )
599624
600- tasks : List [Coroutine [Any , Any , None ]] = [
601- load_position_task (position )
602- for _ , positions in portfolio_positions .items ()
603- for position in positions
604- ]
625+ tasks : List [Coroutine [Any , Any , None ]] = []
626+ for _ , positions in portfolio_positions .items ():
627+ for position in positions :
628+ tasks .append (load_position_task (position ))
629+ for _ , positions in untracked_positions .items ():
630+ for position in positions :
631+ tasks .append (load_position_task (position ))
605632 await log .track_async (tasks , "Loading portfolio positions..." )
606633
607634 table = Table (
@@ -621,24 +648,21 @@ async def load_position_task(pos: PortfolioItem) -> None:
621648 table .add_column ("Exp" , justify = "right" )
622649 table .add_column ("DTE" , justify = "right" )
623650 table .add_column ("ITM?" )
624- first = True
625- for symbol , position in portfolio_positions . items () :
626- if not first :
627- table . add_section ()
628- first = False
651+
652+ def getval ( col : str , conId : int ) -> str :
653+ return position_values [ conId ][ col ]
654+
655+ def add_symbol_positions ( symbol : str , positions : List [ PortfolioItem ]) -> None :
629656 table .add_row (symbol )
630657 sorted_positions = sorted (
631- position ,
658+ positions ,
632659 key = lambda p : (
633660 option_dte (p .contract .lastTradeDateOrContractMonth )
634661 if isinstance (p .contract , Option )
635662 else - 1
636663 ), # Keep stonks on top
637664 )
638665
639- def getval (col : str , conId : int ) -> str :
640- return position_values [conId ][col ]
641-
642666 for pos in sorted_positions :
643667 conId = pos .contract .conId
644668 if isinstance (pos .contract , Stock ):
@@ -670,6 +694,24 @@ def getval(col: str, conId: int) -> str:
670694 getval ("itm?" , conId ),
671695 )
672696
697+ first = True
698+ for symbol , position in portfolio_positions .items ():
699+ if not first :
700+ table .add_section ()
701+ first = False
702+ add_symbol_positions (symbol , position )
703+
704+ if untracked_positions :
705+ table .add_section ()
706+ table .add_row ("Not tracked" )
707+ table .add_section ()
708+ first_untracked = True
709+ for symbol , position in untracked_positions .items ():
710+ if not first_untracked :
711+ table .add_section ()
712+ first_untracked = False
713+ add_symbol_positions (symbol , position )
714+
673715 log .print (table )
674716
675717 return (account_summary , portfolio_positions )
0 commit comments