diff --git a/build_ui.sh b/build_ui.sh
index 2bf8775..de421d9 100755
--- a/build_ui.sh
+++ b/build_ui.sh
@@ -1,13 +1,15 @@
#!/bin/bash
-/usr/bin/pyuic5 ui/csvdatasourceconfigwidget.ui > src/nailab/ui_gen/csvdatasourceconfigwidget.py
-/usr/bin/pyuic5 ui/equitychartwidget.ui > src/nailab/ui_gen/equitychartwidget.py
-/usr/bin/pyuic5 ui/mainwindow.ui > src/nailab/ui_gen/mainwindow.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/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
+pyuic5 ui/csvdatasourceconfigwidget.ui > src/nailab/ui_gen/csvdatasourceconfigwidget.py
+pyuic5 ui/equitychartwidget.ui > src/nailab/ui_gen/equitychartwidget.py
+pyuic5 ui/mainwindow.ui > src/nailab/ui_gen/mainwindow.py
+pyuic5 ui/newdatasourcedialog.ui > src/nailab/ui_gen/newdatasourcedialog.py
+pyuic5 ui/strategywidget.ui > src/nailab/ui_gen/strategywidget.py
+pyuic5 ui/tradeslistwidget.ui > src/nailab/ui_gen/tradeslistwidget.py
+pyuic5 ui/performancewidget.ui > src/nailab/ui_gen/performancewidget.py
+pyuic5 ui/settingswindow.ui > src/nailab/ui_gen/settingswindow.py
+pyuic5 ui/statisticwidget.ui > src/nailab/ui_gen/statisticwidget.py
+
+pyrcc5 nailab.qrc -o src/nailab/nailab_rc.py
diff --git a/src/nailab/data/datasourcemanager.py b/src/nailab/data/datasourcemanager.py
index 6d4819f..fa93499 100644
--- a/src/nailab/data/datasourcemanager.py
+++ b/src/nailab/data/datasourcemanager.py
@@ -1,5 +1,6 @@
from data.csvfolderdatasource import CsvFolderDataSource
+from data.yahoocsvfolderdatasource import YahooCsvFolderDataSource
class DataSourceManager:
@@ -19,6 +20,8 @@ class DataSourceManager:
for s in serialized:
if s[0] == "csv":
self.add_source(CsvFolderDataSource.deserialize(s[1]))
+ if s[0] == "yahoo_csv":
+ self.add_source(YahooCsvFolderDataSource.deserialize(s[1]))
def add_source(self, source):
self.sources.append(source)
diff --git a/src/nailab/data/yahoocsvfolderdatasource.py b/src/nailab/data/yahoocsvfolderdatasource.py
new file mode 100644
index 0000000..60b3e40
--- /dev/null
+++ b/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]
+
diff --git a/src/nailab/execution/executor.py b/src/nailab/execution/executor.py
index 36079d2..990996d 100644
--- a/src/nailab/execution/executor.py
+++ b/src/nailab/execution/executor.py
@@ -3,6 +3,7 @@ import sys
import importlib
from importlib.machinery import SourceFileLoader
import inspect
+from PyQt5 import QtCore
from naiback.strategy import Strategy
@@ -23,6 +24,9 @@ class Executor:
for feed in feeds:
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:
strategy.run()
else:
@@ -34,3 +38,9 @@ class Executor:
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)))
+
diff --git a/src/nailab/execution/portfolioexecutor.py b/src/nailab/execution/portfolioexecutor.py
index 7f7ceab..b2b6055 100644
--- a/src/nailab/execution/portfolioexecutor.py
+++ b/src/nailab/execution/portfolioexecutor.py
@@ -54,18 +54,28 @@ class PortfolioExecutor:
all_trades = list(sorted(trades1 + trades2, key=lambda x: x['entry_time']))
result = []
current_trades = []
+ # TODO customize
+ size_per_position = 1000000
+ cash = 1000000
max_trades = 3
for trade in all_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)
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
+ cash += ct['size'] * ct['exit_price']
current_trades = new_current_trades
return result
@@ -90,6 +100,10 @@ class PortfolioExecutor:
result['long']['number_of_trades'] = len(longs)
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['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'])
@@ -126,22 +140,25 @@ class PortfolioExecutor:
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']['z_score'] = sharpe
result['all']['t_stat'] = tstat
+ result['all']['kelly'] = 0
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']['z_score'] = sharpe
result['long']['t_stat'] = tstat
+ result['long']['kelly'] = 0
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']['z_score'] = sharpe
result['short']['t_stat'] = tstat
+ result['short']['kelly'] = 0
return result
diff --git a/src/nailab/nailab_rc.py b/src/nailab/nailab_rc.py
index 7ca94cf..c7fb1af 100644
--- a/src/nailab/nailab_rc.py
+++ b/src/nailab/nailab_rc.py
@@ -2,13 +2,110 @@
# 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!
from PyQt5 import QtCore
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\
\x89\
\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\
\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\
-\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"\
@@ -195,6 +195,14 @@ qt_resource_name = b"\
\x00\x6f\xa6\x53\
\x00\x69\
\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\
\x04\xca\x57\xa7\
\x00\x6e\
@@ -203,30 +211,47 @@ qt_resource_name = b"\
\x06\xc1\x59\x87\
\x00\x6f\
\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\x02\
\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\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():
- 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():
- 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()
diff --git a/src/nailab/ui/mainwindow.py b/src/nailab/ui/mainwindow.py
index d2fabfb..33591c8 100644
--- a/src/nailab/ui/mainwindow.py
+++ b/src/nailab/ui/mainwindow.py
@@ -16,6 +16,7 @@ from PyQt5.Qsci import *
from ui_gen.mainwindow import Ui_MainWindow
from ui.strategywidget import StrategyWidget
from ui.performancewidget import PerformanceWidget
+from ui.settingswindow import SettingsWindow
from naiback.strategy import Strategy
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.removeTab(tab_index)
+ def showSettingsDialog(self):
+ dlg = SettingsWindow(self)
+ dlg.exec()
+ if dlg.result() == QtWidgets.QDialog.Accepted:
+ dlg.saveSettings()
+
def performanceAnalysis(self):
self.ui.tabs.addTab(PerformanceWidget(), self.tr("Performance analysis"))
diff --git a/src/nailab/ui/newdatasourcedialog.py b/src/nailab/ui/newdatasourcedialog.py
index d55ad2e..ec54d35 100644
--- a/src/nailab/ui/newdatasourcedialog.py
+++ b/src/nailab/ui/newdatasourcedialog.py
@@ -6,6 +6,7 @@ from ui_gen.newdatasourcedialog import Ui_NewDataSourceDialog
from ui.csvdatasourceconfigwidget import CSVDataSourceConfigWidget
from data.csvfolderdatasource import CsvFolderDataSource
+from data.yahoocsvfolderdatasource import YahooCsvFolderDataSource
class NewDataSourceDialog(QtWidgets.QDialog):
@@ -19,6 +20,7 @@ class NewDataSourceDialog(QtWidgets.QDialog):
self.ui.widgets.setCurrentIndex(index)
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())
diff --git a/src/nailab/ui/settingswindow.py b/src/nailab/ui/settingswindow.py
new file mode 100644
index 0000000..d2bc90e
--- /dev/null
+++ b/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()))
diff --git a/src/nailab/ui/statisticwidget.py b/src/nailab/ui/statisticwidget.py
new file mode 100644
index 0000000..5a988ac
--- /dev/null
+++ b/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"]))
+
diff --git a/src/nailab/ui/strategywidget.py b/src/nailab/ui/strategywidget.py
index 0ab0af7..1d30f19 100644
--- a/src/nailab/ui/strategywidget.py
+++ b/src/nailab/ui/strategywidget.py
@@ -11,6 +11,7 @@ from ui_gen.strategywidget import Ui_StrategyWidget
from ui.newdatasourcedialog import NewDataSourceDialog
from ui.equitychartwidget import EquityChartWidget
from ui.tradeslistwidget import TradesListWidget
+from ui.statisticwidget import StatisticWidget
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
@@ -76,6 +77,7 @@ class StrategyWidget(QtWidgets.QWidget):
self.result_widget = None
self.equity_widget = None
self.trades_widget = None
+ self.statistic_widget = None
self.watchdog_handler = FileModifiedHandler(self.file_modified)
self.watchdog = None
@@ -223,8 +225,12 @@ class StrategyWidget(QtWidgets.QWidget):
self.trades_widget = TradesListWidget(self)
self.ui.tabs.addTab(self.trades_widget, "Trades")
+ 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):
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(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.setText(0, "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']))
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']))
+ sharpe_ratio.setText(0, "Z-score")
+ sharpe_ratio.setText(1, "{:.3f}".format(self.result[0]['long']['z_score']))
+ sharpe_ratio.setText(2, "{:.3f}".format(self.result[0]['short']['z_score']))
+ sharpe_ratio.setText(3, "{:.3f}".format(self.result[0]['all']['z_score']))
t_stat = QtWidgets.QTreeWidgetItem(self.result_widget)
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(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)
def setError(self, errmsg):
diff --git a/src/nailab/ui_gen/csvdatasourceconfigwidget.py b/src/nailab/ui_gen/csvdatasourceconfigwidget.py
index 940fac6..8a3626d 100644
--- a/src/nailab/ui_gen/csvdatasourceconfigwidget.py
+++ b/src/nailab/ui_gen/csvdatasourceconfigwidget.py
@@ -2,12 +2,15 @@
# 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
+
class Ui_CSVDataSourceConfigWidget(object):
def setupUi(self, CSVDataSourceConfigWidget):
CSVDataSourceConfigWidget.setObjectName("CSVDataSourceConfigWidget")
@@ -45,4 +48,3 @@ class Ui_CSVDataSourceConfigWidget(object):
CSVDataSourceConfigWidget.setWindowTitle(_translate("CSVDataSourceConfigWidget", "Form"))
self.label.setText(_translate("CSVDataSourceConfigWidget", "CSV folder path"))
self.b_browse.setText(_translate("CSVDataSourceConfigWidget", "..."))
-
diff --git a/src/nailab/ui_gen/equitychartwidget.py b/src/nailab/ui_gen/equitychartwidget.py
index d5ebd13..26b638c 100644
--- a/src/nailab/ui_gen/equitychartwidget.py
+++ b/src/nailab/ui_gen/equitychartwidget.py
@@ -2,12 +2,15 @@
# 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
+
class Ui_EquityChartWidget(object):
def setupUi(self, EquityChartWidget):
EquityChartWidget.setObjectName("EquityChartWidget")
@@ -24,5 +27,4 @@ class Ui_EquityChartWidget(object):
def retranslateUi(self, EquityChartWidget):
_translate = QtCore.QCoreApplication.translate
EquityChartWidget.setWindowTitle(_translate("EquityChartWidget", "Form"))
-
from ui.equitychartcanvas import EquityChartCanvas
diff --git a/src/nailab/ui_gen/mainwindow.py b/src/nailab/ui_gen/mainwindow.py
index 5595ad3..a7fed67 100644
--- a/src/nailab/ui_gen/mainwindow.py
+++ b/src/nailab/ui_gen/mainwindow.py
@@ -2,16 +2,19 @@
# 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
+
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
- MainWindow.resize(1060, 587)
+ MainWindow.resize(1060, 588)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
@@ -23,7 +26,7 @@ class Ui_MainWindow(object):
self.gridLayout.addWidget(self.tabs, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
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.menuFile = QtWidgets.QMenu(self.menubar)
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.setIcon(icon2)
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.actionOpen_strategy)
self.menuFile.addAction(self.actionSave_strategy)
@@ -85,6 +90,7 @@ class Ui_MainWindow(object):
self.toolBar.addSeparator()
self.toolBar.addAction(self.actionExecute)
self.toolBar.addAction(self.actionExecute_in_Portfolio_Mode)
+ self.toolBar.addAction(self.actionSettings)
self.retranslateUi(MainWindow)
self.tabs.setCurrentIndex(-1)
@@ -96,6 +102,7 @@ class Ui_MainWindow(object):
self.actionSave_as.triggered.connect(MainWindow.saveStrategyAs)
self.actionPerformance.triggered.connect(MainWindow.performanceAnalysis)
self.actionExecute_in_Portfolio_Mode.triggered.connect(MainWindow.executeStrategyInPortfolioMode)
+ self.actionSettings.triggered.connect(MainWindow.showSettingsDialog)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
@@ -117,5 +124,5 @@ class Ui_MainWindow(object):
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"))
-
+ self.actionSettings.setText(_translate("MainWindow", "Settings"))
import nailab_rc
diff --git a/src/nailab/ui_gen/newdatasourcedialog.py b/src/nailab/ui_gen/newdatasourcedialog.py
index 15e714a..5d07b79 100644
--- a/src/nailab/ui_gen/newdatasourcedialog.py
+++ b/src/nailab/ui_gen/newdatasourcedialog.py
@@ -2,12 +2,15 @@
# 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
+
class Ui_NewDataSourceDialog(object):
def setupUi(self, NewDataSourceDialog):
NewDataSourceDialog.setObjectName("NewDataSourceDialog")
@@ -36,6 +39,7 @@ class Ui_NewDataSourceDialog(object):
self.cb_sourceType.setSizePolicy(sizePolicy)
self.cb_sourceType.setObjectName("cb_sourceType")
self.cb_sourceType.addItem("")
+ self.cb_sourceType.addItem("")
self.gridLayout.addWidget(self.cb_sourceType, 1, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(NewDataSourceDialog)
self.label_2.setObjectName("label_2")
@@ -56,5 +60,5 @@ class Ui_NewDataSourceDialog(object):
NewDataSourceDialog.setWindowTitle(_translate("NewDataSourceDialog", "New data source"))
self.label.setText(_translate("NewDataSourceDialog", "Data source type"))
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"))
-
diff --git a/src/nailab/ui_gen/performancewidget.py b/src/nailab/ui_gen/performancewidget.py
index 82d5ce4..183189a 100644
--- a/src/nailab/ui_gen/performancewidget.py
+++ b/src/nailab/ui_gen/performancewidget.py
@@ -2,12 +2,15 @@
# 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
+
class Ui_PerformanceWidget(object):
def setupUi(self, PerformanceWidget):
PerformanceWidget.setObjectName("PerformanceWidget")
@@ -77,7 +80,7 @@ class Ui_PerformanceWidget(object):
self.gridLayout.addWidget(self.lw_strategies, 0, 0, 1, 2)
self.retranslateUi(PerformanceWidget)
- self.tab1.setCurrentIndex(1)
+ self.tab1.setCurrentIndex(0)
self.b_add.clicked.connect(PerformanceWidget.addResults)
self.b_remove.clicked.connect(PerformanceWidget.removeResults)
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.b_add.setText(_translate("PerformanceWidget", "Add..."))
self.b_remove.setText(_translate("PerformanceWidget", "Remove"))
-
from ui.correlationchartcanvas import CorrelationChartCanvas
from ui.performancecanvas import PerformanceCanvas
diff --git a/src/nailab/ui_gen/settingswindow.py b/src/nailab/ui_gen/settingswindow.py
new file mode 100644
index 0000000..6ce07dc
--- /dev/null
+++ b/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)"))
diff --git a/src/nailab/ui_gen/statisticwidget.py b/src/nailab/ui_gen/statisticwidget.py
new file mode 100644
index 0000000..e7796b0
--- /dev/null
+++ b/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"))
diff --git a/src/nailab/ui_gen/strategywidget.py b/src/nailab/ui_gen/strategywidget.py
index 6d9f7fd..12ee608 100644
--- a/src/nailab/ui_gen/strategywidget.py
+++ b/src/nailab/ui_gen/strategywidget.py
@@ -2,16 +2,19 @@
# 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
+
class Ui_StrategyWidget(object):
def setupUi(self, StrategyWidget):
StrategyWidget.setObjectName("StrategyWidget")
- StrategyWidget.resize(977, 569)
+ StrategyWidget.resize(977, 666)
self.gridLayout_3 = QtWidgets.QGridLayout(StrategyWidget)
self.gridLayout_3.setContentsMargins(1, 1, 1, 1)
self.gridLayout_3.setObjectName("gridLayout_3")
@@ -28,6 +31,7 @@ class Ui_StrategyWidget(object):
self.gridLayoutWidget = QtWidgets.QWidget(self.splitter)
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.gridLayout_2 = QtWidgets.QGridLayout(self.gridLayoutWidget)
+ self.gridLayout_2.setContentsMargins(2, 2, 2, 2)
self.gridLayout_2.setObjectName("gridLayout_2")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
@@ -44,16 +48,16 @@ class Ui_StrategyWidget(object):
self.b_deleteSource = QtWidgets.QPushButton(self.gridLayoutWidget)
self.b_deleteSource.setObjectName("b_deleteSource")
self.horizontalLayout.addWidget(self.b_deleteSource)
- self.gridLayout_2.addLayout(self.horizontalLayout, 5, 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.gridLayout_2.addLayout(self.horizontalLayout, 7, 0, 1, 2)
self.dte_from = QtWidgets.QDateTimeEdit(self.gridLayoutWidget)
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.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.setChecked(True)
self.rb_allData.setObjectName("rb_allData")
@@ -69,7 +73,7 @@ class Ui_StrategyWidget(object):
self.tw_feeds.setHeaderHidden(True)
self.tw_feeds.setObjectName("tw_feeds")
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)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
@@ -77,7 +81,7 @@ class Ui_StrategyWidget(object):
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy)
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.setObjectName("rb_timeWindow")
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_refresh.setText(_translate("StrategyWidget", "Refresh"))
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.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.label.setText(_translate("StrategyWidget", "From"))
self.rb_timeWindow.setText(_translate("StrategyWidget", "Time window"))
self.tabs.setTabText(self.tabs.indexOf(self.tab), _translate("StrategyWidget", "Code"))
-
from PyQt5 import Qsci
diff --git a/src/nailab/ui_gen/tradeslistwidget.py b/src/nailab/ui_gen/tradeslistwidget.py
index 2b93830..4b5ba5d 100644
--- a/src/nailab/ui_gen/tradeslistwidget.py
+++ b/src/nailab/ui_gen/tradeslistwidget.py
@@ -2,12 +2,15 @@
# 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
+
class Ui_TradesListWidget(object):
def setupUi(self, 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(6, _translate("TradesListWidget", "Exit price"))
self.trades.headerItem().setText(7, _translate("TradesListWidget", "PnL"))
-
diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui
index 550515f..1e1e33a 100644
--- a/ui/mainwindow.ui
+++ b/ui/mainwindow.ui
@@ -7,7 +7,7 @@
0
0
1060
- 587
+ 588
@@ -45,7 +45,7 @@
0
0
1060
- 19
+ 21
@@ -166,6 +167,11 @@
Execute in Portfolio Mode
+
+
+ Settings
+
+
@@ -299,6 +305,22 @@
+
+ actionSettings
+ triggered()
+ MainWindow
+ showSettingsDialog()
+
+
+ -1
+ -1
+
+
+ 529
+ 293
+
+
+
openTrades()
@@ -310,5 +332,6 @@
saveStrategyAs()
performanceAnalysis()
executeStrategyInPortfolioMode()
+ showSettingsDialog()
diff --git a/ui/newdatasourcedialog.ui b/ui/newdatasourcedialog.ui
index 27dfc1c..4b011b4 100644
--- a/ui/newdatasourcedialog.ui
+++ b/ui/newdatasourcedialog.ui
@@ -50,6 +50,11 @@
CSV
+ -
+
+ YahooCSV
+
+
-
diff --git a/ui/performancewidget.ui b/ui/performancewidget.ui
index 8394d6e..00b085c 100644
--- a/ui/performancewidget.ui
+++ b/ui/performancewidget.ui
@@ -29,7 +29,7 @@
-
- 1
+ 0
diff --git a/ui/settingswindow.ui b/ui/settingswindow.ui
new file mode 100644
index 0000000..d53d550
--- /dev/null
+++ b/ui/settingswindow.ui
@@ -0,0 +1,105 @@
+
+
+ SettingsWindow
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Settings
+
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ 4
+
+
+
+ -
+
+
+ Commission (%)
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ Commission (per share)
+
+
+
+ -
+
+
+ 4
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ SettingsWindow
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ SettingsWindow
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/ui/statisticwidget.ui b/ui/statisticwidget.ui
new file mode 100644
index 0000000..2ea6ec3
--- /dev/null
+++ b/ui/statisticwidget.ui
@@ -0,0 +1,113 @@
+
+
+ StatisticWidget
+
+
+
+ 0
+ 0
+ 548
+ 418
+
+
+
+ StatisticWidget
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+ -
+
+
+ 0
+
+
+
+ By ticker
+
+
+
-
+
+
+ true
+
+
+
+ Ticker
+
+
+
+
+ Net profit
+
+
+
+
+ Total trades
+
+
+
+
+ Avg. return
+
+
+
+
+
+
+
+
+ Seasonal
+
+
+ -
+
+
+ true
+
+
+
+
+
+
+
+
+ Net profit
+
+
+
+
+ Total trades
+
+
+
+
+ Avg. return
+
+
+
+
+
+
+
+
+ Returns
+
+
+
+
+
+
+
+
+
diff --git a/ui/strategywidget.ui b/ui/strategywidget.ui
index 36dd940..f16b7cb 100644
--- a/ui/strategywidget.ui
+++ b/ui/strategywidget.ui
@@ -7,7 +7,7 @@
0
0
977
- 569
+ 666
@@ -55,7 +55,19 @@
- -
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
-
-
@@ -106,27 +118,27 @@
- -
-
-
- dd.MM.yyyy H:mm
-
-
-
- -
+
-
dd.MM.yyyy H:mm
- -
+
-
To
+ -
+
+
+ dd.MM.yyyy H:mm
+
+
+
-
@@ -137,7 +149,7 @@
- -
+
-
@@ -161,7 +173,7 @@
- -
+
-