Browse Source

Some updates

master
Denis Tereshkin 5 years ago
parent
commit
fb364a5f37
  1. 20
      build_ui.sh
  2. 3
      src/nailab/data/datasourcemanager.py
  3. 44
      src/nailab/data/yahoocsvfolderdatasource.py
  4. 10
      src/nailab/execution/executor.py
  5. 27
      src/nailab/execution/portfolioexecutor.py
  6. 249
      src/nailab/nailab_rc.py
  7. 7
      src/nailab/ui/mainwindow.py
  8. 8
      src/nailab/ui/newdatasourcedialog.py
  9. 26
      src/nailab/ui/settingswindow.py
  10. 117
      src/nailab/ui/statisticwidget.py
  11. 28
      src/nailab/ui/strategywidget.py
  12. 8
      src/nailab/ui_gen/csvdatasourceconfigwidget.py
  13. 8
      src/nailab/ui_gen/equitychartwidget.py
  14. 17
      src/nailab/ui_gen/mainwindow.py
  15. 10
      src/nailab/ui_gen/newdatasourcedialog.py
  16. 10
      src/nailab/ui_gen/performancewidget.py
  17. 51
      src/nailab/ui_gen/settingswindow.py
  18. 62
      src/nailab/ui_gen/statisticwidget.py
  19. 29
      src/nailab/ui_gen/strategywidget.py
  20. 8
      src/nailab/ui_gen/tradeslistwidget.py
  21. 27
      ui/mainwindow.ui
  22. 5
      ui/newdatasourcedialog.ui
  23. 2
      ui/performancewidget.ui
  24. 105
      ui/settingswindow.ui
  25. 113
      ui/statisticwidget.ui
  26. 38
      ui/strategywidget.ui

20
build_ui.sh

@ -1,13 +1,15 @@
#!/bin/bash #!/bin/bash
/usr/bin/pyuic5 ui/csvdatasourceconfigwidget.ui > src/nailab/ui_gen/csvdatasourceconfigwidget.py pyuic5 ui/csvdatasourceconfigwidget.ui > src/nailab/ui_gen/csvdatasourceconfigwidget.py
/usr/bin/pyuic5 ui/equitychartwidget.ui > src/nailab/ui_gen/equitychartwidget.py pyuic5 ui/equitychartwidget.ui > src/nailab/ui_gen/equitychartwidget.py
/usr/bin/pyuic5 ui/mainwindow.ui > src/nailab/ui_gen/mainwindow.py pyuic5 ui/mainwindow.ui > src/nailab/ui_gen/mainwindow.py
/usr/bin/pyuic5 ui/newdatasourcedialog.ui > src/nailab/ui_gen/newdatasourcedialog.py pyuic5 ui/newdatasourcedialog.ui > src/nailab/ui_gen/newdatasourcedialog.py
/usr/bin/pyuic5 ui/strategywidget.ui > src/nailab/ui_gen/strategywidget.py pyuic5 ui/strategywidget.ui > src/nailab/ui_gen/strategywidget.py
/usr/bin/pyuic5 ui/tradeslistwidget.ui > src/nailab/ui_gen/tradeslistwidget.py pyuic5 ui/tradeslistwidget.ui > src/nailab/ui_gen/tradeslistwidget.py
/usr/bin/pyuic5 ui/performancewidget.ui > src/nailab/ui_gen/performancewidget.py pyuic5 ui/performancewidget.ui > src/nailab/ui_gen/performancewidget.py
pyuic5 ui/settingswindow.ui > src/nailab/ui_gen/settingswindow.py
/usr/bin/pyrcc5 nailab.qrc -o src/nailab/nailab_rc.py pyuic5 ui/statisticwidget.ui > src/nailab/ui_gen/statisticwidget.py
pyrcc5 nailab.qrc -o src/nailab/nailab_rc.py

3
src/nailab/data/datasourcemanager.py

@ -1,5 +1,6 @@
from data.csvfolderdatasource import CsvFolderDataSource from data.csvfolderdatasource import CsvFolderDataSource
from data.yahoocsvfolderdatasource import YahooCsvFolderDataSource
class DataSourceManager: class DataSourceManager:
@ -19,6 +20,8 @@ class DataSourceManager:
for s in serialized: for s in serialized:
if s[0] == "csv": if s[0] == "csv":
self.add_source(CsvFolderDataSource.deserialize(s[1])) self.add_source(CsvFolderDataSource.deserialize(s[1]))
if s[0] == "yahoo_csv":
self.add_source(YahooCsvFolderDataSource.deserialize(s[1]))
def add_source(self, source): def add_source(self, source):
self.sources.append(source) self.sources.append(source)

44
src/nailab/data/yahoocsvfolderdatasource.py

@ -0,0 +1,44 @@
from data.datasource import DataSource
from naiback.data.feeds.yahoofeed import YahooCSVFeed
import glob
import os
class YahooCsvFolderDataSource(DataSource):
def __init__(self, name, path):
super().__init__(name)
self.type = "yahoo_csv"
self.path = path
self.feeds = []
self._discover_feeds()
@classmethod
def deserialize(self, data):
return YahooCsvFolderDataSource(data[0], data[1])
def serialize(self):
return (self.name, self.path)
def available_feeds(self):
return [f[1] for f in self.feeds]
def refresh(self):
self._discover_feeds()
def get_feed(self, feed_id):
for path, f_id in self.feeds:
if f_id == feed_id:
with open(path, 'r') as f:
return YahooCSVFeed(f)
raise NailabError('Unable to obtain feed: {}'.format(feed_id))
def _discover_feeds(self):
files = glob.glob(self.path + '/*.csv')
self.feeds = [(f, os.path.basename(f)) for f in files]

10
src/nailab/execution/executor.py

@ -3,6 +3,7 @@ import sys
import importlib import importlib
from importlib.machinery import SourceFileLoader from importlib.machinery import SourceFileLoader
import inspect import inspect
from PyQt5 import QtCore
from naiback.strategy import Strategy from naiback.strategy import Strategy
@ -23,6 +24,9 @@ class Executor:
for feed in feeds: for feed in feeds:
strategy.add_feed(feed) strategy.add_feed(feed)
(commission_percentage, commission_per_share) = self.load_commissions()
strategy.broker.set_commission(commission_percentage, commission_per_share)
if extents is None: if extents is None:
strategy.run() strategy.run()
else: else:
@ -34,3 +38,9 @@ class Executor:
return (results, trades, equity) return (results, trades, equity)
def load_commissions(self):
s = QtCore.QSettings()
return (float(s.value("commission/percentage", 0)), float(s.value("commission/per_share", 0)))

27
src/nailab/execution/portfolioexecutor.py

@ -54,18 +54,28 @@ class PortfolioExecutor:
all_trades = list(sorted(trades1 + trades2, key=lambda x: x['entry_time'])) all_trades = list(sorted(trades1 + trades2, key=lambda x: x['entry_time']))
result = [] result = []
current_trades = [] current_trades = []
# TODO customize
size_per_position = 1000000
cash = 1000000
max_trades = 3 max_trades = 3
for trade in all_trades: for trade in all_trades:
if len(current_trades) < max_trades: if len(current_trades) < max_trades:
size = math.floor(size_per_position / (trade['entry_price'] * max_trades))
cash -= size * trade['entry_price']
trade['size'] = size
if trade['is_long']:
trade['pnl'] = size * (trade['exit_price'] - trade['entry_price'])
else:
trade['pnl'] = -size * (trade['exit_price'] - trade['entry_price'])
current_trades.append(trade) current_trades.append(trade)
result.append(trade) result.append(trade)
new_current_trades = [] new_current_trades = []
for ct in current_trades: for ct in current_trades:
if ct['exit_time'] < trade['entry_time']: if ct['exit_time'] < trade['entry_time']:
new_current_trades.append(ct) new_current_trades.append(ct)
else: else:
# possibly adjust the balance cash += ct['size'] * ct['exit_price']
pass
current_trades = new_current_trades current_trades = new_current_trades
return result return result
@ -90,6 +100,10 @@ class PortfolioExecutor:
result['long']['number_of_trades'] = len(longs) result['long']['number_of_trades'] = len(longs)
result['short']['number_of_trades'] = len(shorts) result['short']['number_of_trades'] = len(shorts)
result['all']['total_commission'] = 0 # TODO
result['long']['total_commission'] = 0
result['short']['total_commission'] = 0
result['all']['avg'] = render_ratio(result['all']['net_profit'], result['all']['number_of_trades']) 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['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['short']['avg'] = render_ratio(result['short']['net_profit'], result['short']['number_of_trades'])
@ -126,22 +140,25 @@ class PortfolioExecutor:
stddev = np.std(list(map(lambda x: x['pnl'], trades))) stddev = np.std(list(map(lambda x: x['pnl'], trades)))
sharpe = mean / stddev sharpe = mean / stddev
tstat = sharpe * math.sqrt(len(trades)) tstat = sharpe * math.sqrt(len(trades))
result['all']['sharpe_ratio'] = sharpe result['all']['z_score'] = sharpe
result['all']['t_stat'] = tstat result['all']['t_stat'] = tstat
result['all']['kelly'] = 0
mean = np.mean(list(map(lambda x: x['pnl'], longs))) mean = np.mean(list(map(lambda x: x['pnl'], longs)))
stddev = np.std(list(map(lambda x: x['pnl'], longs))) stddev = np.std(list(map(lambda x: x['pnl'], longs)))
sharpe = mean / stddev sharpe = mean / stddev
tstat = sharpe * math.sqrt(len(longs)) tstat = sharpe * math.sqrt(len(longs))
result['long']['sharpe_ratio'] = sharpe result['long']['z_score'] = sharpe
result['long']['t_stat'] = tstat result['long']['t_stat'] = tstat
result['long']['kelly'] = 0
mean = np.mean(list(map(lambda x: x['pnl'], shorts))) mean = np.mean(list(map(lambda x: x['pnl'], shorts)))
stddev = np.std(list(map(lambda x: x['pnl'], shorts))) stddev = np.std(list(map(lambda x: x['pnl'], shorts)))
sharpe = mean / stddev sharpe = mean / stddev
tstat = sharpe * math.sqrt(len(shorts)) tstat = sharpe * math.sqrt(len(shorts))
result['short']['sharpe_ratio'] = sharpe result['short']['z_score'] = sharpe
result['short']['t_stat'] = tstat result['short']['t_stat'] = tstat
result['short']['kelly'] = 0
return result return result

249
src/nailab/nailab_rc.py

@ -2,13 +2,110 @@
# Resource object code # Resource object code
# #
# Created by: The Resource Compiler for PyQt5 (Qt v5.5.1) # Created by: The Resource Compiler for PyQt5 (Qt v5.15.1)
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore from PyQt5 import QtCore
qt_resource_data = b"\ qt_resource_data = b"\
\x00\x00\x03\x8f\
\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\x03\x0c\x49\x44\
\x41\x54\x68\x81\xed\x98\x3b\x6f\x13\x41\x14\x85\xcf\xdd\x75\x22\
\x2b\x7e\xc4\x8b\x82\xac\x74\x48\x3c\x44\x8f\x68\x20\x11\x54\x54\
\x54\x48\x20\x90\x22\x85\x9a\x74\x94\xbc\xc4\xa3\x83\x48\xf4\x54\
\x49\x41\x89\x8c\x22\x7e\x40\xc2\x8f\x48\x49\x81\x14\x51\x61\x93\
\x38\xb1\xe3\xd7\x5e\x0a\x6c\x65\xbc\x33\x76\x76\x66\xef\xda\x29\
\xf8\x24\x37\x33\x9e\xdd\x73\x76\xce\x1d\xdf\x35\x31\x33\x6c\xa9\
\x57\x82\x55\xaa\x7a\x1b\xda\x44\x96\x11\x5e\x08\xad\xaf\x07\xf0\
\x66\xf1\xe6\xfe\x63\x87\x85\xf0\x5c\x16\xa1\x45\x2f\x8c\x32\x02\
\xfb\x87\x01\xd0\xee\x51\x36\xff\xc4\x49\x07\x1c\x0c\x1c\x7c\x9b\
\xbf\x4c\x0d\xba\xa4\x4d\xf8\x00\x17\xad\x0d\x34\xbc\xb0\x7b\x7f\
\xf1\xda\x5e\xc3\x76\xe1\x00\x6b\x03\x7e\xd3\xff\x08\x83\x4e\x9e\
\x67\x80\xec\xae\x45\x44\x6b\xf9\xe5\xfa\xae\xad\x06\x15\x3b\x03\
\xdb\x94\xe1\x26\xee\x98\xa6\x38\xb0\xcd\x3e\x6f\x16\x6e\xd4\x36\
\x2c\x17\x69\x58\x19\x38\xa8\x96\x9e\xa3\x43\x33\x9a\x94\x3c\x03\
\xda\xe8\x38\x92\xe5\x5e\xc5\xca\x00\x1d\xd3\x9a\x71\xa2\x64\x95\
\xfd\xc4\xb9\x57\x89\x6d\xe0\xb0\x12\xdc\xa2\x26\x9d\xd7\x26\x66\
\xfa\x3b\x10\x13\x89\xdc\xab\xc4\x36\xc0\x1d\xbc\x37\x8e\x5b\x65\
\x5f\x26\xf7\x2a\xb1\x0c\xfc\xf9\x1a\x94\xa8\xe1\x5d\xd7\x26\xa8\
\x7f\xfa\xc4\x42\x2e\xf7\x2a\xb1\x0c\xf8\x5d\x7c\x40\x4f\x3f\x24\
\xb9\xc8\x80\x1f\xeb\x3e\xa2\xb9\x57\x89\x17\xa1\x63\xef\x91\x69\
\x38\xee\x2f\xaf\x74\xee\x55\x4e\x35\x50\xaf\x04\xab\xd4\x42\x4e\
\x9b\xc8\xf2\xbf\xcf\xa9\xc8\xe7\x5e\xe5\xf4\x1d\x48\xd4\xf7\xa4\
\x93\x7b\x95\xb1\x06\x12\xf6\x3d\xa9\xe5\x5e\x65\xac\x81\x24\x7d\
\x4f\x9a\xb9\x1f\xba\xcf\xc8\xf7\x81\x6d\xca\x1c\xfe\x0c\x1a\xa6\
\xd6\x21\xbc\xd8\xb3\x6c\x1d\x1c\x61\x7a\x53\x5c\xaa\xbd\x1e\xf7\
\x95\x91\x3b\x20\xd7\xf7\x38\x12\x43\x3c\x30\xc6\x80\x50\xdf\xe3\
\x46\x4c\xf1\xc0\x08\x03\x52\x7d\x8f\x13\x16\xe2\x81\x11\x06\x64\
\xfa\x1e\x07\x2c\xc5\x03\x06\x03\x32\x7d\x8f\x03\x0e\xe2\x01\x83\
\x01\xbf\xe7\xad\x27\xec\x7b\xec\x71\x14\x0f\x98\x22\xd4\xc4\x43\
\xe3\x3d\x9c\xfe\x71\x88\x41\x02\xf1\xc0\xb8\xdf\x01\x07\xaa\x9f\
\x16\x5f\xb6\xf6\xe6\xde\x46\xc7\xfd\xb9\x2e\xb2\x4b\xfb\xfa\x82\
\x84\xe2\x01\xd7\xff\x85\x46\x10\x1e\x66\x9e\x9a\xc6\x67\xae\x18\
\xba\x09\x01\xf1\x80\xe0\x0e\x1c\x6d\x95\xcb\xfc\xbb\xfb\x0b\xe1\
\x70\xfd\x70\xc0\xe0\x72\xe4\xf4\x12\x12\x0f\x08\xee\x40\xd8\xee\
\xac\x47\xc5\xc3\x07\x78\x21\x3d\xf1\x80\xa0\x01\x6a\xd2\xbd\xe8\
\x18\x2f\x84\xc3\x27\x97\xb0\x78\x40\xc8\xc0\xc1\x97\x73\x2b\x68\
\x63\x6e\x68\x70\x16\x60\xb5\xed\x48\x41\x3c\x20\x64\xc0\xeb\xe2\
\x59\x74\x8c\xcb\xe1\x49\xcb\x9d\x92\x78\x40\xc0\xc0\xd1\x56\xb9\
\x8c\x06\x5d\x55\xc7\x38\xc7\xe0\x5c\xff\xe9\xa7\x28\x1e\x10\x30\
\xa0\x15\x2f\xe1\xe4\xd4\x49\x59\x3c\x20\x60\x20\x5a\xbc\x5c\x62\
\x60\x16\x13\x11\x0f\x24\x34\xa0\x15\xef\xe0\xd8\x9c\x90\x78\x20\
\xa1\x81\x68\xf1\xf2\x42\x08\x78\x93\x13\x0f\x24\x30\xa0\x15\xef\
\x2c\xc0\xf3\x78\x37\x49\xf1\x40\x02\x03\x5a\xf1\x06\xfc\xb9\xb8\
\x5c\x7b\x25\xa2\xca\x02\x67\x03\x6a\xf1\x72\x81\x77\xf2\x77\xab\
\x2b\x32\x92\xec\x70\x32\xa0\x16\x2f\x17\x78\xa7\xf0\xa0\x7a\x5b\
\x54\x95\x05\x4e\x06\x06\xc5\x3b\x6d\xf1\x80\x43\x3b\x3d\x68\x9b\
\x39\xc7\xdf\xa7\x2d\x1e\x00\x32\xb6\x0b\xc2\x76\x67\x1d\x39\x9c\
\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\
\x0b\x02\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\x02\x0e\ \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\
@ -87,103 +184,6 @@ 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\
\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\x03\x0c\x49\x44\
\x41\x54\x68\x81\xed\x98\x3b\x6f\x13\x41\x14\x85\xcf\xdd\x75\x22\
\x2b\x7e\xc4\x8b\x82\xac\x74\x48\x3c\x44\x8f\x68\x20\x11\x54\x54\
\x54\x48\x20\x90\x22\x85\x9a\x74\x94\xbc\xc4\xa3\x83\x48\xf4\x54\
\x49\x41\x89\x8c\x22\x7e\x40\xc2\x8f\x48\x49\x81\x14\x51\x61\x93\
\x38\xb1\xe3\xd7\x5e\x0a\x6c\x65\xbc\x33\x76\x76\x66\xef\xda\x29\
\xf8\x24\x37\x33\x9e\xdd\x73\x76\xce\x1d\xdf\x35\x31\x33\x6c\xa9\
\x57\x82\x55\xaa\x7a\x1b\xda\x44\x96\x11\x5e\x08\xad\xaf\x07\xf0\
\x66\xf1\xe6\xfe\x63\x87\x85\xf0\x5c\x16\xa1\x45\x2f\x8c\x32\x02\
\xfb\x87\x01\xd0\xee\x51\x36\xff\xc4\x49\x07\x1c\x0c\x1c\x7c\x9b\
\xbf\x4c\x0d\xba\xa4\x4d\xf8\x00\x17\xad\x0d\x34\xbc\xb0\x7b\x7f\
\xf1\xda\x5e\xc3\x76\xe1\x00\x6b\x03\x7e\xd3\xff\x08\x83\x4e\x9e\
\x67\x80\xec\xae\x45\x44\x6b\xf9\xe5\xfa\xae\xad\x06\x15\x3b\x03\
\xdb\x94\xe1\x26\xee\x98\xa6\x38\xb0\xcd\x3e\x6f\x16\x6e\xd4\x36\
\x2c\x17\x69\x58\x19\x38\xa8\x96\x9e\xa3\x43\x33\x9a\x94\x3c\x03\
\xda\xe8\x38\x92\xe5\x5e\xc5\xca\x00\x1d\xd3\x9a\x71\xa2\x64\x95\
\xfd\xc4\xb9\x57\x89\x6d\xe0\xb0\x12\xdc\xa2\x26\x9d\xd7\x26\x66\
\xfa\x3b\x10\x13\x89\xdc\xab\xc4\x36\xc0\x1d\xbc\x37\x8e\x5b\x65\
\x5f\x26\xf7\x2a\xb1\x0c\xfc\xf9\x1a\x94\xa8\xe1\x5d\xd7\x26\xa8\
\x7f\xfa\xc4\x42\x2e\xf7\x2a\xb1\x0c\xf8\x5d\x7c\x40\x4f\x3f\x24\
\xb9\xc8\x80\x1f\xeb\x3e\xa2\xb9\x57\x89\x17\xa1\x63\xef\x91\x69\
\x38\xee\x2f\xaf\x74\xee\x55\x4e\x35\x50\xaf\x04\xab\xd4\x42\x4e\
\x9b\xc8\xf2\xbf\xcf\xa9\xc8\xe7\x5e\xe5\xf4\x1d\x48\xd4\xf7\xa4\
\x93\x7b\x95\xb1\x06\x12\xf6\x3d\xa9\xe5\x5e\x65\xac\x81\x24\x7d\
\x4f\x9a\xb9\x1f\xba\xcf\xc8\xf7\x81\x6d\xca\x1c\xfe\x0c\x1a\xa6\
\xd6\x21\xbc\xd8\xb3\x6c\x1d\x1c\x61\x7a\x53\x5c\xaa\xbd\x1e\xf7\
\x95\x91\x3b\x20\xd7\xf7\x38\x12\x43\x3c\x30\xc6\x80\x50\xdf\xe3\
\x46\x4c\xf1\xc0\x08\x03\x52\x7d\x8f\x13\x16\xe2\x81\x11\x06\x64\
\xfa\x1e\x07\x2c\xc5\x03\x06\x03\x32\x7d\x8f\x03\x0e\xe2\x01\x83\
\x01\xbf\xe7\xad\x27\xec\x7b\xec\x71\x14\x0f\x98\x22\xd4\xc4\x43\
\xe3\x3d\x9c\xfe\x71\x88\x41\x02\xf1\xc0\xb8\xdf\x01\x07\xaa\x9f\
\x16\x5f\xb6\xf6\xe6\xde\x46\xc7\xfd\xb9\x2e\xb2\x4b\xfb\xfa\x82\
\x84\xe2\x01\xd7\xff\x85\x46\x10\x1e\x66\x9e\x9a\xc6\x67\xae\x18\
\xba\x09\x01\xf1\x80\xe0\x0e\x1c\x6d\x95\xcb\xfc\xbb\xfb\x0b\xe1\
\x70\xfd\x70\xc0\xe0\x72\xe4\xf4\x12\x12\x0f\x08\xee\x40\xd8\xee\
\xac\x47\xc5\xc3\x07\x78\x21\x3d\xf1\x80\xa0\x01\x6a\xd2\xbd\xe8\
\x18\x2f\x84\xc3\x27\x97\xb0\x78\x40\xc8\xc0\xc1\x97\x73\x2b\x68\
\x63\x6e\x68\x70\x16\x60\xb5\xed\x48\x41\x3c\x20\x64\xc0\xeb\xe2\
\x59\x74\x8c\xcb\xe1\x49\xcb\x9d\x92\x78\x40\xc0\xc0\xd1\x56\xb9\
\x8c\x06\x5d\x55\xc7\x38\xc7\xe0\x5c\xff\xe9\xa7\x28\x1e\x10\x30\
\xa0\x15\x2f\xe1\xe4\xd4\x49\x59\x3c\x20\x60\x20\x5a\xbc\x5c\x62\
\x60\x16\x13\x11\x0f\x24\x34\xa0\x15\xef\xe0\xd8\x9c\x90\x78\x20\
\xa1\x81\x68\xf1\xf2\x42\x08\x78\x93\x13\x0f\x24\x30\xa0\x15\xef\
\x2c\xc0\xf3\x78\x37\x49\xf1\x40\x02\x03\x5a\xf1\x06\xfc\xb9\xb8\
\x5c\x7b\x25\xa2\xca\x02\x67\x03\x6a\xf1\x72\x81\x77\xf2\x77\xab\
\x2b\x32\x92\xec\x70\x32\xa0\x16\x2f\x17\x78\xa7\xf0\xa0\x7a\x5b\
\x54\x95\x05\x4e\x06\x06\xc5\x3b\x6d\xf1\x80\x43\x3b\x3d\x68\x9b\
\x39\xc7\xdf\xa7\x2d\x1e\x00\x32\xb6\x0b\xc2\x76\x67\x1d\x39\x9c\
\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\
\x0b\x02\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
" "
qt_resource_name = b"\ qt_resource_name = b"\
@ -195,6 +195,14 @@ 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\x0b\
\x0c\x81\x80\x07\
\x00\x65\
\x00\x78\x00\x65\x00\x63\x00\x75\x00\x74\x00\x65\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\x07\ \x00\x07\
\x04\xca\x57\xa7\ \x04\xca\x57\xa7\
\x00\x6e\ \x00\x6e\
@ -203,30 +211,47 @@ qt_resource_name = b"\
\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\
\x0c\x81\x80\x07\
\x00\x65\
\x00\x78\x00\x65\x00\x63\x00\x75\x00\x74\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\
" "
qt_resource_struct = b"\ qt_resource_struct_v1 = 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\x50\x00\x00\x00\x00\x00\x01\x00\x00\x05\xca\
\x00\x00\x00\x64\x00\x00\x00\x00\x00\x01\x00\x00\x07\xdc\
\x00\x00\x00\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x03\x93\
\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\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\
" "
qt_resource_struct_v2 = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x50\x00\x00\x00\x00\x00\x01\x00\x00\x05\xca\
\x00\x00\x01\x47\xc9\x98\x5f\x00\
\x00\x00\x00\x64\x00\x00\x00\x00\x00\x01\x00\x00\x07\xdc\
\x00\x00\x01\x47\xc9\x98\x5f\x00\
\x00\x00\x00\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x03\x93\
\x00\x00\x01\x47\xc9\x98\x5f\x00\
\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x47\xc9\x98\x5f\x00\
"
qt_version = [int(v) for v in QtCore.qVersion().split('.')]
if qt_version < [5, 8, 0]:
rcc_version = 1
qt_resource_struct = qt_resource_struct_v1
else:
rcc_version = 2
qt_resource_struct = qt_resource_struct_v2
def qInitResources(): def qInitResources():
QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
def qCleanupResources(): def qCleanupResources():
QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
qInitResources() qInitResources()

7
src/nailab/ui/mainwindow.py

@ -16,6 +16,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 ui.performancewidget import PerformanceWidget
from ui.settingswindow import SettingsWindow
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
@ -117,6 +118,12 @@ class MainWindow(QtWidgets.QMainWindow):
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 showSettingsDialog(self):
dlg = SettingsWindow(self)
dlg.exec()
if dlg.result() == QtWidgets.QDialog.Accepted:
dlg.saveSettings()
def performanceAnalysis(self): def performanceAnalysis(self):
self.ui.tabs.addTab(PerformanceWidget(), self.tr("Performance analysis")) self.ui.tabs.addTab(PerformanceWidget(), self.tr("Performance analysis"))

8
src/nailab/ui/newdatasourcedialog.py

@ -6,6 +6,7 @@ from ui_gen.newdatasourcedialog import Ui_NewDataSourceDialog
from ui.csvdatasourceconfigwidget import CSVDataSourceConfigWidget from ui.csvdatasourceconfigwidget import CSVDataSourceConfigWidget
from data.csvfolderdatasource import CsvFolderDataSource from data.csvfolderdatasource import CsvFolderDataSource
from data.yahoocsvfolderdatasource import YahooCsvFolderDataSource
class NewDataSourceDialog(QtWidgets.QDialog): class NewDataSourceDialog(QtWidgets.QDialog):
@ -19,6 +20,7 @@ class NewDataSourceDialog(QtWidgets.QDialog):
self.ui.widgets.setCurrentIndex(index) self.ui.widgets.setCurrentIndex(index)
def get_data_source(self): def get_data_source(self):
return CsvFolderDataSource(self.ui.e_sourceName.text(), self.ui.widgets.currentWidget().ui.e_path.text()) if self.ui.cb_sourceType.currentText() == "CSV":
return CsvFolderDataSource(self.ui.e_sourceName.text(), self.ui.widgets.currentWidget().ui.e_path.text())
else:
return YahooCsvFolderDataSource(self.ui.e_sourceName.text(), self.ui.widgets.currentWidget().ui.e_path.text())

26
src/nailab/ui/settingswindow.py

@ -0,0 +1,26 @@
from ui_gen.settingswindow import Ui_SettingsWindow
from PyQt5 import QtCore, QtGui, QtWidgets
class SettingsWindow(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_SettingsWindow()
self.ui.setupUi(self)
self.loadSettings()
def loadSettings(self):
s = QtCore.QSettings()
self.ui.sb_commissionPercentage.setValue(float(s.value("commission/percentage", 0)));
self.ui.sb_commissionPerShare.setValue(float(s.value("commission/per_share", 0)))
def saveSettings(self):
s = QtCore.QSettings()
s.setValue("commission/percentage", float(self.ui.sb_commissionPercentage.value()))
s.setValue("commission/per_share", float(self.ui.sb_commissionPerShare.value()))

117
src/nailab/ui/statisticwidget.py

@ -0,0 +1,117 @@
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qsci import *
from ui_gen.statisticwidget import Ui_StatisticWidget
import math
def calc_ratio(a, b):
if b != 0:
return a / b
else:
return 0
class StatisticWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_StatisticWidget()
self.ui.setupUi(self)
self.trades = []
def set_trades(self, trades):
self.trades = trades
self.update_statistic()
def update_statistic(self):
self.update_by_ticker()
self.update_seasonality()
def update_by_ticker(self):
self.ui.tw_byTicker.clear()
by_ticker = {}
for trade in self.trades:
try:
by_ticker[trade["security"]].append(trade)
except KeyError:
by_ticker[trade["security"]] = [trade]
for ticker, trades in by_ticker.items():
item = QtWidgets.QTreeWidgetItem(self.ui.tw_byTicker)
stats = self.calc_stats(trades)
item.setText(0, ticker)
item.setText(1, "{:.4f}".format(stats["net_profit"]))
item.setText(2, "{}".format(stats["total_trades"]))
item.setText(3, "{:.2f}%".format(stats["avg_percentage"]))
def calc_stats(self, trades):
net_profit = 0
sum_percentage = 0
for trade in trades:
sum_percentage += trade["profit_percentage"]
net_profit += trade["pnl"]
return { "net_profit" : net_profit,
"total_trades" : len(trades),
"avg_percentage" : calc_ratio(sum_percentage, len(trades)) }
def update_seasonality(self ):
self.ui.tw_seasonality.clear()
by_weekday = {}
by_monthday = {}
by_month = {}
for trade in self.trades:
try:
by_weekday[trade["entry_time"].date().isoweekday()].append(trade)
except KeyError:
by_weekday[trade["entry_time"].date().isoweekday()] = [trade]
try:
by_monthday[trade["entry_time"].date().day].append(trade)
except KeyError:
by_monthday[trade["entry_time"].date().day] = [trade]
try:
by_month[trade["entry_time"].date().month].append(trade)
except KeyError:
by_month[trade["entry_time"].date().month] = [trade]
item_by_weekday = QtWidgets.QTreeWidgetItem(self.ui.tw_seasonality)
item_by_weekday.setText(0, "By weekday")
for weekday, trades in sorted(by_weekday.items()):
item = QtWidgets.QTreeWidgetItem(item_by_weekday)
stats = self.calc_stats(trades)
item.setText(0, "{}".format(weekday))
item.setText(1, "{:.4f}".format(stats["net_profit"]))
item.setText(2, "{}".format(stats["total_trades"]))
item.setText(3, "{:.2f}%".format(stats["avg_percentage"]))
item_by_monthday = QtWidgets.QTreeWidgetItem(self.ui.tw_seasonality)
item_by_monthday.setText(0, "By day-of-month")
for monthday, trades in sorted(by_monthday.items()):
item = QtWidgets.QTreeWidgetItem(item_by_monthday)
stats = self.calc_stats(trades)
item.setText(0, "{:02d}".format(monthday))
item.setText(1, "{:.4f}".format(stats["net_profit"]))
item.setText(2, "{}".format(stats["total_trades"]))
item.setText(3, "{:.2f}%".format(stats["avg_percentage"]))
item_by_month = QtWidgets.QTreeWidgetItem(self.ui.tw_seasonality)
item_by_month.setText(0, "By month")
for month, trades in sorted(by_month.items()):
item = QtWidgets.QTreeWidgetItem(item_by_month)
stats = self.calc_stats(trades)
item.setText(0, "{:02d}".format(month))
item.setText(1, "{:.4f}".format(stats["net_profit"]))
item.setText(2, "{}".format(stats["total_trades"]))
item.setText(3, "{:.2f}%".format(stats["avg_percentage"]))

28
src/nailab/ui/strategywidget.py

@ -11,6 +11,7 @@ from ui_gen.strategywidget import Ui_StrategyWidget
from ui.newdatasourcedialog import NewDataSourceDialog from ui.newdatasourcedialog import NewDataSourceDialog
from ui.equitychartwidget import EquityChartWidget from ui.equitychartwidget import EquityChartWidget
from ui.tradeslistwidget import TradesListWidget from ui.tradeslistwidget import TradesListWidget
from ui.statisticwidget import StatisticWidget
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler from watchdog.events import FileSystemEventHandler
@ -76,6 +77,7 @@ class StrategyWidget(QtWidgets.QWidget):
self.result_widget = None self.result_widget = None
self.equity_widget = None self.equity_widget = None
self.trades_widget = None self.trades_widget = None
self.statistic_widget = None
self.watchdog_handler = FileModifiedHandler(self.file_modified) self.watchdog_handler = FileModifiedHandler(self.file_modified)
self.watchdog = None self.watchdog = None
@ -223,8 +225,12 @@ class StrategyWidget(QtWidgets.QWidget):
self.trades_widget = TradesListWidget(self) self.trades_widget = TradesListWidget(self)
self.ui.tabs.addTab(self.trades_widget, "Trades") self.ui.tabs.addTab(self.trades_widget, "Trades")
self.trades_widget.set_trades(self.result[1]) if self.statistic_widget is None:
self.statistic_widget = StatisticWidget(self)
self.ui.tabs.addTab(self.statistic_widget, "Statistic")
self.trades_widget.set_trades(self.result[1])
self.statistic_widget.set_trades(self.result[1])
def update_result(self): def update_result(self):
if self.result_widget is None: if self.result_widget is None:
@ -265,6 +271,12 @@ class StrategyWidget(QtWidgets.QWidget):
net_profit.setText(2, "{:.3f}".format(self.result[0]['short']['net_profit'])) net_profit.setText(2, "{:.3f}".format(self.result[0]['short']['net_profit']))
net_profit.setText(3, "{:.3f}".format(self.result[0]['all']['net_profit'])) net_profit.setText(3, "{:.3f}".format(self.result[0]['all']['net_profit']))
total_commission = QtWidgets.QTreeWidgetItem(self.result_widget)
total_commission.setText(0, "Total commission")
total_commission.setText(1, "{:.3f}".format(self.result[0]['long']['total_commission']))
total_commission.setText(2, "{:.3f}".format(self.result[0]['short']['total_commission']))
total_commission.setText(3, "{:.3f}".format(self.result[0]['all']['total_commission']))
total_won = QtWidgets.QTreeWidgetItem(self.result_widget) total_won = QtWidgets.QTreeWidgetItem(self.result_widget)
total_won.setText(0, "Total won") total_won.setText(0, "Total won")
total_won.setText(1, "{:.3f}".format(self.result[0]['long']['total_won'])) total_won.setText(1, "{:.3f}".format(self.result[0]['long']['total_won']))
@ -302,10 +314,10 @@ class StrategyWidget(QtWidgets.QWidget):
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 = QtWidgets.QTreeWidgetItem(self.result_widget)
sharpe_ratio.setText(0, "Sharpe ratio") sharpe_ratio.setText(0, "Z-score")
sharpe_ratio.setText(1, "{:.3f}".format(self.result[0]['long']['sharpe_ratio'])) sharpe_ratio.setText(1, "{:.3f}".format(self.result[0]['long']['z_score']))
sharpe_ratio.setText(2, "{:.3f}".format(self.result[0]['short']['sharpe_ratio'])) sharpe_ratio.setText(2, "{:.3f}".format(self.result[0]['short']['z_score']))
sharpe_ratio.setText(3, "{:.3f}".format(self.result[0]['all']['sharpe_ratio'])) sharpe_ratio.setText(3, "{:.3f}".format(self.result[0]['all']['z_score']))
t_stat = QtWidgets.QTreeWidgetItem(self.result_widget) t_stat = QtWidgets.QTreeWidgetItem(self.result_widget)
t_stat.setText(0, "t-statistics") t_stat.setText(0, "t-statistics")
@ -313,6 +325,12 @@ class StrategyWidget(QtWidgets.QWidget):
t_stat.setText(2, "{:.3f}".format(self.result[0]['short']['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'])) t_stat.setText(3, "{:.3f}".format(self.result[0]['all']['t_stat']))
kelly = QtWidgets.QTreeWidgetItem(self.result_widget)
kelly.setText(0, "Kelly ratio")
kelly.setText(1, "{:.1f}%".format(100. * self.result[0]['long']['kelly']))
kelly.setText(2, "{:.1f}%".format(100. * self.result[0]['short']['kelly']))
kelly.setText(3, "{:.1f}%".format(100. * self.result[0]['all']['kelly']))
self.result_widget.resizeColumnToContents(0) self.result_widget.resizeColumnToContents(0)
def setError(self, errmsg): def setError(self, errmsg):

8
src/nailab/ui_gen/csvdatasourceconfigwidget.py

@ -2,12 +2,15 @@
# Form implementation generated from reading ui file 'ui/csvdatasourceconfigwidget.ui' # Form implementation generated from reading ui file 'ui/csvdatasourceconfigwidget.ui'
# #
# Created by: PyQt5 UI code generator 5.5.1 # Created by: PyQt5 UI code generator 5.15.1
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_CSVDataSourceConfigWidget(object): class Ui_CSVDataSourceConfigWidget(object):
def setupUi(self, CSVDataSourceConfigWidget): def setupUi(self, CSVDataSourceConfigWidget):
CSVDataSourceConfigWidget.setObjectName("CSVDataSourceConfigWidget") CSVDataSourceConfigWidget.setObjectName("CSVDataSourceConfigWidget")
@ -45,4 +48,3 @@ class Ui_CSVDataSourceConfigWidget(object):
CSVDataSourceConfigWidget.setWindowTitle(_translate("CSVDataSourceConfigWidget", "Form")) CSVDataSourceConfigWidget.setWindowTitle(_translate("CSVDataSourceConfigWidget", "Form"))
self.label.setText(_translate("CSVDataSourceConfigWidget", "CSV folder path")) self.label.setText(_translate("CSVDataSourceConfigWidget", "CSV folder path"))
self.b_browse.setText(_translate("CSVDataSourceConfigWidget", "...")) self.b_browse.setText(_translate("CSVDataSourceConfigWidget", "..."))

8
src/nailab/ui_gen/equitychartwidget.py

@ -2,12 +2,15 @@
# Form implementation generated from reading ui file 'ui/equitychartwidget.ui' # Form implementation generated from reading ui file 'ui/equitychartwidget.ui'
# #
# Created by: PyQt5 UI code generator 5.5.1 # Created by: PyQt5 UI code generator 5.15.1
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_EquityChartWidget(object): class Ui_EquityChartWidget(object):
def setupUi(self, EquityChartWidget): def setupUi(self, EquityChartWidget):
EquityChartWidget.setObjectName("EquityChartWidget") EquityChartWidget.setObjectName("EquityChartWidget")
@ -24,5 +27,4 @@ class Ui_EquityChartWidget(object):
def retranslateUi(self, EquityChartWidget): def retranslateUi(self, EquityChartWidget):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
EquityChartWidget.setWindowTitle(_translate("EquityChartWidget", "Form")) EquityChartWidget.setWindowTitle(_translate("EquityChartWidget", "Form"))
from ui.equitychartcanvas import EquityChartCanvas from ui.equitychartcanvas import EquityChartCanvas

17
src/nailab/ui_gen/mainwindow.py

@ -2,16 +2,19 @@
# Form implementation generated from reading ui file 'ui/mainwindow.ui' # Form implementation generated from reading ui file 'ui/mainwindow.ui'
# #
# Created by: PyQt5 UI code generator 5.5.1 # Created by: PyQt5 UI code generator 5.15.1
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object): class Ui_MainWindow(object):
def setupUi(self, MainWindow): def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow") MainWindow.setObjectName("MainWindow")
MainWindow.resize(1060, 587) MainWindow.resize(1060, 588)
self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget") self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
@ -23,7 +26,7 @@ class Ui_MainWindow(object):
self.gridLayout.addWidget(self.tabs, 0, 0, 1, 1) self.gridLayout.addWidget(self.tabs, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1060, 19)) self.menubar.setGeometry(QtCore.QRect(0, 0, 1060, 21))
self.menubar.setObjectName("menubar") self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar) self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile") self.menuFile.setObjectName("menuFile")
@ -69,6 +72,8 @@ class Ui_MainWindow(object):
self.actionExecute_in_Portfolio_Mode = QtWidgets.QAction(MainWindow) self.actionExecute_in_Portfolio_Mode = QtWidgets.QAction(MainWindow)
self.actionExecute_in_Portfolio_Mode.setIcon(icon2) self.actionExecute_in_Portfolio_Mode.setIcon(icon2)
self.actionExecute_in_Portfolio_Mode.setObjectName("actionExecute_in_Portfolio_Mode") self.actionExecute_in_Portfolio_Mode.setObjectName("actionExecute_in_Portfolio_Mode")
self.actionSettings = QtWidgets.QAction(MainWindow)
self.actionSettings.setObjectName("actionSettings")
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)
@ -85,6 +90,7 @@ class Ui_MainWindow(object):
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.toolBar.addAction(self.actionExecute_in_Portfolio_Mode)
self.toolBar.addAction(self.actionSettings)
self.retranslateUi(MainWindow) self.retranslateUi(MainWindow)
self.tabs.setCurrentIndex(-1) self.tabs.setCurrentIndex(-1)
@ -96,6 +102,7 @@ class Ui_MainWindow(object):
self.actionSave_as.triggered.connect(MainWindow.saveStrategyAs) self.actionSave_as.triggered.connect(MainWindow.saveStrategyAs)
self.actionPerformance.triggered.connect(MainWindow.performanceAnalysis) self.actionPerformance.triggered.connect(MainWindow.performanceAnalysis)
self.actionExecute_in_Portfolio_Mode.triggered.connect(MainWindow.executeStrategyInPortfolioMode) self.actionExecute_in_Portfolio_Mode.triggered.connect(MainWindow.executeStrategyInPortfolioMode)
self.actionSettings.triggered.connect(MainWindow.showSettingsDialog)
QtCore.QMetaObject.connectSlotsByName(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow): def retranslateUi(self, MainWindow):
@ -117,5 +124,5 @@ class Ui_MainWindow(object):
self.actionSave_as.setText(_translate("MainWindow", "Save as...")) self.actionSave_as.setText(_translate("MainWindow", "Save as..."))
self.actionPerformance.setText(_translate("MainWindow", "Performance")) self.actionPerformance.setText(_translate("MainWindow", "Performance"))
self.actionExecute_in_Portfolio_Mode.setText(_translate("MainWindow", "Execute in Portfolio Mode")) self.actionExecute_in_Portfolio_Mode.setText(_translate("MainWindow", "Execute in Portfolio Mode"))
self.actionSettings.setText(_translate("MainWindow", "Settings"))
import nailab_rc import nailab_rc

10
src/nailab/ui_gen/newdatasourcedialog.py

@ -2,12 +2,15 @@
# Form implementation generated from reading ui file 'ui/newdatasourcedialog.ui' # Form implementation generated from reading ui file 'ui/newdatasourcedialog.ui'
# #
# Created by: PyQt5 UI code generator 5.5.1 # Created by: PyQt5 UI code generator 5.15.1
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_NewDataSourceDialog(object): class Ui_NewDataSourceDialog(object):
def setupUi(self, NewDataSourceDialog): def setupUi(self, NewDataSourceDialog):
NewDataSourceDialog.setObjectName("NewDataSourceDialog") NewDataSourceDialog.setObjectName("NewDataSourceDialog")
@ -36,6 +39,7 @@ class Ui_NewDataSourceDialog(object):
self.cb_sourceType.setSizePolicy(sizePolicy) self.cb_sourceType.setSizePolicy(sizePolicy)
self.cb_sourceType.setObjectName("cb_sourceType") self.cb_sourceType.setObjectName("cb_sourceType")
self.cb_sourceType.addItem("") self.cb_sourceType.addItem("")
self.cb_sourceType.addItem("")
self.gridLayout.addWidget(self.cb_sourceType, 1, 1, 1, 1) self.gridLayout.addWidget(self.cb_sourceType, 1, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(NewDataSourceDialog) self.label_2 = QtWidgets.QLabel(NewDataSourceDialog)
self.label_2.setObjectName("label_2") self.label_2.setObjectName("label_2")
@ -56,5 +60,5 @@ class Ui_NewDataSourceDialog(object):
NewDataSourceDialog.setWindowTitle(_translate("NewDataSourceDialog", "New data source")) NewDataSourceDialog.setWindowTitle(_translate("NewDataSourceDialog", "New data source"))
self.label.setText(_translate("NewDataSourceDialog", "Data source type")) self.label.setText(_translate("NewDataSourceDialog", "Data source type"))
self.cb_sourceType.setItemText(0, _translate("NewDataSourceDialog", "CSV")) self.cb_sourceType.setItemText(0, _translate("NewDataSourceDialog", "CSV"))
self.cb_sourceType.setItemText(1, _translate("NewDataSourceDialog", "YahooCSV"))
self.label_2.setText(_translate("NewDataSourceDialog", "Data source name")) self.label_2.setText(_translate("NewDataSourceDialog", "Data source name"))

10
src/nailab/ui_gen/performancewidget.py

@ -2,12 +2,15 @@
# Form implementation generated from reading ui file 'ui/performancewidget.ui' # Form implementation generated from reading ui file 'ui/performancewidget.ui'
# #
# Created by: PyQt5 UI code generator 5.5.1 # Created by: PyQt5 UI code generator 5.15.1
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_PerformanceWidget(object): class Ui_PerformanceWidget(object):
def setupUi(self, PerformanceWidget): def setupUi(self, PerformanceWidget):
PerformanceWidget.setObjectName("PerformanceWidget") PerformanceWidget.setObjectName("PerformanceWidget")
@ -77,7 +80,7 @@ class Ui_PerformanceWidget(object):
self.gridLayout.addWidget(self.lw_strategies, 0, 0, 1, 2) self.gridLayout.addWidget(self.lw_strategies, 0, 0, 1, 2)
self.retranslateUi(PerformanceWidget) self.retranslateUi(PerformanceWidget)
self.tab1.setCurrentIndex(1) self.tab1.setCurrentIndex(0)
self.b_add.clicked.connect(PerformanceWidget.addResults) self.b_add.clicked.connect(PerformanceWidget.addResults)
self.b_remove.clicked.connect(PerformanceWidget.removeResults) self.b_remove.clicked.connect(PerformanceWidget.removeResults)
self.rb_daily.toggled['bool'].connect(PerformanceWidget.dailyToggled) self.rb_daily.toggled['bool'].connect(PerformanceWidget.dailyToggled)
@ -95,6 +98,5 @@ class Ui_PerformanceWidget(object):
self.tab1.setTabText(self.tab1.indexOf(self.tab_2), _translate("PerformanceWidget", "Correlations")) self.tab1.setTabText(self.tab1.indexOf(self.tab_2), _translate("PerformanceWidget", "Correlations"))
self.b_add.setText(_translate("PerformanceWidget", "Add...")) self.b_add.setText(_translate("PerformanceWidget", "Add..."))
self.b_remove.setText(_translate("PerformanceWidget", "Remove")) self.b_remove.setText(_translate("PerformanceWidget", "Remove"))
from ui.correlationchartcanvas import CorrelationChartCanvas from ui.correlationchartcanvas import CorrelationChartCanvas
from ui.performancecanvas import PerformanceCanvas from ui.performancecanvas import PerformanceCanvas

51
src/nailab/ui_gen/settingswindow.py

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui/settingswindow.ui'
#
# Created by: PyQt5 UI code generator 5.15.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_SettingsWindow(object):
def setupUi(self, SettingsWindow):
SettingsWindow.setObjectName("SettingsWindow")
SettingsWindow.resize(400, 300)
self.gridLayout = QtWidgets.QGridLayout(SettingsWindow)
self.gridLayout.setObjectName("gridLayout")
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout.addItem(spacerItem, 2, 0, 1, 1)
self.sb_commissionPercentage = QtWidgets.QDoubleSpinBox(SettingsWindow)
self.sb_commissionPercentage.setDecimals(4)
self.sb_commissionPercentage.setObjectName("sb_commissionPercentage")
self.gridLayout.addWidget(self.sb_commissionPercentage, 0, 1, 1, 1)
self.label = QtWidgets.QLabel(SettingsWindow)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.buttonBox = QtWidgets.QDialogButtonBox(SettingsWindow)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 2)
self.label_2 = QtWidgets.QLabel(SettingsWindow)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
self.sb_commissionPerShare = QtWidgets.QDoubleSpinBox(SettingsWindow)
self.sb_commissionPerShare.setDecimals(4)
self.sb_commissionPerShare.setObjectName("sb_commissionPerShare")
self.gridLayout.addWidget(self.sb_commissionPerShare, 1, 1, 1, 1)
self.retranslateUi(SettingsWindow)
self.buttonBox.accepted.connect(SettingsWindow.accept)
self.buttonBox.rejected.connect(SettingsWindow.reject)
QtCore.QMetaObject.connectSlotsByName(SettingsWindow)
def retranslateUi(self, SettingsWindow):
_translate = QtCore.QCoreApplication.translate
SettingsWindow.setWindowTitle(_translate("SettingsWindow", "Settings"))
self.label.setText(_translate("SettingsWindow", "Commission (%)"))
self.label_2.setText(_translate("SettingsWindow", "Commission (per share)"))

62
src/nailab/ui_gen/statisticwidget.py

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui/statisticwidget.ui'
#
# Created by: PyQt5 UI code generator 5.15.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_StatisticWidget(object):
def setupUi(self, StatisticWidget):
StatisticWidget.setObjectName("StatisticWidget")
StatisticWidget.resize(548, 418)
self.gridLayout = QtWidgets.QGridLayout(StatisticWidget)
self.gridLayout.setContentsMargins(1, 1, 1, 1)
self.gridLayout.setObjectName("gridLayout")
self.tabWidget = QtWidgets.QTabWidget(StatisticWidget)
self.tabWidget.setObjectName("tabWidget")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.gridLayout_2 = QtWidgets.QGridLayout(self.tab)
self.gridLayout_2.setObjectName("gridLayout_2")
self.tw_byTicker = QtWidgets.QTreeWidget(self.tab)
self.tw_byTicker.setObjectName("tw_byTicker")
self.gridLayout_2.addWidget(self.tw_byTicker, 0, 0, 1, 1)
self.tabWidget.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.tw_seasonality = QtWidgets.QTreeWidget(self.tab_2)
self.tw_seasonality.setObjectName("tw_seasonality")
self.gridLayout_3.addWidget(self.tw_seasonality, 0, 0, 1, 1)
self.tabWidget.addTab(self.tab_2, "")
self.tab_3 = QtWidgets.QWidget()
self.tab_3.setObjectName("tab_3")
self.tabWidget.addTab(self.tab_3, "")
self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1)
self.retranslateUi(StatisticWidget)
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(StatisticWidget)
def retranslateUi(self, StatisticWidget):
_translate = QtCore.QCoreApplication.translate
StatisticWidget.setWindowTitle(_translate("StatisticWidget", "StatisticWidget"))
self.tw_byTicker.setSortingEnabled(True)
self.tw_byTicker.headerItem().setText(0, _translate("StatisticWidget", "Ticker"))
self.tw_byTicker.headerItem().setText(1, _translate("StatisticWidget", "Net profit"))
self.tw_byTicker.headerItem().setText(2, _translate("StatisticWidget", "Total trades"))
self.tw_byTicker.headerItem().setText(3, _translate("StatisticWidget", "Avg. return"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("StatisticWidget", "By ticker"))
self.tw_seasonality.setSortingEnabled(True)
self.tw_seasonality.headerItem().setText(1, _translate("StatisticWidget", "Net profit"))
self.tw_seasonality.headerItem().setText(2, _translate("StatisticWidget", "Total trades"))
self.tw_seasonality.headerItem().setText(3, _translate("StatisticWidget", "Avg. return"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("StatisticWidget", "Seasonal"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("StatisticWidget", "Returns"))

29
src/nailab/ui_gen/strategywidget.py

@ -2,16 +2,19 @@
# Form implementation generated from reading ui file 'ui/strategywidget.ui' # Form implementation generated from reading ui file 'ui/strategywidget.ui'
# #
# Created by: PyQt5 UI code generator 5.5.1 # Created by: PyQt5 UI code generator 5.15.1
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_StrategyWidget(object): class Ui_StrategyWidget(object):
def setupUi(self, StrategyWidget): def setupUi(self, StrategyWidget):
StrategyWidget.setObjectName("StrategyWidget") StrategyWidget.setObjectName("StrategyWidget")
StrategyWidget.resize(977, 569) StrategyWidget.resize(977, 666)
self.gridLayout_3 = QtWidgets.QGridLayout(StrategyWidget) self.gridLayout_3 = QtWidgets.QGridLayout(StrategyWidget)
self.gridLayout_3.setContentsMargins(1, 1, 1, 1) self.gridLayout_3.setContentsMargins(1, 1, 1, 1)
self.gridLayout_3.setObjectName("gridLayout_3") self.gridLayout_3.setObjectName("gridLayout_3")
@ -28,6 +31,7 @@ class Ui_StrategyWidget(object):
self.gridLayoutWidget = QtWidgets.QWidget(self.splitter) self.gridLayoutWidget = QtWidgets.QWidget(self.splitter)
self.gridLayoutWidget.setObjectName("gridLayoutWidget") self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.gridLayout_2 = QtWidgets.QGridLayout(self.gridLayoutWidget) self.gridLayout_2 = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.gridLayout_2.setContentsMargins(2, 2, 2, 2)
self.gridLayout_2.setObjectName("gridLayout_2") self.gridLayout_2.setObjectName("gridLayout_2")
self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout") self.horizontalLayout.setObjectName("horizontalLayout")
@ -44,16 +48,16 @@ class Ui_StrategyWidget(object):
self.b_deleteSource = QtWidgets.QPushButton(self.gridLayoutWidget) self.b_deleteSource = QtWidgets.QPushButton(self.gridLayoutWidget)
self.b_deleteSource.setObjectName("b_deleteSource") self.b_deleteSource.setObjectName("b_deleteSource")
self.horizontalLayout.addWidget(self.b_deleteSource) self.horizontalLayout.addWidget(self.b_deleteSource)
self.gridLayout_2.addLayout(self.horizontalLayout, 5, 0, 1, 2) self.gridLayout_2.addLayout(self.horizontalLayout, 7, 0, 1, 2)
self.dte_to = QtWidgets.QDateTimeEdit(self.gridLayoutWidget)
self.dte_to.setObjectName("dte_to")
self.gridLayout_2.addWidget(self.dte_to, 3, 1, 1, 1)
self.dte_from = QtWidgets.QDateTimeEdit(self.gridLayoutWidget) self.dte_from = QtWidgets.QDateTimeEdit(self.gridLayoutWidget)
self.dte_from.setObjectName("dte_from") self.dte_from.setObjectName("dte_from")
self.gridLayout_2.addWidget(self.dte_from, 2, 1, 1, 1) self.gridLayout_2.addWidget(self.dte_from, 4, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(self.gridLayoutWidget) self.label_2 = QtWidgets.QLabel(self.gridLayoutWidget)
self.label_2.setObjectName("label_2") self.label_2.setObjectName("label_2")
self.gridLayout_2.addWidget(self.label_2, 3, 0, 1, 1) self.gridLayout_2.addWidget(self.label_2, 5, 0, 1, 1)
self.dte_to = QtWidgets.QDateTimeEdit(self.gridLayoutWidget)
self.dte_to.setObjectName("dte_to")
self.gridLayout_2.addWidget(self.dte_to, 5, 1, 1, 1)
self.rb_allData = QtWidgets.QRadioButton(self.gridLayoutWidget) self.rb_allData = QtWidgets.QRadioButton(self.gridLayoutWidget)
self.rb_allData.setChecked(True) self.rb_allData.setChecked(True)
self.rb_allData.setObjectName("rb_allData") self.rb_allData.setObjectName("rb_allData")
@ -69,7 +73,7 @@ class Ui_StrategyWidget(object):
self.tw_feeds.setHeaderHidden(True) self.tw_feeds.setHeaderHidden(True)
self.tw_feeds.setObjectName("tw_feeds") self.tw_feeds.setObjectName("tw_feeds")
self.tw_feeds.headerItem().setText(0, "1") self.tw_feeds.headerItem().setText(0, "1")
self.gridLayout_2.addWidget(self.tw_feeds, 4, 0, 1, 2) self.gridLayout_2.addWidget(self.tw_feeds, 6, 0, 1, 2)
self.label = QtWidgets.QLabel(self.gridLayoutWidget) self.label = QtWidgets.QLabel(self.gridLayoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
@ -77,7 +81,7 @@ class Ui_StrategyWidget(object):
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy) self.label.setSizePolicy(sizePolicy)
self.label.setObjectName("label") self.label.setObjectName("label")
self.gridLayout_2.addWidget(self.label, 2, 0, 1, 1) self.gridLayout_2.addWidget(self.label, 4, 0, 1, 1)
self.rb_timeWindow = QtWidgets.QRadioButton(self.gridLayoutWidget) self.rb_timeWindow = QtWidgets.QRadioButton(self.gridLayoutWidget)
self.rb_timeWindow.setObjectName("rb_timeWindow") self.rb_timeWindow.setObjectName("rb_timeWindow")
self.gridLayout_2.addWidget(self.rb_timeWindow, 1, 0, 1, 2) self.gridLayout_2.addWidget(self.rb_timeWindow, 1, 0, 1, 2)
@ -111,12 +115,11 @@ class Ui_StrategyWidget(object):
self.b_addSource.setText(_translate("StrategyWidget", "Add source")) self.b_addSource.setText(_translate("StrategyWidget", "Add source"))
self.b_refresh.setText(_translate("StrategyWidget", "Refresh")) self.b_refresh.setText(_translate("StrategyWidget", "Refresh"))
self.b_deleteSource.setText(_translate("StrategyWidget", "Delete source")) self.b_deleteSource.setText(_translate("StrategyWidget", "Delete source"))
self.dte_to.setDisplayFormat(_translate("StrategyWidget", "dd.MM.yyyy H:mm"))
self.dte_from.setDisplayFormat(_translate("StrategyWidget", "dd.MM.yyyy H:mm")) self.dte_from.setDisplayFormat(_translate("StrategyWidget", "dd.MM.yyyy H:mm"))
self.label_2.setText(_translate("StrategyWidget", "To")) self.label_2.setText(_translate("StrategyWidget", "To"))
self.dte_to.setDisplayFormat(_translate("StrategyWidget", "dd.MM.yyyy H:mm"))
self.rb_allData.setText(_translate("StrategyWidget", "All data")) self.rb_allData.setText(_translate("StrategyWidget", "All data"))
self.label.setText(_translate("StrategyWidget", "From")) self.label.setText(_translate("StrategyWidget", "From"))
self.rb_timeWindow.setText(_translate("StrategyWidget", "Time window")) self.rb_timeWindow.setText(_translate("StrategyWidget", "Time window"))
self.tabs.setTabText(self.tabs.indexOf(self.tab), _translate("StrategyWidget", "Code")) self.tabs.setTabText(self.tabs.indexOf(self.tab), _translate("StrategyWidget", "Code"))
from PyQt5 import Qsci from PyQt5 import Qsci

8
src/nailab/ui_gen/tradeslistwidget.py

@ -2,12 +2,15 @@
# Form implementation generated from reading ui file 'ui/tradeslistwidget.ui' # Form implementation generated from reading ui file 'ui/tradeslistwidget.ui'
# #
# Created by: PyQt5 UI code generator 5.5.1 # Created by: PyQt5 UI code generator 5.15.1
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_TradesListWidget(object): class Ui_TradesListWidget(object):
def setupUi(self, TradesListWidget): def setupUi(self, TradesListWidget):
TradesListWidget.setObjectName("TradesListWidget") TradesListWidget.setObjectName("TradesListWidget")
@ -40,4 +43,3 @@ class Ui_TradesListWidget(object):
self.trades.headerItem().setText(5, _translate("TradesListWidget", "Exit time")) self.trades.headerItem().setText(5, _translate("TradesListWidget", "Exit time"))
self.trades.headerItem().setText(6, _translate("TradesListWidget", "Exit price")) self.trades.headerItem().setText(6, _translate("TradesListWidget", "Exit price"))
self.trades.headerItem().setText(7, _translate("TradesListWidget", "PnL")) self.trades.headerItem().setText(7, _translate("TradesListWidget", "PnL"))

27
ui/mainwindow.ui

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1060</width> <width>1060</width>
<height>587</height> <height>588</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -45,7 +45,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1060</width> <width>1060</width>
<height>19</height> <height>21</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
@ -91,6 +91,7 @@
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionExecute"/> <addaction name="actionExecute"/>
<addaction name="actionExecute_in_Portfolio_Mode"/> <addaction name="actionExecute_in_Portfolio_Mode"/>
<addaction name="actionSettings"/>
</widget> </widget>
<action name="actionOpenTrades"> <action name="actionOpenTrades">
<property name="text"> <property name="text">
@ -166,6 +167,11 @@
<string>Execute in Portfolio Mode</string> <string>Execute in Portfolio Mode</string>
</property> </property>
</action> </action>
<action name="actionSettings">
<property name="text">
<string>Settings</string>
</property>
</action>
</widget> </widget>
<resources> <resources>
<include location="../nailab.qrc"/> <include location="../nailab.qrc"/>
@ -299,6 +305,22 @@
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>actionSettings</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>showSettingsDialog()</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>
@ -310,5 +332,6 @@
<slot>saveStrategyAs()</slot> <slot>saveStrategyAs()</slot>
<slot>performanceAnalysis()</slot> <slot>performanceAnalysis()</slot>
<slot>executeStrategyInPortfolioMode()</slot> <slot>executeStrategyInPortfolioMode()</slot>
<slot>showSettingsDialog()</slot>
</slots> </slots>
</ui> </ui>

5
ui/newdatasourcedialog.ui

@ -50,6 +50,11 @@
<string>CSV</string> <string>CSV</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>YahooCSV</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="0" column="0">

2
ui/performancewidget.ui

@ -29,7 +29,7 @@
<item row="0" column="2"> <item row="0" column="2">
<widget class="QTabWidget" name="tab1"> <widget class="QTabWidget" name="tab1">
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="tab"> <widget class="QWidget" name="tab">
<attribute name="title"> <attribute name="title">

105
ui/settingswindow.ui

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsWindow</class>
<widget class="QDialog" name="SettingsWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="sb_commissionPercentage">
<property name="decimals">
<number>4</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Commission (%)</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Commission (per share)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="sb_commissionPerShare">
<property name="decimals">
<number>4</number>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>SettingsWindow</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SettingsWindow</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

113
ui/statisticwidget.ui

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StatisticWidget</class>
<widget class="QWidget" name="StatisticWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>548</width>
<height>418</height>
</rect>
</property>
<property name="windowTitle">
<string>StatisticWidget</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<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">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>By ticker</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QTreeWidget" name="tw_byTicker">
<property name="sortingEnabled">
<bool>true</bool>
</property>
<column>
<property name="text">
<string>Ticker</string>
</property>
</column>
<column>
<property name="text">
<string>Net profit</string>
</property>
</column>
<column>
<property name="text">
<string>Total trades</string>
</property>
</column>
<column>
<property name="text">
<string>Avg. return</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Seasonal</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QTreeWidget" name="tw_seasonality">
<property name="sortingEnabled">
<bool>true</bool>
</property>
<column>
<property name="text">
<string/>
</property>
</column>
<column>
<property name="text">
<string>Net profit</string>
</property>
</column>
<column>
<property name="text">
<string>Total trades</string>
</property>
</column>
<column>
<property name="text">
<string>Avg. return</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Returns</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

38
ui/strategywidget.ui

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>977</width> <width>977</width>
<height>569</height> <height>666</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -55,7 +55,19 @@
</property> </property>
<widget class="QWidget" name="gridLayoutWidget"> <widget class="QWidget" name="gridLayoutWidget">
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<item row="5" column="0" colspan="2"> <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="7" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QPushButton" name="b_addSource"> <widget class="QPushButton" name="b_addSource">
@ -106,27 +118,27 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="3" column="1"> <item row="4" column="1">
<widget class="QDateTimeEdit" name="dte_to">
<property name="displayFormat">
<string>dd.MM.yyyy H:mm</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDateTimeEdit" name="dte_from"> <widget class="QDateTimeEdit" name="dte_from">
<property name="displayFormat"> <property name="displayFormat">
<string>dd.MM.yyyy H:mm</string> <string>dd.MM.yyyy H:mm</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="5" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>To</string> <string>To</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1">
<widget class="QDateTimeEdit" name="dte_to">
<property name="displayFormat">
<string>dd.MM.yyyy H:mm</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2"> <item row="0" column="0" colspan="2">
<widget class="QRadioButton" name="rb_allData"> <widget class="QRadioButton" name="rb_allData">
<property name="text"> <property name="text">
@ -137,7 +149,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0" colspan="2"> <item row="6" column="0" colspan="2">
<widget class="QTreeWidget" name="tw_feeds"> <widget class="QTreeWidget" name="tw_feeds">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -161,7 +173,7 @@
</column> </column>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="4" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred">

Loading…
Cancel
Save