Skip to content

Commit 28c4e06

Browse files
committed
FIX : Load data files
1 parent 336b04b commit 28c4e06

File tree

8 files changed

+191
-75
lines changed

8 files changed

+191
-75
lines changed

SkinokBacktraderUI.py

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#sys.path.append('D:/perso/trading/anaconda3/backtrader2')
2222
import backtrader as bt
2323
from CerebroEnhanced import *
24+
from PyQt6 import QtWidgets
2425

2526
import sys, os
2627
from DataFile import DataFile
@@ -72,6 +73,8 @@ def __init__(self):
7273

7374
self.dataManager = DataManager()
7475

76+
self.datafileName_to_dataFile={}
77+
7578
# Restore previous session for faster tests
7679
self.loadConfig()
7780

@@ -87,15 +90,15 @@ def loadConfig(self):
8790
# Load previous data files
8891
#for timeframe in self.timeFrameIndex.keys():
8992

90-
for timeframe in userConfig.data.keys():
93+
for timeFrame in userConfig.data.keys():
9194

9295
dataFile = DataFile()
9396

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
97+
dataFile.filePath = userConfig.data[timeFrame]['filePath']
98+
dataFile.fileName = userConfig.data[timeFrame]['fileName']
99+
dataFile.timeFormat = userConfig.data[timeFrame]['timeFormat']
100+
dataFile.separator = userConfig.data[timeFrame]['separator']
101+
dataFile.timeFrame = timeFrame
99102

100103
if not dataFile.timeFormat in self.dataFiles:
101104
dataFile.dataFrame, errorMessage = self.dataManager.loadDataFrame(dataFile)
@@ -104,19 +107,34 @@ def loadConfig(self):
104107

105108
isEmpty = False
106109

110+
self.datafileName_to_dataFile[dataFile.fileName] = dataFile
111+
107112
# REALLY UGLY : it should be a function of user interface
108113
items = self.interface.strategyTesterUI.loadDataFileUI.dataFilesListWidget.findItems(dataFile.fileName, QtCore.Qt.MatchFixedString)
109114

110115
if len(items) == 0:
111-
self.interface.strategyTesterUI.loadDataFileUI.dataFilesListWidget.addItem(os.path.basename(dataFile.filePath))
116+
self.interface.strategyTesterUI.loadDataFileUI.dataFilesListWidget.addItem(dataFile.fileName)
112117

113-
self.dataFiles[dataFile.timeFrame] = dataFile;
118+
self.dataFiles[dataFile.timeFrame] = dataFile
114119

115120
if not isEmpty:
116121
self.importData()
117122

118123
pass
119124

125+
def removeTimeframe(self, timeFrame):
126+
127+
# Delete from controler
128+
del self.dataFiles[timeFrame]
129+
130+
# Delete from chart
131+
self.interface.deleteChartDock(timeFrame)
132+
133+
# Delete from Cerebro ?
134+
self.resetCerebro()
135+
136+
pass
137+
120138
def resetCerebro(self):
121139

122140
# create a "Cerebro" engine instance
@@ -211,19 +229,21 @@ def addStrategy(self, strategyName):
211229

212230
pass
213231

214-
def strategyParametersChanged(self, lineEdit, parameterName, parameterOldValue):
232+
def strategyParametersChanged(self, widget, parameterName, parameterOldValue):
215233

216234
# todo something
217-
if len(lineEdit.text()) > 0:
235+
if len(widget.text()) > 0:
218236

219237
param = self.strategyClass.params._get(self.strategyClass.params,parameterName)
220238

221-
if isinstance(param, int):
222-
self.strategyParameters[parameterName] = int(lineEdit.text())
239+
if isinstance(param, bool):
240+
self.strategyParameters[parameterName] = widget.checkState() == QtCore.Qt.CheckState.Checked
241+
elif isinstance(param, int):
242+
self.strategyParameters[parameterName] = int(widget.text())
223243
elif isinstance(param, float):
224-
self.strategyParameters[parameterName] = float(lineEdit.text())
244+
self.strategyParameters[parameterName] = float(widget.text())
225245
else:
226-
self.strategyParameters[parameterName] = lineEdit.text()
246+
self.strategyParameters[parameterName] = widget.text()
227247

228248
pass
229249

finplotWindow.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ def __init__(self, dockArea, dockChart, interface):
4343

4444
self.last_ax_data_xtick = []
4545

46-
4746
pass
4847

4948
#########

loadDataFilesUI.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ def loadDataFileFromConfig(self, dataPath, datetimeFormat, separator):
114114

115115
return df
116116

117+
117118
def deleteFile(self):
118119

119120
listItems=self.dataFilesListWidget.selectedItems()
@@ -122,13 +123,13 @@ def deleteFile(self):
122123
itemTaken = self.dataFilesListWidget.takeItem(self.dataFilesListWidget.row(item))
123124

124125
# Delete from dataFrames
125-
del self.controller.dataframes[itemTaken.text()]
126-
127-
# Delete from Cerebro ?
126+
timeFrame = self.controller.datafileName_to_dataFile[itemTaken.text()].timeFrame
128127

128+
# Delete from controler
129+
self.controller.removeTimeframe(timeFrame)
129130

130131
# Delete from config
131-
self.userConfig.removeParameter(timeframe);
132+
self.userConfig.removeParameter(timeFrame)
132133

133134
pass
134135

strategies/AiStableBaselinesModel.py

Lines changed: 119 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@
2020

2121
import metaStrategy as mt
2222

23-
from stable_baselines3 import PPO
23+
from stable_baselines3 import PPO, DQN
2424

2525
import numpy as np
2626
from enum import Enum
2727

2828
import pandas as pd
29+
import pandas_ta as ta
2930

3031
from custom_indicators import BollingerBandsBandwitch
3132

@@ -40,10 +41,14 @@ class AiStableBaselinesModel(mt.MetaStrategy):
4041

4142
params = (
4243
('model', ""), # Model name
43-
('tradeSize', 5000),
44+
('tradeSize', 10.0),
45+
('use_ATR_SL', True),
4446
('atrperiod', 14), # ATR Period (standard)
45-
('atrdist_SL', 3), # ATR distance for stop price
46-
('atrdist_TP', 5), # ATR distance for take profit price
47+
('atrdist_SL', 3.0), # ATR distance for stop price
48+
('atrdist_TP', 5.0), # ATR distance for take profit price
49+
('use_Fixed_SL', False),
50+
('fixed_SL', 50.0), # Fixed distance Stop Loss
51+
('fixed_TP', 100.0), # Fixed distance Take Profit
4752
)
4853

4954
def notify_order(self, order):
@@ -60,7 +65,7 @@ def __init__(self, *argv):
6065
super().__init__(argv[0])
6166

6267
# Ichi indicator
63-
self.ichimoku = bt.ind.Ichimoku()
68+
self.ichimoku = bt.ind.Ichimoku(self.data)
6469

6570
# To set the stop price
6671
self.atr = bt.indicators.ATR(self.data, period=self.p.atrperiod)
@@ -86,71 +91,78 @@ def __init__(self, *argv):
8691

8792
self.macd = bt.ind.MACDHisto(self.data)
8893

94+
self.normalization_bounds_df_min = self.loadNormalizationBoundsCsv( "C:/perso/AI/AI_Framework/prepared_data/normalization_bounds_min.csv" ).transpose()
95+
self.normalization_bounds_df_max = self.loadNormalizationBoundsCsv( "C:/perso/AI/AI_Framework/prepared_data/normalization_bounds_max.csv" ).transpose()
96+
8997
pass
9098

9199
def start(self):
92100
self.order = None # sentinel to avoid operrations on pending order
93101

94102
# Load the model
95103
self.model = PPO.load(self.p.model)
104+
#self.model = DQN.load(self.p.model)
96105
pass
97106

98107
def next(self):
99108

100109
self.obseravation = self.next_observation()
101110

111+
# Normalize observation
112+
#self.normalizeObservations()
113+
102114
# Do nothing if a parameter is not valid yet (basically wait for all idicators to be loaded)
115+
103116
if pd.isna(self.obseravation).any():
104117
print("Waiting indicators")
105118
return
106-
119+
107120
# Prepare data for Model
108121
action, _states = self.model.predict(self.obseravation) # deterministic=True
109122

110123
if not self.position: # not in the market
111124

125+
# TP & SL calculation
126+
loss_dist = self.atr[0] * self.p.atrdist_SL if self.p.use_ATR_SL else self.p.fixed_SL
127+
profit_dist = self.atr[0] * self.p.atrdist_TP if self.p.use_ATR_SL else self.p.fixed_TP
128+
112129
if action == Action.SELL.value:
113130
self.order = self.sell(size=self.p.tradeSize)
114-
ldist = self.atr[0] * self.p.atrdist_SL
115-
self.lstop = self.data.close[0] + ldist
116-
pdist = self.atr[0] * self.p.atrdist_TP
117-
self.take_profit = self.data.close[0] - pdist
131+
self.lstop = self.data.close[0] + loss_dist
132+
self.take_profit = self.data.close[0] - profit_dist
118133

119134
elif action == Action.BUY.value:
120135
self.order = self.buy(size=self.p.tradeSize)
121-
ldist = self.atr[0] * self.p.atrdist_SL
122-
self.lstop = self.data.close[0] - ldist
123-
pdist = self.atr[0] * self.p.atrdist_TP
124-
self.take_profit = self.data.close[0] + pdist
136+
self.lstop = self.data.close[0] - loss_dist
137+
self.take_profit = self.data.close[0] + profit_dist
125138

126139
else: # in the market
127140
pclose = self.data.close[0]
128-
pstop = self.lstop # seems to be the bug
129141

130-
if (not ((pstop<pclose<self.take_profit)|(pstop>pclose>self.take_profit))):
142+
if (not ((self.lstop<pclose<self.take_profit)|(self.lstop>pclose>self.take_profit))):
131143
self.close() # Close position
132144

133145
pass
134146

135147
# Here you have to transform self object price and indicators into a np.array input for AI Model
136148
# How you do it depend on your AI Model inputs
137149
# Strategy is in the data preparation for AI :D
138-
def next_observation(self):
139-
140-
# https://stackoverflow.com/questions/53979199/tensorflow-keras-returning-multiple-predictions-while-expecting-one
150+
# https://stackoverflow.com/questions/53979199/tensorflow-keras-returning-multiple-predictions-while-expecting-one
141151

142-
# Ichimoku
143-
#inputs = [ self.ichimoku.tenkan[0], self.ichimoku.kijun[0], self.ichimoku.senkou[0], self.ichimoku.senkou_lead[0], self.ichimoku.chikou[0] ]
152+
def next_observation(self):
144153

145154
# OHLCV
146155
inputs = [ self.data.open[0],self.data.high[0],self.data.low[0],self.data.close[0],self.data.volume[0] ]
147156

148-
# Stochastic
149-
inputs = inputs + [self.stochastic.percK[0], self.stochastic.percD[0]]
157+
# Ichimoku
158+
inputs = inputs + [ self.ichimoku.senkou_span_a[0], self.ichimoku.senkou_span_b[0], self.ichimoku.tenkan_sen[0], self.ichimoku.kijun_sen[0], self.ichimoku.chikou_span[0] ]
150159

151160
# Rsi
152161
inputs = inputs + [self.rsi.rsi[0]]
153162

163+
# Stochastic
164+
inputs = inputs + [self.stochastic.percK[0], self.stochastic.percD[0]]
165+
154166
# bbands
155167
inputs = inputs + [self.bbands.bot[0]] # BBL
156168
inputs = inputs + [self.bbands.mid[0]] # BBM
@@ -180,4 +192,87 @@ def next_observation(self):
180192

181193
return np.array(inputs)
182194

183-
pass
195+
pass
196+
197+
198+
def normalizeObservations(self):
199+
200+
MIN_BITCOIN_VALUE = 10_000.0
201+
MAX_BITCOIN_VALUE = 80_000.0
202+
203+
self.obseravation_normalized = np.empty(len(self.obseravation))
204+
try:
205+
# Normalize data
206+
for index, value in enumerate(self.obseravation):
207+
if value < 100.0:
208+
self.obseravation_normalized[index] = (value - 0.0) / (100.0 - 0.0)
209+
else:
210+
self.obseravation_normalized[index] = (value - MIN_BITCOIN_VALUE) / (MAX_BITCOIN_VALUE - MIN_BITCOIN_VALUE)
211+
212+
#self.obseravation_normalized = (self.obseravation-self.normalization_bounds_df_min) / (self.normalization_bounds_df_max-self.normalization_bounds_df_min)
213+
214+
215+
except ValueError as err:
216+
return None, "ValueError error:" + str(err)
217+
except AttributeError as err:
218+
return None, "AttributeError error:" + str(err)
219+
except IndexError as err:
220+
return None, "IndexError error:" + str(err)
221+
except:
222+
aie = 1
223+
224+
pass
225+
226+
def loadNormalizationBoundsCsv(self, filePath):
227+
228+
# Try importing data file
229+
# We should code a widget that ask for options as : separators, date format, and so on...
230+
try:
231+
232+
# Python contains
233+
if pd.__version__<'2.0.0':
234+
df = pd.read_csv(filePath,
235+
sep=";",
236+
parse_dates=None,
237+
date_parser=lambda x: pd.to_datetime(x, format=""),
238+
skiprows=0,
239+
header=None,
240+
index_col=0)
241+
else:
242+
df = pd.read_csv(filePath,
243+
sep=";",
244+
parse_dates=None,
245+
date_format="",
246+
skiprows=0,
247+
header=None,
248+
index_col=0)
249+
250+
return df
251+
252+
except ValueError as err:
253+
return None, "ValueError error:" + str(err)
254+
except AttributeError as err:
255+
return None, "AttributeError error:" + str(err)
256+
except IndexError as err:
257+
return None, "IndexError error:" + str(err)
258+
259+
pass
260+
261+
262+
# https://stackoverflow.com/questions/53321608/extract-dataframe-from-pandas-datafeed-in-backtrader
263+
def __bt_to_pandas__(self, btdata, len):
264+
get = lambda mydata: mydata.get(ago=0, size=len)
265+
266+
fields = {
267+
'open': get(btdata.open),
268+
'high': get(btdata.high),
269+
'low': get(btdata.low),
270+
'close': get(btdata.close),
271+
'volume': get(btdata.volume)
272+
}
273+
time = [btdata.num2date(x) for x in get(btdata.datetime)]
274+
275+
return pd.DataFrame(data=fields, index=time)
276+
277+
pass
278+

userConfig.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def saveParameter(self, parameter, value):
3737
pass
3838

3939
def removeParameter(self, parameter):
40-
if parameter in self.data[parameter]:
40+
if parameter in self.data:
4141
del self.data[parameter]
4242
self.saveConfig()
4343
pass

userData.json

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
{
22
"M5": {
3-
"fileName": "EURUSD_M5.csv",
4-
"filePath": "C:/perso/trading/anaconda3/backtrader-ichimoku/data/Source 1/EURUSD_M5.csv",
3+
"fileName": "BTC_USDT_USDT-5m-futures-train.csv",
4+
"filePath": "C:/perso/AI/AI_Framework/freqtrade_data/binance/futures/BTC_USDT_USDT-5m-futures-train.csv",
55
"separator": ",",
6-
"timeFormat": "%Y-%m-%d %H:%M",
6+
"timeFormat": "%Y-%m-%d %H:%M:%S",
77
"timeFrame": "M5"
8-
},
9-
"M15": {
10-
"fileName": "EURUSD_M15.csv",
11-
"filePath": "C:/perso/trading/anaconda3/backtrader-ichimoku/data/Source 1/EURUSD_M15.csv",
12-
"separator": ",",
13-
"timeFormat": "%Y-%m-%d %H:%M",
14-
"timeFrame": "M15"
158
}
169
}

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