HellAmbro / Trading212API

Unofficial Trading212 API
MIT License
57 stars 27 forks source link

Trading212 updated their web application and the current pytrading212 library doesn't work. #36

Closed capoveur closed 1 month ago

capoveur commented 3 months ago

I set about making the changes but my js/html is too poor. Most of the Selenium strategies used to interact with the legacy app don't work at all with the new app.

HellAmbro commented 3 months ago

Hi, thanks for reporting. I saw the changes between legacy and new app. I will do my best to adapt this api in the next few days.

capoveur commented 3 months ago

Awesome, wasn't expecting such a fast response! You guy's work deserves more appreciation. Is there a way I can donate?

HellAmbro commented 3 months ago

Hi, thanks for your support. I have made some changes to adapt the new application. Let me know if there are some issues i will check asap. The new version is 0.2.7. If you want to support this work you can find some ways here or you can contact me at frambrosini1998@gmail.com.

capoveur commented 3 months ago

I'll update tomorrow morning before the market opens just in case any dependency changes cause a snowball of errors in my code, and I'll let you know if anything doesn't work. I see in your constants.py that you also had to resort to painfully long xpaths because the T212 team hadn't given anything unique identifiers lol. Thanks again for the update!

capoveur commented 3 months ago

Ok, I updated this morning. Dependencies fine, just had to update selenium. I've been fitting in the changes to my program and I've got some small fixes;

In constants.py, the SELECTOR_MENU_BUTTON has changed; the final div:nth-child() is now child(6) not child(5)

The get_portfolio_composition() function didn't work with the new T212 format, so I made a start on updating it;


    def get_portfolio_composition(self):
        """Get Portfolio Composition"""
        # click portfolio section from top menu
        condition = expected_conditions.visibility_of_element_located(
            (By.CSS_SELECTOR, constants.SELECTOR_PORTFOLIO_BUTTON)
        )

        WebDriverWait(self.driver, 30).until(condition)
        self.driver.find_element(By.CSS_SELECTOR, constants.SELECTOR_PORTFOLIO_BUTTON).click()

        positions = []
        try:
            for item in self.driver.find_elements(By.XPATH, "//div[@data-testid=\'eq-portfolio-tab-investment-item\']"):
                child_nodes = item.find_elements(By.CSS_SELECTOR, ("*"))
                texts = []
                for child_node in child_nodes:
                    if len(child_node.text) > 0:
                        texts.append(child_node.text)
                position = Position(texts[3], texts[4], texts[6], texts[7])
                positions.append(position.__dict__)
        except Exception as e:
            logging.error(e)  # portfolio is empty
        return positions

Where

constants.SELECTOR_PORTFOLIO_BUTTON = ("#root > div > div > div > div > div > div > div:nth-child(1) > div > "
                             "div.css-175oi2r.r-1p0dtai.r-1d2f490.r-u8s1d.r-zchlnj.r-ipm5af.r-12vffkv > div > div > "
                             "div:nth-child(1) > div > div > div:nth-child(2) > div > div:nth-child(2)")

However, this isn't a complete fix as the ticker instrument codes are no longer given from the investment-items in the investments tab. So the Position(texts[3], texts[4], texts[6], texts[7]) call correctly provides value, quantity, and total_return from texts 4,6,7 but the instrument_code (texts[3]) is actually just the full name of the ticker. I'm looking for a way to get this info now.

capoveur commented 3 months ago

Ok, sorted out the missing instrument_code. It's not included as an attribute but the image given for each investment is titled {instrument_code}.png so you can just extract it from there:


    def get_portfolio_composition(self):
        """Get Portfolio Composition"""
        # click portfolio section from top menu
        condition = expected_conditions.visibility_of_element_located(
            (By.CSS_SELECTOR, constants.SELECTOR_PORTFOLIO_BUTTON)
        )

        WebDriverWait(self.driver, 60).until(condition)
        self.driver.find_element(By.CSS_SELECTOR, constants.SELECTOR_PORTFOLIO_BUTTON).click()

        positions = []
        try:
            for item in self.driver.find_elements(By.XPATH, "//div[@data-testid=\'eq-portfolio-tab-investment-item\']"):
                child_nodes = item.find_elements(By.CSS_SELECTOR, ("*"))
                texts = []
                instrument_code = '_'
                for child_node in child_nodes:
                    if child_node.get_attribute("src"):
                        ic_long = child_node.get_attribute("src")
                        slash_ind = len(ic_long)-1
                        while ic_long[slash_ind] != '/':
                            slash_ind-=1
                        instrument_code = ic_long[slash_ind+1:ic_long.find('.png')]
                    if len(child_node.text) > 0:
                        texts.append(child_node.text)
                position = Position(instrument_code, texts[4], texts[6], texts[7])
                positions.append(position.__dict__)
        except Exception as e:
            logging.error(e)  # portfolio is empty
        return positions
capoveur commented 3 months ago

Scratch that last comment. Just found out that not all tickers have a png thumbnail associated with them. I found a different solution;


    def get_portfolio_composition(self):
        """Get Portfolio Composition"""
        # click portfolio section from top menu
        condition = expected_conditions.visibility_of_element_located(
            (By.CSS_SELECTOR, constants.SELECTOR_PORTFOLIO_BUTTON)
        )
        WebDriverWait(self.driver, 60).until(condition)
        self.driver.find_element(By.CSS_SELECTOR, constants.SELECTOR_PORTFOLIO_BUTTON).click()
        positions = []
        try:
            alloc_list = self.driver.find_element(By.XPATH, "//div[@data-testid=\'undefined-flat-list\']")
            child_nodes = alloc_list.find_elements(By.CSS_SELECTOR, ("*"))
            instrument_codes = []
            instrument_names = []
            for child_node in child_nodes:
                if child_node.get_attribute("data-testid"):
                    instrument_codes.append(child_node.get_attribute("data-testid"))
                if child_node.text:
                    instrument_names.append(child_node.text)
            instrument_names = instrument_names[0].split('\n')

            for item in self.driver.find_elements(By.XPATH, "//div[@data-testid=\'eq-portfolio-tab-investment-item\']"):
                child_nodes = item.find_elements(By.CSS_SELECTOR, ("*"))
                texts = []
                instrument_code = '_'
                for child_node in child_nodes:
                    if child_node.text:
                        texts.append(child_node.text)
                instrument_code = instrument_codes[instrument_names.index(texts[-5].upper())]
                position = Position(instrument_code, texts[-4], texts[-2], texts[-1])
                positions.append(position.__dict__)

        except Exception as e:
            logging.error(e)  # portfolio is empty
        return positions
HellAmbro commented 3 months ago

Thanks for your support. I honestly haven't tested that method. I will try to update it by tomorrow. Let me know if there are some problems with other things so i Will try to fix everything.

HellAmbro commented 2 months ago

Hi, i Will look at It ASAP. Sorry for the late but in Italy (where i m from) today Is holiday. I Will try to look at It tomorrow or Friday. Thanks for the patience.

Best regards Francesco

Il lun 29 apr 2024, 16:57 capoveur @.***> ha scritto:

Hey quick question; it seems that when placing Equity Orders, it always uses the base currency of the account - do you know of a way to change that to a different currency the account holds?

— Reply to this email directly, view it on GitHub https://github.com/HellAmbro/Trading212API/issues/36#issuecomment-2082966798, or unsubscribe https://github.com/notifications/unsubscribe-auth/AKY54CHFOZ5GU73IDEXWX5LY7ZNUNAVCNFSM6AAAAABFTZFU4KVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAOBSHE3DMNZZHA . You are receiving this because you commented.Message ID: @.***>

capoveur commented 2 months ago

Hi Francesco, don't worry I found a simple solution to the above problem; you can add currencyCode='USD' to the kwargs of EquityOrders and that will set the currency to USD.

Enjoy your holiday

HellAmbro commented 2 months ago

Hi, thanks for the tip, i Will add to the source code ASAP. Or since that Is your idea you can do the feature yourself and open a merge request to contenute to the project as you wants. Thanks

Best regards Francesco

Il mer 1 mag 2024, 12:23 capoveur @.***> ha scritto:

Hi Francesco, don't worry I found a simple solution to the above problem; you can add currencyCode='USD' to the kwargs of EquityOrders and that will set the currency to USD.

Enjoy your holiday

— Reply to this email directly, view it on GitHub https://github.com/HellAmbro/Trading212API/issues/36#issuecomment-2088258852, or unsubscribe https://github.com/notifications/unsubscribe-auth/AKY54CCMVYBC2TOZ7SVRRULZAC7CFAVCNFSM6AAAAABFTZFU4KVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAOBYGI2TQOBVGI . You are receiving this because you commented.Message ID: @.***>