Esempio n. 1
0
class ProQuant:
    def __init__(self, enable_headless_mode=True, headless_resolution=(1920,1080)):
        self.browser = Browser(enable_headless_mode=enable_headless_mode,headless_resolution=headless_resolution)
        self.logged_in = False

    def login(self, username, password):
        """

        :param username: string, username of ProQuant account
        :param password: string, password of ProQuant account

        :return: bool if login was successful
        """

        self.browser.driver.get(proquant_url)

        self.browser.wait_for_page_load('Log in')

        self.browser.driver.find_element_by_xpath("//*[text()='Log in']").click()

        username_field = self.browser.driver.find_element_by_xpath("//input[@placeholder='Username or email']")
        username_field.send_keys(username)

        password_field = self.browser.driver.find_element_by_xpath("//input[@placeholder='Password']")
        password_field.send_keys(password)

        password_field.send_keys(Keys.ENTER)

        self.browser.wait_for_page_load('My Strategies')

        errors = ["Error" in self.browser.driver.page_source, "Something went wrong" in self.browser.driver.page_source,
                  "Incorrect username or password" in self.browser.driver.page_source]

        # Alternative to check for successful login
        # self.browser.driver.find_element_by_class_name("main-tab-bar")

        if True in errors:
            return False
        else:
            self.logged_in = True
            return True

    def __parse_strategy_page(self):
        def fetch_basic_strategy_info():
            strategy_header = self.browser.driver.find_element_by_css_selector("[class=\"css-1dbjc4n r-1awozwy r-1pz39u2 r-14lw9ot r-1777fci r-d9fdf6 r-tvv088\"]")
            basic_info_dict = {}

            strategy_friendly_name = strategy_header.text.split('\n')[1]
            strategy_market = strategy_header.text.split('\n')[0]
            conditional_desc = self.browser.driver.find_element_by_css_selector("[class=\"css-901oao r-1niwhzg r-80ss5y r-98loyc r-1b43r93 r-16dba41 r-10yl4k\"]").text

            basic_info_dict['friendly_name'] = strategy_friendly_name
            basic_info_dict['market'] = strategy_market
            basic_info_dict['conditional_statement'] = conditional_desc
            basic_info_dict['basic_info'] = {}

            basic_info = self.browser.driver.find_elements_by_css_selector("[class=\"css-1dbjc4n r-1awozwy r-14lw9ot r-1loqt21 r-18u37iz r-1k9zyfm r-d9fdf6 r-1otgn73 r-eafdt9 r-1i6wzkk r-lrvibr\"]")
            for info_piece in basic_info:
                info_piece = info_piece.text.split('\n')
                info_name, info_value = info_piece[0], info_piece[1]

                basic_info_dict['basic_info'][info_name] = info_value

            return basic_info_dict

        def iterate_visible_buttons(class_name):
            indicators = []
            for condition in self.browser.driver.find_elements_by_css_selector("[class=\""+class_name+"\"]"):
                condition.click()
                time.sleep(0.4)

                indicator_params = self.browser.driver.find_element_by_css_selector("[class=\"" + "css-1dbjc4n r-1kihuf0 r-14lw9ot r-1f0042m r-1ik5qf4 r-13qz1uu" + "\"]").text

                indicators.append(condition.text)
                indicators.append(indicator_params)

                close_param_button = self.browser.driver.find_elements_by_css_selector("[class=\"css-901oao r-cqee49\"]")[-1:][0]
                close_param_button.click()

            return indicators

        def format_rule_params(rule_list):
            rule_params = {}
            for i in range(0, len(rule_list)):
                line = rule_list[i]

                if "PARAMETERS" in line:
                    indicator_name = rule_list[i - 1]
                    indicator_split = line.split('\n')
                    indicator_param_desc = indicator_split[2]

                    rule_params[indicator_name] = {}
                    rule_params[indicator_name]['desc'] = indicator_param_desc

                    indicator_params = indicator_split[3:]
                    for j in range(0, len(indicator_params), 2):
                        param_name = indicator_params[j]
                        param_value = indicator_params[j + 1]
                        rule_params[indicator_name][param_name] = param_value

            return rule_params

        def finalize_rule_format(rule_format, rule_params):
            rules = {}

            rule_type = rule_format[0]
            rules[rule_type] = {}
            rules[rule_type]['desc'] = rule_format[1]
            for i in range(2, len(rule_format), 3):
                rule_name = rule_format[i]
                rule_desc = rule_format[i + 1]

                rules[rule_type][rule_name] = {}
                rules[rule_type][rule_name]['desc'] = rule_desc
                rules[rule_type][rule_name]['params'] = rule_params[rule_name]

            return rules

        self.browser.wait_for_page_load("CONDITIONS")

        if "Entry/exit rules hidden by owner" in self.browser.driver.page_source:
            return False

        strategy_info = {}
        strategy_info.update(fetch_basic_strategy_info())

        strategy_info['rules'] = {}

        # region fetch specific rules for long and short positions
        long_indicator_button = self.browser.driver.find_elements_by_css_selector("[aria-label=\"label.long\"]")
        short_indicator_button = self.browser.driver.find_elements_by_css_selector("[aria-label=\"label.short\"]")

        has_long_rules = len(short_indicator_button) == 1
        has_short_rules = len(short_indicator_button) == 1

        if has_long_rules:
            long_indicator_button = long_indicator_button[0]

            long_desc = list(map(self.browser.extract_text_from_element, self.browser.driver.find_elements_by_css_selector("[class=\"" + 'css-1dbjc4n r-1f4vckg r-qklmqi r-d9fdf6 r-1sxzll1' + "\"]")[:2]))
            long_rules = iterate_visible_buttons('css-901oao r-1niwhzg r-jwli3a r-98loyc r-ubezar r-b88u0q r-135wba7 r-zl2h9q')
            long_rules = format_rule_params(long_rules)

            long_entry_rules = finalize_rule_format(long_desc[0].split('\n'), long_rules)
            long_exit_rules = finalize_rule_format(long_desc[1].split('\n'), long_rules)
            strategy_info['rules'].update(long_entry_rules)
            strategy_info['rules'].update(long_exit_rules)

        if has_short_rules:
            short_indicator_button = short_indicator_button[0]
            if has_long_rules:
                short_indicator_button.click()
                time.sleep(1)

            short_desc = list(map(self.browser.extract_text_from_element, self.browser.driver.find_elements_by_css_selector("[class=\"" + 'css-1dbjc4n r-1f4vckg r-qklmqi r-d9fdf6 r-1sxzll1' + "\"]")[-2:]))
            short_rules = iterate_visible_buttons('css-901oao r-1niwhzg r-cqee49 r-98loyc r-ubezar r-b88u0q r-135wba7 r-zl2h9q')
            short_rules = format_rule_params(short_rules)

            short_entry_rules = finalize_rule_format(short_desc[0].split('\n'), short_rules)
            short_exit_rules = finalize_rule_format(short_desc[1].split('\n'), short_rules)
            strategy_info['rules'].update(short_entry_rules)
            strategy_info['rules'].update(short_exit_rules)
        #endregion
        return strategy_info

    def __parse_strategy_page_positions(self):
        def format_position(raw_position):
            raw_position = raw_position.split('\n')
            formatted_position = {
                "market":raw_position[0],
                "open_order": raw_position[6],
                "open_time": raw_position[7],
                "close_order":raw_position[3],
                "close_time":raw_position[4],
                "result": raw_position[1]
            }
            return formatted_position

        self.browser.wait_for_page_load("Closed by") # wait for trade history to load

        positions = {}
        positions["open"] = []
        positions["closed"] = []

        open_positions = self.browser.driver.find_elements_by_css_selector("[class=\"css-1dbjc4n r-14lw9ot r-rwogv6 r-wgabs5 r-17gur6a r-od3qrc r-1777fci r-p1pxzi r-mvpalk r-1pcd2l5\"]")
        open_positions = list(map(self.browser.extract_text_from_element,open_positions))
        has_open_positions = len(open_positions) > 0

        history = self.browser.driver.find_elements_by_css_selector("[class=\"css-1dbjc4n r-14lw9ot r-1px6drs r-od3qrc r-1777fci r-ku1wi2 r-1oy2gb8 r-1pcd2l5\"]")
        history = list(map(self.browser.extract_text_from_element, history))
        has_history = len(history) > 0

        for position in open_positions:
            positions["open"].append(format_position(position))
        for position in history:
            positions["closed"].append(format_position(position))

        return positions

    def get_strategies(self, return_index=False):
        if not self.logged_in:
            return False

        self.browser.driver.get(proquant_url)
        self.browser.wait_for_page_load("Total result")

        all_strategies = []
        all_strategies_result = self.browser.driver.find_elements_by_css_selector("[class=\"css-1dbjc4n r-14lw9ot r-13awgt0 r-1udh08x\"]")
        for i in range(0, len(all_strategies_result)):
            element = all_strategies_result[i]
            try:
                strategy = {
                    'market': element.text.split('\n')[0],
                    'friendly_name': element.text.split('\n')[1]
                }
                if return_index:
                    strategy['index']= i

                all_strategies.append(strategy)
            except:
                pass

        return all_strategies

    def go_to_positions(self):
        self.browser.wait_for_page_load("Positions")
        time.sleep(1)  # wait for positions button to load

        basic_info = self.browser.driver.find_elements_by_css_selector("[class=\"css-1dbjc4n r-1awozwy r-14lw9ot r-1loqt21 r-18u37iz r-1k9zyfm r-d9fdf6 r-1otgn73 r-eafdt9 r-1i6wzkk r-lrvibr\"]")
        for i in range(0, len(basic_info)):
            info_piece = basic_info[i]
            info_piece = info_piece.text.split('\n')
            info_name, info_value = info_piece[0], info_piece[1]
            if info_name == "Positions":
                if int(info_value) > 0:
                    basic_info[i].click()
                    return True
                else:
                    return False

    def go_to_strategy_by_name(self, strategy_market, strategy_friendly_name):
        if not self.logged_in:
            return False

        all_strategies = self.get_strategies(True)
        found_strategy = False
        element_to_click_index = 0
        for strategy in all_strategies:
            if strategy['market'] == strategy_market and strategy['friendly_name'] == strategy_friendly_name:
                found_strategy = True
                element_to_click_index = strategy['index']
                break

        if found_strategy:
            self.browser.driver.find_elements_by_css_selector("[class=\"css-1dbjc4n r-14lw9ot r-13awgt0 r-1udh08x\"]")[element_to_click_index].click()
            return True
        else:
            return False

    def get_positions_by_link(self, link):
        self.browser.driver.get(link)
        if not self.go_to_positions():
            return False
        else:
            return self.__parse_strategy_page_positions()

    def get_positions_by_name(self, strategy_market, strategy_friendly_name):
        if not self.go_to_strategy_by_name(strategy_market, strategy_friendly_name):
            return False
        elif not self.go_to_positions():
            return False
        else:
            return self.__parse_strategy_page_positions()

    def get_strategy_by_link(self, link):
        """Get strategy info by link
        e.x. https://app.proquant.com/shared/strategy/3a0669e4-2c56-4c11-bf4e-50a13efa5979
        """

        self.browser.driver.get(link)

        return self.__parse_strategy_page()

    def get_strategy_by_name(self, strategy_market, strategy_friendly_name):
        if not self.go_to_strategy_by_name(strategy_market, strategy_friendly_name):
            return False
        else:
            return self.__parse_strategy_page()

    def close(self):
        self.browser.driver.close()
        del self