Skip to content

Commit 32e6c7f

Browse files
committed
Rework on Strategy UI + fix minor bugs
1 parent e3d1d3d commit 32e6c7f

10 files changed

+1043
-298
lines changed

SkinokBacktraderUI.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ def importData(self, fileNames):
169169
self.interface.drawChart(df, timeframe)
170170

171171
# Enable run button
172-
self.interface.strategyTesterUI.runBacktestPB.setEnabled(True)
172+
self.interface.strategyTesterUI.runBacktestBtn.setEnabled(True)
173173

174174
return True
175175

finplotWindow.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def __init__(self, dockArea, dockChart, interface):
4343

4444
self.last_ax_data_xtick = []
4545

46-
46+
4747
pass
4848

4949
#########
@@ -68,6 +68,7 @@ def createPlotWidgets(self, timeframe):
6868
# Ax Profit & Loss
6969
self.interface.strategyResultsUI.ResultsTabWidget.widget(1).layout().addWidget(self.axPnL.ax_widget)
7070

71+
fplt.add_crosshair_info(self.update_crosshair_text, ax=self.ax0)
7172
pass
7273

7374
def drawCandles(self):
@@ -76,7 +77,7 @@ def drawCandles(self):
7677

7778
#self.hover_label = fplt.add_legend('', ax=self.ax0)
7879
#fplt.set_time_inspector(self.update_legend_text, ax=self.ax0, when='hover', data=data)
79-
fplt.add_crosshair_info(self.update_crosshair_text, ax=self.ax0)
80+
8081

8182
# Inside plot widget controls
8283
#self.createControlPanel(self.ax0.ax_widget)
@@ -238,9 +239,9 @@ def update_legend_text(self, x, y, ax, data):
238239
pass
239240

240241
def update_crosshair_text(self,x, y, xtext, ytext):
241-
ytext = '%s \n open: %.4f\n close: %.4f\n high: %.4f\n low: %.4f' \
242-
% (ytext, self.data.iloc[x].Open, self.data.iloc[x].Close, self.data.iloc[x].High, self.data.iloc[x].Low)
243-
return xtext, ytext
242+
ytext = '%s \n Open: %.5f\n Close: %.5f\n High: %.5f\n Low: %.5f' \
243+
% (ytext, self.data.iloc[x].Open, self.data.iloc[x].Close, self.data.iloc[x].High, self.data.iloc[x].Low)
244+
return xtext,ytext
244245

245246
def activateDarkMode(self, activated):
246247

strategyResultsUI.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@
44

55
class StrategyResultsUI(QtWidgets.QWidget):
66

7-
def __init__(self, controller):
7+
def __init__(self, controller, parent = None):
88
super(StrategyResultsUI, self).__init__()
99

1010
self.controller = controller
1111

12+
self.parent = parent
13+
1214
# It does not finish by a "/"
1315
self.current_dir_path = os.path.dirname(os.path.realpath(__file__))
1416

1517
uic.loadUi( self.current_dir_path + "/ui/strategyResults.ui", self)
1618

17-
self.SummaryGB = self.findChild(QtWidgets.QGroupBox, "SummaryGB")
19+
self.summaryTableWidget= self.findChild(QtWidgets.QTableWidget, "summaryTableWidget")
1820
self.TradesGB = self.findChild(QtWidgets.QGroupBox, "TradesGB")
1921

strategyTesterUI.py

+117-7
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,58 @@
11
from PyQt6 import QtCore, QtWidgets, uic
22

33
import os
4+
import loadDataFilesUI
45

56
class StrategyTesterUI(QtWidgets.QWidget):
67

7-
def __init__(self, controller):
8+
def __init__(self, controller, parentWindow):
89
super(StrategyTesterUI, self).__init__()
910

1011
self.controller = controller
1112

13+
self.parent = parentWindow
14+
1215
# It does not finish by a "/"
1316
self.current_dir_path = os.path.dirname(os.path.realpath(__file__))
1417

1518
uic.loadUi( self.current_dir_path + "/ui/strategyTester.ui", self)
19+
20+
# Data
21+
self.importDataBtn = self.findChild(QtWidgets.QPushButton, "importDataBtn")
22+
self.importDataBtn.clicked.connect( self.loadData )
23+
24+
# Strategy type PushButtons
25+
self.strategyTypeAITensorFlowBtn = self.findChild(QtWidgets.QPushButton, "strategyTypeAITensorFlowBtn")
26+
self.strategyTypeAiStablebaselinesBtn = self.findChild(QtWidgets.QPushButton, "strategyTypeAiStablebaselinesBtn")
27+
self.strategyTypeAlgoBtn = self.findChild(QtWidgets.QPushButton, "strategyTypeAlgoBtn")
28+
self.strategyTypeAiTorchBtn = self.findChild(QtWidgets.QPushButton, "strategyTypeAiTorchBtn")
1629

17-
self.runBacktestPB = self.findChild(QtWidgets.QPushButton, "runBacktestPB")
18-
self.runBacktestPB.clicked.connect(self.run)
30+
self.strategyTypeDetailsSW = self.findChild(QtWidgets.QStackedWidget, "strategyTypeDetailsSW")
1931

20-
self.runningStratPB = self.findChild(QtWidgets.QProgressBar, "runningStratPB")
32+
# Ai Algo
33+
self.AiModelPathLE = self.findChild(QtWidgets.QLineEdit, "AiModelPathLE")
34+
self.AiModelPathBtn = self.findChild(QtWidgets.QPushButton, "AiModelPathBtn")
2135

36+
# Custom Algo
37+
self.runningStratBtn = self.findChild(QtWidgets.QProgressBar, "runningStratBtn")
2238
self.strategyNameCB = self.findChild(QtWidgets.QComboBox, "strategyNameCB")
39+
40+
# Run button
41+
self.runBacktestBtn = self.findChild(QtWidgets.QPushButton, "runBacktestBtn")
42+
43+
# Connect ui buttons
44+
self.strategyTypeAITensorFlowBtn.clicked.connect(self.strategyTypeAITensorFlowActivated)
45+
self.strategyTypeAiStablebaselinesBtn.clicked.connect(self.strategyTypeAiStablebaselinesActivated)
46+
self.strategyTypeAiTorchBtn.clicked.connect(self.strategyTypeAiTorchActivated)
47+
self.strategyTypeAlgoBtn.clicked.connect(self.strategyTypeAlgoActivated)
48+
49+
self.AiModelPathBtn.clicked.connect(self.openAiFileDialog)
50+
2351
self.strategyNameCB.currentIndexChanged.connect(self.strategyNameActivated)
52+
self.runBacktestBtn.clicked.connect(self.run)
2453

25-
self.runBacktestPB.setEnabled(False)
54+
# Init Run button to false waiting for user inputs
55+
self.runBacktestBtn.setEnabled(False)
2656

2757
def initialize(self):
2858

@@ -33,16 +63,96 @@ def initialize(self):
3363
self.strategyBaseName = []
3464
for stratName in self.strategyNames:
3565
# here remove file extension
36-
self.strategyBaseName.append(QtCore.QFileInfo(stratName).baseName())
66+
if not stratName.startswith('Ai'):
67+
self.strategyBaseName.append(QtCore.QFileInfo(stratName).baseName())
3768

3869
self.strategyNameCB.addItems(self.strategyBaseName)
3970
self.strategyNameCB.setCurrentIndex(self.strategyNameCB.count()-1)
71+
72+
#
73+
self.loadDataFileUI = loadDataFilesUI.LoadDataFilesUI(self.controller, self.parent)
74+
self.loadDataFileUI.hide()
75+
pass
76+
77+
def loadData(self):
78+
self.loadDataFileUI.show()
4079
pass
4180

4281
def run(self):
4382
self.controller.run()
83+
pass
4484

4585
def strategyNameActivated(self):
4686
stratBaseName = self.strategyNameCB.currentText()
4787
self.controller.addStrategy(stratBaseName)
48-
88+
pass
89+
90+
def strategyTypeAITensorFlowActivated(self):
91+
if self.strategyTypeAITensorFlowBtn.isChecked():
92+
self.strategyTypeDetailsSW.setCurrentIndex(1)
93+
self.AiModelChecked = "AiTensorFlowModel"
94+
pass
95+
96+
def strategyTypeAiStablebaselinesActivated(self):
97+
if self.strategyTypeAiStablebaselinesBtn.isChecked():
98+
self.strategyTypeDetailsSW.setCurrentIndex(1)
99+
self.AiModelChecked = "AiStableBaselinesModel"
100+
pass
101+
102+
def strategyTypeAiTorchActivated(self):
103+
if self.strategyTypeAiTorchBtn.isChecked():
104+
self.strategyTypeDetailsSW.setCurrentIndex(1)
105+
self.AiModelChecked = "AiTorchModel"
106+
pass
107+
108+
def strategyTypeAlgoActivated(self):
109+
if self.strategyTypeAlgoBtn.isChecked():
110+
self.strategyTypeDetailsSW.setCurrentIndex(0)
111+
pass
112+
113+
def openAiFileDialog(self):
114+
115+
if self.AiModelChecked == "AiTensorFlowModel":
116+
self.loadTFModel()
117+
elif self.AiModelChecked == "AiStableBaselinesModel":
118+
self.loadStableBaselinesModel()
119+
elif self.AiModelChecked == "AiTorchModel":
120+
self.loadTorchModel()
121+
122+
pass
123+
124+
# Load an AI Model from Tensor Flow framework
125+
def loadTFModel(self):
126+
127+
ai_model_dir = QtWidgets.QFileDialog.getExistingDirectory(self.parent,"Open Tensorflow Model", self.current_dir_path)
128+
129+
self.controller.addStrategy(self.AiModelChecked)
130+
131+
self.AiModelPathLE.setText(ai_model_dir)
132+
self.controller.strategyParametersSave("model", ai_model_dir)
133+
134+
pass
135+
136+
# Load an AI Model from Stable Baselines framework
137+
def loadStableBaselinesModel(self):
138+
139+
ai_model_zip_file = QtWidgets.QFileDialog.getOpenFileName(self.parent,"Open Torch Model", self.current_dir_path, "*.zip")[0]
140+
141+
self.controller.addStrategy(self.AiModelChecked)
142+
143+
self.AiModelPathLE.setText(ai_model_zip_file)
144+
self.controller.strategyParametersSave("model", ai_model_zip_file)
145+
146+
pass
147+
148+
# Load an AI Model from Py Torch framework
149+
def loadTorchModel(self):
150+
151+
ai_model_zip_file = QtWidgets.QFileDialog.getOpenFileName(self.parent,"Open Torch Model", self.current_dir_path, "*.zip")[0]
152+
153+
self.controller.addStrategy(self.AiModelChecked)
154+
155+
self.AiModelPathLE.setText(ai_model_zip_file)
156+
self.controller.strategyParametersSave("model", ai_model_zip_file)
157+
158+
pass

ui/loadDataFiles_ui.py

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Form implementation generated from reading ui file 'c:\perso\trading\anaconda3\backtrader-ichimoku\ui\loadDataFiles.ui'
2+
#
3+
# Created by: PyQt6 UI code generator 6.5.0
4+
#
5+
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
6+
# run again. Do not edit this file unless you know what you are doing.
7+
8+
9+
from PyQt6 import QtCore, QtGui, QtWidgets
10+
11+
12+
class Ui_Form(object):
13+
def setupUi(self, Form):
14+
Form.setObjectName("Form")
15+
Form.resize(488, 458)
16+
self.gridLayout_2 = QtWidgets.QGridLayout(Form)
17+
self.gridLayout_2.setObjectName("gridLayout_2")
18+
self.dataFilesListWidget = QtWidgets.QListWidget(parent=Form)
19+
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
20+
sizePolicy.setHorizontalStretch(0)
21+
sizePolicy.setVerticalStretch(0)
22+
sizePolicy.setHeightForWidth(self.dataFilesListWidget.sizePolicy().hasHeightForWidth())
23+
self.dataFilesListWidget.setSizePolicy(sizePolicy)
24+
self.dataFilesListWidget.setObjectName("dataFilesListWidget")
25+
self.gridLayout_2.addWidget(self.dataFilesListWidget, 3, 0, 1, 1)
26+
self.label_4 = QtWidgets.QLabel(parent=Form)
27+
self.label_4.setObjectName("label_4")
28+
self.gridLayout_2.addWidget(self.label_4, 2, 0, 1, 1)
29+
self.importPB = QtWidgets.QPushButton(parent=Form)
30+
self.importPB.setMinimumSize(QtCore.QSize(0, 40))
31+
self.importPB.setObjectName("importPB")
32+
self.gridLayout_2.addWidget(self.importPB, 5, 0, 1, 2)
33+
self.verticalLayout = QtWidgets.QVBoxLayout()
34+
self.verticalLayout.setObjectName("verticalLayout")
35+
self.gridLayout_2.addLayout(self.verticalLayout, 3, 1, 1, 1)
36+
self.groupBox = QtWidgets.QGroupBox(parent=Form)
37+
self.groupBox.setObjectName("groupBox")
38+
self.gridLayout = QtWidgets.QGridLayout(self.groupBox)
39+
self.gridLayout.setObjectName("gridLayout")
40+
self.semicolonRB = QtWidgets.QRadioButton(parent=self.groupBox)
41+
self.semicolonRB.setObjectName("semicolonRB")
42+
self.gridLayout.addWidget(self.semicolonRB, 3, 3, 1, 1)
43+
self.label = QtWidgets.QLabel(parent=self.groupBox)
44+
self.label.setObjectName("label")
45+
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
46+
self.label_3 = QtWidgets.QLabel(parent=self.groupBox)
47+
self.label_3.setObjectName("label_3")
48+
self.gridLayout.addWidget(self.label_3, 1, 0, 1, 1)
49+
self.label_2 = QtWidgets.QLabel(parent=self.groupBox)
50+
self.label_2.setObjectName("label_2")
51+
self.gridLayout.addWidget(self.label_2, 3, 0, 1, 1)
52+
self.tabRB = QtWidgets.QRadioButton(parent=self.groupBox)
53+
self.tabRB.setChecked(True)
54+
self.tabRB.setObjectName("tabRB")
55+
self.gridLayout.addWidget(self.tabRB, 3, 1, 1, 1)
56+
self.commaRB = QtWidgets.QRadioButton(parent=self.groupBox)
57+
self.commaRB.setObjectName("commaRB")
58+
self.gridLayout.addWidget(self.commaRB, 3, 2, 1, 1)
59+
self.filePathLE = QtWidgets.QLineEdit(parent=self.groupBox)
60+
self.filePathLE.setObjectName("filePathLE")
61+
self.gridLayout.addWidget(self.filePathLE, 0, 1, 1, 3)
62+
self.openFilePB = QtWidgets.QToolButton(parent=self.groupBox)
63+
self.openFilePB.setObjectName("openFilePB")
64+
self.gridLayout.addWidget(self.openFilePB, 0, 4, 1, 1)
65+
self.datetimeFormatLE = QtWidgets.QComboBox(parent=self.groupBox)
66+
self.datetimeFormatLE.setObjectName("datetimeFormatLE")
67+
self.gridLayout.addWidget(self.datetimeFormatLE, 1, 1, 1, 4)
68+
self.loadFilePB = QtWidgets.QPushButton(parent=self.groupBox)
69+
self.loadFilePB.setMinimumSize(QtCore.QSize(0, 40))
70+
self.loadFilePB.setObjectName("loadFilePB")
71+
self.gridLayout.addWidget(self.loadFilePB, 4, 1, 1, 4)
72+
self.errorLabel = QtWidgets.QLabel(parent=self.groupBox)
73+
self.errorLabel.setStyleSheet("color: red")
74+
self.errorLabel.setText("")
75+
self.errorLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
76+
self.errorLabel.setObjectName("errorLabel")
77+
self.gridLayout.addWidget(self.errorLabel, 5, 0, 1, 5)
78+
self.gridLayout_2.addWidget(self.groupBox, 0, 0, 1, 2)
79+
self.label_5 = QtWidgets.QLabel(parent=Form)
80+
self.label_5.setStyleSheet("font-style: italic")
81+
self.label_5.setScaledContents(False)
82+
self.label_5.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
83+
self.label_5.setWordWrap(True)
84+
self.label_5.setObjectName("label_5")
85+
self.gridLayout_2.addWidget(self.label_5, 4, 0, 1, 1)
86+
87+
self.retranslateUi(Form)
88+
QtCore.QMetaObject.connectSlotsByName(Form)
89+
90+
def retranslateUi(self, Form):
91+
_translate = QtCore.QCoreApplication.translate
92+
Form.setWindowTitle(_translate("Form", "Import one or multiple data files"))
93+
self.label_4.setText(_translate("Form", "List of all files to import in cerebro"))
94+
self.importPB.setText(_translate("Form", "Import all data files"))
95+
self.groupBox.setTitle(_translate("Form", "Loading a new data file"))
96+
self.semicolonRB.setText(_translate("Form", "semicolon"))
97+
self.label.setText(_translate("Form", "Import a new data file"))
98+
self.label_3.setText(_translate("Form", "Date time format"))
99+
self.label_2.setText(_translate("Form", "Separator"))
100+
self.tabRB.setText(_translate("Form", "tab"))
101+
self.commaRB.setText(_translate("Form", "comma"))
102+
self.openFilePB.setText(_translate("Form", "..."))
103+
self.loadFilePB.setText(_translate("Form", "Load .CSV file"))
104+
self.label_5.setText(_translate("Form", "Files should be ordered from lower (on top) to higher timeframe (at bottom)."))

0 commit comments

Comments
 (0)