def _treeview_sort_column(tv, col, reverse): """ https://stackoverflow.com/questions/1966929/tk-treeview-column-sort :param tv: tree view to sort :param col: column index to sort :param reverse: True: reverse sort :return: - """ try: l = [(tv.set(k, col), k) for k in tv.get_children('')] try: l.sort(key=lambda t: float(t[0]), reverse=reverse) except ValueError as e: l.sort(reverse=reverse) for index, (val, k) in enumerate(l): tv.move(k, '', index) tv.heading(col, command=lambda: GuiUtils._treeview_sort_column( tv, col, not reverse)) except Exception as e: logger.error("Exception Could not sort the given treeview: " + str(e) + "\n" + str(traceback.format_exc()))
def _method_to_execute(self, stock_data_container): """ Method to execute implemented for multi threading, executed for every sublist :param stock_data_container_sub_list: sub list of the whole stock data container (already split) :return: nothing, sublist in changed """ if stock_data_container.stock_ticker() != "": if stock_data_container not in self.stock_data_container_list \ or len(stock_data_container.historical_stock_data()) <= 0 \ or self.reload_stockdata: stock52_w = self._get_ticker_data_with_webreader( stock_data_container.stock_ticker(), stock_data_container.stock_exchange(), self._parameter_dict['data_source'], self._parameter_dict['weeks_delta']) stock_data_container.set_historical_stock_data(stock52_w) try: if stock52_w is not None and len(stock52_w) > 0: curr_prize = stock52_w[ GlobalVariables.get_stock_data_labels_dict() ["Close"]][len(stock52_w) - 1] stock_data_container.set_stock_current_prize( curr_prize) except Exception as e: logger.error("Could not set curr_prize of stock " + stock_data_container.get_stock_name() + " " + str(e) + "\n" + str(traceback.format_exc())) self.update_status("HistoricalDataReader:")
def execute_order(self, stock_ticker, order_type='LMT', action='SELL', quantity=1, limit_price=1, security_type='STK', exchange='SMART', currency='USD'): """ Create a contract and a order, read order id, execute the order, save the order to file. :param stock_ticker: stock ticker :param order_type: oder type , ex: LMT for limit orders :param action: BUY / SELL :param quantity: number of stocks to order :param limit_price: limit price to buy or sell :param security_type: STK for stocks :param exchange: echange to trade, SMART :param currency: USD / EUR :return: """ # Create a contract via SMART order routing current_contract = self._create_contract(stock_ticker, security_type, exchange, exchange, currency) current_order = self._create_order(order_type, quantity, action, limit_price) # Use the connection to the send the order to IB order_id = self._read_current_order_id() try: self.tws_conn.placeOrder(int(order_id), current_contract, current_order) text_line = str(datetime.now()) + "," + str(stock_ticker) + "," + str(order_id) + "," + str( order_type) + "," + str(action) + "," + str(quantity) + "," + str(limit_price) + "," + str( security_type) + "," + str(exchange) + "," + str(currency) logger.info("***************************************") logger.info("Order was placed: " + text_line) logger.info("***************************************") print(text_line) except Exception as e: logger.error("Unexpected Exception : " + str(e) + "\n" + str(traceback.format_exc())) self._save_current_order(order_id, stock_ticker, order_type, action, quantity, limit_price, security_type, exchange, currency)
def get_required_parameters_with_default_parameters(): """ Return a dict with required strategy parameters and default parameter values. :return: dict with required values and default parameters """ all_strategy_parameters_dict = {} strat_factory = StrategyFactory() strategies_dict = strat_factory.get_implemented_classes() for strat_class_key in list(strategies_dict.keys()): try: def_params = strategies_dict[ strat_class_key].get_required_parameters_with_default_parameters( ) all_strategy_parameters_dict.update(def_params) except NotImplementedError as nie: logger.error("Exception for strategy class " + strat_class_key + ": " + str(nie) + "\n" + str(traceback.format_exc())) all_strategy_parameters_dict = { 'Strategies': all_strategy_parameters_dict } other_params = GlobalVariables.get_other_parameters_with_default_parameters( ) all_strategy_parameters_dict.update({"OtherParameters": other_params}) backtesting_params = GlobalVariables.get_backtesting_parameters_with_default_parameters( ) all_strategy_parameters_dict.update(backtesting_params) return all_strategy_parameters_dict
def result_stock_data_container_list_changed(self): """ Update the columns and data of the view, of stock data container list changed. Additionally, it adds the not already available columns to the MvcModel and fill not available columns with dummy. :return: - """ tree = w.Scrolledtreeview1 tree.delete(*tree.get_children()) stock_data_container_list = self.model.result_stock_data_container_list.get( ) # sort the list by rank newlist = sorted(stock_data_container_list, key=lambda x: x.get_rank(), reverse=True) for result_container in newlist: try: is_updated = self.model.update_column_list( result_container.get_names_and_values().keys()) if is_updated: init_result_table(self.view.Scrolledtreeview1, self.model.get_column_list()) GuiUtils.insert_into_treeview( self.view.Scrolledtreeview1, self.model.get_column_list(), result_container.get_names_and_values(), "Stock") # append all COLUMNS to file --> new layout leads to new line with header FileUtils.append_text_list_to_file( self.model.get_column_list(), GlobalVariables.get_data_files_path() + "ScreeningResults.csv", True, ",") # append VALUES to file values = result_container.get_names_and_values().values() text = ','.join(str(e) for e in values) FileUtils.append_textline_to_file( text, GlobalVariables.get_data_files_path() + "ScreeningResults.csv", True) except Exception as e: logger.error("Exception: " + str(e) + "\n" + str(traceback.format_exc())) continue # add a sort functionality for each column, when click on header GuiUtils.advanced_sorting(self.view.Scrolledtreeview1, self.model.get_column_list(), True) # add a color for the entries in tree view for color in GlobalVariables.get_row_colors().keys(): tree.tag_configure( GlobalVariables.get_row_colors()[color], background=GlobalVariables.get_row_colors()[color])
def signal_is_volume_raising_within_check_days(stock, check_days, min_cnt): """ Checks, if the volume is rising within the check days, with at least minimum value. Args: stock: stock data check_days: number of days to check min_cnt: min raising days within check days Returns: True, if volume is raising Raises: NotImplementedError: if parameters are None """ if stock is None or check_days is None or min_cnt is None: raise NotImplementedError data_len = len(stock) if data_len < 5: #must have enough data return None #TODO #TOD raise IndexError raise_cnt = 0 i = check_days save_val = False while i > 0: try: vol_cur = stock.iloc[data_len - i][GlobalVariables.get_stock_data_labels_dict()['Volume']] if not save_val: if i is data_len: vol_last = 0 else: vol_last = stock.iloc[data_len - i - 1][GlobalVariables.get_stock_data_labels_dict()['Volume']] if vol_cur > vol_last: raise_cnt += 1 save_val = False else: save_val = True except Exception as e: logger.error("Unexpected Exception : " + str(e) + "\n" + str(traceback.format_exc())) i -= 1 if raise_cnt < min_cnt: return None return True
def evaluate_list_box_selection(evt, log_text, own_widget=None): widget = evt.widget if own_widget is None or own_widget is widget: try: selected_text_list = [ widget.get(i) for i in widget.curselection() ] except Exception as e: logger.error("Selection failed:" + str(e) + "\n" + str(traceback.format_exc())) selected_text_list = [] logger.info(log_text + str(selected_text_list)) return selected_text_list
def load_analysis_parameters_from_file(self, file_path, required_parameters): """ Loads the parameters into the GUI from a given filepath and file. :param required_parameters: a dict with all required parameters, must be all in file :param file_path: file path and name as string to load file :return: nothing """ try: items = None if os.path.isfile(file_path): with open(file_path, "rb") as f: items = pickle.load(f) self.current_parameterfile = file_path if self.accept_parameters_from_text(items, required_parameters): override_params = False else: override_params = messagebox.askyesno( "Parameter file is not valid!", "Do you want to CREATE a new file with default parameters?" ) else: override_params = True if override_params: self.model.analysis_parameters.clear() self.model.analysis_parameters.update(required_parameters) self.model.available_strategies_list.clear() for item in required_parameters['Strategies']: self.model.available_strategies_list.append(item) messagebox.showinfo("Parameterfile created", "New parameters created!") except Exception as e: messagebox.showerror( "Parameter file is not valid!", "Please choose a valid parameters file:" + str(e) + "\n" + str(traceback.format_exc())) logger.error( "Exception while loading other parameter from file: " + str(e) + "\n" + str(traceback.format_exc())) return
def read_tickers_and_data_from_file(stock_data_container_file): """ Read the pickle file with stock data container for tickers and data. :param stock_data_container_file: pickle data file :return: container list """ stock_data_container_list = [] if ".pickle" in stock_data_container_file: if os.path.exists(stock_data_container_file): logger.info("Start reading tickers from file...") with open(stock_data_container_file, "rb") as f: stock_data_container_list += pickle.load(f) else: logger.error("Please select a *.pickle file for the stock_data_container_file!") return stock_data_container_list
def treeview_add_header_sort_column(tv, col, reverse): """ Adds a sort function to header https://stackoverflow.com/questions/1966929/tk-treeview-column-sort :param tv: tree view to sort :param col: column index to sort :param reverse: True: reverse sort :return: - """ try: l = [(tv.set(k, col), k) for k in tv.get_children('')] tv.heading(col, command=lambda: GuiUtils._treeview_sort_column( tv, col, not reverse)) except Exception as e: logger.error("Exception Could not sort the given treeview: " + str(e) + "\n" + str(traceback.format_exc()))
def _method_to_execute(self, stock_data_container): """ This method is abstract, implement the real list execution instead. :param argument: A single element of the list to execute :return: should return the result or add it to the list and return nothing """ try: if len(stock_data_container.historical_stock_data()) > 0: self.add_signals(stock_data_container, self.analysis_parameters) result = evaluate_signals(self.signal_list) if result is not None: stock_data_container.update_used_strategy_and_recommendation( self.__class__.__name__, "BUY") self.result_list.append(stock_data_container) except Exception as e: logger.error("Unexpected Exception : " + str(e) + "\n" + str(traceback.format_exc()))
def init(top, gui, *args, **kwargs): logging.basicConfig(level=logging.INFO) global w, top_level, root, app w = gui top_level = top root = top app = MvcController(root, w) # ######################## try: # load the last saved file path req_params = StrategyFactory.get_required_parameters_with_default_parameters( ) last_used_parameter_file_path = GlobalVariables.get_last_used_parameter_file( ) config = configparser.ConfigParser() config.read(last_used_parameter_file_path) try: param_file = (config["Parameters"]['parameterfile']) multi_file_path_str = config["Parameters"][ 'backteststockselection'] if "," in multi_file_path_str: multi_file_path = multi_file_path_str.split(',') else: multi_file_path = [multi_file_path_str] # multi_file_path = ast.literal_eval(multi_file_path_str) app.load_backtesting_stocks_from_file(multi_file_path) except Exception as e: param_file = "" app.load_analysis_parameters_from_file(param_file, req_params) except Exception as e: logger.error("Exception while opening last saved file path: " + str(e) + "\n" + str(traceback.format_exc())) # ######################## return app
def insert_text_into_gui(element, text, delete=False, start=1.0, end=END): """ optionally deletes the given element and optionally insert text into given element. :param element: element to insert into (ex: Scrolledtext) :param text: text to insert :param delete: true, if delete content first :param start: start case, ex.: 1.0 or 0 :param end: END tag :return: nothing """ try: if delete: element.delete(start, end) if text is not None and len(text) > 0: element.insert(end, text) element.see("end") except Exception as e: logger.error("Can not insert text into gui: " + str(e) + "\n" + str(traceback.format_exc()))
def on_double_click_Scrolledtreeview1(self, event): """ On double click event for scrolledtreeview1, opens a finance web page :param event: :return: """ try: cur_selection = self.view.Scrolledtreeview1.selection()[0] cur_stock = self.view.Scrolledtreeview1.item(cur_selection) # TODO je nachj container unterschiedlich 2 --> name is first # TODO einstellen der aufruf seite stock_name = cur_stock['values'][2] url_to_open = "http://www.finanzen.at/suchergebnisse?_type=Aktien&_search=" wb.open_new_tab(url_to_open + stock_name) except IndexError as e: pass # nothing to do for index error (may clicked at header) except Exception as e: logger.error("Exception while opening result stock: " + str(e) + "\n" + str(traceback.format_exc()))
def start_screening_repetitive(self): """ Start the screening repetitive, due to interval, if screening is not already running. :return: nothing """ if self.model.thread_state.get( ) is GlobalVariables.get_screening_states()['repetitive_screening']: try: self.background_scheduler.shutdown() except Exception as e: logger.error("Unexpected Exception : " + str(e) + "\n" + str(traceback.format_exc())) self.model.thread_state.set( GlobalVariables.get_screening_states()['not_running']) else: try: if not self._check_if_strategy_can_run(): return # interval time, defines the cycle time to restart the screening interval_sec = self.model.analysis_parameters.get( )['OtherParameters']['AutoTrading'][ 'RepetitiveScreeningInterval'] self.background_scheduler.add_job( self.start_screening_repetitive_mode, 'interval', seconds=interval_sec, next_run_time=datetime.now()) self.background_scheduler.start() self.model.thread_state.set( GlobalVariables.get_screening_states() ['repetitive_screening']) except Exception as e: logger.error("Unexpected Exception : " + str(e) + "\n" + str(traceback.format_exc())) self.model.thread_state.set( GlobalVariables.get_screening_states()['not_running'])
def _method_to_execute(self, stock_data_container): try: self.add_signals(stock_data_container, self.analysis_parameters) result = evaluate_signals(self.signal_list) if result is not None and result is not False: ppd = stock_data_container.positive_prob_dist() if ppd >= 0.5: rec = "BUY" else: rec = "SELL" stock_data_container.update_used_strategy_and_recommendation( self.__class__.__name__, rec) self.result_list.append(stock_data_container) except Exception as e: logger.error("For stock: " + str(stock_data_container.get_stock_name()) + ": " + str(e) + "\n" + str(traceback.format_exc())) self.update_status("SimplePatternNewsStrategy:")
def screening(self): """ Method to start the screening once, should be executed in a THREAD. :return: nothing, results are saved in the model. """ try: if not self._check_if_strategy_can_run(): return self.model.is_background_thread_running.set(True) selection_values = self.model.selected_strategies_list.get() logger.info("Screening started...") self.model.result_stock_data_container_list.clear() analysis_params = self.model.analysis_parameters.get() results = run_analysis(selection_values, analysis_params['Strategies'], analysis_params['OtherParameters']) self.model.result_stock_data_container_list.extend(results) except Exception as e: logger.error("Exception while screening: " + str(e) + "\n" + str(traceback.format_exc())) self.model.is_background_thread_running.set(False)
def accept_parameters_from_text(self, params_dict, required_parameters): """ Method to accept the changes in the scrolled text for the parameters, if the shape and keys of parameters dict is same as required parameters dict. :return: True, if parameters are valid and updated. """ try: if isinstance(params_dict, dict): for param_key in params_dict.keys(): if not param_key in required_parameters.keys() or len( params_dict[param_key]) <= 0: logger.error( "Parameter keys faulty, please insert correct parameters!" ) return False if not CommonUtils.have_dicts_same_shape( required_parameters, params_dict): logger.error( "Parameter shapes are faulty, please insert correct parameters!" ) return False self.model.analysis_parameters.clear() self.model.analysis_parameters.update(params_dict) self.model.available_strategies_list.clear() for item in params_dict['Strategies']: self.model.available_strategies_list.append(item) logger.info("Analysis parameters Read") else: logger.error( "Parameters are no dict, please insert correct parameters!" ) return False except Exception as e: logger.error("Exception while opening result stock: " + str(e) + "\n" + str(traceback.format_exc())) return False return True
def backtesting(self): """ Method to start the backtesting once, should be executed in a THREAD. :return: nothing, results are saved in the model. """ try: strategy_selections = self.model.selected_strategies_list.get() selected_backtesting_analyzers_str = self.model.selected_backtesting_analyzers_list.get( ) selected_backtesting_stocks = self.model.selected_backtesting_stocks_list.get( ) if strategy_selections == "" or not len(strategy_selections) is 1: messagebox.showerror( "Selection Error", "Please select exactly ONE strategie to run in backtesting!" ) return if selected_backtesting_stocks == "" or len( selected_backtesting_stocks) <= 0: messagebox.showerror( "Selection Error", "Please select stocks to run in backtesting first!") return if selected_backtesting_analyzers_str == "" or len( selected_backtesting_analyzers_str) <= 0: continue_backtesting = messagebox.askyesno( "Analyzer Selection", "No additional analyzer ist selected! Do you want to start backtesting anyway?" ) if not continue_backtesting: return data_backtesting_analyzers = [] for ana in self.model.available_backtesting_analyzers_list.get(): for selected_backtesting_analyzer_str in selected_backtesting_analyzers_str: if selected_backtesting_analyzer_str in ana.__name__: data_backtesting_analyzers.append(ana) available_backtesting_stocks_data = self.model.available_backtesting_stocks_list.get( ) selected_backtesting_stocks_data = [] for selected_backtesting_stock_str in selected_backtesting_stocks: for available_backtesting_stock_data in available_backtesting_stocks_data: if selected_backtesting_stock_str in available_backtesting_stock_data: selected_backtesting_stocks_data.append( available_backtesting_stock_data) break req_params = StrategyFactory.get_required_parameters_with_default_parameters( ) at_objects = w.scrollable_frame_parameters.form.at_objects content_others = w.scrollable_frame_parameters.form.get_parameters( at_objects) if not self.accept_parameters_from_text(content_others, req_params): messagebox.showerror("Parameter file is not valid!", "Please choose a valid parameters file!") return self.model.is_background_thread_running.set(True) logger.info("") logger.info( "*********************************************************************************************" ) logger.info( "******************** Backtesting started... *************************************************" ) bf = BacktestingFactory() backtesting_parameters = self.model.analysis_parameters.get( )["BacktestingParameters"] bt_framework_name = backtesting_parameters['BacktestingFramework'] tbt = bf.prepare(bt_framework_name) # get the selection of the first strategy analysis_params = self.model.analysis_parameters.get( )['Strategies'][strategy_selections[0]] # get the risk model value without key risk_models = self.model.analysis_parameters.get( )['OtherParameters']['RiskModels'] backtesting_result_instance, backtest_result = tbt.run_test( selected_backtesting_stocks_data, strategy_selections[0], backtesting_parameters, analysis_params, risk_models, data_backtesting_analyzers) insert_text_into_gui(self.view.Scrolledtext_analyzer_results, "", delete=True, start=1.0) # get items of analyzers and append it to the name analyzers = backtest_result[0].analyzers for analyzer in analyzers: ana_res = analyzer.get_analysis() items = list(ana_res.items()) text_list = [] for i in items: text = ': '.join(str(e) for e in i) text_list.append(text) final_text = '\n'.join(str(e) for e in text_list) insert_text_into_gui( self.view.Scrolledtext_analyzer_results, str(analyzer.__class__.__name__) + ":\n" + str(final_text) + "\n\n") portvalue = round(backtesting_result_instance.broker.getvalue(), 2) pnl = round(portvalue - backtesting_parameters['initial_cash'], 2) # Print out the final result insert_text_into_gui( self.view.Scrolledtext_analyzer_results, 'Final Portfolio Value: ${}'.format(portvalue) + "\n") insert_text_into_gui( self.view.Scrolledtext_analyzer_results, 'Profit/Loss (rounded 2 places): ${}'.format(pnl)) self.model.backtesting_result_instance.set( backtesting_result_instance) except Exception as e: logger.error("Exception while backtesting: " + str(e) + "\n" + str(traceback.format_exc())) self.model.is_background_thread_running.set(False)
def error_handler(self, msg): """Handles the capturing of error messages""" print("Server Error: %s" % msg) if msg.errorCode not in info_codes: # this is just a ping logger.error("Unexpected Exception : %s" % msg)
def _get_ticker_data_with_webreader(self, ticker, stock_exchange, data_source, weeks_delta): """ Method to read the data from the web or from temp file. :param stock_exchange: current stock exchange place (de, en..) :param ticker: ticker of the stock :param stock_dfs_file: file to load data or save the data from web :param data_source: google or yahoo :param reload_stockdata: true, to load from web, otherwise from temp file :param weeks_delta: delta from now to read the past: 52 means 52 weeks in the past :return: a dataframe df with ticker data """ assert len( ticker ) < 17, "ATTENTION: ticker length is long, maybe it is a name not a ticker: " + ticker df = [] if ticker == "" or ticker == '' or len(ticker) <= 0: logger.error("EXCEPTION reading because ticker is empty") return df # TODO 11 ticker = optimize_name_for_yahoo(ticker) # TODO nicht nur für yahoo ticker_exchange = ticker if ticker_exchange == "" or ticker_exchange == '' or len( ticker_exchange) <= 0: logger.error("EXCEPTION reading because ticker is empty (2)") return df # TODO 3: yahoo does not take en, so skip # if _stock_exchange != '' and _stock_exchange is not None and _stock_exchange != "en" and data_source == 'yahoo': # ticker_exchange += "." + _stock_exchange # TODO autmatisieren von pandas=?? # for i in range(0, 2): #TODO 4 try: end = dt.datetime.now() start = (end - dt.timedelta(weeks=weeks_delta)) df = data.DataReader(ticker_exchange, data_source, start, end, 3, 0.05) # if len(df) > 0: # break except KeyError as ke: logger.error("No Stock data read for: " + str(ticker_exchange)) except Exception as e: logger.error(str(e) + "\n" + str(traceback.format_exc())) # exception but the df is filled --> ok # if len(df) > 0: # break # TODO performance: wird dann langsam # from time import sleep # sleep(0.1) # Time in seconds. if len(df) <= 0: logger.error("EXCEPTION reading because data is empty, " + 'FAILED: Reading {}'.format(ticker_exchange)) return df
def buy_recommendations(broker, stocks, max_num_of_different_stocks_to_buy): """ Automatically buy the recommendations from result list :return: - """ from Utils.Logger_Instance import logger broker.connect() if len(stocks) <= 0: return try: # sort stocks by rank sorted_stock_container_list = sorted(stocks, key=lambda x: x.get_rank(), reverse=True) for i in range(len(sorted_stock_container_list)): if max_num_of_different_stocks_to_buy <= 0: # do not buy more stocks logger.info("Max number of stocks to buy reached.") return # check if there are enough data to create stop buy and stop loss limit orders if not are_order_information_available("BUY", sorted_stock_container_list[i]) or \ not are_order_information_available("SELL", sorted_stock_container_list[i]): logger.info("Not enough information for order available: " + str(sorted_stock_container_list[i])) break # trade only, if not already traded today orders = broker.read_orders() date_time_str = None # find the last entry for curr_order_num in range(len(orders)): if orders['stock_ticker'][curr_order_num].startswith( sorted_stock_container_list[i].stock_ticker()): date_time_str = (orders['datetime'][curr_order_num]) if date_time_str is not None: next_day_or_later = is_next_day_or_later( str(datetime.now()), "%Y-%m-%d %H:%M:%S.%f", date_time_str, "%Y-%m-%d %H:%M:%S.%f") # do not trade same recommendation again on one day if not next_day_or_later: logger.info( "No current recommendations to buy available for " + str(sorted_stock_container_list[i])) break # get stock quantity, only even number qty = int(sorted_stock_container_list[i].get_position_size()) # rank < 0 means sell recommendation, > 0 a buy if sorted_stock_container_list[i].get_rank() > 0: # stop buy limit order broker.execute_order( sorted_stock_container_list[i].stock_ticker(), 'LMT', 'BUY', qty, sorted_stock_container_list[i].get_stop_buy()) # stop loss limit order broker.execute_order( sorted_stock_container_list[i].stock_ticker(), 'LMT', 'SELL', qty, sorted_stock_container_list[i].get_stop_loss()) max_num_of_different_stocks_to_buy = max_num_of_different_stocks_to_buy - 1 # get the response # TODO without sleep sleep(0.5) error_message_list = broker.get_and_clear_error_message_list() if len(error_message_list) > 0: for error_msg in error_message_list: logger.error( "Unexpected response from broker while autotrading: " + str(error_msg)) # TODO what to do in case of an error? except Exception as e: err_msg = "Unexpected Exception while autotrading: " + str( e) + "\n" + str(traceback.format_exc()) logger.error(err_msg) print(err_msg) broker.disconnect()