Skip to content

Commit 336b04b

Browse files
committed
Add load config to avoid settings data file at each launch
1 parent 2944f22 commit 336b04b

13 files changed

+205
-155
lines changed

DataFile.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
class DataFile():
3+
4+
def __init__(self):
5+
6+
# File location
7+
self.filePath = ""
8+
self.fileName = ""
9+
10+
# File import parameters
11+
self.timeFrame = ""
12+
self.separator = ""
13+
self.timeFormat = ""
14+
15+
# Panda data frame
16+
self.dataFrame = None
17+
18+
pass
19+
20+
21+
22+
23+
24+

SkinokBacktraderUI.py

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from CerebroEnhanced import *
2424

2525
import sys, os
26+
from DataFile import DataFile
2627

2728
from dataManager import DataManager
2829
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + '/observers')
@@ -38,6 +39,8 @@
3839
from wallet import Wallet
3940
from userConfig import UserConfig
4041

42+
from PyQt6 import QtCore
43+
4144
class SkinokBacktraderUI:
4245

4346
def __init__(self):
@@ -48,7 +51,7 @@ def __init__(self):
4851

4952
# Init attributes
5053
self.strategyParameters = {}
51-
self.dataframes = {}
54+
self.dataFiles = {}
5255

5356
# Global is here to update the Ui in observers easily, if you find a better way, don't hesistate to tell me (Skinok)
5457
global interface
@@ -82,23 +85,32 @@ def loadConfig(self):
8285
isEmpty = True
8386

8487
# Load previous data files
85-
for timeframe in self.timeFrameIndex.keys():
88+
#for timeframe in self.timeFrameIndex.keys():
8689

87-
if timeframe in userConfig.data.keys():
88-
89-
filePath = userConfig.data[timeframe]['filePath']
90-
timeFormat = userConfig.data[timeframe]['timeFormat']
91-
separator = userConfig.data[timeframe]['separator']
90+
for timeframe in userConfig.data.keys():
91+
92+
dataFile = DataFile()
93+
94+
dataFile.filePath = userConfig.data[timeframe]['filePath']
95+
dataFile.fileName = userConfig.data[timeframe]['fileName']
96+
dataFile.timeFormat = userConfig.data[timeframe]['timeFormat']
97+
dataFile.separator = userConfig.data[timeframe]['separator']
98+
dataFile.timeFrame = timeframe
9299

93-
fileName = os.path.basename(filePath)
100+
if not dataFile.timeFormat in self.dataFiles:
101+
dataFile.dataFrame, errorMessage = self.dataManager.loadDataFrame(dataFile)
94102

95-
df, errorMessage = self.dataManager.loadDataFile(filePath,timeFormat, separator)
103+
if dataFile.dataFrame is not None:
96104

97-
if df is not None:
98-
self.dataframes[fileName] = df
99105
isEmpty = False
100-
else:
101-
print(f" Error loading user data file : {errorMessage}")
106+
107+
# REALLY UGLY : it should be a function of user interface
108+
items = self.interface.strategyTesterUI.loadDataFileUI.dataFilesListWidget.findItems(dataFile.fileName, QtCore.Qt.MatchFixedString)
109+
110+
if len(items) == 0:
111+
self.interface.strategyTesterUI.loadDataFileUI.dataFilesListWidget.addItem(os.path.basename(dataFile.filePath))
112+
113+
self.dataFiles[dataFile.timeFrame] = dataFile;
102114

103115
if not isEmpty:
104116
self.importData()
@@ -140,17 +152,17 @@ def resetCerebro(self):
140152
def importData(self):
141153

142154
try:
143-
144-
fileNames = list(self.dataframes.keys())
155+
156+
timeFrames = list(self.dataFiles.keys())
145157

146158
# Sort data by timeframe
147159
# For cerebro, we need to add lower timeframes first
148-
fileNames.sort( key=lambda x: self.timeFrameIndex[self.dataManager.findTimeFrame(self.dataframes[x])] )
160+
timeFrames.sort( key=lambda x: self.timeFrameIndex[x] )
149161

150162
# Files should be loaded in the good order
151-
for fileName in fileNames:
163+
for timeFrame in timeFrames:
152164

153-
df = self.dataframes[fileName]
165+
df = self.dataFiles[timeFrame].dataFrame
154166

155167
# Datetime first column : 2012-12-28 17:45:00
156168
#self.dataframe['TimeInt'] = pd.to_datetime(self.dataframe.index).astype('int64') # use finplot's internal representation, which is ns
@@ -161,24 +173,21 @@ def importData(self):
161173
# Add data to cerebro : only add data when all files have been selected for multi-timeframes
162174
self.cerebro.adddata(self.data) # Add the data feed
163175

164-
# Find timeframe
165-
timeframe = self.dataManager.findTimeFrame(df)
166-
167176
# Create the chart window for the good timeframe (if it does not already exists?)
168-
self.interface.createChartDock(timeframe)
177+
self.interface.createChartDock(timeFrame)
169178

170179
# Draw charts based on input data
171-
self.interface.drawChart(df, timeframe)
180+
self.interface.drawChart(df, timeFrame)
172181

173182
# Enable run button
174183
self.interface.strategyTesterUI.runBacktestBtn.setEnabled(True)
175184

176185
return True
177186

178187
except AttributeError as e:
179-
print("AttributeError error:" + str(e))
188+
print("AttributeError error:" + str(e) + " " + str(sys.exc_info()[0]))
180189
except KeyError as e:
181-
print("KeyError error:" + str(e))
190+
print("KeyError error:" + str(e) + " " + str(sys.exc_info()[0]))
182191
except:
183192
print("Unexpected error:" + str(sys.exc_info()[0]))
184193
return False
@@ -262,12 +271,11 @@ def displayStrategyResults(self):
262271
#self.interface.createTransactionsUI(self.portfolio_transactions)
263272
self.interface.fillSummaryUI(self.strat_results.stats.broker.cash[0], self.strat_results.stats.broker.value[0], self.strat_results.analyzers.ta.get_analysis())
264273
self.interface.fillTradesUI(self.strat_results._trades.items())
265-
274+
self.interface.dock_strategyResultsUI.show()
266275
#self.interface.drawTrades(self.strat_results._trades.items())
267276
#Orders filters
268277
self.myOrders = []
269278
for order in self.strat_results._orders:
270-
271279
if order.status in [order.Completed]:
272280
self.myOrders.append(order)
273281

@@ -281,7 +289,7 @@ def displayStrategyResults(self):
281289
pnl_data['cash'] = self.wallet.cash_list
282290

283291
# really uggly
284-
pnl_data['time'] = list(self.dataframes.values())[0].index
292+
pnl_data['time'] = list(self.dataFiles.values())[0].dataFrame.index
285293

286294
# draw charts
287295
df = pd.DataFrame(pnl_data)

dataManager.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,27 +32,27 @@ def DatetimeFormat(self, dataFilePath):
3232

3333
# Return True if loading is successfull & the error string if False
3434
# dataPath is the full file path
35-
def loadDataFile(self, dataPath, datetimeFormat, separator):
35+
def loadDataFrame(self, loadDataFile):
3636

3737
# Try importing data file
3838
# We should code a widget that ask for options as : separators, date format, and so on...
3939
try:
4040

4141
# Python contains
4242
if pd.__version__<'2.0.0':
43-
df = pd.read_csv(dataPath,
44-
sep=separator,
43+
df = pd.read_csv(loadDataFile.filePath,
44+
sep=loadDataFile.separator,
4545
parse_dates=[0],
46-
date_parser=lambda x: pd.to_datetime(x, format=datetimeFormat),
46+
date_parser=lambda x: pd.to_datetime(x, format=loadDataFile.timeFormat),
4747
skiprows=0,
4848
header=0,
4949
names=["Time", "Open", "High", "Low", "Close", "Volume"],
5050
index_col=0)
5151
else:
52-
df = pd.read_csv(dataPath,
53-
sep=separator,
52+
df = pd.read_csv(loadDataFile.filePath,
53+
sep=loadDataFile.separator,
5454
parse_dates=[0],
55-
date_format=datetimeFormat,
55+
date_format=loadDataFile.timeFormat,
5656
skiprows=0,
5757
header=0,
5858
names=["Time", "Open", "High", "Low", "Close", "Volume"],

finplotWindow.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,6 @@ def drawOrders(self, orders = None):
129129
##############
130130
if order.isbuy():
131131

132-
direction = "buy"
133-
134132
# Tracer les traites allant des ouvertures de positions vers la fermeture de position
135133
if currentPositionSize < 0:
136134

@@ -172,8 +170,6 @@ def drawOrders(self, orders = None):
172170
# Sell
173171
##############
174172
elif order.issell():
175-
direction = "sell"
176-
177173

178174
if currentPositionSize < 0:
179175
# Augmentation de la postion
@@ -220,7 +216,7 @@ def drawOrders(self, orders = None):
220216
currentPositionSize += order.size
221217

222218
# Todo: We could display the size of the order with a label on the chart
223-
fplt.add_order(bt.num2date(order.executed.dt), order.executed.price, direction, ax=self.ax0)
219+
fplt.add_order(bt.num2date(order.executed.dt), order.executed.price, order.isbuy(), ax=self.ax0)
224220

225221
pass
226222

loadDataFilesUI.py

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from PyQt6 import QtCore, QtWidgets, uic
22
import pandas as pd
3-
import os
3+
import os
4+
from DataFile import DataFile
45

56
from userConfig import UserConfig
67
from dataManager import DataManager
@@ -33,13 +34,16 @@ def __init__(self, controller, parent = None):
3334
self.deletePB = self.findChild(QtWidgets.QLineEdit, "deletePB")
3435
self.importPB = self.findChild(QtWidgets.QPushButton, "importPB")
3536

37+
self.deleteDataFilePB = self.findChild(QtWidgets.QPushButton, "deleteDataFilePB")
38+
3639
self.errorLabel = self.findChild(QtWidgets.QLabel, "errorLabel")
3740

3841
self.dataFilesListWidget = self.findChild(QtWidgets.QListWidget, "dataFilesListWidget")
3942

4043
# Connect slots : open file
4144
self.openFilePB.clicked.connect( self.openFileDialog )
42-
self.loadFilePB.clicked.connect( self.loadFile )
45+
self.loadFilePB.clicked.connect( self.createDataFile )
46+
self.deleteDataFilePB.clicked.connect(self.deleteFile)
4347
self.importPB.clicked.connect( self.importFiles )
4448

4549
self.dataManager = DataManager()
@@ -55,56 +59,83 @@ def openFileDialog(self):
5559

5660
pass
5761

58-
def loadFile(self):
62+
def createDataFile(self):
5963

60-
# try loading file by controller
61-
separator = '\t' if self.tabRB.isChecked() else ',' if self.commaRB.isChecked() else ';'
64+
dataFile = DataFile()
6265

63-
timeFormat = self.datetimeFormatLE.text()
66+
# try loading file by controller
67+
dataFile.separator = '\t' if self.tabRB.isChecked() else ',' if self.commaRB.isChecked() else ';'
68+
dataFile.timeFormat = self.datetimeFormatLE.text()
69+
dataFile.filePath = self.dataFilePath
70+
dataFile.fileName = os.path.basename(self.dataFilePath)
6471

65-
if not self.dataFilePath in self.controller.dataframes:
72+
if not dataFile.timeFormat in self.controller.dataFiles:
6673

67-
fileName = os.path.basename(self.dataFilePath)
74+
dataFile.dataFrame, errorMessage = self.dataManager.loadDataFrame(dataFile)
6875

69-
df, errorMessage = self.dataManager.loadDataFile(self.dataFilePath, timeFormat, separator)
76+
# Store data file in the user config parameters to later use
77+
dataFile.timeFrame = self.dataManager.findTimeFrame(dataFile.dataFrame)
7078

71-
if df is not None:
72-
73-
# ugly to reference the controller this way
74-
self.controller.dataframes[fileName] = df
79+
if dataFile.dataFrame is not None:
7580

7681
self.errorLabel.setStyleSheet("color:green")
7782
self.errorLabel.setText("The file has been loaded correctly.")
7883

7984
# Add file name
80-
items = self.dataFilesListWidget.findItems(fileName, QtCore.Qt.MatchFixedString)
85+
items = self.dataFilesListWidget.findItems(dataFile.fileName, QtCore.Qt.MatchFixedString)
8186

8287
if len(items) == 0:
83-
self.dataFilesListWidget.addItem(os.path.basename(self.dataFilePath))
84-
85-
# Store data file in the user config parameters to later use
86-
timeframe = self.dataManager.findTimeFrame(df)
87-
self.userConfig.saveParameter(timeframe, {"filePath": self.dataFilePath, "separator" : separator, "timeFormat": timeFormat})
88+
self.dataFilesListWidget.addItem(os.path.basename(dataFile.filePath))
89+
90+
self.controller.dataFiles[dataFile.timeFrame] = dataFile;
91+
self.userConfig.saveObject(dataFile.timeFrame, dataFile)
8892

8993
else:
9094
self.errorLabel.setStyleSheet("color:red")
9195
self.errorLabel.setText(errorMessage)
92-
96+
else:
97+
self.errorLabel.setStyleSheet("color:red")
98+
self.errorLabel.setText("The file is already in the list")
9399
pass
94100

95101

96-
def importFiles(self):
102+
def loadDataFileFromConfig(self, dataPath, datetimeFormat, separator):
103+
104+
fileName = os.path.basename(dataPath)
105+
df, errorMessage = self.dataManager.loadDataFrame(dataPath, datetimeFormat, separator)
106+
107+
if df is not None:
97108

98-
# Get all element in list widget
99-
#items = []
100-
#for x in range(self.dataFilesListWidget.count()):
101-
#items.append(self.dataFilesListWidget.item(x).text())
109+
# Add file name
110+
items = self.dataFilesListWidget.findItems(fileName, QtCore.Qt.MatchFixedString)
102111

103-
# Sort item by timeframe
112+
if len(items) == 0:
113+
self.dataFilesListWidget.addItem(os.path.basename(dataPath))
114+
115+
return df
116+
117+
def deleteFile(self):
118+
119+
listItems=self.dataFilesListWidget.selectedItems()
120+
if not listItems: return
121+
for item in listItems:
122+
itemTaken = self.dataFilesListWidget.takeItem(self.dataFilesListWidget.row(item))
123+
124+
# Delete from dataFrames
125+
del self.controller.dataframes[itemTaken.text()]
126+
127+
# Delete from Cerebro ?
128+
129+
130+
# Delete from config
131+
self.userConfig.removeParameter(timeframe);
132+
133+
pass
134+
135+
def importFiles(self):
104136

105137
# Give all ordered data path to the controller
106138
if self.controller.importData():
107-
self.dataFilesListWidget.clear()
108139
self.hide()
109140

110141
pass

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy