diff --git a/src/nailab/execution/executor.py b/src/nailab/execution/executor.py index 41458d7..3323c27 100644 --- a/src/nailab/execution/executor.py +++ b/src/nailab/execution/executor.py @@ -20,4 +20,7 @@ class Executor: strategy.add_feed(feed) strategy.run() + results = strategy.get_analyzer('stats').get_result() + return results + diff --git a/src/nailab/ui/applicationwindow.py b/src/nailab/ui/applicationwindow.py index 5dfaf4d..ae8b5b3 100644 --- a/src/nailab/ui/applicationwindow.py +++ b/src/nailab/ui/applicationwindow.py @@ -5,6 +5,7 @@ from nailab.data.datasource import DataSource from nailab.data.datasourcemanager import DataSourceManager from nailab.execution.executor import Executor +from .resultstable import ResultsTableWidget from .tabmanager import TabManager class ApplicationWindow: @@ -48,7 +49,15 @@ class ApplicationWindow: feeds.append(feed) e = Executor() - e.execute_from_file(self.tab_manager.get_current_source_path(), feeds) + result = e.execute_from_file(self.tab_manager.get_current_source_path(), feeds) + + self._add_results_page(result) + + def _add_results_page(self, results): + res_widget = ResultsTableWidget() + res_widget.set_results(results) + res_widget.show_all() + self.tab_manager.new_misc_tab(res_widget) def _init_tv_datasource(self, builder): self.datasourcemanager = DataSourceManager() diff --git a/src/nailab/ui/resultstable.py b/src/nailab/ui/resultstable.py new file mode 100644 index 0000000..5b703bc --- /dev/null +++ b/src/nailab/ui/resultstable.py @@ -0,0 +1,52 @@ + +from gi.repository import Gtk + +from prettytable import PrettyTable + +def render_float(a): + return "{:.3f}".format(a) + +def render_ratio(a, b): + if b != 0: + return a / b + else: + return "∞" + +class ResultsTableWidget(Gtk.Box): + + def __init__(self): + super().__init__(Gtk.Orientation.VERTICAL) + + self.buffer = Gtk.TextBuffer() + self.text_result = Gtk.TextView() + self.text_result.set_buffer(self.buffer) + self.add(self.text_result) + + style_ctx = self.text_result.get_style_context() + self.provider = Gtk.CssProvider() + self.provider.load_from_data(b'GtkTextView { font-family: "Monospace"; }') + style_ctx.add_provider(self.provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) + + + def set_results(self, results): + self.buffer.set_text(self.generate_plain_text(results)) + + def generate_plain_text(self, stats): + + table = PrettyTable() + table.field_names = ["", "All positions", "Long only", "Short only"] + table.add_row(["Net profit", render_float(stats['all']['net_profit']), render_float(stats['long']['net_profit']), render_float(stats['short']['net_profit'])]) + table.add_row(["Bars in trade", stats['all']['bars_in_trade'], stats['long']['bars_in_trade'], stats['short']['bars_in_trade']]) + table.add_row(["Profit per bar", render_float(stats['all']['profit_per_bar']), render_float(stats['long']['profit_per_bar']), render_float(stats['short']['profit_per_bar'])]) + table.add_row(["Number of trades", stats['all']['number_of_trades'], stats['long']['number_of_trades'], stats['short']['number_of_trades']]) + table.add_row(["Avg. profit", render_float(stats['all']['avg']), render_float(stats['long']['avg']), render_float(stats['short']['avg'])]) + table.add_row(["Avg. profit, %", render_float(stats['all']['avg_percentage']), render_float(stats['long']['avg_percentage']), render_float(stats['short']['avg_percentage'])]) + table.add_row(["Avg. bars in trade", render_float(stats['all']['avg_bars']), render_float(stats['long']['avg_bars']), render_float(stats['short']['avg_bars'])]) + table.add_row(["Winning trades", stats['all']['won'], stats['long']['won'], stats['short']['won']]) + table.add_row(["Gross profit", render_float(stats['all']['total_won']), render_float(stats['long']['total_won']), render_float(stats['short']['total_won'])]) + table.add_row(["Losing trades", stats['all']['lost'], stats['long']['lost'], stats['short']['lost']]) + table.add_row(["Gross loss", render_float(stats['all']['total_lost']), render_float(stats['long']['total_lost']), render_float(stats['short']['total_lost'])]) + table.add_row(["Profit factor", render_float(stats['all']['profit_factor']), render_float(stats['long']['profit_factor']), render_float(stats['short']['profit_factor'])]) + + return table.get_string() + diff --git a/src/nailab/ui/tabmanager.py b/src/nailab/ui/tabmanager.py index 4c2b9d4..88e013b 100644 --- a/src/nailab/ui/tabmanager.py +++ b/src/nailab/ui/tabmanager.py @@ -9,12 +9,37 @@ class TabManager(GObject.Object): super().__init__() self.notebook = notebook self.widgets = {} + self.id_counter = 1 + self.source_paths = {} + + def new_misc_tab(self, widget): + tab_id = self._next_tab_id() + self.widgets[tab_id] = widget + header = Gtk.HBox() + title_label = Gtk.Label('Result') + image = Gtk.Image() + image.set_from_stock(Gtk.STOCK_CLOSE, Gtk.IconSize.MENU) + close_button = Gtk.Button() + close_button.set_image(image) + close_button.set_relief(Gtk.ReliefStyle.NONE) + close_button.connect('clicked', self.close_cb, tab_id) + + header.pack_start(title_label, + expand=True, fill=True, padding=0) + header.pack_end(close_button, + expand=False, fill=False, padding=0) + header.show_all() + + index = self.notebook.append_page(widget, header) + self.notebook.set_current_page(index) def new_tab(self, source_file): with open(source_file, 'r') as f: + tab_id = self._next_tab_id() + self.source_paths[tab_id] = source_file (sv, sv_controller) = self._init_sourceeditor() sv_controller.set_source_text(f.read()) - self.widgets[source_file] = sv + self.widgets[tab_id] = sv sv.show_all() header = Gtk.HBox() title_label = Gtk.Label(source_file) @@ -23,7 +48,7 @@ class TabManager(GObject.Object): close_button = Gtk.Button() close_button.set_image(image) close_button.set_relief(Gtk.ReliefStyle.NONE) - close_button.connect('clicked', self.close_cb, source_file) + close_button.connect('clicked', self.close_cb, tab_id) header.pack_start(title_label, expand=True, fill=True, padding=0) @@ -33,19 +58,21 @@ class TabManager(GObject.Object): index = self.notebook.append_page(sv, header) self.notebook.set_current_page(index) - def close_cb(self, arg, source_file): - index = self._widget_num_by_source_file(source_file) + def close_cb(self, arg, tab_id): + index = self._widget_num_by_tab_id(tab_id) self.notebook.remove_page(index) - del self.widgets[source_file] + del self.widgets[tab_id] + if tab_id in self.source_paths: + del self.source_paths[tab_id] def get_current_source_path(self): index = self.notebook.get_current_page() w = self.notebook.get_nth_page(index) for k, v in self.widgets.items(): if v == w: - return k + return self.source_paths[k] - raise KeyError('Invalid widget') + return None def _init_sourceeditor(self): scroll = Gtk.ScrolledWindow() @@ -67,8 +94,12 @@ class TabManager(GObject.Object): return (scroll, sourceviewcontroller) - def _widget_num_by_source_file(self, source_file): + def _widget_num_by_tab_id(self, tab_id): for i in range(0, self.notebook.get_n_pages()): - if self.widgets[source_file] == self.notebook.get_nth_page(i): + if self.widgets[tab_id] == self.notebook.get_nth_page(i): return i return None + + def _next_tab_id(self): + self.id_counter += 1 + return self.id_counter