def __buildDriver__(self, driver_options): # Ubuntu if (os.name == 'posix'): chromedriver = 'chromedriver' # Windows if (os.name == 'nt'): chromedriver = 'chromedriver.exe' if chromedriver: if config.DRIVER_NAME == 'Chrome': logger.info("Using Chrome Driver ...") options = webdriver.ChromeOptions() options.add_experimental_option("excludeSwitches", ["enable-automation"]) options.add_experimental_option('useAutomationExtension', False) options.add_experimental_option('w3c', False) for driver_option in driver_options: # if "--proxy-server" in driver_option: # print(f"\nADDING PROXY: [{driver_option}]\n") options.add_argument(driver_option) capabilities = None if config.PROXY: # print(f"\nADDING PROXY: [{config.PROXY}]\n") # prox = Proxy() # prox.proxy_type = ProxyType.MANUAL # prox.http_proxy = config.PROXY # # prox.socks_proxy = config.PROXY # prox.ssl_proxy = config.PROXY capabilities = webdriver.DesiredCapabilities.CHROME capabilities['loggingPrefs'] = {'performance': 'ALL'} # prox.add_to_capabilities(capabilities) if capabilities: self.driver = webdriver.Chrome( desired_capabilities=capabilities, options=options) # self.driver = config.DRIVER_NAME(desired_capabilities=capabilities, options=options) else: self.driver = webdriver.Chrome(chromedriver, options=options) # self.driver = config.DRIVER_NAME(chromedriver, options=options) else: logger.info("Using Firefox Driver ...") self.driver = webdriver.Firefox() self.min_wait = WebDriverWait(self.driver, 5) self.max_wait = WebDriverWait(self.driver, 20) self.els_css = self.driver.find_elements_by_css_selector self.el_css = self.driver.find_element_by_css_selector self.els_xpath = self.driver.find_elements_by_xpath self.el_xpath = self.driver.find_element_by_xpath self.driver.set_script_timeout(30) return self.driver
def get_today_prices(self, time_slots_count=200): if time_slots_count < 0: raise ValueError("time_slots_count cannot be in negative") logging.info("getting today prices ...") r = requests.get( config.Todayprices_Url.format(time_slots_count, helpers.device_id())) return r.json()
def trade_insights(self, Insights, open_markets_only=True, sort_by="growth"): if sort_by not in ("percentage", "growth"): raise Exception( f'<sort_by> value must be in {("percentage", "growth").join(" OR ")} ' ) logging.info( f"Getting Trade Insights of {'opened' if open_markets_only else 'all'} markets ..." ) ClosingPrices = sorted(self.get_closing_prices(), key=lambda k: float(k['InstrumentId'])) Instruments = sorted(self.get_instruments(), key=lambda k: float(k['InstrumentID'])) InstrumentIds = [ Instrument["InstrumentID"] for Instrument in Instruments ] res = [] for elem in ClosingPrices: if elem['InstrumentId'] not in InstrumentIds: continue if open_markets_only and elem['IsMarketOpen'] == False: continue ElemInsight = helpers.find_in_list(Insights, 'instrumentId', elem['InstrumentId']) if not ElemInsight: continue InstrumentDetail = helpers.find_in_list(Instruments, 'InstrumentID', elem['InstrumentId']) elem.update({ k: v for k, v in InstrumentDetail.items() if k in ( "InstrumentDisplayName", "SymbolFull", ) }) elem.update(ElemInsight) res.append(elem) res = sorted(res, key=lambda k: float(k[sort_by]), reverse=True) helpers.set_data( data=res, path=config.temp_dir + f"/{'opened' if open_markets_only else 'all'}_markets_insights_by_{sort_by}.json" ) return res
def switch_account(self, to_demo=True): if to_demo and self.current_account_type == 'VIRTUAL': logger.info("Already on <VIRTUAL Account>") return True elif to_demo is False and self.current_account_type == 'REAL': logger.info("Already on <REAL Account>") return True if to_demo: self.close_popup() self.toggle_account_switcher if self.els_css('et-select-body-option'): self.els_css('et-select-body-option')[-1].click() self.els_css('.toggle-account-button')[0].click() logger.info("Switched to <VIRTUAL Account>") else: self.close_popup() self.toggle_account_switcher if self.els_css('et-select-body-option'): self.els_css('et-select-body-option')[0].click() self.els_css('.toggle-account-button')[0].click() logger.info("Switched to <REAL Account>") return True
def get_etoro_instance(): # Open Web Driver etoro_instance = Etoro() #Login To Site login_status = etoro_instance.login() if login_status is True: if config.AccountType == "VIRTUAL": logger.info(f"Switching to VIRTUAL Account ...") time.sleep(5) etoro_instance.switch_account(to_demo=True) time.sleep(3) if config.AccountType != etoro_instance.current_account_type: raise Exception( f"Switch Account Failed. should be <{config.AccountType}>" f" instead of <{etoro_instance.current_account_type}> !") return etoro_instance elif login_status is False and (etoro_instance.has_server_error or etoro_instance.site_not_reachable()): retries = 2 while (login_status is False and retries < etoro_instance.MAX_RETRIES): logger.warning( f'Error Login :: {config.login_fail_msg}, Auto Retrying:' f'{retries}/{etoro_instance.MAX_RETRIES}') # del etoro_instance # etoro_instance = Etoro() etoro_instance.login() login_status = etoro_instance.get_loggedin_username() retries += 1 if login_status is False: raise Exception("Max retries done to login but failed") else: raise Exception("Login Failed. Check Username or Password")
def login(self): try: self.openUrl(config.URL, title_display="<Etoro>") if self.already_loggedin: logger.info(f'Already Logged as User:"******".') return True logger.info(f'Attempting Login as User:"******".') driver_status = self.get_status_of_driver() if driver_status == "Dead": logger.error("Cannot Login ([Error:: '<Driver Not Alive>'") return False # logger.info('Driver getting url: {}'.format(config.URL)) self.do_login_request() login_status = self.get_loggedin_username() if login_status: logger.info("Login Successful: '{}'".format(login_status)) return True else: return False except Exception as e: logger.error("Cannot Login ([Error:: '<{}>'])".format(str(e))) return False
def do_login_request(self): if config.URL not in self.driver.current_url: self.openUrl(config.URL, title_display="Attempting Login <Etoro>") logger.info("Please wait Logging in ...") # self.wait_and_get_elems(config.user_elem) # self.wait_and_get_elems(config.password_elem)[0].send_keys(config.PASSWORD) # sleep(1) # self.wait_and_get_elems(config.submit_elem)[0].click() # sleep(2) # Login screen elements username_el = self.wait_and_get_elems(config.user_elem) if not username_el: if config.URL in self.driver.current_url: if self.has_server_error: raise Exception(self.has_server_error) else: raise Exception("Could not access site.") elif self.already_loggedin: return True else: raise Exception("Unknown error or access failed.") username_el = username_el[0] password_el = self.wait_and_get_elems(config.password_elem)[0] remember_me_el = self.wait_and_get_elems(config.remember_me_elem)[0] login_button_el = self.wait_and_get_elems(config.submit_elem)[0] # Perform the actions username_el.send_keys(config.USER) sleep(config.WAIT_BETWEEN_USER_ACTIONS) password_el.send_keys(config.PASSWORD) sleep(config.WAIT_BETWEEN_USER_ACTIONS) remember_me_el.click() sleep(config.WAIT_BETWEEN_USER_ACTIONS) login_button_el.click() sleep(config.WAIT_BETWEEN_USER_ACTIONS)
def addLastOrderedTrade(openedTrade): """ add last ordered trades in json file""" trades = json_file_data(config.last_trade_done_file) or [] #if openedTrade is selling trade => # find already exisiting trade for openedTrade in history and if buying found remove buying one if (openedTrade['IsBuy'] == False): openedTradeHistory = find_in_list(trades, "InstrumentID", openedTrade["InstrumentID"]) if openedTradeHistory and openedTradeHistory["IsBuy"] == True: logging.info('Removing openedTrade buying trade history ...') trades.remove(openedTradeHistory) instrumentData = find_instrument_by_id(openedTrade['InstrumentID']) openedTrade.update({ k: v for k, v in instrumentData.items() if k in ("InstrumentDisplayName", "SymbolFull") }) trades.append(openedTrade) set_data(trades, config.last_trade_done_file) return json_file_data(config.last_trade_done_file)
def today_price_analysis(self, stocks_sort_by, time_slots_count=24, open_markets_only=True, time_slots_pick=2): logging.info( f"getting today's price analysis of {'opened' if open_markets_only else 'all'}" f" markets sorted by {stocks_sort_by} ...") file_name = f"markets_{'opened' if open_markets_only else 'all'}" \ f"_{time_slots_count}_time_slots" \ f"_time_compare_{time_slots_pick}_today_price_analysis.json" res = [] for P in self.today_market_prices(time_slots_count=time_slots_count, open_markets_only=open_markets_only): PriceStats = {} InstrumentId = P['InstrumentId'] Prices = P['Prices'] if len(Prices) < time_slots_pick: # print(Prices, "\n") continue # d2 = parse(Prices[-1]['ToTime']) # d1 = parse(Prices[-3]['ToTime']) # print("dis -> ",(d2-d1).seconds/60) PricesToMonitor = Prices[int(f"-{time_slots_pick}"):] PricesRange = [_P['Price'] for _P in PricesToMonitor] PricesDateRange = [_P['ToTime'] for _P in PricesToMonitor] PricesRange = np.array(PricesRange, dtype=float) Dx = np.diff(PricesRange) / PricesRange[:-1] * 100 # MaxIncrease = sum([100 * (b - a) / a for a, b in zip(PricesRange[::1], PricesRange[1::1])])/len(PricesRange) MaxIncrease = Dx.max() if Dx.any() else 0.00 MeanIncrease = Dx.mean() if Dx.any() else 0.00 PriceStats["InstrumentId"] = InstrumentId PriceStats["MaxIncrease"] = MaxIncrease PriceStats["MeanIncrease"] = MeanIncrease PriceStats["PricesRange"] = PricesToMonitor PriceStats["OfficialClosingPrice"] = P['OfficialClosingPrice'] PriceStats["IsMarketOpen"] = P['IsMarketOpen'] PriceStats["OfficialClosingPrice"] = P['OfficialClosingPrice'] PriceStats["ClosingPrices"] = P['ClosingPrices'] PriceStats["InstrumentDisplayName"] = P.get( 'InstrumentDisplayName') PriceStats["ExchangeID"] = P.get('ExchangeID') PriceStats["SymbolFull"] = P.get('SymbolFull') res.append(PriceStats) res = sorted(res, key=lambda k: float(k[stocks_sort_by]), reverse=True) helpers.set_data(data=res, path=f"{config.temp_dir}/{file_name}") return res
def get_closing_prices(self): logging.info("getting closing prices ...") r = requests.get(config.Closingprices_Url + helpers.device_id()) return r.json()
def get_instruments(self): logging.info("getting instruments data ...") return helpers.get_instruments(config.instruments_file)
def trade(self, ins, IsBuy=True): if helpers.is_digit(ins): InstrumentData = helpers.find_instrument_by_id(ins) else: InstrumentData = helpers.find_instrument_by_symbol(ins) if not InstrumentData: raise Exception(f"no Instrument found by: '{ins}'") Instrument = InstrumentData['SymbolFull'] url = config.Market_Url.format(Instrument.lower().strip()) res = "" if url not in self.driver.current_url: self.openUrl( url, title_display=f"Going to Trade <{Instrument}> IsBuy <{IsBuy}>") self.wait_and_get_elems('div[automation-id="trade-button"]')[0].click() if self.wait_and_get_elems( 'a[href="https://www.etoro.com/trading/market-hours-and-events/"]' )[0].text == 'MARKET CLOSED': res = f"[MARKET CLOSED] Trade Cannot Be Opened for <{Instrument}> IsBuy:{IsBuy}" logger.warning(res) return False, res if IsBuy: self.wait_and_get_elems( 'button[ng-class="{active: model.isBuy }"]')[0].click() else: #sell self.wait_and_get_elems( 'button[ng-class="{active: !model.isBuy }"]')[0].click() #select leverage: self.wait_and_get_elems( 'div[data-etoro-automation-id="execution-leverage-tab-title-value"]' )[0].click() leverages = self.wait_and_get_elems( 'a[ng-repeat="leverage in model.displayLeverages"]') leverages[-1].click() # select max leverage value if config.Trailing_Stop_Loss: self.el_css( "div[data-etoro-automation-id='execution-stop-loss-tab-title-label']" ).click() self.el_css( "input[data-etoro-automation-id='execution-stop-loss-editing-tsl-check-box']" ).click() if self.is_on_rate_view: #switch to amount view self.wait_and_get_elems( "a[data-etoro-automation-id='execution-stop-loss-amount-editing-switch-to-rate-button']" )[0].click() stoploss_input = self.els_css( "input[data-etoro-automation-id='input']")[-1] stoploss_input.clear() amount_input = self.els_css( "input[data-etoro-automation-id='input']")[0] amount_input_val = float( amount_input.get_attribute("value").replace("$", "").replace( ",", "")) if config.Max_Stop_Loss: stoploss_input.send_keys( '999999' + Keys.RETURN) #this will validate and selects max SL else: stoploss_input.send_keys( f'-{amount_input_val*float(config.Stop_Loss)}' + Keys.RETURN) #click trade button self.wait_and_get_elems( 'button[data-etoro-automation-id="execution-open-position-button"]' )[0].click() res = f"Trade Opened for <{Instrument}> IsBuy:{IsBuy} at {datetime.now()}" logger.info(res) #getting login info to check trade successfull ? openedTrade = helpers.isOrderOpened(InstrumentData["InstrumentID"], self.get_login_info, True) #save last trade done helpers.addLastOrderedTrade(openedTrade) return bool(openedTrade != {}), res
def buy_trade(etoro_instance): try: detail = "" # etoro_instance.login() #checking current balance clientCredit = helpers.clientCredit( login_data=etoro_instance.get_login_info) open_markets_only = config.analyze_open_markets_only logger.info( f"Analyzing Today Stocks For {'opened' if open_markets_only else 'all'} markets ..." ) analyzer = AnalyzeStocks() top_markets = analyzer.today_price_analysis( stocks_sort_by=config.stocks_sort_by, time_slots_count=24, open_markets_only=open_markets_only, time_slots_pick=2) # top_markets = analyzer.trade_insights( # etoro_instance.get_insights(), # open_markets_only=open_markets_only, sort_by="growth") for t1, top_market in enumerate(top_markets): logger.info(f"\nGoing to open Buying Trade for :\n{top_market}\n") buy_trade, buy_trade_res = etoro_instance.trade( ins=top_market.get("InstrumentId"), IsBuy=True) if buy_trade is False: detail = ( f"Couldnot open buying position for: '{top_market.get('SymbolFull')}'" f" reason :\n{buy_trade_res}\n") logger.warning(detail) else: detail = ( f"Opened buying position for: '{top_market.get('SymbolFull')}'" f" response :\n{buy_trade_res}\n") logger.info(detail) break logger.info(f"\nGoing to update User Date ...") user_data = etoro_instance.get_login_info logger.info(f"\nGot User Data :\n{user_data}\n") logger.info(f"\nGoing to update User Trade History ...") user_trade_history = etoro_instance.get_trade_history logger.info(f"\nGot User Trade History :\n{user_trade_history}\n") msg = '<buy_trade> finished ...' print('+' * len(msg)) print(msg) print('+' * len(msg)) except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] err_detail = e, fname, exc_tb.tb_lineno detail = f"Following Error occured in buy_trade ::\n{err_detail}\n" logger.error(detail) #write trade logs helpers.write_csv("Buying", detail)
def sell_trade(etoro_instance): try: detail = "" # etoro_instance.login() logger.info( f"Going to get last Ordered Trades (For Opening Selling Position ) ..." ) lastOrderedTrades = helpers.lastOrderedTrade(isBuy=True) if not lastOrderedTrades: logger.info( f"No Last Ordered Trades found to open a selling position.") for t1, lastOrderedTrade in enumerate(lastOrderedTrades): instrumentID = lastOrderedTrade['InstrumentID'] instrumentData = helpers.find_instrument_by_id(instrumentID) lastOrderedTrade.update({ k: v for k, v in instrumentData.items() if k in ( "InstrumentDisplayName", "SymbolFull", ) }) instrumentSymbol = instrumentData["SymbolFull"] instrumentDisplayName = instrumentData["InstrumentDisplayName"] instrumentTitle = f"{instrumentSymbol} - {instrumentDisplayName}" positionID = lastOrderedTrade['PositionID'] logger.info( f"\n[{t1+1}/{len(lastOrderedTrades)}] :\n{lastOrderedTrade}\n") logger.info(f"Going to get User Trade History ...") tradeHistory = etoro_instance.get_trade_history closedOrder = helpers.isOrderClosed( positionID, data_list=tradeHistory, path=config.closed_trade_history_file) if not closedOrder: logger.info(f"<[{instrumentTitle}]: postionID->" f"{positionID} instrumentID->{instrumentID}>" " is not closed yet. skipping this...") continue # here opens a selling postion for this trade logger.info( f"Going to open Selling Trade for: {instrumentTitle}\n") sell_trade, sell_trade_res = etoro_instance.trade(ins=instrumentID, IsBuy=False) if sell_trade is False: detail = ( f"Couldnot open selling position for: '{instrumentTitle}'" f" reason :\n{sell_trade_res}\n") logger.warning(detail) else: detail = (f"Opened selling position for: '{instrumentTitle}'" f" response :\n{sell_trade_res}\n") logger.info(detail) logger.info(f"\nGoing to update User Date ...") user_data = etoro_instance.get_login_info logger.info(f"\nGot User Data :\n{user_data}\n") logger.info(f"\nGoing to update User Trade History ...") user_trade_history = etoro_instance.get_trade_history logger.info(f"\nGot User Trade History :\n{user_trade_history}\n") msg = "<sell_trade> finished ..." print('+' * len(msg)) print(msg) print('+' * len(msg)) except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] err_detail = e, fname, exc_tb.tb_lineno detail = f"Following Error occured in sell_trade ::\n{err_detail}\n" logger.error(detail) #write trade logs helpers.write_csv("Selling", detail)
if __name__ == '__main__': exchange_to_follow = helpers.get_exchange_info(config.Default_Exchange) day_of_week_opens = exchange_to_follow['Market Opens'].split()[0].lower( ).replace("day", "") day_of_week_closes = exchange_to_follow['Market Closes'].split()[0].lower( ).replace("day", "") exchange_closing = datetime.strptime( exchange_to_follow['Market Closes'].split()[1], "%H:%M") hour_minute = exchange_closing - timedelta( minutes=config.Exchange_Schedular_Minutes) day_of_week = f"{day_of_week_opens}-{day_of_week_closes}" logger.info( f"\nScheduler started at: {datetime.now().astimezone(pytz.timezone('UTC'))}\n" ) logger.info( f"Trader For <{exchange_to_follow['Exchange']}> scheduled to run at:" f"\n[Day Weeks: {day_of_week}]\t[Exchange closing hour: " f"{exchange_closing.hour}:{exchange_closing.minute:02d} UTC]\t[Job starts at: " f"{hour_minute.hour}:{hour_minute.minute:02d} UTC]\n") #graps etoro instance with logged in driver and swticed to virtual/real account etoro_instance = get_etoro_instance() #scheduler = BlockingScheduler({'apscheduler.timezone': 'Europe/London'}) scheduler = BlockingScheduler({'apscheduler.timezone': 'UTC'}) # schedular to buy_trade scheduler.add_job(buy_trade,