Browse Source

Performance analysis, portfolio mode

master
Denis Tereshkin 6 years ago
parent
commit
b41bf59aaf
  1. 1
      build_ui.sh
  2. 155
      src/nailab/execution/portfolioexecutor.py
  3. 154
      src/nailab/nailab_rc.py
  4. 138
      src/nailab/ui/correlationchartcanvas.py
  5. 30
      src/nailab/ui/mainwindow.py
  6. 92
      src/nailab/ui/performancecanvas.py
  7. 86
      src/nailab/ui/performancewidget.py
  8. 26
      src/nailab/ui/strategywidget.py
  9. 24
      src/nailab/ui/tradeslistwidget.py
  10. 15
      src/nailab/ui_gen/mainwindow.py
  11. 100
      src/nailab/ui_gen/performancewidget.py
  12. 9
      src/nailab/ui_gen/tradeslistwidget.py
  13. 56
      ui/mainwindow.ui
  14. 247
      ui/performancewidget.ui
  15. 44
      ui/tradeslistwidget.ui

1
build_ui.sh

@ -6,6 +6,7 @@
/usr/bin/pyuic5 ui/newdatasourcedialog.ui > src/nailab/ui_gen/newdatasourcedialog.py /usr/bin/pyuic5 ui/newdatasourcedialog.ui > src/nailab/ui_gen/newdatasourcedialog.py
/usr/bin/pyuic5 ui/strategywidget.ui > src/nailab/ui_gen/strategywidget.py /usr/bin/pyuic5 ui/strategywidget.ui > src/nailab/ui_gen/strategywidget.py
/usr/bin/pyuic5 ui/tradeslistwidget.ui > src/nailab/ui_gen/tradeslistwidget.py /usr/bin/pyuic5 ui/tradeslistwidget.ui > src/nailab/ui_gen/tradeslistwidget.py
/usr/bin/pyuic5 ui/performancewidget.ui > src/nailab/ui_gen/performancewidget.py
/usr/bin/pyrcc5 nailab.qrc -o src/nailab/nailab_rc.py /usr/bin/pyrcc5 nailab.qrc -o src/nailab/nailab_rc.py

155
src/nailab/execution/portfolioexecutor.py

@ -0,0 +1,155 @@
import sys
import importlib
from importlib.machinery import SourceFileLoader
import inspect
from naiback.strategy import Strategy
import numpy as np
import math
def render_float(a):
return "{:.3f}".format(a)
def render_ratio(a, b):
if b != 0:
return a / b
else:
return 0
class PortfolioExecutor:
def __init__(self):
pass
def execute_from_file(self, path, feeds, extents=None):
if "execution._current_strategy" in sys.modules:
del sys.modules["execution._current_strategy"]
loader = SourceFileLoader('execution._current_strategy', path)
mod = loader.load_module()
for item in inspect.getmembers(mod, inspect.isclass):
klass = item[1]
if klass is not Strategy and issubclass(klass, Strategy):
all_trades = []
for feed in feeds:
strategy = klass()
strategy.add_feed(feed)
if extents is None:
strategy.run()
else:
strategy.run(from_time=extents[0], to_time=extents[1])
trades = strategy.get_analyzer('tradeslist').get_result()
all_trades = self.merge_trades(all_trades, trades)
results = self.calc_stats(all_trades)
equity = self.calc_equity(all_trades)
return (results, all_trades, equity)
def merge_trades(self, trades1, trades2):
all_trades = list(sorted(trades1 + trades2, key=lambda x: x['entry_time']))
result = []
current_trades = []
max_trades = 3
for trade in all_trades:
if len(current_trades) < max_trades:
current_trades.append(trade)
result.append(trade)
new_current_trades = []
for ct in current_trades:
if ct['exit_time'] < trade['entry_time']:
new_current_trades.append(ct)
else:
# possibly adjust the balance
pass
current_trades = new_current_trades
return result
def calc_stats(self, trades):
longs = list(filter(lambda x: x['is_long'], trades))
shorts = list(filter(lambda x: not x['is_long'], trades))
result = { 'all' : {}, 'long' : {}, 'short' : {} }
result['all']['net_profit'] = sum([t['pnl'] for t in trades])
result['long']['net_profit'] = sum([t['pnl'] for t in longs])
result['short']['net_profit'] = sum([t['pnl'] for t in shorts])
result['all']['bars_in_trade'] = 0 # TODO?
result['long']['bars_in_trade'] = 0
result['short']['bars_in_trade'] = 0
result['all']['profit_per_bar'] = 0
result['long']['profit_per_bar'] = 0
result['short']['profit_per_bar'] = 0
result['all']['number_of_trades'] = len(trades)
result['long']['number_of_trades'] = len(longs)
result['short']['number_of_trades'] = len(shorts)
result['all']['avg'] = render_ratio(result['all']['net_profit'], result['all']['number_of_trades'])
result['long']['avg'] = render_ratio(result['long']['net_profit'], result['long']['number_of_trades'])
result['short']['avg'] = render_ratio(result['short']['net_profit'], result['short']['number_of_trades'])
result['all']['avg_percentage'] = render_ratio(sum([t['profit_percentage'] for t in trades]), result['all']['number_of_trades'])
result['long']['avg_percentage'] = render_ratio(sum([t['profit_percentage'] for t in longs]), result['long']['number_of_trades'])
result['short']['avg_percentage'] = render_ratio(sum([t['profit_percentage'] for t in shorts]), result['short']['number_of_trades'])
result['all']['avg_bars'] = render_ratio(result['all']['bars_in_trade'], result['all']['number_of_trades'])
result['long']['avg_bars'] = render_ratio(result['long']['bars_in_trade'], result['long']['number_of_trades'])
result['short']['avg_bars'] = render_ratio(result['short']['bars_in_trade'], result['short']['number_of_trades'])
result['all']['won'] = len(list(filter(lambda t: t['pnl'] > 0, trades)))
result['long']['won'] = len(list(filter(lambda t: t['pnl'] > 0, longs)))
result['short']['won'] = len(list(filter(lambda t: t['pnl'] > 0, shorts)))
result['all']['lost'] = len(list(filter(lambda t: t['pnl'] <= 0, trades)))
result['long']['lost'] = len(list(filter(lambda t: t['pnl'] <= 0, longs)))
result['short']['lost'] = len(list(filter(lambda t: t['pnl'] <= 0, shorts)))
result['all']['total_won'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] > 0, trades)))
result['long']['total_won'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] > 0, longs)))
result['short']['total_won'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] > 0, shorts)))
result['all']['total_lost'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] <= 0, trades)))
result['long']['total_lost'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] <= 0, longs)))
result['short']['total_lost'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] <= 0, shorts)))
result['all']['profit_factor'] = render_ratio(result['all']['total_won'], -result['all']['total_lost'])
result['long']['profit_factor'] = render_ratio(result['long']['total_won'], -result['long']['total_lost'])
result['short']['profit_factor'] = render_ratio(result['short']['total_won'], -result['short']['total_lost'])
mean = np.mean(list(map(lambda x: x['pnl'], trades)))
stddev = np.std(list(map(lambda x: x['pnl'], trades)))
sharpe = mean / stddev
tstat = sharpe * math.sqrt(len(trades))
result['all']['sharpe_ratio'] = sharpe
result['all']['t_stat'] = tstat
mean = np.mean(list(map(lambda x: x['pnl'], longs)))
stddev = np.std(list(map(lambda x: x['pnl'], longs)))
sharpe = mean / stddev
tstat = sharpe * math.sqrt(len(longs))
result['long']['sharpe_ratio'] = sharpe
result['long']['t_stat'] = tstat
mean = np.mean(list(map(lambda x: x['pnl'], shorts)))
stddev = np.std(list(map(lambda x: x['pnl'], shorts)))
sharpe = mean / stddev
tstat = sharpe * math.sqrt(len(shorts))
result['short']['sharpe_ratio'] = sharpe
result['short']['t_stat'] = tstat
return result
def calc_equity(self, trades):
sorted_trades = sorted(trades, key=lambda x: x['exit_time'])
equity = []
current = 0
for t in sorted_trades:
current += t['pnl']
equity.append(current)

154
src/nailab/nailab_rc.py

@ -9,7 +9,7 @@
from PyQt5 import QtCore from PyQt5 import QtCore
qt_resource_data = b"\ qt_resource_data = b"\
\x00\x00\x02\x33\ \x00\x00\x02\x0e\
\x89\ \x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x30\x00\x00\x00\x30\x08\x06\x00\x00\x00\x57\x02\xf9\x87\ \x00\x00\x30\x00\x00\x00\x30\x08\x06\x00\x00\x00\x57\x02\xf9\x87\
@ -17,36 +17,33 @@ qt_resource_data = b"\
\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x1b\xaf\x00\x00\x1b\xaf\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x1b\xaf\x00\x00\x1b\xaf\
\x01\x5e\x1a\x91\x1c\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x01\x5e\x1a\x91\x1c\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\xb0\x49\x44\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\x8b\x49\x44\
\x41\x54\x68\x81\xed\x9a\x31\x6a\x02\x41\x14\x86\xff\x59\x05\x25\ \x41\x54\x68\x81\xed\x99\x4f\x4a\xc3\x40\x14\x87\x7f\x49\x53\x94\
\x8d\x21\x10\xc1\xca\x23\xd8\x06\x1b\x21\x5d\xae\x13\x1b\xab\x14\ \x0a\x2a\xa2\xe2\x9f\x45\xf1\x04\x3d\x42\xc5\x9d\x1b\x71\x29\x81\
\xc1\xc2\x2a\xe7\xf0\x14\x42\x9a\x25\x6d\x4e\x10\x2c\x12\x83\x1b\ \x9e\xa0\x17\xe8\x11\x7a\x0f\x37\x5d\x8a\x07\x08\xba\xc9\xde\x13\
\x16\x5d\x31\xba\xee\xcc\xbc\x14\x21\x90\x80\x6f\xd0\xe8\xfa\x58\ \x94\x2e\x6a\x4b\x5a\x28\xad\x34\x28\x49\xe6\x79\x82\x37\x24\x99\
\x98\xaf\xdc\x19\xd8\xef\x73\xc6\xb1\x18\x15\x11\xa1\xc8\x04\xd2\ \xc6\x17\x61\xbe\xed\x1b\xde\xfc\x3e\xe6\x31\x09\x8c\x43\x44\xf8\
\x02\x87\x52\x76\x0d\x8e\xc2\xb0\x09\x1b\x0c\x08\xd4\x51\x50\x8d\ \xcf\xb8\xd2\x01\x4c\xf1\x74\xc5\x20\x0c\xdb\x50\xee\x90\x40\x5d\
\x53\x49\x01\x00\x81\x26\x0a\xea\x11\x81\xed\x5d\xb7\xdb\x63\x6e\ \x07\xce\xe5\x2e\x37\x9e\x2e\xa3\xf7\xde\xe3\xc3\xad\x69\x1f\xf6\
\x9e\xe2\xb6\xd0\x28\x0c\x9b\xa0\xe0\x19\x84\x5a\x6e\x96\xbb\xa0\ \x04\x82\x30\x6c\x83\xdc\x0f\x00\x4f\xbb\x0e\x0f\x00\x5f\xf1\xb6\
\x30\x87\xb2\x2d\x2e\x82\xdf\x42\x36\x18\x88\xcb\x03\x00\xa1\x06\ \xfb\xfc\xf2\xfa\x66\xda\x87\x1f\x21\xe5\x0e\x41\x38\x32\xdd\x40\
\x1b\x0c\xb8\x61\x36\x80\x40\x9d\x7c\x8c\xf6\xc7\xe5\xc2\x06\x9c\ \xc7\x2e\x24\x58\x01\x02\xdd\x99\x34\xce\x8b\xa9\x04\x2b\xe0\xc0\
\x7a\xcf\xbb\x70\xb9\x14\xfe\x14\xf2\x01\xd2\xf8\x00\x69\x7c\x80\ \x39\x2f\xdb\xb4\x28\x26\x12\xb5\xb9\x85\xca\x4a\xd4\x46\x00\x28\
\x34\x3e\x40\x1a\x1f\x20\x8d\x0f\x90\xc6\x07\x48\xe3\x03\xa4\xf1\ \x27\x51\x2b\x01\xa0\xb8\x84\x98\x80\xd7\x68\xb0\xb5\x22\x12\x62\
\x01\xd2\xf8\x00\x69\x7c\x80\x34\x3e\x40\x1a\x1f\x20\x0d\x1b\xa0\ \x02\xad\xbd\x7d\x6d\x3d\xaf\x84\x98\xc0\xd9\xf1\x09\x5c\x57\xbf\
\x8d\x39\xa5\x87\x13\x97\x0b\x1b\xf0\x99\xae\x73\x91\xf9\x0f\x2e\ \x7d\x1e\x09\x31\x81\xa6\xe7\xe1\xe6\xe2\x1a\x87\xad\x03\xa3\x71\
\x17\x36\x20\x9a\xc5\xb0\xd6\xe6\x22\xb4\x0f\xd6\x5a\x44\xb3\x98\ \xd2\xfe\x0b\x55\x4d\xd3\xf3\x70\x75\x9a\xeb\x73\xd3\xe5\x0a\xb5\
\x1d\x67\xaf\x98\x32\xad\xf1\xf2\xfe\x8a\xcb\xf3\x0b\x9c\x55\xaa\ \xbb\x85\x8a\x62\x05\xa4\xb1\x02\xd2\x58\x01\x69\xac\x80\x34\x56\
\x28\x97\x4a\xb9\x08\x72\x6c\xb2\x0d\xe6\x49\x82\x24\x5d\x21\xd3\ \x40\x1a\x2b\x20\x8d\x15\x90\xc6\x0a\x48\x63\x05\xa4\xb1\x02\xd2\
\x9a\x9d\xe7\xbc\x23\xcb\xb4\xc6\xdb\xc7\xf4\xe8\x72\x00\x30\x9d\ \x58\x01\x69\xac\x80\x34\x56\x40\x1a\x56\x20\xcd\xb2\xbf\xcc\xa1\
\x4c\x76\x9a\x57\x6f\xb8\xaf\x29\x0a\x7f\x0a\xb1\x2b\x60\xad\x45\ \x45\x97\x85\x15\x88\x7f\xbe\x2b\x09\x53\x06\x5d\x16\x56\x60\xb1\
\x10\xe4\xdf\x77\x7b\x75\xbf\xf5\xf9\xc3\xd3\xdd\x1f\x17\x0e\xd6\ \x5e\x45\x4a\xa9\x4a\x02\x15\x41\x29\x85\xc5\x7a\x15\x71\x75\x7e\
\x70\x93\xa6\x07\x68\x1d\x17\x97\x0b\xbb\x02\xcb\xc5\x62\x5a\xa9\ \x84\x92\x24\x18\xcf\xa7\xd8\xc4\x5b\x91\x71\x4a\xb3\x0c\x9b\x78\
\x56\xeb\x4a\xa9\x5c\xa4\x7e\xf8\xfd\x49\x6f\x83\x88\xb0\x5c\x2c\ \x8b\xf1\x7c\x8a\x34\x49\x02\x6e\x1d\xfb\x3e\xa0\x40\x83\x24\x4d\
\xd8\x2f\x22\xbb\x02\xc6\xe8\x51\x1c\x45\x58\xaf\x56\xa2\xc7\x69\ \xef\x3f\x97\x51\xa5\xaf\xf5\x39\x58\x13\x68\xc0\x15\xd9\x13\xe8\
\x1c\x45\x30\x46\x8f\xb8\x71\x76\x05\x34\xd0\x53\xc6\xdc\x24\xb3\ \xfb\xfe\x84\x40\x1d\x07\x18\x01\x98\x55\x12\x4d\xcf\xcc\x01\x46\
\x99\xe8\x5d\xb1\x35\x66\xae\x81\x1e\x37\xce\xae\x40\xbf\xdb\x1d\ \x04\xea\xf4\x7d\x7f\xc2\x2d\xfa\x05\x0e\x3c\xa3\x06\xde\xce\x5a\
\x67\xa0\x16\x40\x43\x05\xec\x76\xe6\x1d\x91\xef\x77\xd2\x30\x03\ \xc4\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\xb5\xfa\xdd\xee\xfe\x7f\x35\x28\x0a\x85\xff\x1d\xf8\x02\xd1\xfa\
\xa2\xe7\x75\x3b\xf6\xd5\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
\x60\x82\
\x00\x00\x02\x8f\ \x00\x00\x02\x8f\
\x89\ \x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@ -90,6 +87,44 @@ qt_resource_data = b"\
\x41\xe9\x31\x00\xcb\x38\xfb\x94\x19\x77\x07\x00\x96\xa1\xf4\x58\ \x41\xe9\x31\x00\xcb\x38\xfb\x94\x19\x77\x07\x00\x96\xa1\xf4\x58\
\x31\x9f\x2d\x91\xfb\xb7\x8a\x65\x3f\x00\xdf\xf9\xa7\x43\x02\xf4\ \x31\x9f\x2d\x91\xfb\xb7\x8a\x65\x3f\x00\xdf\xf9\xa7\x43\x02\xf4\
\x78\xd9\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x78\xd9\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\x00\x00\x02\x33\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x30\x00\x00\x00\x30\x08\x06\x00\x00\x00\x57\x02\xf9\x87\
\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x1b\xaf\x00\x00\x1b\xaf\
\x01\x5e\x1a\x91\x1c\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\xb0\x49\x44\
\x41\x54\x68\x81\xed\x9a\x31\x6a\x02\x41\x14\x86\xff\x59\x05\x25\
\x8d\x21\x10\xc1\xca\x23\xd8\x06\x1b\x21\x5d\xae\x13\x1b\xab\x14\
\xc1\xc2\x2a\xe7\xf0\x14\x42\x9a\x25\x6d\x4e\x10\x2c\x12\x83\x1b\
\x16\x5d\x31\xba\xee\xcc\xbc\x14\x21\x90\x80\x6f\xd0\xe8\xfa\x58\
\x98\xaf\xdc\x19\xd8\xef\x73\xc6\xb1\x18\x15\x11\xa1\xc8\x04\xd2\
\x02\x87\x52\x76\x0d\x8e\xc2\xb0\x09\x1b\x0c\x08\xd4\x51\x50\x8d\
\x53\x49\x01\x00\x81\x26\x0a\xea\x11\x81\xed\x5d\xb7\xdb\x63\x6e\
\x9e\xe2\xb6\xd0\x28\x0c\x9b\xa0\xe0\x19\x84\x5a\x6e\x96\xbb\xa0\
\x30\x87\xb2\x2d\x2e\x82\xdf\x42\x36\x18\x88\xcb\x03\x00\xa1\x06\
\x1b\x0c\xb8\x61\x36\x80\x40\x9d\x7c\x8c\xf6\xc7\xe5\xc2\x06\x9c\
\x7a\xcf\xbb\x70\xb9\x14\xfe\x14\xf2\x01\xd2\xf8\x00\x69\x7c\x80\
\x34\x3e\x40\x1a\x1f\x20\x8d\x0f\x90\xc6\x07\x48\xe3\x03\xa4\xf1\
\x01\xd2\xf8\x00\x69\x7c\x80\x34\x3e\x40\x1a\x1f\x20\x0d\x1b\xa0\
\x8d\x39\xa5\x87\x13\x97\x0b\x1b\xf0\x99\xae\x73\x91\xf9\x0f\x2e\
\x17\x36\x20\x9a\xc5\xb0\xd6\xe6\x22\xb4\x0f\xd6\x5a\x44\xb3\x98\
\x1d\x67\xaf\x98\x32\xad\xf1\xf2\xfe\x8a\xcb\xf3\x0b\x9c\x55\xaa\
\x28\x97\x4a\xb9\x08\x72\x6c\xb2\x0d\xe6\x49\x82\x24\x5d\x21\xd3\
\x9a\x9d\xe7\xbc\x23\xcb\xb4\xc6\xdb\xc7\xf4\xe8\x72\x00\x30\x9d\
\x4c\x76\x9a\x57\x6f\xb8\xaf\x29\x0a\x7f\x0a\xb1\x2b\x60\xad\x45\
\x10\xe4\xdf\x77\x7b\x75\xbf\xf5\xf9\xc3\xd3\xdd\x1f\x17\x0e\xd6\
\x70\x93\xa6\x07\x68\x1d\x17\x97\x0b\xbb\x02\xcb\xc5\x62\x5a\xa9\
\x56\xeb\x4a\xa9\x5c\xa4\x7e\xf8\xfd\x49\x6f\x83\x88\xb0\x5c\x2c\
\xd8\x2f\x22\xbb\x02\xc6\xe8\x51\x1c\x45\x58\xaf\x56\xa2\xc7\x69\
\x1c\x45\x30\x46\x8f\xb8\x71\x76\x05\x34\xd0\x53\xc6\xdc\x24\xb3\
\x99\xe8\x5d\xb1\x35\x66\xae\x81\x1e\x37\xce\xae\x40\xbf\xdb\x1d\
\x67\xa0\x16\x40\x43\x05\xec\x76\xe6\x1d\x91\xef\x77\xd2\x30\x03\
\xb5\xfa\xdd\xee\xfe\x7f\x35\x28\x0a\x85\xff\x1d\xf8\x02\xd1\xfa\
\xa2\xe7\x75\x3b\xf6\xd5\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
\x60\x82\
\x00\x00\x03\x8f\ \x00\x00\x03\x8f\
\x89\ \x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@ -149,41 +184,6 @@ qt_resource_data = b"\
\x09\xf1\x80\x43\x84\x88\xf8\xc7\x59\x11\x0f\x08\xbf\x13\x4f\x03\ \x09\xf1\x80\x43\x84\x88\xf8\xc7\x59\x11\x0f\x08\xbf\x13\x4f\x03\
\xd1\x77\xe2\x69\xf0\xdf\xc0\xb4\xf9\x0b\x47\x70\x6d\x4b\x01\x24\ \xd1\x77\xe2\x69\xf0\xdf\xc0\xb4\xf9\x0b\x47\x70\x6d\x4b\x01\x24\
\x0b\x02\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x0b\x02\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\x00\x00\x02\x0e\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x30\x00\x00\x00\x30\x08\x06\x00\x00\x00\x57\x02\xf9\x87\
\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x1b\xaf\x00\x00\x1b\xaf\
\x01\x5e\x1a\x91\x1c\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\x8b\x49\x44\
\x41\x54\x68\x81\xed\x99\x4f\x4a\xc3\x40\x14\x87\x7f\x49\x53\x94\
\x0a\x2a\xa2\xe2\x9f\x45\xf1\x04\x3d\x42\xc5\x9d\x1b\x71\x29\x81\
\x9e\xa0\x17\xe8\x11\x7a\x0f\x37\x5d\x8a\x07\x08\xba\xc9\xde\x13\
\x94\x2e\x6a\x4b\x5a\x28\xad\x34\x28\x49\xe6\x79\x82\x37\x24\x99\
\xc6\x17\x61\xbe\xed\x1b\xde\xfc\x3e\xe6\x31\x09\x8c\x43\x44\xf8\
\xcf\xb8\xd2\x01\x4c\xf1\x74\xc5\x20\x0c\xdb\x50\xee\x90\x40\x5d\
\x07\xce\xe5\x2e\x37\x9e\x2e\xa3\xf7\xde\xe3\xc3\xad\x69\x1f\xf6\
\x04\x82\x30\x6c\x83\xdc\x0f\x00\x4f\xbb\x0e\x0f\x00\x5f\xf1\xb6\
\xfb\xfc\xf2\xfa\x66\xda\x87\x1f\x21\xe5\x0e\x41\x38\x32\xdd\x40\
\xc7\x2e\x24\x58\x01\x02\xdd\x99\x34\xce\x8b\xa9\x04\x2b\xe0\xc0\
\x39\x2f\xdb\xb4\x28\x26\x12\xb5\xb9\x85\xca\x4a\xd4\x46\x00\x28\
\x27\x51\x2b\x01\xa0\xb8\x84\x98\x80\xd7\x68\xb0\xb5\x22\x12\x62\
\x02\xad\xbd\x7d\x6d\x3d\xaf\x84\x98\xc0\xd9\xf1\x09\x5c\x57\xbf\
\x7d\x1e\x09\x31\x81\xa6\xe7\xe1\xe6\xe2\x1a\x87\xad\x03\xa3\x71\
\xd2\xfe\x0b\x55\x4d\xd3\xf3\x70\x75\x9a\xeb\x73\xd3\xe5\x0a\xb5\
\xbb\x85\x8a\x62\x05\xa4\xb1\x02\xd2\x58\x01\x69\xac\x80\x34\x56\
\x40\x1a\x2b\x20\x8d\x15\x90\xc6\x0a\x48\x63\x05\xa4\xb1\x02\xd2\
\x58\x01\x69\xac\x80\x34\x56\x40\x1a\x56\x20\xcd\xb2\xbf\xcc\xa1\
\x45\x97\x85\x15\x88\x7f\xbe\x2b\x09\x53\x06\x5d\x16\x56\x60\xb1\
\x5e\x45\x4a\xa9\x4a\x02\x15\x41\x29\x85\xc5\x7a\x15\x71\x75\x7e\
\x84\x92\x24\x18\xcf\xa7\xd8\xc4\x5b\x91\x71\x4a\xb3\x0c\x9b\x78\
\x8b\xf1\x7c\x8a\x34\x49\x02\x6e\x1d\xfb\x3e\xa0\x40\x83\x24\x4d\
\xef\x3f\x97\x51\xa5\xaf\xf5\x39\x58\x13\x68\xc0\x15\xd9\x13\xe8\
\xfb\xfe\x84\x40\x1d\x07\x18\x01\x98\x55\x12\x4d\xcf\xcc\x01\x46\
\x04\xea\xf4\x7d\x7f\xc2\x2d\xfa\x05\x0e\x3c\xa3\x06\xde\xce\x5a\
\xc4\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
" "
qt_resource_name = b"\ qt_resource_name = b"\
@ -195,32 +195,32 @@ qt_resource_name = b"\
\x00\x6f\xa6\x53\ \x00\x6f\xa6\x53\
\x00\x69\ \x00\x69\
\x00\x63\x00\x6f\x00\x6e\x00\x73\ \x00\x63\x00\x6f\x00\x6e\x00\x73\
\x00\x08\ \x00\x07\
\x08\xc8\x58\x67\ \x04\xca\x57\xa7\
\x00\x73\ \x00\x6e\
\x00\x61\x00\x76\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x65\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\
\x00\x08\ \x00\x08\
\x06\xc1\x59\x87\ \x06\xc1\x59\x87\
\x00\x6f\ \x00\x6f\
\x00\x70\x00\x65\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x70\x00\x65\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
\x00\x08\
\x08\xc8\x58\x67\
\x00\x73\
\x00\x61\x00\x76\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\
\x00\x0b\ \x00\x0b\
\x0c\x81\x80\x07\ \x0c\x81\x80\x07\
\x00\x65\ \x00\x65\
\x00\x78\x00\x65\x00\x63\x00\x75\x00\x74\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x78\x00\x65\x00\x63\x00\x75\x00\x74\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\
\x00\x07\
\x04\xca\x57\xa7\
\x00\x6e\
\x00\x65\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\
" "
qt_resource_struct = b"\ qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03\ \x00\x00\x00\x0e\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03\
\x00\x00\x00\x66\x00\x00\x00\x00\x00\x01\x00\x00\x08\x5d\
\x00\x00\x00\x34\x00\x00\x00\x00\x00\x01\x00\x00\x02\x37\
\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x00\x4a\x00\x00\x00\x00\x00\x01\x00\x00\x04\xca\ \x00\x00\x00\x32\x00\x00\x00\x00\x00\x01\x00\x00\x02\x12\
\x00\x00\x00\x48\x00\x00\x00\x00\x00\x01\x00\x00\x04\xa5\
\x00\x00\x00\x5e\x00\x00\x00\x00\x00\x01\x00\x00\x06\xdc\
" "
def qInitResources(): def qInitResources():

138
src/nailab/ui/correlationchartcanvas.py

@ -0,0 +1,138 @@
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qsci import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
class CorrelationChartCanvas(FigureCanvas):
def __init__(self, parent=None, width=6, height=6, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
FigureCanvas.__init__(self, self.fig)
self.setParent(parent)
self.trades_series = {}
self.mode = "daily"
def setMode(self, mode):
if mode not in ["daily", "monthly"]:
return
self.mode = mode
self._refresh()
def add_trades_series(self, name, trades):
self.trades_series[name] = trades
self._refresh()
def add_trades_series_batched(self, trades_list):
for series in trades_list:
self.trades_series[series["name"]] = series["trades"]
self._refresh()
def remove_trades_series(self, name):
del self.trades_series[name]
self._refresh()
def _refresh(self):
self.fig.clear()
if len(self.trades_series) == 0:
return
corr_matrix = np.zeros((len(self.trades_series), len(self.trades_series)))
x = 0
y = 0
names = []
for n1, trades1 in self.trades_series.items():
names.append(n1)
for n2, trades2 in self.trades_series.items():
corr_matrix[x][y] = self._calc_correlation(trades1, trades2)
y += 1
x += 1
y = 0
ax1 = self.figure.add_subplot(111)
ax1.imshow(corr_matrix, cmap="BrBG", norm=matplotlib.colors.Normalize(vmin=-1.0, vmax=1.0))
ax1.set_title("Returns correlation")
ax1.set_xticks(np.arange(len(names)))
ax1.set_yticks(np.arange(len(names)))
ax1.set_xticklabels(names)
ax1.set_yticklabels(names)
for i in range(len(self.trades_series)):
for j in range(len(self.trades_series)):
col = 'k'
if corr_matrix[i, j] > 0.7:
col = 'w'
text = ax1.text(j, i, "{:.2f}".format(corr_matrix[i, j]), ha="center", va="center", color=col, fontsize="x-small")
plt.setp(ax1.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")
self.draw()
def _calc_correlation(self, trades1, trades2):
t1 = {}
all_dates = set()
for trade in trades1:
all_dates.add(self._trade_date(trade))
try:
t1[self._trade_date(trade)] += trade["percentage"]
except KeyError:
t1[self._trade_date(trade)] = trade["percentage"]
t2 = {}
for trade in trades2:
all_dates.add(self._trade_date(trade))
try:
t2[self._trade_date(trade)] += trade["percentage"]
except KeyError:
t2[self._trade_date(trade)] = trade["percentage"]
ret1 = []
for d in all_dates:
try:
ret1.append(t1[d])
except KeyError:
ret1.append(0)
ret2 = []
for d in all_dates:
try:
ret2.append(t2[d])
except KeyError:
ret2.append(0)
mean1 = np.mean(ret1)
mean2 = np.mean(ret2)
sigma1 = np.std(ret1)
sigma2 = np.std(ret2)
s = 0
for i in range(len(all_dates)):
x1 = ret1[i] - mean1
x2 = ret2[i] - mean2
s += x1 * x2
s /= len(all_dates)
s /= (sigma1 * sigma2)
return s
def _trade_date(self, trade):
if self.mode == "daily":
return trade["exit_time"].date()
else:
return trade["exit_time"].date().replace(day=1)

30
src/nailab/ui/mainwindow.py

@ -7,6 +7,7 @@ import sys
import traceback import traceback
from execution.executor import Executor from execution.executor import Executor
from execution.portfolioexecutor import PortfolioExecutor
from data.datasourcemanager import DataSourceManager from data.datasourcemanager import DataSourceManager
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -14,6 +15,7 @@ from PyQt5.Qsci import *
from ui_gen.mainwindow import Ui_MainWindow from ui_gen.mainwindow import Ui_MainWindow
from ui.strategywidget import StrategyWidget from ui.strategywidget import StrategyWidget
from ui.performancewidget import PerformanceWidget
from naiback.strategy import Strategy from naiback.strategy import Strategy
from templates.new_strategy import new_strategy_template from templates.new_strategy import new_strategy_template
@ -23,7 +25,6 @@ class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.sources = []
self.datasourcemanager = DataSourceManager() self.datasourcemanager = DataSourceManager()
self.datasourcemanager.load_sources(QtCore.QSettings()) self.datasourcemanager.load_sources(QtCore.QSettings())
@ -92,15 +93,34 @@ class MainWindow(QtWidgets.QMainWindow):
self.ui.tabs.currentWidget().setError(self.tr("Successful run")) self.ui.tabs.currentWidget().setError(self.tr("Successful run"))
except Exception as e: except Exception as e:
self.ui.tabs.currentWidget().setError(traceback.format_exc()) self.ui.tabs.currentWidget().setError(traceback.format_exc())
def executeStrategyInPortfolioMode(self):
source_file = self.ui.tabs.currentWidget().source_file
executor = PortfolioExecutor()
selected_feed_ids = self.ui.tabs.currentWidget().get_selected_feeds()
selected_feeds = []
for (source_id, feed_id) in selected_feed_ids:
try:
selected_feeds.append(self.datasourcemanager.get_source(source_id).get_feed(feed_id))
except Exception as e:
self.ui.tabs.currentWidget().setError(traceback.format_exc())
try:
result = executor.execute_from_file(source_file, selected_feeds, self.ui.tabs.currentWidget().get_time_window())
self.ui.tabs.currentWidget().set_result(result)
self.ui.tabs.currentWidget().setError(self.tr("Successful run"))
except Exception as e:
self.ui.tabs.currentWidget().setError(traceback.format_exc())
def tabCloseRequested(self, tab_index): def tabCloseRequested(self, tab_index):
del self.sources[tab_index] if self.ui.tabs.widget(tab_index).widget_type() == "strategy":
self.ui.tabs.widget(tab_index).save_state() self.ui.tabs.widget(tab_index).save_state()
self.ui.tabs.removeTab(tab_index) self.ui.tabs.removeTab(tab_index)
def performanceAnalysis(self):
self.ui.tabs.addTab(PerformanceWidget(), self.tr("Performance analysis"))
def _makeEditor(self, source_file=None, tab_name="Untitled", content=None): def _makeEditor(self, source_file=None, tab_name="Untitled", content=None):
editor = StrategyWidget(self.datasourcemanager, source_file, self, content) editor = StrategyWidget(self.datasourcemanager, source_file, self, content)
self.sources.append(source_file)
self.ui.tabs.addTab(editor, tab_name) self.ui.tabs.addTab(editor, tab_name)
editor.savedChanged.connect(self.savedChanged) editor.savedChanged.connect(self.savedChanged)

92
src/nailab/ui/performancecanvas.py

@ -0,0 +1,92 @@
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qsci import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np
class PerformanceCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
FigureCanvas.__init__(self, self.fig)
self.setParent(parent)
self.trades_series = {}
def add_trades_series(self, name, trades):
self.trades_series[name] = trades
self._refresh()
def add_trades_series_batched(self, trades_list):
for series in trades_list:
self.trades_series[series["name"]] = series["trades"]
self._refresh()
def remove_trades_series(self, name):
del self.trades_series[name]
self._refresh()
def _refresh(self):
self.fig.clear()
if len(self.trades_series) == 0:
return
all_trades = []
for k, v in self.trades_series.items():
for trade in v:
all_trades.append(trade)
all_trades = sorted(all_trades, key=lambda x: x["exit_time"])
cumpnl = np.cumsum(list(map(lambda x: x["percentage"], all_trades)))
max_equity = 0
drawdown = []
for x in cumpnl:
if x > max_equity:
max_equity = x
drawdown.append(0)
else:
drawdown.append(x - max_equity)
monthly_returns = {}
cur_month = None
cur_pnl = 0
for trade in all_trades:
trade_month = trade["exit_time"].strftime("%Y-%m")
if trade_month != cur_month and cur_month is not None:
monthly_returns[cur_month] = cur_pnl
cur_pnl = 0
cur_month = trade_month
if cur_month is None:
cur_month = trade_month
cur_pnl += trade["percentage"]
monthly_returns[cur_month] = cur_pnl
monthly_returns_raw = []
for k, v in monthly_returns.items():
monthly_returns_raw.append((k, v))
monthly_returns_raw = sorted(monthly_returns_raw, key=lambda x: x[0])
self.monthly_returns_x = list(map(lambda x: x[0], monthly_returns_raw))
self.monthly_returns_y = list(map(lambda x: x[1], monthly_returns_raw))
ax1 = self.figure.add_subplot(211)
ax1.plot(cumpnl, "b-")
ax1.set_title("Cumulative PnL")
ax1_2 = ax1.twinx()
ax1_2.plot(drawdown, "r-")
ax2 = self.figure.add_subplot(212)
ax2.bar(self.monthly_returns_x, self.monthly_returns_y)
ax2.set_title("Monthly PnL")
plt.setp(ax2.get_xticklabels(), rotation=90, ha="right", rotation_mode="anchor")
self.draw()

86
src/nailab/ui/performancewidget.py

@ -0,0 +1,86 @@
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qsci import *
from ui_gen.performancewidget import Ui_PerformanceWidget
from pathlib import PurePath
import datetime
import json
import numpy as np
class PerformanceWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_PerformanceWidget()
self.ui.setupUi(self)
self.trades_series = {}
def widget_type(self):
return "performance"
def dailyToggled(self, v):
if v:
self.ui.correlationCanvas.setMode("daily")
def monthlyToggled(self, v):
if v:
self.ui.correlationCanvas.setMode("monthly")
def addResults(self):
filenames, _ = QtWidgets.QFileDialog.getOpenFileNames(self, self.tr("Select file to open"), "", self.tr("JSON files (*.json);;All Files (*)"))
results = []
for filename in filenames:
with open(filename, "rt") as f:
name = PurePath(filename).name
QtWidgets.QListWidgetItem(name, self.ui.lw_strategies)
trades = json.loads(f.read())
for trade in trades:
trade["entry_time"] = datetime.datetime.strptime(trade["entry_time"], "%Y-%m-%d %H:%M:%S")
trade["exit_time"] = datetime.datetime.strptime(trade["exit_time"], "%Y-%m-%d %H:%M:%S")
self.trades_series[name] = trades
results.append({"name" : name, "trades" : trades})
self.ui.canvas.add_trades_series_batched(results)
self.ui.correlationCanvas.add_trades_series_batched(results)
self.update_stats()
def removeResults(self):
selected_rows = sorted(map(lambda x: self.ui.lw_strategies.row(x), self.ui.lw_strategies.selectedItems()))
for row in reversed(selected_rows):
item = self.ui.lw_strategies.takeItem(row)
self.ui.canvas.remove_trades_series(item.text())
self.ui.correlationCanvas.remove_trades_series(item.text())
self.update_stats()
def update_stats(self):
self.ui.tw_stats.clear()
monmean = np.mean(self.ui.canvas.monthly_returns_y)
monstd = np.std(self.ui.canvas.monthly_returns_y)
if monstd == 0:
monz = 0
else:
monz = monmean / monstd
self.add_stat("Monthly mean", monmean)
self.add_stat("Monthly std. dev.", monstd)
self.add_stat("Z-Score", monz)
self.ui.tw_stats.resizeColumnToContents(0)
self.ui.tw_stats.resizeColumnToContents(1)
def add_stat(self, name, value):
item = QtWidgets.QTreeWidgetItem(self.ui.tw_stats)
item.setText(0, name)
if isinstance(value, str):
item.setText(1, str)
elif isinstance(value, float):
item.setText(1, "{:.3f}".format(value))
else:
item.setText(1, str(value))

26
src/nailab/ui/strategywidget.py

@ -94,6 +94,9 @@ class StrategyWidget(QtWidgets.QWidget):
except Exception as e: except Exception as e:
print("Exception: ", e) print("Exception: ", e)
def widget_type(self):
return "strategy"
def is_saved(self): def is_saved(self):
return self.saved return self.saved
@ -191,12 +194,21 @@ class StrategyWidget(QtWidgets.QWidget):
cumpnl = np.cumsum(pnl) cumpnl = np.cumsum(pnl)
drawdown = [] drawdown = []
cur_max = 0 cur_max = 0
drawdown_lengths = []
cur_drawdown = 0
cur_drawdown_max = 0
for i in range(0, len(cumpnl)): for i in range(0, len(cumpnl)):
if cumpnl[i] > cur_max: if cumpnl[i] > cur_max:
cur_max = cumpnl[i] cur_max = cumpnl[i]
drawdown.append(0) drawdown.append(0)
if cur_drawdown > 0:
drawdown_lengths.append((cur_drawdown, cur_drawdown_max))
cur_drawdown = 0
cur_drawdown_max = 0
else: else:
drawdown.append(-(cur_max - cumpnl[i])) drawdown.append(-(cur_max - cumpnl[i]))
cur_drawdown += 1
cur_drawdown_max = max(cur_drawdown_max, cur_max - cumpnl[i])
if self.equity_widget is None: if self.equity_widget is None:
self.equity_widget = EquityChartWidget(self) self.equity_widget = EquityChartWidget(self)
self.ui.tabs.addTab(self.equity_widget, "Equity") self.ui.tabs.addTab(self.equity_widget, "Equity")
@ -204,6 +216,8 @@ class StrategyWidget(QtWidgets.QWidget):
#self.equity_widget.set_data(self.result[2]) #self.equity_widget.set_data(self.result[2])
self.equity_widget.set_data(cumpnl, np.array(drawdown)) self.equity_widget.set_data(cumpnl, np.array(drawdown))
print(drawdown_lengths)
def update_trades_list(self): def update_trades_list(self):
if self.trades_widget is None: if self.trades_widget is None:
self.trades_widget = TradesListWidget(self) self.trades_widget = TradesListWidget(self)
@ -287,6 +301,18 @@ class StrategyWidget(QtWidgets.QWidget):
profit_factor.setText(2, "{:.3f}".format(self.result[0]['short']['profit_factor'])) profit_factor.setText(2, "{:.3f}".format(self.result[0]['short']['profit_factor']))
profit_factor.setText(3, "{:.3f}".format(self.result[0]['all']['profit_factor'])) profit_factor.setText(3, "{:.3f}".format(self.result[0]['all']['profit_factor']))
sharpe_ratio = QtWidgets.QTreeWidgetItem(self.result_widget)
sharpe_ratio.setText(0, "Sharpe ratio")
sharpe_ratio.setText(1, "{:.3f}".format(self.result[0]['long']['sharpe_ratio']))
sharpe_ratio.setText(2, "{:.3f}".format(self.result[0]['short']['sharpe_ratio']))
sharpe_ratio.setText(3, "{:.3f}".format(self.result[0]['all']['sharpe_ratio']))
t_stat = QtWidgets.QTreeWidgetItem(self.result_widget)
t_stat.setText(0, "t-statistics")
t_stat.setText(1, "{:.3f}".format(self.result[0]['long']['t_stat']))
t_stat.setText(2, "{:.3f}".format(self.result[0]['short']['t_stat']))
t_stat.setText(3, "{:.3f}".format(self.result[0]['all']['t_stat']))
self.result_widget.resizeColumnToContents(0) self.result_widget.resizeColumnToContents(0)
def setError(self, errmsg): def setError(self, errmsg):

24
src/nailab/ui/tradeslistwidget.py

@ -4,6 +4,9 @@ from PyQt5.Qsci import *
from ui_gen.tradeslistwidget import Ui_TradesListWidget from ui_gen.tradeslistwidget import Ui_TradesListWidget
import json
import math
class TradesListWidget(QtWidgets.QWidget): class TradesListWidget(QtWidgets.QWidget):
def __init__(self, parent=None): def __init__(self, parent=None):
@ -43,3 +46,24 @@ class TradesListWidget(QtWidgets.QWidget):
for i in range(0, 8): for i in range(0, 8):
item.setBackground(i, brush) item.setBackground(i, brush)
def exportToFile(self):
filename, _ = QtWidgets.QFileDialog.getSaveFileName(None, self.tr("Select output file"), "", self.tr("JSON files (*.json);;All Files (*)"))
with open(filename, "wt") as f:
output = []
for trade in self.trades:
if trade["is_long"]:
p = 100.0 * (trade["exit_price"] / trade["entry_price"] - 1)
log_ret = math.log(trade["exit_price"] / trade["entry_price"])
else:
p = 100.0 * (trade["entry_price"] / trade["exit_price"] - 1)
log_ret = math.log(trade["entry_price"] / trade["exit_price"])
t = { "pnl" : trade["pnl"],
"percentage" : p,
"log_return" : log_ret,
"entry_time" : trade["entry_time"].strftime("%Y-%m-%d %H:%M:%S"),
"exit_time" : trade["exit_time"].strftime("%Y-%m-%d %H:%M:%S") }
output.append(t)
f.write(json.dumps(output, indent=4, sort_keys=True))

15
src/nailab/ui_gen/mainwindow.py

@ -29,6 +29,8 @@ class Ui_MainWindow(object):
self.menuFile.setObjectName("menuFile") self.menuFile.setObjectName("menuFile")
self.menuBacktest = QtWidgets.QMenu(self.menubar) self.menuBacktest = QtWidgets.QMenu(self.menubar)
self.menuBacktest.setObjectName("menuBacktest") self.menuBacktest.setObjectName("menuBacktest")
self.menuAnalysis = QtWidgets.QMenu(self.menubar)
self.menuAnalysis.setObjectName("menuAnalysis")
MainWindow.setMenuBar(self.menubar) MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar") self.statusbar.setObjectName("statusbar")
@ -62,19 +64,27 @@ class Ui_MainWindow(object):
self.actionExit.setObjectName("actionExit") self.actionExit.setObjectName("actionExit")
self.actionSave_as = QtWidgets.QAction(MainWindow) self.actionSave_as = QtWidgets.QAction(MainWindow)
self.actionSave_as.setObjectName("actionSave_as") self.actionSave_as.setObjectName("actionSave_as")
self.actionPerformance = QtWidgets.QAction(MainWindow)
self.actionPerformance.setObjectName("actionPerformance")
self.actionExecute_in_Portfolio_Mode = QtWidgets.QAction(MainWindow)
self.actionExecute_in_Portfolio_Mode.setIcon(icon2)
self.actionExecute_in_Portfolio_Mode.setObjectName("actionExecute_in_Portfolio_Mode")
self.menuFile.addAction(self.actionNew_strategy) self.menuFile.addAction(self.actionNew_strategy)
self.menuFile.addAction(self.actionOpen_strategy) self.menuFile.addAction(self.actionOpen_strategy)
self.menuFile.addAction(self.actionSave_strategy) self.menuFile.addAction(self.actionSave_strategy)
self.menuFile.addAction(self.actionSave_as) self.menuFile.addAction(self.actionSave_as)
self.menuFile.addAction(self.actionExit) self.menuFile.addAction(self.actionExit)
self.menuBacktest.addAction(self.actionExecute) self.menuBacktest.addAction(self.actionExecute)
self.menuAnalysis.addAction(self.actionPerformance)
self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuBacktest.menuAction()) self.menubar.addAction(self.menuBacktest.menuAction())
self.menubar.addAction(self.menuAnalysis.menuAction())
self.toolBar.addAction(self.actionNew_strategy) self.toolBar.addAction(self.actionNew_strategy)
self.toolBar.addAction(self.actionOpen_strategy) self.toolBar.addAction(self.actionOpen_strategy)
self.toolBar.addAction(self.actionSave_strategy) self.toolBar.addAction(self.actionSave_strategy)
self.toolBar.addSeparator() self.toolBar.addSeparator()
self.toolBar.addAction(self.actionExecute) self.toolBar.addAction(self.actionExecute)
self.toolBar.addAction(self.actionExecute_in_Portfolio_Mode)
self.retranslateUi(MainWindow) self.retranslateUi(MainWindow)
self.tabs.setCurrentIndex(-1) self.tabs.setCurrentIndex(-1)
@ -84,6 +94,8 @@ class Ui_MainWindow(object):
self.actionExecute.triggered.connect(MainWindow.executeStrategy) self.actionExecute.triggered.connect(MainWindow.executeStrategy)
self.actionSave_strategy.triggered.connect(MainWindow.saveStrategy) self.actionSave_strategy.triggered.connect(MainWindow.saveStrategy)
self.actionSave_as.triggered.connect(MainWindow.saveStrategyAs) self.actionSave_as.triggered.connect(MainWindow.saveStrategyAs)
self.actionPerformance.triggered.connect(MainWindow.performanceAnalysis)
self.actionExecute_in_Portfolio_Mode.triggered.connect(MainWindow.executeStrategyInPortfolioMode)
QtCore.QMetaObject.connectSlotsByName(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow): def retranslateUi(self, MainWindow):
@ -91,6 +103,7 @@ class Ui_MainWindow(object):
MainWindow.setWindowTitle(_translate("MainWindow", "Nailab")) MainWindow.setWindowTitle(_translate("MainWindow", "Nailab"))
self.menuFile.setTitle(_translate("MainWindow", "File")) self.menuFile.setTitle(_translate("MainWindow", "File"))
self.menuBacktest.setTitle(_translate("MainWindow", "Backtest")) self.menuBacktest.setTitle(_translate("MainWindow", "Backtest"))
self.menuAnalysis.setTitle(_translate("MainWindow", "Analysis"))
self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar")) self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
self.actionOpenTrades.setText(_translate("MainWindow", "Open...")) self.actionOpenTrades.setText(_translate("MainWindow", "Open..."))
self.actionNew_strategy.setText(_translate("MainWindow", "New strategy")) self.actionNew_strategy.setText(_translate("MainWindow", "New strategy"))
@ -102,5 +115,7 @@ class Ui_MainWindow(object):
self.actionSave_strategy.setShortcut(_translate("MainWindow", "Ctrl+S")) self.actionSave_strategy.setShortcut(_translate("MainWindow", "Ctrl+S"))
self.actionExit.setText(_translate("MainWindow", "Exit")) self.actionExit.setText(_translate("MainWindow", "Exit"))
self.actionSave_as.setText(_translate("MainWindow", "Save as...")) self.actionSave_as.setText(_translate("MainWindow", "Save as..."))
self.actionPerformance.setText(_translate("MainWindow", "Performance"))
self.actionExecute_in_Portfolio_Mode.setText(_translate("MainWindow", "Execute in Portfolio Mode"))
import nailab_rc import nailab_rc

100
src/nailab/ui_gen/performancewidget.py

@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui/performancewidget.ui'
#
# Created by: PyQt5 UI code generator 5.5.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_PerformanceWidget(object):
def setupUi(self, PerformanceWidget):
PerformanceWidget.setObjectName("PerformanceWidget")
PerformanceWidget.resize(780, 556)
self.gridLayout = QtWidgets.QGridLayout(PerformanceWidget)
self.gridLayout.setContentsMargins(2, 2, 2, 2)
self.gridLayout.setObjectName("gridLayout")
self.tab1 = QtWidgets.QTabWidget(PerformanceWidget)
self.tab1.setObjectName("tab1")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.gridLayout_2 = QtWidgets.QGridLayout(self.tab)
self.gridLayout_2.setContentsMargins(1, 1, 1, 1)
self.gridLayout_2.setObjectName("gridLayout_2")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setContentsMargins(4, 4, 4, 4)
self.verticalLayout.setObjectName("verticalLayout")
self.canvas = PerformanceCanvas(self.tab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.canvas.sizePolicy().hasHeightForWidth())
self.canvas.setSizePolicy(sizePolicy)
self.canvas.setObjectName("canvas")
self.verticalLayout.addWidget(self.canvas)
self.tw_stats = QtWidgets.QTreeWidget(self.tab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tw_stats.sizePolicy().hasHeightForWidth())
self.tw_stats.setSizePolicy(sizePolicy)
self.tw_stats.setObjectName("tw_stats")
self.tw_stats.header().setVisible(False)
self.verticalLayout.addWidget(self.tw_stats)
self.gridLayout_2.addLayout(self.verticalLayout, 0, 0, 1, 1)
self.tab1.addTab(self.tab, "")
self.tab_2 = QtWidgets.QWidget()
self.tab_2.setObjectName("tab_2")
self.gridLayout_3 = QtWidgets.QGridLayout(self.tab_2)
self.gridLayout_3.setObjectName("gridLayout_3")
self.rb_daily = QtWidgets.QRadioButton(self.tab_2)
self.rb_daily.setChecked(True)
self.rb_daily.setObjectName("rb_daily")
self.gridLayout_3.addWidget(self.rb_daily, 0, 0, 1, 1)
self.rb_monthly = QtWidgets.QRadioButton(self.tab_2)
self.rb_monthly.setObjectName("rb_monthly")
self.gridLayout_3.addWidget(self.rb_monthly, 0, 1, 1, 1)
self.correlationCanvas = CorrelationChartCanvas(self.tab_2)
self.correlationCanvas.setObjectName("correlationCanvas")
self.gridLayout_3.addWidget(self.correlationCanvas, 1, 0, 1, 2)
self.tab1.addTab(self.tab_2, "")
self.gridLayout.addWidget(self.tab1, 0, 2, 1, 1)
self.b_add = QtWidgets.QPushButton(PerformanceWidget)
self.b_add.setObjectName("b_add")
self.gridLayout.addWidget(self.b_add, 1, 0, 1, 1)
self.b_remove = QtWidgets.QPushButton(PerformanceWidget)
self.b_remove.setObjectName("b_remove")
self.gridLayout.addWidget(self.b_remove, 1, 1, 1, 1)
self.lw_strategies = QtWidgets.QListWidget(PerformanceWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lw_strategies.sizePolicy().hasHeightForWidth())
self.lw_strategies.setSizePolicy(sizePolicy)
self.lw_strategies.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.lw_strategies.setObjectName("lw_strategies")
self.gridLayout.addWidget(self.lw_strategies, 0, 0, 1, 2)
self.retranslateUi(PerformanceWidget)
self.tab1.setCurrentIndex(1)
self.b_add.clicked.connect(PerformanceWidget.addResults)
self.b_remove.clicked.connect(PerformanceWidget.removeResults)
self.rb_daily.toggled['bool'].connect(PerformanceWidget.dailyToggled)
self.rb_monthly.toggled['bool'].connect(PerformanceWidget.monthlyToggled)
QtCore.QMetaObject.connectSlotsByName(PerformanceWidget)
def retranslateUi(self, PerformanceWidget):
_translate = QtCore.QCoreApplication.translate
PerformanceWidget.setWindowTitle(_translate("PerformanceWidget", "Form"))
self.tw_stats.headerItem().setText(0, _translate("PerformanceWidget", "1"))
self.tw_stats.headerItem().setText(1, _translate("PerformanceWidget", "2"))
self.tab1.setTabText(self.tab1.indexOf(self.tab), _translate("PerformanceWidget", "Returns and Drawdowns"))
self.rb_daily.setText(_translate("PerformanceWidget", "Daily"))
self.rb_monthly.setText(_translate("PerformanceWidget", "Monthly"))
self.tab1.setTabText(self.tab1.indexOf(self.tab_2), _translate("PerformanceWidget", "Correlations"))
self.b_add.setText(_translate("PerformanceWidget", "Add..."))
self.b_remove.setText(_translate("PerformanceWidget", "Remove"))
from ui.correlationchartcanvas import CorrelationChartCanvas
from ui.performancecanvas import PerformanceCanvas

9
src/nailab/ui_gen/tradeslistwidget.py

@ -15,16 +15,23 @@ class Ui_TradesListWidget(object):
self.gridLayout = QtWidgets.QGridLayout(TradesListWidget) self.gridLayout = QtWidgets.QGridLayout(TradesListWidget)
self.gridLayout.setContentsMargins(1, 1, 1, 1) self.gridLayout.setContentsMargins(1, 1, 1, 1)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.b_exportToFile = QtWidgets.QPushButton(TradesListWidget)
self.b_exportToFile.setObjectName("b_exportToFile")
self.gridLayout.addWidget(self.b_exportToFile, 1, 0, 1, 1)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem, 1, 1, 1, 1)
self.trades = QtWidgets.QTreeWidget(TradesListWidget) self.trades = QtWidgets.QTreeWidget(TradesListWidget)
self.trades.setObjectName("trades") self.trades.setObjectName("trades")
self.gridLayout.addWidget(self.trades, 0, 0, 1, 1) self.gridLayout.addWidget(self.trades, 0, 0, 1, 2)
self.retranslateUi(TradesListWidget) self.retranslateUi(TradesListWidget)
self.b_exportToFile.clicked.connect(TradesListWidget.exportToFile)
QtCore.QMetaObject.connectSlotsByName(TradesListWidget) QtCore.QMetaObject.connectSlotsByName(TradesListWidget)
def retranslateUi(self, TradesListWidget): def retranslateUi(self, TradesListWidget):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
TradesListWidget.setWindowTitle(_translate("TradesListWidget", "Form")) TradesListWidget.setWindowTitle(_translate("TradesListWidget", "Form"))
self.b_exportToFile.setText(_translate("TradesListWidget", "Export to file..."))
self.trades.headerItem().setText(0, _translate("TradesListWidget", "D")) self.trades.headerItem().setText(0, _translate("TradesListWidget", "D"))
self.trades.headerItem().setText(1, _translate("TradesListWidget", "Amount")) self.trades.headerItem().setText(1, _translate("TradesListWidget", "Amount"))
self.trades.headerItem().setText(2, _translate("TradesListWidget", "Security")) self.trades.headerItem().setText(2, _translate("TradesListWidget", "Security"))

56
ui/mainwindow.ui

@ -64,8 +64,15 @@
</property> </property>
<addaction name="actionExecute"/> <addaction name="actionExecute"/>
</widget> </widget>
<widget class="QMenu" name="menuAnalysis">
<property name="title">
<string>Analysis</string>
</property>
<addaction name="actionPerformance"/>
</widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuBacktest"/> <addaction name="menuBacktest"/>
<addaction name="menuAnalysis"/>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"/> <widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar"> <widget class="QToolBar" name="toolBar">
@ -83,6 +90,7 @@
<addaction name="actionSave_strategy"/> <addaction name="actionSave_strategy"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionExecute"/> <addaction name="actionExecute"/>
<addaction name="actionExecute_in_Portfolio_Mode"/>
</widget> </widget>
<action name="actionOpenTrades"> <action name="actionOpenTrades">
<property name="text"> <property name="text">
@ -144,6 +152,20 @@
<string>Save as...</string> <string>Save as...</string>
</property> </property>
</action> </action>
<action name="actionPerformance">
<property name="text">
<string>Performance</string>
</property>
</action>
<action name="actionExecute_in_Portfolio_Mode">
<property name="icon">
<iconset resource="../nailab.qrc">
<normaloff>:/main/icons/execute.png</normaloff>:/main/icons/execute.png</iconset>
</property>
<property name="text">
<string>Execute in Portfolio Mode</string>
</property>
</action>
</widget> </widget>
<resources> <resources>
<include location="../nailab.qrc"/> <include location="../nailab.qrc"/>
@ -245,6 +267,38 @@
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>actionPerformance</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>performanceAnalysis()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>529</x>
<y>293</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionExecute_in_Portfolio_Mode</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>executeStrategyInPortfolioMode()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>529</x>
<y>293</y>
</hint>
</hints>
</connection>
</connections> </connections>
<slots> <slots>
<slot>openTrades()</slot> <slot>openTrades()</slot>
@ -254,5 +308,7 @@
<slot>executeStrategy()</slot> <slot>executeStrategy()</slot>
<slot>saveStrategy()</slot> <slot>saveStrategy()</slot>
<slot>saveStrategyAs()</slot> <slot>saveStrategyAs()</slot>
<slot>performanceAnalysis()</slot>
<slot>executeStrategyInPortfolioMode()</slot>
</slots> </slots>
</ui> </ui>

247
ui/performancewidget.ui

@ -0,0 +1,247 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PerformanceWidget</class>
<widget class="QWidget" name="PerformanceWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>780</width>
<height>556</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item row="0" column="2">
<widget class="QTabWidget" name="tab1">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Returns and Drawdowns</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="PerformanceCanvas" name="canvas" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="tw_stats">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>1</string>
</property>
</column>
<column>
<property name="text">
<string>2</string>
</property>
</column>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Correlations</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QRadioButton" name="rb_daily">
<property name="text">
<string>Daily</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="rb_monthly">
<property name="text">
<string>Monthly</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="CorrelationChartCanvas" name="correlationCanvas" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="b_add">
<property name="text">
<string>Add...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="b_remove">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QListWidget" name="lw_strategies">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>PerformanceCanvas</class>
<extends>QWidget</extends>
<header>ui/performancecanvas.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>CorrelationChartCanvas</class>
<extends>QWidget</extends>
<header>ui/correlationchartcanvas.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>b_add</sender>
<signal>clicked()</signal>
<receiver>PerformanceWidget</receiver>
<slot>addResults()</slot>
<hints>
<hint type="sourcelabel">
<x>127</x>
<y>539</y>
</hint>
<hint type="destinationlabel">
<x>262</x>
<y>543</y>
</hint>
</hints>
</connection>
<connection>
<sender>b_remove</sender>
<signal>clicked()</signal>
<receiver>PerformanceWidget</receiver>
<slot>removeResults()</slot>
<hints>
<hint type="sourcelabel">
<x>239</x>
<y>539</y>
</hint>
<hint type="destinationlabel">
<x>372</x>
<y>542</y>
</hint>
</hints>
</connection>
<connection>
<sender>rb_daily</sender>
<signal>toggled(bool)</signal>
<receiver>PerformanceWidget</receiver>
<slot>dailyToggled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>314</x>
<y>44</y>
</hint>
<hint type="destinationlabel">
<x>508</x>
<y>537</y>
</hint>
</hints>
</connection>
<connection>
<sender>rb_monthly</sender>
<signal>toggled(bool)</signal>
<receiver>PerformanceWidget</receiver>
<slot>monthlyToggled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>572</x>
<y>47</y>
</hint>
<hint type="destinationlabel">
<x>647</x>
<y>546</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>addResults()</slot>
<slot>removeResults()</slot>
<slot>dailyToggled(bool)</slot>
<slot>monthlyToggled(bool)</slot>
</slots>
</ui>

44
ui/tradeslistwidget.ui

@ -26,7 +26,27 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>1</number> <number>1</number>
</property> </property>
<item row="0" column="0"> <item row="1" column="0">
<widget class="QPushButton" name="b_exportToFile">
<property name="text">
<string>Export to file...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="2">
<widget class="QTreeWidget" name="trades"> <widget class="QTreeWidget" name="trades">
<column> <column>
<property name="text"> <property name="text">
@ -73,5 +93,25 @@
</layout> </layout>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections>
<connection>
<sender>b_exportToFile</sender>
<signal>clicked()</signal>
<receiver>TradesListWidget</receiver>
<slot>exportToFile()</slot>
<hints>
<hint type="sourcelabel">
<x>78</x>
<y>550</y>
</hint>
<hint type="destinationlabel">
<x>101</x>
<y>551</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>exportToFile()</slot>
</slots>
</ui> </ui>

Loading…
Cancel
Save