tradezero_api.portfolio
1from __future__ import annotations 2 3import warnings 4from typing import overload, Optional, Literal 5 6import pandas as pd 7from selenium.webdriver.common.by import By 8from selenium.webdriver.remote.webdriver import WebDriver 9 10from .enums import PortfolioTab, OrderType 11 12 13class Portfolio: 14 def __init__(self, driver: WebDriver): 15 self.driver = driver 16 17 @overload 18 def portfolio(self, return_type: Literal['df'] = 'df') -> Optional[pd.DataFrame]: 19 ... 20 21 @overload 22 def portfolio(self, return_type: Literal['dict']) -> Optional[dict]: 23 ... 24 25 def portfolio(self, return_type: Literal['df', 'dict'] = 'df') -> pd.DataFrame | dict | None: 26 """ 27 return the Portfolio table as a pandas.DataFrame or nested dict, with the symbol column as index. 28 the column names are the following: 'type', 'qty', 'p_close', 'entry', 29 'price', 'change', '%change', 'day_pnl', 'pnl', 'overnight' 30 note that if the portfolio is empty Pandas won't be able to locate the table, 31 and therefore will return None 32 33 :param return_type: 'df' or 'dict' 34 :return: pandas.DataFrame or None if table empty 35 """ 36 portfolio_symbols = self.driver.find_elements(By.XPATH, '//*[@id="opTable-1"]/tbody/tr/td[1]') 37 df = pd.read_html(self.driver.page_source, attrs={'id': 'opTable-1'})[0] 38 39 if len(portfolio_symbols) == 0 or df.loc[0, 0].lower() == "you have no open positions.": 40 warnings.warn('Portfolio is empty') 41 return None 42 43 df.columns = [ 44 'symbol', 'type', 'qty', 'p_close', 'entry', 'price', 'change', '%change', 'day_pnl', 'pnl', 'overnight' 45 ] 46 df = df.set_index('symbol') 47 if return_type == 'dict': 48 return df.to_dict('index') 49 return df 50 51 def open_orders(self) -> pd.DataFrame: 52 """ 53 return DF with only positions that were opened today (intraday positions) 54 55 :return: pandas.DataFrame 56 """ 57 df = self.portfolio() 58 59 # if there are no open position: return an empty dataframe 60 if df is None: 61 return pd.DataFrame() 62 63 filt = df['overnight'] == 'Yes' 64 return df.loc[~filt] 65 66 def invested(self, symbol) -> bool: 67 """ 68 returns True if the given symbol is in portfolio, else: false 69 70 :param symbol: str: e.g: 'aapl', 'amd', 'NVDA', 'GM' 71 :return: bool 72 """ 73 data = self.portfolio('dict') 74 if data is None: 75 return False 76 77 return symbol.upper() in data.keys() 78 79 def _switch_portfolio_tab(self, tab: PortfolioTab) -> None: 80 """ 81 Switch the focus to a given tab 82 83 Note that this is idem-potent, meaning you can switch twice consecutively in the same tab. 84 85 :param tab: enum of PortfolioTab 86 :return: None 87 """ 88 portfolio_tab = self.driver.find_element(By.ID, tab) 89 portfolio_tab.click() 90 91 def get_active_orders(self, return_type: str = 'df'): 92 """ 93 Get a dataframe with all the active orders and their info 94 95 :param return_type: 'df' or 'dict' 96 :return: dataframe or dictionary (based on the return_type parameter) 97 """ 98 active_orders = self.driver.find_elements(By.XPATH, '//*[@id="aoTable-1"]/tbody/tr[@order-id]') 99 if len(active_orders) == 0: 100 warnings.warn('There are no active orders') 101 return 102 103 df = pd.read_html(self.driver.page_source, attrs={'id': 'aoTable-1'})[0] 104 df = df.drop(0, axis=1) # remove the first column which contains the button "CANCEL" 105 df.columns = ['ref_number', 'symbol', 'side', 'qty', 'type', 'status', 'tif', 'limit', 'stop', 'placed'] 106 # df = df.set_index('symbol') # cant set it as a column since its not always unique 107 108 if return_type == 'dict': 109 return df.to_dict('index') 110 return df 111 112 def symbol_present_in_active_orders(self, symbol: str) -> bool: 113 """ 114 Check if a given symbol is present in the active orders tab 115 116 :param symbol: 117 :return: True or False 118 """ 119 return symbol.upper() in self.get_active_orders()['symbol'].values 120 121 def cancel_active_order(self, symbol: str, order_type: OrderType) -> None: 122 """ 123 Cancel a pending order 124 125 :param symbol: 126 :param order_type: enum of OrderType 127 :return: None 128 """ 129 symbol = symbol.upper() 130 self._switch_portfolio_tab(tab=PortfolioTab.active_orders) 131 132 df = self.get_active_orders() 133 assert symbol in df['symbol'].values, f'Given symbol {symbol} is not present in the active orders tab' 134 135 # find the ref-id of all the orders we have to cancel: 136 filt = (df['symbol'] == symbol) & (df['type'] == order_type) 137 ids_to_cancel = df[filt]['ref_number'].values 138 ids_to_cancel = [x.replace('S.', '') for x in ids_to_cancel] 139 140 for order_id in ids_to_cancel: 141 cancel_button = self.driver.find_element( 142 By.XPATH, f'//div[@id="portfolio-content-tab-ao-1"]//*[@order-id="{order_id}"]/td[@class="red"]') 143 cancel_button.click()
class
Portfolio:
14class Portfolio: 15 def __init__(self, driver: WebDriver): 16 self.driver = driver 17 18 @overload 19 def portfolio(self, return_type: Literal['df'] = 'df') -> Optional[pd.DataFrame]: 20 ... 21 22 @overload 23 def portfolio(self, return_type: Literal['dict']) -> Optional[dict]: 24 ... 25 26 def portfolio(self, return_type: Literal['df', 'dict'] = 'df') -> pd.DataFrame | dict | None: 27 """ 28 return the Portfolio table as a pandas.DataFrame or nested dict, with the symbol column as index. 29 the column names are the following: 'type', 'qty', 'p_close', 'entry', 30 'price', 'change', '%change', 'day_pnl', 'pnl', 'overnight' 31 note that if the portfolio is empty Pandas won't be able to locate the table, 32 and therefore will return None 33 34 :param return_type: 'df' or 'dict' 35 :return: pandas.DataFrame or None if table empty 36 """ 37 portfolio_symbols = self.driver.find_elements(By.XPATH, '//*[@id="opTable-1"]/tbody/tr/td[1]') 38 df = pd.read_html(self.driver.page_source, attrs={'id': 'opTable-1'})[0] 39 40 if len(portfolio_symbols) == 0 or df.loc[0, 0].lower() == "you have no open positions.": 41 warnings.warn('Portfolio is empty') 42 return None 43 44 df.columns = [ 45 'symbol', 'type', 'qty', 'p_close', 'entry', 'price', 'change', '%change', 'day_pnl', 'pnl', 'overnight' 46 ] 47 df = df.set_index('symbol') 48 if return_type == 'dict': 49 return df.to_dict('index') 50 return df 51 52 def open_orders(self) -> pd.DataFrame: 53 """ 54 return DF with only positions that were opened today (intraday positions) 55 56 :return: pandas.DataFrame 57 """ 58 df = self.portfolio() 59 60 # if there are no open position: return an empty dataframe 61 if df is None: 62 return pd.DataFrame() 63 64 filt = df['overnight'] == 'Yes' 65 return df.loc[~filt] 66 67 def invested(self, symbol) -> bool: 68 """ 69 returns True if the given symbol is in portfolio, else: false 70 71 :param symbol: str: e.g: 'aapl', 'amd', 'NVDA', 'GM' 72 :return: bool 73 """ 74 data = self.portfolio('dict') 75 if data is None: 76 return False 77 78 return symbol.upper() in data.keys() 79 80 def _switch_portfolio_tab(self, tab: PortfolioTab) -> None: 81 """ 82 Switch the focus to a given tab 83 84 Note that this is idem-potent, meaning you can switch twice consecutively in the same tab. 85 86 :param tab: enum of PortfolioTab 87 :return: None 88 """ 89 portfolio_tab = self.driver.find_element(By.ID, tab) 90 portfolio_tab.click() 91 92 def get_active_orders(self, return_type: str = 'df'): 93 """ 94 Get a dataframe with all the active orders and their info 95 96 :param return_type: 'df' or 'dict' 97 :return: dataframe or dictionary (based on the return_type parameter) 98 """ 99 active_orders = self.driver.find_elements(By.XPATH, '//*[@id="aoTable-1"]/tbody/tr[@order-id]') 100 if len(active_orders) == 0: 101 warnings.warn('There are no active orders') 102 return 103 104 df = pd.read_html(self.driver.page_source, attrs={'id': 'aoTable-1'})[0] 105 df = df.drop(0, axis=1) # remove the first column which contains the button "CANCEL" 106 df.columns = ['ref_number', 'symbol', 'side', 'qty', 'type', 'status', 'tif', 'limit', 'stop', 'placed'] 107 # df = df.set_index('symbol') # cant set it as a column since its not always unique 108 109 if return_type == 'dict': 110 return df.to_dict('index') 111 return df 112 113 def symbol_present_in_active_orders(self, symbol: str) -> bool: 114 """ 115 Check if a given symbol is present in the active orders tab 116 117 :param symbol: 118 :return: True or False 119 """ 120 return symbol.upper() in self.get_active_orders()['symbol'].values 121 122 def cancel_active_order(self, symbol: str, order_type: OrderType) -> None: 123 """ 124 Cancel a pending order 125 126 :param symbol: 127 :param order_type: enum of OrderType 128 :return: None 129 """ 130 symbol = symbol.upper() 131 self._switch_portfolio_tab(tab=PortfolioTab.active_orders) 132 133 df = self.get_active_orders() 134 assert symbol in df['symbol'].values, f'Given symbol {symbol} is not present in the active orders tab' 135 136 # find the ref-id of all the orders we have to cancel: 137 filt = (df['symbol'] == symbol) & (df['type'] == order_type) 138 ids_to_cancel = df[filt]['ref_number'].values 139 ids_to_cancel = [x.replace('S.', '') for x in ids_to_cancel] 140 141 for order_id in ids_to_cancel: 142 cancel_button = self.driver.find_element( 143 By.XPATH, f'//div[@id="portfolio-content-tab-ao-1"]//*[@order-id="{order_id}"]/td[@class="red"]') 144 cancel_button.click()
def
portfolio( self, return_type: Literal['df', 'dict'] = 'df') -> pandas.core.frame.DataFrame | dict | None:
26 def portfolio(self, return_type: Literal['df', 'dict'] = 'df') -> pd.DataFrame | dict | None: 27 """ 28 return the Portfolio table as a pandas.DataFrame or nested dict, with the symbol column as index. 29 the column names are the following: 'type', 'qty', 'p_close', 'entry', 30 'price', 'change', '%change', 'day_pnl', 'pnl', 'overnight' 31 note that if the portfolio is empty Pandas won't be able to locate the table, 32 and therefore will return None 33 34 :param return_type: 'df' or 'dict' 35 :return: pandas.DataFrame or None if table empty 36 """ 37 portfolio_symbols = self.driver.find_elements(By.XPATH, '//*[@id="opTable-1"]/tbody/tr/td[1]') 38 df = pd.read_html(self.driver.page_source, attrs={'id': 'opTable-1'})[0] 39 40 if len(portfolio_symbols) == 0 or df.loc[0, 0].lower() == "you have no open positions.": 41 warnings.warn('Portfolio is empty') 42 return None 43 44 df.columns = [ 45 'symbol', 'type', 'qty', 'p_close', 'entry', 'price', 'change', '%change', 'day_pnl', 'pnl', 'overnight' 46 ] 47 df = df.set_index('symbol') 48 if return_type == 'dict': 49 return df.to_dict('index') 50 return df
return the Portfolio table as a pandas.DataFrame or nested dict, with the symbol column as index. the column names are the following: 'type', 'qty', 'p_close', 'entry', 'price', 'change', '%change', 'day_pnl', 'pnl', 'overnight' note that if the portfolio is empty Pandas won't be able to locate the table, and therefore will return None
Parameters
- return_type: 'df' or 'dict'
Returns
pandas.DataFrame or None if table empty
def
open_orders(self) -> pandas.core.frame.DataFrame:
52 def open_orders(self) -> pd.DataFrame: 53 """ 54 return DF with only positions that were opened today (intraday positions) 55 56 :return: pandas.DataFrame 57 """ 58 df = self.portfolio() 59 60 # if there are no open position: return an empty dataframe 61 if df is None: 62 return pd.DataFrame() 63 64 filt = df['overnight'] == 'Yes' 65 return df.loc[~filt]
return DF with only positions that were opened today (intraday positions)
Returns
pandas.DataFrame
def
invested(self, symbol) -> bool:
67 def invested(self, symbol) -> bool: 68 """ 69 returns True if the given symbol is in portfolio, else: false 70 71 :param symbol: str: e.g: 'aapl', 'amd', 'NVDA', 'GM' 72 :return: bool 73 """ 74 data = self.portfolio('dict') 75 if data is None: 76 return False 77 78 return symbol.upper() in data.keys()
returns True if the given symbol is in portfolio, else: false
Parameters
- symbol: str: e.g: 'aapl', 'amd', 'NVDA', 'GM'
Returns
bool
def
get_active_orders(self, return_type: str = 'df'):
92 def get_active_orders(self, return_type: str = 'df'): 93 """ 94 Get a dataframe with all the active orders and their info 95 96 :param return_type: 'df' or 'dict' 97 :return: dataframe or dictionary (based on the return_type parameter) 98 """ 99 active_orders = self.driver.find_elements(By.XPATH, '//*[@id="aoTable-1"]/tbody/tr[@order-id]') 100 if len(active_orders) == 0: 101 warnings.warn('There are no active orders') 102 return 103 104 df = pd.read_html(self.driver.page_source, attrs={'id': 'aoTable-1'})[0] 105 df = df.drop(0, axis=1) # remove the first column which contains the button "CANCEL" 106 df.columns = ['ref_number', 'symbol', 'side', 'qty', 'type', 'status', 'tif', 'limit', 'stop', 'placed'] 107 # df = df.set_index('symbol') # cant set it as a column since its not always unique 108 109 if return_type == 'dict': 110 return df.to_dict('index') 111 return df
Get a dataframe with all the active orders and their info
Parameters
- return_type: 'df' or 'dict'
Returns
dataframe or dictionary (based on the return_type parameter)
def
symbol_present_in_active_orders(self, symbol: str) -> bool:
113 def symbol_present_in_active_orders(self, symbol: str) -> bool: 114 """ 115 Check if a given symbol is present in the active orders tab 116 117 :param symbol: 118 :return: True or False 119 """ 120 return symbol.upper() in self.get_active_orders()['symbol'].values
Check if a given symbol is present in the active orders tab
Parameters
- symbol:
Returns
True or False
122 def cancel_active_order(self, symbol: str, order_type: OrderType) -> None: 123 """ 124 Cancel a pending order 125 126 :param symbol: 127 :param order_type: enum of OrderType 128 :return: None 129 """ 130 symbol = symbol.upper() 131 self._switch_portfolio_tab(tab=PortfolioTab.active_orders) 132 133 df = self.get_active_orders() 134 assert symbol in df['symbol'].values, f'Given symbol {symbol} is not present in the active orders tab' 135 136 # find the ref-id of all the orders we have to cancel: 137 filt = (df['symbol'] == symbol) & (df['type'] == order_type) 138 ids_to_cancel = df[filt]['ref_number'].values 139 ids_to_cancel = [x.replace('S.', '') for x in ids_to_cancel] 140 141 for order_id in ids_to_cancel: 142 cancel_button = self.driver.find_element( 143 By.XPATH, f'//div[@id="portfolio-content-tab-ao-1"]//*[@order-id="{order_id}"]/td[@class="red"]') 144 cancel_button.click()
Cancel a pending order
Parameters
- symbol:
- order_type: enum of OrderType
Returns
None