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()
Portfolio(driver: selenium.webdriver.remote.webdriver.WebDriver)
15    def __init__(self, driver: WebDriver):
16        self.driver = driver
driver
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

def cancel_active_order(self, symbol: str, order_type: tradezero_api.enums.OrderType) -> None:
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