15 changed files with 1092 additions and 85 deletions
@ -0,0 +1,155 @@ |
|||||||
|
|
||||||
|
|
||||||
|
import sys |
||||||
|
import importlib |
||||||
|
from importlib.machinery import SourceFileLoader |
||||||
|
import inspect |
||||||
|
|
||||||
|
from naiback.strategy import Strategy |
||||||
|
|
||||||
|
import numpy as np |
||||||
|
import math |
||||||
|
|
||||||
|
def render_float(a): |
||||||
|
return "{:.3f}".format(a) |
||||||
|
|
||||||
|
|
||||||
|
def render_ratio(a, b): |
||||||
|
if b != 0: |
||||||
|
return a / b |
||||||
|
else: |
||||||
|
return 0 |
||||||
|
|
||||||
|
class PortfolioExecutor: |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
pass |
||||||
|
|
||||||
|
def execute_from_file(self, path, feeds, extents=None): |
||||||
|
if "execution._current_strategy" in sys.modules: |
||||||
|
del sys.modules["execution._current_strategy"] |
||||||
|
loader = SourceFileLoader('execution._current_strategy', path) |
||||||
|
mod = loader.load_module() |
||||||
|
for item in inspect.getmembers(mod, inspect.isclass): |
||||||
|
klass = item[1] |
||||||
|
if klass is not Strategy and issubclass(klass, Strategy): |
||||||
|
all_trades = [] |
||||||
|
for feed in feeds: |
||||||
|
strategy = klass() |
||||||
|
strategy.add_feed(feed) |
||||||
|
|
||||||
|
if extents is None: |
||||||
|
strategy.run() |
||||||
|
else: |
||||||
|
strategy.run(from_time=extents[0], to_time=extents[1]) |
||||||
|
|
||||||
|
trades = strategy.get_analyzer('tradeslist').get_result() |
||||||
|
all_trades = self.merge_trades(all_trades, trades) |
||||||
|
|
||||||
|
results = self.calc_stats(all_trades) |
||||||
|
equity = self.calc_equity(all_trades) |
||||||
|
return (results, all_trades, equity) |
||||||
|
|
||||||
|
def merge_trades(self, trades1, trades2): |
||||||
|
all_trades = list(sorted(trades1 + trades2, key=lambda x: x['entry_time'])) |
||||||
|
result = [] |
||||||
|
current_trades = [] |
||||||
|
max_trades = 3 |
||||||
|
for trade in all_trades: |
||||||
|
if len(current_trades) < max_trades: |
||||||
|
current_trades.append(trade) |
||||||
|
result.append(trade) |
||||||
|
new_current_trades = [] |
||||||
|
for ct in current_trades: |
||||||
|
if ct['exit_time'] < trade['entry_time']: |
||||||
|
new_current_trades.append(ct) |
||||||
|
else: |
||||||
|
# possibly adjust the balance |
||||||
|
pass |
||||||
|
current_trades = new_current_trades |
||||||
|
return result |
||||||
|
|
||||||
|
def calc_stats(self, trades): |
||||||
|
longs = list(filter(lambda x: x['is_long'], trades)) |
||||||
|
shorts = list(filter(lambda x: not x['is_long'], trades)) |
||||||
|
|
||||||
|
result = { 'all' : {}, 'long' : {}, 'short' : {} } |
||||||
|
result['all']['net_profit'] = sum([t['pnl'] for t in trades]) |
||||||
|
result['long']['net_profit'] = sum([t['pnl'] for t in longs]) |
||||||
|
result['short']['net_profit'] = sum([t['pnl'] for t in shorts]) |
||||||
|
|
||||||
|
result['all']['bars_in_trade'] = 0 # TODO? |
||||||
|
result['long']['bars_in_trade'] = 0 |
||||||
|
result['short']['bars_in_trade'] = 0 |
||||||
|
|
||||||
|
result['all']['profit_per_bar'] = 0 |
||||||
|
result['long']['profit_per_bar'] = 0 |
||||||
|
result['short']['profit_per_bar'] = 0 |
||||||
|
|
||||||
|
result['all']['number_of_trades'] = len(trades) |
||||||
|
result['long']['number_of_trades'] = len(longs) |
||||||
|
result['short']['number_of_trades'] = len(shorts) |
||||||
|
|
||||||
|
result['all']['avg'] = render_ratio(result['all']['net_profit'], result['all']['number_of_trades']) |
||||||
|
result['long']['avg'] = render_ratio(result['long']['net_profit'], result['long']['number_of_trades']) |
||||||
|
result['short']['avg'] = render_ratio(result['short']['net_profit'], result['short']['number_of_trades']) |
||||||
|
|
||||||
|
result['all']['avg_percentage'] = render_ratio(sum([t['profit_percentage'] for t in trades]), result['all']['number_of_trades']) |
||||||
|
result['long']['avg_percentage'] = render_ratio(sum([t['profit_percentage'] for t in longs]), result['long']['number_of_trades']) |
||||||
|
result['short']['avg_percentage'] = render_ratio(sum([t['profit_percentage'] for t in shorts]), result['short']['number_of_trades']) |
||||||
|
|
||||||
|
result['all']['avg_bars'] = render_ratio(result['all']['bars_in_trade'], result['all']['number_of_trades']) |
||||||
|
result['long']['avg_bars'] = render_ratio(result['long']['bars_in_trade'], result['long']['number_of_trades']) |
||||||
|
result['short']['avg_bars'] = render_ratio(result['short']['bars_in_trade'], result['short']['number_of_trades']) |
||||||
|
|
||||||
|
result['all']['won'] = len(list(filter(lambda t: t['pnl'] > 0, trades))) |
||||||
|
result['long']['won'] = len(list(filter(lambda t: t['pnl'] > 0, longs))) |
||||||
|
result['short']['won'] = len(list(filter(lambda t: t['pnl'] > 0, shorts))) |
||||||
|
|
||||||
|
result['all']['lost'] = len(list(filter(lambda t: t['pnl'] <= 0, trades))) |
||||||
|
result['long']['lost'] = len(list(filter(lambda t: t['pnl'] <= 0, longs))) |
||||||
|
result['short']['lost'] = len(list(filter(lambda t: t['pnl'] <= 0, shorts))) |
||||||
|
|
||||||
|
result['all']['total_won'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] > 0, trades))) |
||||||
|
result['long']['total_won'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] > 0, longs))) |
||||||
|
result['short']['total_won'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] > 0, shorts))) |
||||||
|
|
||||||
|
result['all']['total_lost'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] <= 0, trades))) |
||||||
|
result['long']['total_lost'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] <= 0, longs))) |
||||||
|
result['short']['total_lost'] = sum(map(lambda t: t['pnl'], filter(lambda t: t['pnl'] <= 0, shorts))) |
||||||
|
|
||||||
|
result['all']['profit_factor'] = render_ratio(result['all']['total_won'], -result['all']['total_lost']) |
||||||
|
result['long']['profit_factor'] = render_ratio(result['long']['total_won'], -result['long']['total_lost']) |
||||||
|
result['short']['profit_factor'] = render_ratio(result['short']['total_won'], -result['short']['total_lost']) |
||||||
|
|
||||||
|
mean = np.mean(list(map(lambda x: x['pnl'], trades))) |
||||||
|
stddev = np.std(list(map(lambda x: x['pnl'], trades))) |
||||||
|
sharpe = mean / stddev |
||||||
|
tstat = sharpe * math.sqrt(len(trades)) |
||||||
|
result['all']['sharpe_ratio'] = sharpe |
||||||
|
result['all']['t_stat'] = tstat |
||||||
|
|
||||||
|
mean = np.mean(list(map(lambda x: x['pnl'], longs))) |
||||||
|
stddev = np.std(list(map(lambda x: x['pnl'], longs))) |
||||||
|
sharpe = mean / stddev |
||||||
|
tstat = sharpe * math.sqrt(len(longs)) |
||||||
|
result['long']['sharpe_ratio'] = sharpe |
||||||
|
result['long']['t_stat'] = tstat |
||||||
|
|
||||||
|
mean = np.mean(list(map(lambda x: x['pnl'], shorts))) |
||||||
|
stddev = np.std(list(map(lambda x: x['pnl'], shorts))) |
||||||
|
sharpe = mean / stddev |
||||||
|
tstat = sharpe * math.sqrt(len(shorts)) |
||||||
|
result['short']['sharpe_ratio'] = sharpe |
||||||
|
result['short']['t_stat'] = tstat |
||||||
|
|
||||||
|
return result |
||||||
|
|
||||||
|
def calc_equity(self, trades): |
||||||
|
sorted_trades = sorted(trades, key=lambda x: x['exit_time']) |
||||||
|
equity = [] |
||||||
|
current = 0 |
||||||
|
for t in sorted_trades: |
||||||
|
current += t['pnl'] |
||||||
|
equity.append(current) |
||||||
|
|
||||||
@ -0,0 +1,138 @@ |
|||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets |
||||||
|
from PyQt5.Qsci import * |
||||||
|
|
||||||
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas |
||||||
|
from matplotlib.figure import Figure |
||||||
|
import matplotlib.pyplot as plt |
||||||
|
import matplotlib |
||||||
|
|
||||||
|
import numpy as np |
||||||
|
|
||||||
|
|
||||||
|
class CorrelationChartCanvas(FigureCanvas): |
||||||
|
def __init__(self, parent=None, width=6, height=6, dpi=100): |
||||||
|
self.fig = Figure(figsize=(width, height), dpi=dpi) |
||||||
|
|
||||||
|
FigureCanvas.__init__(self, self.fig) |
||||||
|
self.setParent(parent) |
||||||
|
|
||||||
|
self.trades_series = {} |
||||||
|
self.mode = "daily" |
||||||
|
|
||||||
|
def setMode(self, mode): |
||||||
|
if mode not in ["daily", "monthly"]: |
||||||
|
return |
||||||
|
|
||||||
|
self.mode = mode |
||||||
|
self._refresh() |
||||||
|
|
||||||
|
def add_trades_series(self, name, trades): |
||||||
|
self.trades_series[name] = trades |
||||||
|
self._refresh() |
||||||
|
|
||||||
|
def add_trades_series_batched(self, trades_list): |
||||||
|
for series in trades_list: |
||||||
|
self.trades_series[series["name"]] = series["trades"] |
||||||
|
self._refresh() |
||||||
|
|
||||||
|
def remove_trades_series(self, name): |
||||||
|
del self.trades_series[name] |
||||||
|
self._refresh() |
||||||
|
|
||||||
|
def _refresh(self): |
||||||
|
self.fig.clear() |
||||||
|
|
||||||
|
if len(self.trades_series) == 0: |
||||||
|
return |
||||||
|
|
||||||
|
corr_matrix = np.zeros((len(self.trades_series), len(self.trades_series))) |
||||||
|
x = 0 |
||||||
|
y = 0 |
||||||
|
names = [] |
||||||
|
for n1, trades1 in self.trades_series.items(): |
||||||
|
names.append(n1) |
||||||
|
for n2, trades2 in self.trades_series.items(): |
||||||
|
corr_matrix[x][y] = self._calc_correlation(trades1, trades2) |
||||||
|
y += 1 |
||||||
|
x += 1 |
||||||
|
y = 0 |
||||||
|
|
||||||
|
ax1 = self.figure.add_subplot(111) |
||||||
|
ax1.imshow(corr_matrix, cmap="BrBG", norm=matplotlib.colors.Normalize(vmin=-1.0, vmax=1.0)) |
||||||
|
ax1.set_title("Returns correlation") |
||||||
|
ax1.set_xticks(np.arange(len(names))) |
||||||
|
ax1.set_yticks(np.arange(len(names))) |
||||||
|
ax1.set_xticklabels(names) |
||||||
|
ax1.set_yticklabels(names) |
||||||
|
for i in range(len(self.trades_series)): |
||||||
|
for j in range(len(self.trades_series)): |
||||||
|
col = 'k' |
||||||
|
if corr_matrix[i, j] > 0.7: |
||||||
|
col = 'w' |
||||||
|
text = ax1.text(j, i, "{:.2f}".format(corr_matrix[i, j]), ha="center", va="center", color=col, fontsize="x-small") |
||||||
|
|
||||||
|
plt.setp(ax1.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor") |
||||||
|
|
||||||
|
self.draw() |
||||||
|
|
||||||
|
def _calc_correlation(self, trades1, trades2): |
||||||
|
t1 = {} |
||||||
|
all_dates = set() |
||||||
|
for trade in trades1: |
||||||
|
all_dates.add(self._trade_date(trade)) |
||||||
|
try: |
||||||
|
t1[self._trade_date(trade)] += trade["percentage"] |
||||||
|
except KeyError: |
||||||
|
t1[self._trade_date(trade)] = trade["percentage"] |
||||||
|
|
||||||
|
t2 = {} |
||||||
|
for trade in trades2: |
||||||
|
all_dates.add(self._trade_date(trade)) |
||||||
|
try: |
||||||
|
t2[self._trade_date(trade)] += trade["percentage"] |
||||||
|
except KeyError: |
||||||
|
t2[self._trade_date(trade)] = trade["percentage"] |
||||||
|
|
||||||
|
|
||||||
|
ret1 = [] |
||||||
|
for d in all_dates: |
||||||
|
try: |
||||||
|
ret1.append(t1[d]) |
||||||
|
except KeyError: |
||||||
|
ret1.append(0) |
||||||
|
|
||||||
|
ret2 = [] |
||||||
|
for d in all_dates: |
||||||
|
try: |
||||||
|
ret2.append(t2[d]) |
||||||
|
except KeyError: |
||||||
|
ret2.append(0) |
||||||
|
|
||||||
|
mean1 = np.mean(ret1) |
||||||
|
mean2 = np.mean(ret2) |
||||||
|
sigma1 = np.std(ret1) |
||||||
|
sigma2 = np.std(ret2) |
||||||
|
|
||||||
|
s = 0 |
||||||
|
for i in range(len(all_dates)): |
||||||
|
x1 = ret1[i] - mean1 |
||||||
|
x2 = ret2[i] - mean2 |
||||||
|
s += x1 * x2 |
||||||
|
s /= len(all_dates) |
||||||
|
s /= (sigma1 * sigma2) |
||||||
|
|
||||||
|
return s |
||||||
|
|
||||||
|
|
||||||
|
def _trade_date(self, trade): |
||||||
|
if self.mode == "daily": |
||||||
|
return trade["exit_time"].date() |
||||||
|
else: |
||||||
|
return trade["exit_time"].date().replace(day=1) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,92 @@ |
|||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets |
||||||
|
from PyQt5.Qsci import * |
||||||
|
|
||||||
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas |
||||||
|
from matplotlib.figure import Figure |
||||||
|
import matplotlib.pyplot as plt |
||||||
|
|
||||||
|
import numpy as np |
||||||
|
|
||||||
|
|
||||||
|
class PerformanceCanvas(FigureCanvas): |
||||||
|
def __init__(self, parent=None, width=5, height=4, dpi=100): |
||||||
|
self.fig = Figure(figsize=(width, height), dpi=dpi) |
||||||
|
|
||||||
|
FigureCanvas.__init__(self, self.fig) |
||||||
|
self.setParent(parent) |
||||||
|
|
||||||
|
self.trades_series = {} |
||||||
|
|
||||||
|
|
||||||
|
def add_trades_series(self, name, trades): |
||||||
|
self.trades_series[name] = trades |
||||||
|
self._refresh() |
||||||
|
|
||||||
|
def add_trades_series_batched(self, trades_list): |
||||||
|
for series in trades_list: |
||||||
|
self.trades_series[series["name"]] = series["trades"] |
||||||
|
self._refresh() |
||||||
|
|
||||||
|
def remove_trades_series(self, name): |
||||||
|
del self.trades_series[name] |
||||||
|
self._refresh() |
||||||
|
|
||||||
|
def _refresh(self): |
||||||
|
self.fig.clear() |
||||||
|
|
||||||
|
if len(self.trades_series) == 0: |
||||||
|
return |
||||||
|
|
||||||
|
all_trades = [] |
||||||
|
for k, v in self.trades_series.items(): |
||||||
|
for trade in v: |
||||||
|
all_trades.append(trade) |
||||||
|
|
||||||
|
all_trades = sorted(all_trades, key=lambda x: x["exit_time"]) |
||||||
|
|
||||||
|
cumpnl = np.cumsum(list(map(lambda x: x["percentage"], all_trades))) |
||||||
|
|
||||||
|
max_equity = 0 |
||||||
|
drawdown = [] |
||||||
|
for x in cumpnl: |
||||||
|
if x > max_equity: |
||||||
|
max_equity = x |
||||||
|
drawdown.append(0) |
||||||
|
else: |
||||||
|
drawdown.append(x - max_equity) |
||||||
|
|
||||||
|
monthly_returns = {} |
||||||
|
cur_month = None |
||||||
|
cur_pnl = 0 |
||||||
|
|
||||||
|
for trade in all_trades: |
||||||
|
trade_month = trade["exit_time"].strftime("%Y-%m") |
||||||
|
if trade_month != cur_month and cur_month is not None: |
||||||
|
monthly_returns[cur_month] = cur_pnl |
||||||
|
cur_pnl = 0 |
||||||
|
cur_month = trade_month |
||||||
|
if cur_month is None: |
||||||
|
cur_month = trade_month |
||||||
|
cur_pnl += trade["percentage"] |
||||||
|
|
||||||
|
monthly_returns[cur_month] = cur_pnl |
||||||
|
monthly_returns_raw = [] |
||||||
|
for k, v in monthly_returns.items(): |
||||||
|
monthly_returns_raw.append((k, v)) |
||||||
|
|
||||||
|
monthly_returns_raw = sorted(monthly_returns_raw, key=lambda x: x[0]) |
||||||
|
self.monthly_returns_x = list(map(lambda x: x[0], monthly_returns_raw)) |
||||||
|
self.monthly_returns_y = list(map(lambda x: x[1], monthly_returns_raw)) |
||||||
|
|
||||||
|
ax1 = self.figure.add_subplot(211) |
||||||
|
ax1.plot(cumpnl, "b-") |
||||||
|
ax1.set_title("Cumulative PnL") |
||||||
|
ax1_2 = ax1.twinx() |
||||||
|
ax1_2.plot(drawdown, "r-") |
||||||
|
|
||||||
|
ax2 = self.figure.add_subplot(212) |
||||||
|
ax2.bar(self.monthly_returns_x, self.monthly_returns_y) |
||||||
|
ax2.set_title("Monthly PnL") |
||||||
|
plt.setp(ax2.get_xticklabels(), rotation=90, ha="right", rotation_mode="anchor") |
||||||
|
self.draw() |
||||||
@ -0,0 +1,86 @@ |
|||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets |
||||||
|
from PyQt5.Qsci import * |
||||||
|
|
||||||
|
from ui_gen.performancewidget import Ui_PerformanceWidget |
||||||
|
|
||||||
|
from pathlib import PurePath |
||||||
|
|
||||||
|
import datetime |
||||||
|
import json |
||||||
|
import numpy as np |
||||||
|
|
||||||
|
class PerformanceWidget(QtWidgets.QWidget): |
||||||
|
def __init__(self, parent=None): |
||||||
|
super().__init__(parent) |
||||||
|
|
||||||
|
self.ui = Ui_PerformanceWidget() |
||||||
|
self.ui.setupUi(self) |
||||||
|
self.trades_series = {} |
||||||
|
|
||||||
|
def widget_type(self): |
||||||
|
return "performance" |
||||||
|
|
||||||
|
def dailyToggled(self, v): |
||||||
|
if v: |
||||||
|
self.ui.correlationCanvas.setMode("daily") |
||||||
|
|
||||||
|
def monthlyToggled(self, v): |
||||||
|
if v: |
||||||
|
self.ui.correlationCanvas.setMode("monthly") |
||||||
|
|
||||||
|
def addResults(self): |
||||||
|
filenames, _ = QtWidgets.QFileDialog.getOpenFileNames(self, self.tr("Select file to open"), "", self.tr("JSON files (*.json);;All Files (*)")) |
||||||
|
results = [] |
||||||
|
for filename in filenames: |
||||||
|
with open(filename, "rt") as f: |
||||||
|
name = PurePath(filename).name |
||||||
|
QtWidgets.QListWidgetItem(name, self.ui.lw_strategies) |
||||||
|
trades = json.loads(f.read()) |
||||||
|
for trade in trades: |
||||||
|
trade["entry_time"] = datetime.datetime.strptime(trade["entry_time"], "%Y-%m-%d %H:%M:%S") |
||||||
|
trade["exit_time"] = datetime.datetime.strptime(trade["exit_time"], "%Y-%m-%d %H:%M:%S") |
||||||
|
|
||||||
|
self.trades_series[name] = trades |
||||||
|
results.append({"name" : name, "trades" : trades}) |
||||||
|
|
||||||
|
self.ui.canvas.add_trades_series_batched(results) |
||||||
|
self.ui.correlationCanvas.add_trades_series_batched(results) |
||||||
|
self.update_stats() |
||||||
|
|
||||||
|
|
||||||
|
def removeResults(self): |
||||||
|
selected_rows = sorted(map(lambda x: self.ui.lw_strategies.row(x), self.ui.lw_strategies.selectedItems())) |
||||||
|
for row in reversed(selected_rows): |
||||||
|
item = self.ui.lw_strategies.takeItem(row) |
||||||
|
self.ui.canvas.remove_trades_series(item.text()) |
||||||
|
self.ui.correlationCanvas.remove_trades_series(item.text()) |
||||||
|
|
||||||
|
self.update_stats() |
||||||
|
|
||||||
|
def update_stats(self): |
||||||
|
self.ui.tw_stats.clear() |
||||||
|
monmean = np.mean(self.ui.canvas.monthly_returns_y) |
||||||
|
monstd = np.std(self.ui.canvas.monthly_returns_y) |
||||||
|
if monstd == 0: |
||||||
|
monz = 0 |
||||||
|
else: |
||||||
|
monz = monmean / monstd |
||||||
|
self.add_stat("Monthly mean", monmean) |
||||||
|
self.add_stat("Monthly std. dev.", monstd) |
||||||
|
self.add_stat("Z-Score", monz) |
||||||
|
|
||||||
|
self.ui.tw_stats.resizeColumnToContents(0) |
||||||
|
self.ui.tw_stats.resizeColumnToContents(1) |
||||||
|
|
||||||
|
|
||||||
|
def add_stat(self, name, value): |
||||||
|
item = QtWidgets.QTreeWidgetItem(self.ui.tw_stats) |
||||||
|
item.setText(0, name) |
||||||
|
if isinstance(value, str): |
||||||
|
item.setText(1, str) |
||||||
|
elif isinstance(value, float): |
||||||
|
item.setText(1, "{:.3f}".format(value)) |
||||||
|
else: |
||||||
|
item.setText(1, str(value)) |
||||||
|
|
||||||
@ -0,0 +1,100 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'ui/performancewidget.ui' |
||||||
|
# |
||||||
|
# Created by: PyQt5 UI code generator 5.5.1 |
||||||
|
# |
||||||
|
# WARNING! All changes made in this file will be lost! |
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets |
||||||
|
|
||||||
|
class Ui_PerformanceWidget(object): |
||||||
|
def setupUi(self, PerformanceWidget): |
||||||
|
PerformanceWidget.setObjectName("PerformanceWidget") |
||||||
|
PerformanceWidget.resize(780, 556) |
||||||
|
self.gridLayout = QtWidgets.QGridLayout(PerformanceWidget) |
||||||
|
self.gridLayout.setContentsMargins(2, 2, 2, 2) |
||||||
|
self.gridLayout.setObjectName("gridLayout") |
||||||
|
self.tab1 = QtWidgets.QTabWidget(PerformanceWidget) |
||||||
|
self.tab1.setObjectName("tab1") |
||||||
|
self.tab = QtWidgets.QWidget() |
||||||
|
self.tab.setObjectName("tab") |
||||||
|
self.gridLayout_2 = QtWidgets.QGridLayout(self.tab) |
||||||
|
self.gridLayout_2.setContentsMargins(1, 1, 1, 1) |
||||||
|
self.gridLayout_2.setObjectName("gridLayout_2") |
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout() |
||||||
|
self.verticalLayout.setContentsMargins(4, 4, 4, 4) |
||||||
|
self.verticalLayout.setObjectName("verticalLayout") |
||||||
|
self.canvas = PerformanceCanvas(self.tab) |
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) |
||||||
|
sizePolicy.setHorizontalStretch(0) |
||||||
|
sizePolicy.setVerticalStretch(0) |
||||||
|
sizePolicy.setHeightForWidth(self.canvas.sizePolicy().hasHeightForWidth()) |
||||||
|
self.canvas.setSizePolicy(sizePolicy) |
||||||
|
self.canvas.setObjectName("canvas") |
||||||
|
self.verticalLayout.addWidget(self.canvas) |
||||||
|
self.tw_stats = QtWidgets.QTreeWidget(self.tab) |
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum) |
||||||
|
sizePolicy.setHorizontalStretch(0) |
||||||
|
sizePolicy.setVerticalStretch(0) |
||||||
|
sizePolicy.setHeightForWidth(self.tw_stats.sizePolicy().hasHeightForWidth()) |
||||||
|
self.tw_stats.setSizePolicy(sizePolicy) |
||||||
|
self.tw_stats.setObjectName("tw_stats") |
||||||
|
self.tw_stats.header().setVisible(False) |
||||||
|
self.verticalLayout.addWidget(self.tw_stats) |
||||||
|
self.gridLayout_2.addLayout(self.verticalLayout, 0, 0, 1, 1) |
||||||
|
self.tab1.addTab(self.tab, "") |
||||||
|
self.tab_2 = QtWidgets.QWidget() |
||||||
|
self.tab_2.setObjectName("tab_2") |
||||||
|
self.gridLayout_3 = QtWidgets.QGridLayout(self.tab_2) |
||||||
|
self.gridLayout_3.setObjectName("gridLayout_3") |
||||||
|
self.rb_daily = QtWidgets.QRadioButton(self.tab_2) |
||||||
|
self.rb_daily.setChecked(True) |
||||||
|
self.rb_daily.setObjectName("rb_daily") |
||||||
|
self.gridLayout_3.addWidget(self.rb_daily, 0, 0, 1, 1) |
||||||
|
self.rb_monthly = QtWidgets.QRadioButton(self.tab_2) |
||||||
|
self.rb_monthly.setObjectName("rb_monthly") |
||||||
|
self.gridLayout_3.addWidget(self.rb_monthly, 0, 1, 1, 1) |
||||||
|
self.correlationCanvas = CorrelationChartCanvas(self.tab_2) |
||||||
|
self.correlationCanvas.setObjectName("correlationCanvas") |
||||||
|
self.gridLayout_3.addWidget(self.correlationCanvas, 1, 0, 1, 2) |
||||||
|
self.tab1.addTab(self.tab_2, "") |
||||||
|
self.gridLayout.addWidget(self.tab1, 0, 2, 1, 1) |
||||||
|
self.b_add = QtWidgets.QPushButton(PerformanceWidget) |
||||||
|
self.b_add.setObjectName("b_add") |
||||||
|
self.gridLayout.addWidget(self.b_add, 1, 0, 1, 1) |
||||||
|
self.b_remove = QtWidgets.QPushButton(PerformanceWidget) |
||||||
|
self.b_remove.setObjectName("b_remove") |
||||||
|
self.gridLayout.addWidget(self.b_remove, 1, 1, 1, 1) |
||||||
|
self.lw_strategies = QtWidgets.QListWidget(PerformanceWidget) |
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Expanding) |
||||||
|
sizePolicy.setHorizontalStretch(0) |
||||||
|
sizePolicy.setVerticalStretch(0) |
||||||
|
sizePolicy.setHeightForWidth(self.lw_strategies.sizePolicy().hasHeightForWidth()) |
||||||
|
self.lw_strategies.setSizePolicy(sizePolicy) |
||||||
|
self.lw_strategies.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) |
||||||
|
self.lw_strategies.setObjectName("lw_strategies") |
||||||
|
self.gridLayout.addWidget(self.lw_strategies, 0, 0, 1, 2) |
||||||
|
|
||||||
|
self.retranslateUi(PerformanceWidget) |
||||||
|
self.tab1.setCurrentIndex(1) |
||||||
|
self.b_add.clicked.connect(PerformanceWidget.addResults) |
||||||
|
self.b_remove.clicked.connect(PerformanceWidget.removeResults) |
||||||
|
self.rb_daily.toggled['bool'].connect(PerformanceWidget.dailyToggled) |
||||||
|
self.rb_monthly.toggled['bool'].connect(PerformanceWidget.monthlyToggled) |
||||||
|
QtCore.QMetaObject.connectSlotsByName(PerformanceWidget) |
||||||
|
|
||||||
|
def retranslateUi(self, PerformanceWidget): |
||||||
|
_translate = QtCore.QCoreApplication.translate |
||||||
|
PerformanceWidget.setWindowTitle(_translate("PerformanceWidget", "Form")) |
||||||
|
self.tw_stats.headerItem().setText(0, _translate("PerformanceWidget", "1")) |
||||||
|
self.tw_stats.headerItem().setText(1, _translate("PerformanceWidget", "2")) |
||||||
|
self.tab1.setTabText(self.tab1.indexOf(self.tab), _translate("PerformanceWidget", "Returns and Drawdowns")) |
||||||
|
self.rb_daily.setText(_translate("PerformanceWidget", "Daily")) |
||||||
|
self.rb_monthly.setText(_translate("PerformanceWidget", "Monthly")) |
||||||
|
self.tab1.setTabText(self.tab1.indexOf(self.tab_2), _translate("PerformanceWidget", "Correlations")) |
||||||
|
self.b_add.setText(_translate("PerformanceWidget", "Add...")) |
||||||
|
self.b_remove.setText(_translate("PerformanceWidget", "Remove")) |
||||||
|
|
||||||
|
from ui.correlationchartcanvas import CorrelationChartCanvas |
||||||
|
from ui.performancecanvas import PerformanceCanvas |
||||||
@ -0,0 +1,247 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<ui version="4.0"> |
||||||
|
<class>PerformanceWidget</class> |
||||||
|
<widget class="QWidget" name="PerformanceWidget"> |
||||||
|
<property name="geometry"> |
||||||
|
<rect> |
||||||
|
<x>0</x> |
||||||
|
<y>0</y> |
||||||
|
<width>780</width> |
||||||
|
<height>556</height> |
||||||
|
</rect> |
||||||
|
</property> |
||||||
|
<property name="windowTitle"> |
||||||
|
<string>Form</string> |
||||||
|
</property> |
||||||
|
<layout class="QGridLayout" name="gridLayout"> |
||||||
|
<property name="leftMargin"> |
||||||
|
<number>2</number> |
||||||
|
</property> |
||||||
|
<property name="topMargin"> |
||||||
|
<number>2</number> |
||||||
|
</property> |
||||||
|
<property name="rightMargin"> |
||||||
|
<number>2</number> |
||||||
|
</property> |
||||||
|
<property name="bottomMargin"> |
||||||
|
<number>2</number> |
||||||
|
</property> |
||||||
|
<item row="0" column="2"> |
||||||
|
<widget class="QTabWidget" name="tab1"> |
||||||
|
<property name="currentIndex"> |
||||||
|
<number>1</number> |
||||||
|
</property> |
||||||
|
<widget class="QWidget" name="tab"> |
||||||
|
<attribute name="title"> |
||||||
|
<string>Returns and Drawdowns</string> |
||||||
|
</attribute> |
||||||
|
<layout class="QGridLayout" name="gridLayout_2"> |
||||||
|
<property name="leftMargin"> |
||||||
|
<number>1</number> |
||||||
|
</property> |
||||||
|
<property name="topMargin"> |
||||||
|
<number>1</number> |
||||||
|
</property> |
||||||
|
<property name="rightMargin"> |
||||||
|
<number>1</number> |
||||||
|
</property> |
||||||
|
<property name="bottomMargin"> |
||||||
|
<number>1</number> |
||||||
|
</property> |
||||||
|
<item row="0" column="0"> |
||||||
|
<layout class="QVBoxLayout" name="verticalLayout"> |
||||||
|
<property name="leftMargin"> |
||||||
|
<number>4</number> |
||||||
|
</property> |
||||||
|
<property name="topMargin"> |
||||||
|
<number>4</number> |
||||||
|
</property> |
||||||
|
<property name="rightMargin"> |
||||||
|
<number>4</number> |
||||||
|
</property> |
||||||
|
<property name="bottomMargin"> |
||||||
|
<number>4</number> |
||||||
|
</property> |
||||||
|
<item> |
||||||
|
<widget class="PerformanceCanvas" name="canvas" native="true"> |
||||||
|
<property name="sizePolicy"> |
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred"> |
||||||
|
<horstretch>0</horstretch> |
||||||
|
<verstretch>0</verstretch> |
||||||
|
</sizepolicy> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item> |
||||||
|
<widget class="QTreeWidget" name="tw_stats"> |
||||||
|
<property name="sizePolicy"> |
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Maximum"> |
||||||
|
<horstretch>0</horstretch> |
||||||
|
<verstretch>0</verstretch> |
||||||
|
</sizepolicy> |
||||||
|
</property> |
||||||
|
<attribute name="headerVisible"> |
||||||
|
<bool>false</bool> |
||||||
|
</attribute> |
||||||
|
<column> |
||||||
|
<property name="text"> |
||||||
|
<string>1</string> |
||||||
|
</property> |
||||||
|
</column> |
||||||
|
<column> |
||||||
|
<property name="text"> |
||||||
|
<string>2</string> |
||||||
|
</property> |
||||||
|
</column> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</widget> |
||||||
|
<widget class="QWidget" name="tab_2"> |
||||||
|
<attribute name="title"> |
||||||
|
<string>Correlations</string> |
||||||
|
</attribute> |
||||||
|
<layout class="QGridLayout" name="gridLayout_3"> |
||||||
|
<item row="0" column="0"> |
||||||
|
<widget class="QRadioButton" name="rb_daily"> |
||||||
|
<property name="text"> |
||||||
|
<string>Daily</string> |
||||||
|
</property> |
||||||
|
<property name="checked"> |
||||||
|
<bool>true</bool> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="0" column="1"> |
||||||
|
<widget class="QRadioButton" name="rb_monthly"> |
||||||
|
<property name="text"> |
||||||
|
<string>Monthly</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="1" column="0" colspan="2"> |
||||||
|
<widget class="CorrelationChartCanvas" name="correlationCanvas" native="true"/> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</widget> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="1" column="0"> |
||||||
|
<widget class="QPushButton" name="b_add"> |
||||||
|
<property name="text"> |
||||||
|
<string>Add...</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="1" column="1"> |
||||||
|
<widget class="QPushButton" name="b_remove"> |
||||||
|
<property name="text"> |
||||||
|
<string>Remove</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="0" column="0" colspan="2"> |
||||||
|
<widget class="QListWidget" name="lw_strategies"> |
||||||
|
<property name="sizePolicy"> |
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Expanding"> |
||||||
|
<horstretch>0</horstretch> |
||||||
|
<verstretch>0</verstretch> |
||||||
|
</sizepolicy> |
||||||
|
</property> |
||||||
|
<property name="selectionMode"> |
||||||
|
<enum>QAbstractItemView::ExtendedSelection</enum> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</widget> |
||||||
|
<customwidgets> |
||||||
|
<customwidget> |
||||||
|
<class>PerformanceCanvas</class> |
||||||
|
<extends>QWidget</extends> |
||||||
|
<header>ui/performancecanvas.h</header> |
||||||
|
<container>1</container> |
||||||
|
</customwidget> |
||||||
|
<customwidget> |
||||||
|
<class>CorrelationChartCanvas</class> |
||||||
|
<extends>QWidget</extends> |
||||||
|
<header>ui/correlationchartcanvas.h</header> |
||||||
|
<container>1</container> |
||||||
|
</customwidget> |
||||||
|
</customwidgets> |
||||||
|
<resources/> |
||||||
|
<connections> |
||||||
|
<connection> |
||||||
|
<sender>b_add</sender> |
||||||
|
<signal>clicked()</signal> |
||||||
|
<receiver>PerformanceWidget</receiver> |
||||||
|
<slot>addResults()</slot> |
||||||
|
<hints> |
||||||
|
<hint type="sourcelabel"> |
||||||
|
<x>127</x> |
||||||
|
<y>539</y> |
||||||
|
</hint> |
||||||
|
<hint type="destinationlabel"> |
||||||
|
<x>262</x> |
||||||
|
<y>543</y> |
||||||
|
</hint> |
||||||
|
</hints> |
||||||
|
</connection> |
||||||
|
<connection> |
||||||
|
<sender>b_remove</sender> |
||||||
|
<signal>clicked()</signal> |
||||||
|
<receiver>PerformanceWidget</receiver> |
||||||
|
<slot>removeResults()</slot> |
||||||
|
<hints> |
||||||
|
<hint type="sourcelabel"> |
||||||
|
<x>239</x> |
||||||
|
<y>539</y> |
||||||
|
</hint> |
||||||
|
<hint type="destinationlabel"> |
||||||
|
<x>372</x> |
||||||
|
<y>542</y> |
||||||
|
</hint> |
||||||
|
</hints> |
||||||
|
</connection> |
||||||
|
<connection> |
||||||
|
<sender>rb_daily</sender> |
||||||
|
<signal>toggled(bool)</signal> |
||||||
|
<receiver>PerformanceWidget</receiver> |
||||||
|
<slot>dailyToggled(bool)</slot> |
||||||
|
<hints> |
||||||
|
<hint type="sourcelabel"> |
||||||
|
<x>314</x> |
||||||
|
<y>44</y> |
||||||
|
</hint> |
||||||
|
<hint type="destinationlabel"> |
||||||
|
<x>508</x> |
||||||
|
<y>537</y> |
||||||
|
</hint> |
||||||
|
</hints> |
||||||
|
</connection> |
||||||
|
<connection> |
||||||
|
<sender>rb_monthly</sender> |
||||||
|
<signal>toggled(bool)</signal> |
||||||
|
<receiver>PerformanceWidget</receiver> |
||||||
|
<slot>monthlyToggled(bool)</slot> |
||||||
|
<hints> |
||||||
|
<hint type="sourcelabel"> |
||||||
|
<x>572</x> |
||||||
|
<y>47</y> |
||||||
|
</hint> |
||||||
|
<hint type="destinationlabel"> |
||||||
|
<x>647</x> |
||||||
|
<y>546</y> |
||||||
|
</hint> |
||||||
|
</hints> |
||||||
|
</connection> |
||||||
|
</connections> |
||||||
|
<slots> |
||||||
|
<slot>addResults()</slot> |
||||||
|
<slot>removeResults()</slot> |
||||||
|
<slot>dailyToggled(bool)</slot> |
||||||
|
<slot>monthlyToggled(bool)</slot> |
||||||
|
</slots> |
||||||
|
</ui> |
||||||
Loading…
Reference in new issue