From d180610fc45e046b5a2e5d41d65c029acdb3482a Mon Sep 17 00:00:00 2001 From: "lucas.toniutti" <lucas.toniutti@etu.hesge.ch> Date: Thu, 29 Jun 2023 01:16:11 +0200 Subject: [PATCH] Ajout d'un nouveau indicateur + importation/exporation des simulations --- main/.DS_Store | Bin 8196 -> 8196 bytes main/Decision.py | 147 ++++++-- main/__pycache__/Decision.cpython-310.pyc | Bin 6583 -> 8087 bytes main/__pycache__/settings.cpython-310.pyc | Bin 263 -> 163 bytes main/__pycache__/simulation.cpython-310.pyc | Bin 13384 -> 13418 bytes main/backend.py | 388 +++++++++++++++++--- main/settings.py | 3 - main/simulation.py | 18 +- main/static/.DS_Store | Bin 6148 -> 6148 bytes main/templates/base.html | 8 +- main/templates/importSimulation.html | 154 ++++++++ main/templates/index.html | 147 +++++--- main/templates/mySimulation.html | 138 +++++++ 13 files changed, 848 insertions(+), 155 deletions(-) delete mode 100644 main/settings.py create mode 100644 main/templates/importSimulation.html create mode 100644 main/templates/mySimulation.html diff --git a/main/.DS_Store b/main/.DS_Store index 6b5e74f656eba8670e3dd860975b345bb0c5faef..36bd23a29fd01e459ee9050142a5b5a30ac952f2 100644 GIT binary patch delta 125 zcmZp1XmOa}&nUAoU^hRb%w!&ciIdBOOF4`zEp!x&4NWJ17Pg<<Bq+W)U$B#rpPQkW zA(f$op@bomA&((_@<kzW)@}v{hIy0U3jKrY+5A~}1tWsDSX7s3GrPn$mdVFNt|AL- GF#!MpaU@g# delta 152 zcmZp1XmOa}&nUeyU^hRb^kg1^iHzGP&l4zA6R)l|vCvU4GP9`FQK+^wGSE>lF*d8M z<>U}m*0&Cd&(6us%kP|QCMeI?HMv1hYIA{LC*x#&;m;h#CMG%xMy3{<wM14hau^$# o>nIpmm~Fl#s>?L7!F)5j#5Wd>|6sttFnNNQ*yJG526Sa60I=CF#sB~S diff --git a/main/Decision.py b/main/Decision.py index 7ebc93d..4ce5faa 100644 --- a/main/Decision.py +++ b/main/Decision.py @@ -14,7 +14,6 @@ import os SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) sys.path.append(os.path.dirname(SCRIPT_DIR)) -import settings import asyncio import websockets from orders import TradesManagement @@ -59,8 +58,11 @@ class Decision: self.amount = None self.export_result = [] self.mas = [] - #Launch a simualtion for a given market ==> focused on MA - def simulationGen(self,sim,symbol,sizeCandle_min,mas): + self.timeRandom = [] + self.tmpRandom = 0 + self.tickRandom = 0 + #Launch a simualtion for a given market + def simulationGen(self,sim,symbol,sizeCandle_min,indicator,mas=[],timeRandom=[]): cpt = 0 self.sim = sim random.seed(dt.now()) @@ -78,29 +80,52 @@ class Decision: print(f"time period = {self.timePeriod}") self.sim.indexTicker +=1 - #Iterate on each price of a market - for i in range(1,len(sim.histo_prices)): - self.sim.indexTicker +=1 - self.indexTickerSim+=1 - try: - price = self.sim.histo_prices[self.indexTickerSim][0] - date = self.sim.histo_prices[self.indexTickerSim][1] - except: - print("Erreur dans la simulation dans le calcul du SMA.") - print(f"Liste len : {len(self.sim.histo_prices)}") - print(f"Index : {self.indexTickerSim}") - break + if indicator == "SMA": + #Iterate on each price of a market for SMA calculation + for i in range(1,len(sim.histo_prices)): + self.sim.indexTicker +=1 + self.indexTickerSim+=1 + try: + price = self.sim.histo_prices[self.indexTickerSim][0] + date = self.sim.histo_prices[self.indexTickerSim][1] + except: + print("Erreur dans la simulation dans le calcul du SMA.") + print(f"Liste len : {len(self.sim.histo_prices)}") + print(f"Index : {self.indexTickerSim}") + break - self.pricesSim.append([price,date]) - #Launch SMA calculation + self.pricesSim.append([price,date]) + #Launch SMA calculation - # Mettre les décisions - self.calculateSMA(date,symbol,price,mas) - if self.indexTickerSim >= len(sim.histo_prices)-1: - break - print(f"date End = {date}") - #add results to list of results - self.export_result.append(sim.endSimulation(mas)) + # Mettre les décisions + self.calculateSMA(date,symbol,price,mas) + if self.indexTickerSim >= len(sim.histo_prices)-1: + break + print(f"date End = {date}") + #add results to list of results + self.export_result.append(sim.endSimulation(mas)) + if indicator == "random": + #Iterate on each price of a market for random decision + for i in range(1,len(sim.histo_prices)): + self.sim.indexTicker +=1 + self.indexTickerSim +=1 + try: + price = self.sim.histo_prices[self.indexTickerSim][0] + date = self.sim.histo_prices[self.indexTickerSim][1] + except: + print("Erreur dans la simulation dans le calcul du random.") + print(f"Liste len : {len(self.sim.histo_prices)}") + print(f"Index : {self.indexTickerSim}") + break + self.pricesSim.append([price,date]) + + self.calculateRandom(date,symbol,price,timeRandom) + if self.indexTickerSim >= len(sim.histo_prices)-1: + break + print(f"date End = {date}") + self.export_result.append(sim.endSimulation(timeRandom)) + else: + print("Error in the indicator choice") #Detection of the crossing of a mooving average def detectCrossMA(self,short_ma,long_ma,price,date): @@ -114,14 +139,17 @@ class Decision: self.crossed = "down" #Launch our decision algorithme for a simulated or realtime - def launch(self,dateStart, dateEnd, show_graph = True, SL = 0.03, TP = 0.03, candle_size = 10, mas=[20,40]): + def launch(self,dateStart, dateEnd, indicator, show_graph = True, SL = 0.03, TP = 0.03, candle_size = 10 ,mas=[20,40], timeRandom=[15,20]): testSL = [float(SL)] testTP = [float(TP)] candle_sizes = [int(candle_size)] # Moyenne glissante # mas = [[10,20],[5,15]] - self.mas = [mas] + if indicator == "SMA": + self.mas = [mas] + if indicator == "random": + self.timeRandom = [timeRandom] markets = ["BTCUSDT"] bases= ["BTC"] @@ -147,19 +175,29 @@ class Decision: #amout is 10% of balance self.amount = balance*0.1 - for sl in testSL: - self.sim.sl = sl - for tp in testTP: - self.sim.tp = tp - for c in candle_sizes: - for ma in self.mas: - print(f"Simulation of market : {market} with sl = {sl} and tp = {tp} and candle_size = {c} and ma = {ma}") - print("Amount = ",self.amount) - print(self.sim) - self.simulationGen(self.sim,market,c,ma) - # self.sim.resetSimul(sl, tp, market,bases[i], targets[i]) - # self.resetLists(market) - # plot_MA_Price_Orders(self.sma['BTCUSDT'],self.sim.histo_prices,[20,40],self.sim.active_orders,"BTCUSDT") + if indicator == "SMA": + for sl in testSL: + self.sim.sl = sl + for tp in testTP: + self.sim.tp = tp + for c in candle_sizes: + for ma in self.mas: + print(f"Simulation of market : {market} with sl = {sl} and tp = {tp} and candle_size = {c} and ma = {ma}") + print("Amount = ",self.amount) + print(self.sim) + self.simulationGen(self.sim,market,c,indicator,mas=ma) + if indicator == "random": + for sl in testSL: + self.sim.sl = sl + for tp in testTP: + self.sim.tp = tp + for c in candle_sizes: + for timeRand in self.timeRandom: + print(f"Simulation of market : {market} with sl = {sl} and tp = {tp} and candle_size = {c} and random time = {timeRand}") + print("Amount = ",self.amount) + print(self.sim) + self.simulationGen(self.sim,market,c,indicator,timeRandom=timeRand) + export_Simulation_All(self.export_result) @@ -188,6 +226,10 @@ class Decision: self.amount = None self.export_result = [] self.mas = [] + self.timeRandom = [] + self.tmpRandom = 0 + self.tickRandom = 0 + #Format string to datetime def formatDateStr(self,datestr,format): return datetime.strptime(datestr,format) @@ -294,4 +336,29 @@ class Decision: self.detectCrossMA(ma1,ma2,price,date) return 1 return 0 - #print(self.sma) \ No newline at end of file + #print(self.sma) + + #Choose a random time to buy or sell + def calculateRandom(self, date, symbol, price, timeRandom): + if self.init: + self.init = False + self.randomAVorVA(date,price) + return 1 + if not self.init: + # 1 tick = 10s + if self.sim.end_Active < self.sim.indexTicker and self.tmpRandom == 0: + self.tmpRandom = 1 + rand = random.randint(timeRandom[0],timeRandom[1]) + rand = (rand*60)/10 + self.tickRandom = self.sim.indexTicker + rand + if self.sim.indexTicker == self.tickRandom: + self.tmpRandom = 0 + self.randomAVorVA(date,price) + return 1 + + def randomAVorVA(self, date,price): + rand = random.randint(0,1) + if rand == 0: + self.sim.placeOrder(price,self.amount,date,"AV","GTC") + elif rand == 1: + self.sim.placeOrder(price,self.amount,date,"VA","GTC") \ No newline at end of file diff --git a/main/__pycache__/Decision.cpython-310.pyc b/main/__pycache__/Decision.cpython-310.pyc index 31410ac5b63ed1dafc47bab0ccf17b90fc033e4f..588d4e35b656af4aa8b0f4a4dc1a0ff0f28b0a07 100644 GIT binary patch delta 3691 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 delta 2319 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 HTwVbHBIfU9 diff --git a/main/__pycache__/settings.cpython-310.pyc b/main/__pycache__/settings.cpython-310.pyc index 65766bb99a0b8558a53a733e3133f29d579d0426..a58aa08ba9ef7cf1551657b2ad446cc9e41e96f2 100644 GIT binary patch delta 142 zcmZo?TFmH?&&$ijz`($;)qipdh<*$rkTD|z1A_wt14A(j0|P?}LokCTqu)w~B9JhG z_~kaSUCy>RGq*G+u_U#$sH7+{B{MHQxu~+BBws%_F*8rUIJKlCGcUbZub}c4hfQvN SN@-529mteoCI$uu76t%w>mWY> literal 263 zcmd1j<>g{vU|`tiSe9bOz`*br#6iYP3=9ko3=9m#JPZsBDGVu$Eeuf%DNMl(n#{?_ z>OgEp1_lOakXjCqTE;|%6oz01O(wsWAW2QeTigXj`RPTe#l=pEMJpMKm>C!##4k(z z(BjmhV*Q-b<iuiqm(=3ylKcXFr^Mup)SUbx{ltQTlA^?v%)E5{+{Da0{o>S;lFYpH zV!eXOTP&G*nI#~Li$N~sU@Br|U|{gmWCYU)D<CWu1_p*(95%W6DWy57c5q!h3;?>D BGpGOn diff --git a/main/__pycache__/simulation.cpython-310.pyc b/main/__pycache__/simulation.cpython-310.pyc index e6f8c914f44e21d1aee9d3a287b06ef1cb1f8865..8369cbfc9bebda0632912157ca577bca7636a32c 100644 GIT binary patch delta 2843 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 K9uQ^}Vg>+Hgq33e delta 2838 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 diff --git a/main/backend.py b/main/backend.py index cb890d2..7382429 100644 --- a/main/backend.py +++ b/main/backend.py @@ -25,17 +25,30 @@ from matplotlib.dates import DateFormatter from matplotlib.lines import Line2D import json import plotly +import zipfile +import binascii + + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.backends import default_backend + +import base64 import time import pandas as pd from matplotlib.figure import Figure import datetime import plotly.graph_objects as go +import plotly.io as pio + from Decision import Decision import base64 import io -import settings from APIWrappers.ApiManagement import createOrder, createOrderLimit, deleteOrder, getBalance, getTicker, getOrders @@ -44,9 +57,6 @@ from APIWrappers.ApiManagement import createOrder, createOrderLimit, deleteOrder async_mode = "eventlet" app = Flask(__name__) - -# app.config['SECRET_KEY'] = 'secret!' - socket_ = SocketIO(app, async_mode=async_mode,cors_allowed_origins="*") market = "BTCUSDT" @@ -56,9 +66,16 @@ dec = Decision("simulated",market) simulationInProgress = False dictError = {} -stickyForm = dict(takeProfit='3.0',stopLoss='3.0',dateSimStart='2022-02-05',dateSimEnd='2022-04-09',indicatorSelect='',MA1='20',MA2='40') +stickyForm = dict(takeProfit='3.0',stopLoss='3.0',dateSimStart='2022-02-05',dateSimEnd='2022-04-09',indicatorSelect='',MA1='20',MA2='40',minimumTime='15',maximumTime='60') resetParams = 1 - +lstSimulations = [] +lstErrorSimulations = [] +showImportError = False +figJson = None +fig = None +results = None +nbrDoublons = 0 +indicatorSim = None @app.route('/') def index(): @@ -66,18 +83,21 @@ def index(): global dictError global stickyForm global resetParams - - figJson = None + global figJson + global results results = None + figJson = None if resetParams == 1: - stickyForm = dict(takeProfit='3.0',stopLoss='3.0',dateSimStart='2022-02-05',dateSimEnd='2022-04-09',indicatorSelect='',MA1='20',MA2='40') + stickyForm = dict(takeProfit='3.0',stopLoss='3.0',dateSimStart='2022-02-05',dateSimEnd='2022-04-09',indicatorSelect='',MA1='20',MA2='40',minimumTime='15',maximumTime='60') if dec.export_result != []: simulationInProgress = False # figJson = createFig(dec.sim.histo_prices, dec.sim.active_orders, dec.sma[market], dec.mas[0], market) tmpOrder = [dec.sim.winsAV, dec.sim.lossAV, dec.sim.winsVA, dec.sim.lossVA] - # print(tmpOrder) - figJson = createFigPyplot(market,dec.sma[market], dec.mas[0], tmpOrder) + if indicatorSim == "SMA": + figJson = createFigPyplot(market,tmpOrder, mas=dec.sma[market], arraySma=dec.mas[0]) + if indicatorSim == "random": + figJson = createFigPyplot(market,tmpOrder) results = getResults() reponse = make_response(render_template('index.html', async_mode=socket_.async_mode, inProgress=simulationInProgress, fig=figJson, res=results, err=dictError, stickyForm=stickyForm)) # reponse.headers['Content-Security-Policy'] = "style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com/ajax/libs/plotly.js/2.5.1/plotly.min.js https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js https://fonts.googleapis.com/ https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" @@ -86,6 +106,24 @@ def index(): dictError = {} return reponse +@app.route('/mySimulation') +def mySimulation(): + global showImportError + global lstErrorSimulations + global nbrDoublons + + if lstSimulations == []: + print("lstSimulations is empty") + return redirect("/importSimulation") + + if showImportError == False: + print("showImportError is False") + lstErrorSimulations = [] + nbrDoublons = 0 + if showImportError: + print("showImportError is True") + showImportError = False + return render_template('mySimulation.html', lstSimulations=lstSimulations, err=lstErrorSimulations, doublons=nbrDoublons) @app.route("/startSimulation", methods=["POST"]) def startSimulation(): @@ -94,8 +132,8 @@ def startSimulation(): global simulationInProgress global stickyForm global resetParams + global indicatorSim resetParams = 2 - # genral input stickyForm = request.form.to_dict() tp = request.form.get("takeProfit") sl = request.form.get("stopLoss") @@ -104,6 +142,8 @@ def startSimulation(): dateSimStart = request.form.get("dateSimStart") + " 00:00:00.000000Z" dateSimEnd = request.form.get("dateSimEnd") + " 23:59:59.000000Z" ma = None + timeMin = 0 + timeMax = 0 indicator = request.form.get("indicatorSelect") if indicator == "SMA": @@ -113,7 +153,13 @@ def startSimulation(): ma = [int(ma1),int(ma2)] except: return redirect(request.referrer) - else: + elif indicator == "random": + try: + timeMin = int(request.form.get("minimumTime")) + timeMax = int(request.form.get("maximumTime")) + except: + return redirect(request.referrer) + else: return redirect(request.referrer) try: tp = float(tp) @@ -122,11 +168,11 @@ def startSimulation(): except: return redirect(request.referrer) - dictError = checkGeneralInput(tp, sl, candleSize, dateSimStart, dateSimEnd, indicator, ma) + dictError = checkGeneralInput(tp, sl, candleSize, dateSimStart, dateSimEnd, indicator, ma, timeMin, timeMax) tp = tp/100 sl = sl/100 - + timeRandom = [timeMin, timeMax] dictValue = dictError.values() if 'is-invalid' in dictValue: return redirect(request.referrer) @@ -136,21 +182,171 @@ def startSimulation(): if not simulationInProgress: resetParams = 0 - settings.init() dec.resetLists(market) simulationInProgress = True - thread = Thread(target=dec.launch, args=(dateSimStart,dateSimEnd,False,sl,tp,candleSize,ma)) + indicatorSim = indicator + thread = Thread(target=dec.launch, args=(dateSimStart,dateSimEnd,indicator,False,sl,tp,candleSize,ma,timeRandom)) thread.start() return redirect(request.referrer) +@app.route("/importSimulation") +def importSimulation(): + global showImportError + global lstErrorSimulations + global nbrDoublons + if showImportError == False: + lstErrorSimulations = [] + nbrDoublons = 0 + if showImportError: + showImportError = False + return render_template('importSimulation.html', err=lstErrorSimulations, doublons=nbrDoublons) + +@app.route("/uploadFile", methods=["POST"]) +def uploadFile(): + global lstSimulations + global lstErrorSimulations + global showImportError + global nbrDoublons + nbrDoublons = 0 + lstErrorSimulations = [] + showImportError = True + files = request.files.getlist('fileInput') + files += request.files.getlist('filesDragAndDrop') + for file in files: + filename = file.filename + fileExtension = os.path.splitext(filename)[1].lower() + file.seek(0) + + typeOfFile = file.content_type + + if fileExtension not in ['.json', '.zip']: + lstErrorSimulations.append("L'extension du fichier n'est pas prise en charge.") + continue + if typeOfFile == "application/json": + fileData = file.read() + try: + fileData = fileData.decode('utf-8') + except UnicodeDecodeError: + lstErrorSimulations.append("Le fichier JSON ("+ file.filename +") n'est pas encodé en UTF-8.") + continue + try: + jsonFile = json.loads(fileData) + if 'signature' not in jsonFile: + lstErrorSimulations.append("Le fichier JSON ("+ file.filename +") ne contient pas de signature.") + continue + try: + signature = base64.b64decode(jsonFile.pop('signature')) + dataStr = json.dumps(jsonFile, sort_keys=True) + except: + lstErrorSimulations.append("La signature du fichier JSON ("+ file.filename +") n'est pas correcte.") + continue + try: + publicKey.verify( + signature=signature, + data=dataStr.encode(), + padding=padding.PSS( + mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH + ), + algorithm=hashes.SHA256() + ) + for profit in jsonFile['res']['moneyProfits']: + jsonFile['res']['moneyProfits'][profit] = float(jsonFile['res']['moneyProfits'][profit]) + for losse in jsonFile['res']['moneyLosses']: + jsonFile['res']['moneyLosses'][losse] = float(jsonFile['res']['moneyLosses'][losse]) + jsonFile['filename'] = file.filename + lstSimulations.append(jsonFile) + except InvalidSignature: + lstErrorSimulations.append("La signature du fichier JSON ("+ file.filename +") n'est pas correcte.") + continue + except json.JSONDecodeError: + lstErrorSimulations.append("Le contenu du fichier JSON ("+ file.filename +") n'est pas valide.") + continue + elif typeOfFile == "application/zip": + nameZIP = filename + try: + fileData = file.read() + z = zipfile.ZipFile(io.BytesIO(fileData)) + for filename in z.namelist(): + if filename.startswith('__MACOSX/'): + continue + if not filename.endswith('.json'): + lstErrorSimulations.append("Le fichier ZIP ("+nameZIP+") contient un fichier non JSON ("+ filename +").") + continue + json_data = z.read(filename) + try: + json_data = json_data.decode("utf-8") + jsonFile = json.loads(json_data) + if 'signature' not in jsonFile: + lstErrorSimulations.append("Le fichier JSON ("+ filename +") dans le ZIP ("+nameZIP+") ne contient pas de signature.") + continue + try: + signature = base64.b64decode(jsonFile.pop('signature')) + dataStr = json.dumps(jsonFile, sort_keys=True) + except: + lstErrorSimulations.append("La signature du fichier JSON ("+ filename +") dans le ZIP ("+nameZIP+") n'est pas correcte.") + continue + try: + publicKey.verify( + signature=signature, + data=dataStr.encode(), + padding=padding.PSS( + mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH + ), + algorithm=hashes.SHA256() + ) + except InvalidSignature: + lstErrorSimulations.append("La signature du fichier JSON ("+ filename +") dans le ZIP ("+nameZIP+") n'est pas correcte.") + continue + for profit in jsonFile['res']['moneyProfits']: + jsonFile['res']['moneyProfits'][profit] = float(jsonFile['res']['moneyProfits'][profit]) + for losse in jsonFile['res']['moneyLosses']: + jsonFile['res']['moneyLosses'][losse] = float(jsonFile['res']['moneyLosses'][losse]) + jsonFile['filename'] = filename + lstSimulations.append(jsonFile) + except json.JSONDecodeError: + lstErrorSimulations.append("Le contenu du fichier JSON ("+ filename +") dans le ZIP ("+nameZIP+") n'est pas valide.") + continue + except zipfile.BadZipFile: + lstErrorSimulations.append("Le fichier ZIP ("+ filename +") est invalide ou corrompu.") + continue + else: + lstErrorSimulations.append("Le format du fichier ("+ filename +") n'est pas pris en charge.") + continue + nbrDoublons = 0 + i = 0 + while i < len(lstSimulations): + j = i + 1 + while j < len(lstSimulations): + if lstSimulations[i]['params'] == lstSimulations[j]['params']: + print("doublon supp : ", lstSimulations[j]['filename']) + del lstSimulations[j] + nbrDoublons += 1 + else: + j += 1 + i += 1 + return jsonify({'nbrFileUpload': len(lstSimulations)}) @app.route("/progress") def progress(): - if settings.progressBar < 60: - settings.progressBar += 4 - return jsonify({'progress': settings.progressBar}) + if dec.sim.progressBar < 60: + dec.sim.progressBar += 4 + return jsonify({'progress': dec.sim.progressBar}) +@app.route("/deleteSimulation", methods=["GET"]) +def deleteSimulation(): + global lstSimulations + + index = request.args.get('index') + try: + index = int(index) + if index < len(lstSimulations): + del lstSimulations[index] + except: + return redirect('/mySimulation') + return redirect('/mySimulation') @app.route("/resetSimulation") def resetSimulation(): @@ -175,8 +371,60 @@ def resetSimulationWithParams(): return redirect('/') +@app.route("/restartSimulationWithParamsFromIndex") +def restartSimulationWithParamsFromIndex(): + global simulationInProgress + global resetParams + global lstSimulations + global stickyForm + + index = request.args.get('index') + try: + index = int(index) + except: + return redirect('/mySimulation') + if not simulationInProgress: + if index >= len(lstSimulations): + return redirect('/mySimulation') + params = lstSimulations[index]['params'] + stickyForm = params + resetParams = 0 + dec.export_result = [] + + return redirect('/') + +@app.route("/signJSON", methods=["POST"]) +def signJSON(): + global results + if fig != None: + image = request.get_json() + resultsCopy = results + for profit in resultsCopy['moneyProfits']: + resultsCopy['moneyProfits'][profit] = str(resultsCopy['moneyProfits'][profit]) + for losse in resultsCopy['moneyLosses']: + resultsCopy['moneyLosses'][losse] = str(resultsCopy['moneyLosses'][losse]) + jsonData = { + 'image': image['image'], + 'res': resultsCopy, + 'params': stickyForm, + } + jsonData = json.dumps(jsonData, sort_keys=True) + signature = privateKey.sign( + data=jsonData.encode(), + padding=padding.PSS( + mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH + ), + algorithm=hashes.SHA256() + ) + base64Signature = base64.b64encode(signature).decode() + jsonData = json.loads(jsonData) + jsonData['signature'] = base64Signature + return jsonify(jsonData) + return jsonify({}) + -def checkGeneralInput(takeprofit, stoploss, candleSize, dateSimStart, dateSimEnd, indicator, ma): +def checkGeneralInput(takeprofit, stoploss, candleSize, dateSimStart, dateSimEnd, indicator, ma, timeMin, timeMax): dictError = {} # Check if takeprofit and stoploss are between 0 and 100 if takeprofit > 100 or takeprofit < 1: @@ -207,9 +455,16 @@ def checkGeneralInput(takeprofit, stoploss, candleSize, dateSimStart, dateSimEnd dictError['ma'] = 'is-invalid' else: dictError['ma'] = 'is-valid' + elif indicator == "random": + if timeMin > timeMax or timeMin < 15 or timeMax > 1440 or timeMax < timeMin or timeMin == timeMax: + dictError['random'] = 'is-invalid' + else: + dictError['random'] = 'is-valid' return dictError -def createFigPyplot(market, mas, arraySma, orders): +def createFigPyplot(market, orders, mas=[], arraySma=[]): + global fig + dataChart = dec.sim.candle_prices months = {"January": "Janvier", "February": "Février", "March": "Mars", "April": "Avril", "May": "Mai", "June": "Juin", "July": "Juillet", "August": "Août", "September": "Septembre", "October": "Octobre", "November": "Novembre", "December": "Décembre"} @@ -241,27 +496,26 @@ def createFigPyplot(market, mas, arraySma, orders): name="Prix" )]) - - smaData = {sma: {'price': [], 'date': []} for sma in arraySma} - for sma, values in mas.items(): - for value in values: - smaData[int(sma)]['price'].append(value[0]) - smaData[int(sma)]['date'].append(value[1]) - - for i, sma in enumerate(arraySma): - fig.add_trace( - go.Scatter( - x=smaData[sma]['date'], - y=smaData[sma]['price'], - mode='lines', - name=f'SMA{sma}', - hoverinfo="none", - line=dict(color=['rgba(0, 0, 255, 0.3)', 'rgba(128, 0, 128, 0.3)'][i]) + if indicatorSim == "SMA": + smaData = {sma: {'price': [], 'date': []} for sma in arraySma} + for sma, values in mas.items(): + for value in values: + smaData[int(sma)]['price'].append(value[0]) + smaData[int(sma)]['date'].append(value[1]) + + for i, sma in enumerate(arraySma): + fig.add_trace( + go.Scatter( + x=smaData[sma]['date'], + y=smaData[sma]['price'], + mode='lines', + name=f'SMA{sma}', + hoverinfo="none", + line=dict(color=['rgba(0, 0, 255, 0.3)', 'rgba(128, 0, 128, 0.3)'][i]) + ) ) - ) avWins, avLoss, vaWins, vaLoss = orders - print(avWins) for data, color, symbol, name, endName in [ (avWins, 'green', 'triangle-up', 'AV Gains', 'Fin AV Gains'), @@ -279,7 +533,8 @@ def createFigPyplot(market, mas, arraySma, orders): marker=dict( symbol=symbol, size=17, - color=color + color=color, + line=dict(color='blue', width=1) ), legendgroup=name ) @@ -315,20 +570,21 @@ def createFigPyplot(market, mas, arraySma, orders): ) ) fig.update_layout( + margin=dict(l=10, r=10, t=50, b=10), xaxis_rangeslider_visible=False, title="Simulation sur le marché " + market, xaxis_title="Date", yaxis_title="Prix", hovermode="closest", + plot_bgcolor='#f2f2f2', hoverlabel=dict( bgcolor="rgba(123, 132, 144, 0.4)", font_size=16, align="auto", ) ) - fig.update_layout(plot_bgcolor='#f2f2f2') fig.update_yaxes(fixedrange=False) - fig.update_layout() + # fig.update_layout() figJson = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder) return figJson @@ -337,17 +593,7 @@ def createFig(prices,orders,mas,periodes,market): prices = dec.sim.histo_prices prices=pd.DataFrame(prices,columns=['price','date']) - - print(prices) - print("//////////////////////////////// -> orders") - print(orders) - print("//////////////////////////////// -> mas") - print(mas) - print("//////////////////////////////// -> periodes") - print(periodes) - print("//////////////////////////////// -> market") - - print(market) + figJson= dec.sim.candle_prices fig = go.Figure() @@ -442,6 +688,40 @@ def getResults(): dict['moneyLosses'][i] = dict['startBalance'][i] * (round(dict['losses'][j],2) * 0.01) return dict +def getKeys(): + privateKeyPath = "privateKey.pem" + publicKeyPath = "publicKey.pem" + + if os.path.exists(privateKeyPath) and os.path.exists(publicKeyPath): + with open(privateKeyPath, "rb") as key_file: + privateKey = serialization.load_pem_private_key( + data=key_file.read(), + password=None + ) + with open(publicKeyPath, "rb") as key_file: + publicKey = serialization.load_pem_public_key( + data=key_file.read() + ) + else: + privateKey = rsa.generate_private_key(public_exponent=65537, key_size=2048) + publicKey = privateKey.public_key() + + with open(privateKeyPath, "wb") as key_file: + key_file.write(privateKey.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption() + )) + + with open(publicKeyPath, "wb") as key_file: + key_file.write(publicKey.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo + )) + + return privateKey, publicKey + +privateKey, publicKey = getKeys() # #Route wallet ==> order creation diff --git a/main/settings.py b/main/settings.py deleted file mode 100644 index 87bf53a..0000000 --- a/main/settings.py +++ /dev/null @@ -1,3 +0,0 @@ -def init(): - global progressBar - progressBar = 0 \ No newline at end of file diff --git a/main/simulation.py b/main/simulation.py index 4100240..1f08122 100644 --- a/main/simulation.py +++ b/main/simulation.py @@ -28,7 +28,6 @@ import random import numpy.polynomial.polynomial as poly import pickle -import settings import sys import os @@ -67,7 +66,9 @@ class Simulation: self.end_Active = 0 self.base = base self.target = target - + self.progressBar = 0 + self.orderFinished = True + #Get closing prices and date from histo_candles into 1 list def reformatPrices(self): print("REFORMAT PRICES") @@ -129,7 +130,8 @@ class Simulation: self.market = market self.start_time =dt.now() self.candle_prices = [] - + self.progressBar = 0 + self.orderFinished = True #Get ochl candle and polynomial transform to get a price ticker and a given time periode def calcOCLH(self,slices_sec,periode_base,format,symbol,show_graph,dateStart,dateEnd): @@ -147,7 +149,8 @@ class Simulation: self.histo_candles = getHistoCandles(symbol,dateStart, dateEnd) # print(self.histo_candles) candles = self.histo_candles - pourcentage = settings.progressBar + # pourcentage = settings.progressBar + pourcentage = self.progressBar for c in candles: Candles = {} xs=[] @@ -176,7 +179,8 @@ class Simulation: totalDuration = (tmpDateEnd - tmpDateStart).total_seconds() elapsedDuration = (currentDate - tmpDateStart).total_seconds() - settings.progressBar = pourcentage + (elapsedDuration / totalDuration) * (95-pourcentage) + # settings.progressBar = pourcentage + (elapsedDuration / totalDuration) * (95-pourcentage) + self.progressBar = pourcentage + (elapsedDuration / totalDuration) * (95-pourcentage) # print(percentage) #print(f"DATE Candle : {dt.fromtimestamp(date)}") date = dt.fromtimestamp(date) - datetime.timedelta(0,periode_base) @@ -264,6 +268,7 @@ class Simulation: return self.end_Active else: + # self.orderFinished = True # print("Can't place, 1 order is still active") # print("End active : ",self.end_Active) return self.end_Active @@ -359,7 +364,8 @@ class Simulation: "VA_loss" : nb_lossVA,"avg_time_order":self.avgtime_order, "losses": losses, "profits":profits} print(res) - settings.progressBar = 100 + self.progressBar = 100 + # settings.progressBar = 100 return res #Calculate profit of the simulation diff --git a/main/static/.DS_Store b/main/static/.DS_Store index a31724f9a298e95e1f177e5a6234051e3051b4af..3c2f395e2e1ed09fb39bc71d99c2a344f0d5ac21 100644 GIT binary patch delta 47 zcmZoMXffCj%*423atKo)w?uWdk)?%>g0Z3L<nv6@j9rsoGs!b{Z{}wH&9a%D<1aq| DVL}e& delta 46 zcmZoMXffCj%*42TatKo)mw0uxv9Xzsg1PzR3ry0Cos-`%$uo9s=3)NLvYCzJA3p$9 CV-B$Z diff --git a/main/templates/base.html b/main/templates/base.html index 391fa2e..53540b6 100644 --- a/main/templates/base.html +++ b/main/templates/base.html @@ -14,7 +14,6 @@ </head> <body id="page-top" class="d-flex flex-column min-vh-100"> - <!-- Navigation--> <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top" id="mainNav"> <div class="container px-4"> <a class="navbar-brand" href="{{ url_for('index')}}">Silmulateur de trading de cryptomonnaies</a> @@ -23,8 +22,7 @@ class="navbar-toggler-icon"></span></button> <div class="collapse navbar-collapse" id="navbarResponsive"> <ul class="navbar-nav ms-auto"> - <!-- <li class="nav-item"><a class="nav-link" href="#launchSimulation">Démarrer une simulation</a></li> --> - <li class="nav-item"><a class="nav-link" href="#simulations">Mes simulations</a></li> + <li class="nav-item"><a class="nav-link" href="{{ url_for('mySimulation') }}">Mes simulations</a></li> <li class="nav-item"><a class="nav-link" href="#comparaison">Comparaison</a></li> </ul> </div> @@ -32,8 +30,7 @@ </nav> {% block content %} {% endblock %} - <!-- Footer--> - <footer class="py-5 bg-dark"> + <footer class="py-4 bg-dark"> <div class="container-fluid px-4"> <div class="row justify-content-center"> <div class="col-12 text-center"> @@ -42,7 +39,6 @@ </div> </div> </footer> - <!-- Bootstrap core JS--> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script> </body> </html> \ No newline at end of file diff --git a/main/templates/importSimulation.html b/main/templates/importSimulation.html new file mode 100644 index 0000000..a16d02a --- /dev/null +++ b/main/templates/importSimulation.html @@ -0,0 +1,154 @@ +{% extends 'base.html' %} +{% block content %} +<section class="gradient-custom p-5 h-100 align-items-center my-auto"> + <div class="container py-5 h-100"> + <div class="row justify-content-center align-items-center h-100"> + <div class="col-12 col-lg-9 col-xl-7"> + <h3 class="mb-4 pb-2 pb-md-0 mb-md-2">Importation de simulations</h3> + <div class="card shadow-2-strong card-registration" style="border-radius: 15px;"> + <div class="card-body p-4 p-md-5 dropzone" ondragover="handleDragOver(event)"> + <form action="uploadFile" method="post" enctype="multipart/form-data"> + <div class="container"> + <div class="row"> + <div class="col-lg-12"> + <div class="text-center"> + <h3>Faites glisser et déposez les fichiers JSON ou ZIP ici</h3> + <i class="bi bi-cloud-upload"></i> + <p>Ou sélectionnez les fichiers en cliquant ici :</p> + <input class="form-control form-control-sm w-50 mx-auto" type="file" + name="fileInput" accept=".json,.zip" capture="*.json,*.zip" multiple> + </div> + </div> + </div> + </div> + <br> + <table id="fileTable" class="table"></table> + </div> + </div> + <br> + {% for error in err %} + <div class="alert alert-danger alert-dismissible fade show" role="alert"> + {{ error }} + <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> + </div> + {% endfor %} + <div class="row text-center"> + <div class="mt-4 pt-2"> + <button type="submit" class="btn btn-primary btn-lg">Envoyer</button> + </div> + </div> + </form> + </div> + </div> + </div> +</section> +<script> + var filesToUpload = []; + + function handleDragOver(event) { + event.preventDefault(); + event.dataTransfer.dropEffect = 'move'; + document.querySelector('.dropzone').style.backgroundColor = '#f2f2f2'; + document.querySelector('.dropzone').style.borderRadius = '15px'; + } + + function handleDragLeave(event) { + event.preventDefault(); + document.querySelector('.dropzone').style.backgroundColor = ''; + } + + function handleDrop(event) { + event.preventDefault(); + + var files = event.dataTransfer.files; + for (var i = 0, f; f = files[i]; i++) { + if (!['application/json', 'application/zip'].includes(f.type)) { + alert('Type de fichier invalide. Veuillez uploader un fichier JSON ou ZIP.'); + continue; + } + + filesToUpload.push(f); + createFileElement(f); + } + + document.querySelector('.dropzone').style.backgroundColor = ''; + } + + function createFileElement(file) { + var row = document.createElement('tr'); + + var fileName = document.createElement('td'); + fileName.textContent = file.name; + row.appendChild(fileName); + + var removeFile = document.createElement('td'); + var trashIcon = document.createElement('i'); + trashIcon.classList.add('bi'); + trashIcon.classList.add('bi-trash'); + removeFile.appendChild(trashIcon); + + + removeFile.style.color = 'red'; + removeFile.style.cursor = 'pointer'; + removeFile.style.textAlign = 'right'; + removeFile.addEventListener('click', function (e) { + var index = filesToUpload.indexOf(file); + if (index > -1) { + filesToUpload.splice(index, 1); + } + row.remove(); + }); + row.appendChild(removeFile); + + row.style.borderBottom = '1px solid #000'; + row.style.borderTop = '1px solid #000'; + + document.querySelector('#fileTable').appendChild(row); + } + + + function handleSubmit(event) { + event.preventDefault(); + + var fileInput = document.querySelector('input[name="fileInput"]'); + + for (var i = 0, f; f = fileInput.files[i]; i++) { + if (!['application/json', 'application/zip'].includes(f.type)) { + alert('Type de fichier invalide. Veuillez uploader un fichier JSON ou ZIP.'); + continue; + } + filesToUpload.push(f); + } + + var formData = new FormData(); + + for (var i = 0, f; f = filesToUpload[i]; i++) { + formData.append('filesDragAndDrop', f, f.name); + } + + fetch('uploadFile', { method: 'POST', body: formData }) + .then(response => response.json()) + .then(data => { + filesToUpload = []; + fileInput.value = ''; + + window.location.href = "/mySimulation"; + }) + .catch(error => { + console.error('Erreur:', error); + }); + + + } + + var dropzone = document.querySelector('.dropzone'); + dropzone.addEventListener('dragover', handleDragOver, false); + dropzone.addEventListener('dragleave', handleDragLeave, false); + dropzone.addEventListener('drop', handleDrop, false); + + var submitButton = document.querySelector('button[type="submit"]'); + submitButton.addEventListener('click', handleSubmit, false); + + dropzone.style.overflowY = 'scroll'; +</script> +{% endblock %} \ No newline at end of file diff --git a/main/templates/index.html b/main/templates/index.html index b93db00..9bbb17b 100644 --- a/main/templates/index.html +++ b/main/templates/index.html @@ -115,11 +115,11 @@ {% else %} <option value="random">Aléatoire</option> {% endif %} - {% if stickyForm['indicatorSelect'] == 'test' %} + <!-- {% if stickyForm['indicatorSelect'] == 'test' %} <option value="test" selected>test</option> {% else %} <option value="test">test</option> - {% endif %} + {% endif %} --> </select> <div class="valid-feedback"> Correct ! @@ -167,17 +167,35 @@ <div class="row" id="rowRandom" style="display: none;"> <div class="col-md-6 mb-4"> <div class="form-outline"> - <label class="form-label" for="M">Random</label> - <input type="number" class="form-control form-control-lg" id="MA1" name="MA1" - max="100" min="5" value="20" step="5"> + <label class="form-label" for="minimumTime">Temps aléatoire minimum + (minute)</label> + <input type="number" class="form-control form-control-lg {{ err['random'] }}" + id="minimumTime" name="minimumTime" max="1440" min="15" + value="{{ stickyForm['minimumTime']}}" step="5"> + <div class="valid-feedback"> + Correct ! + </div> + <div class="invalid-feedback"> + Veuillez entrer un nombre entre 15 et 1440 minutes. Le temps aléatoire minimum + doit être plus petit que le maximum. + </div> </div> </div> <div class="col-md-6 mb-4"> <div class="form-outline"> - <label class="form-label" for="M">Random</label> - <input type="number" class="form-control form-control-lg" id="MA2" name="MA2" - max="105" min="10" value="40" step="5"> + <label class="form-label" for="maximumTime">Temps aléatoire maximum + (minute)</label> + <input type="number" class="form-control form-control-lg {{ err['random'] }}" + id="maximumTime" name="maximumTime" max="1440" min="15" + value="{{ stickyForm['maximumTime']}}" step="5"> + <div class="valid-feedback"> + Correct ! + </div> + <div class="invalid-feedback"> + Veuillez entrer un nombre entre 15 et 1440 minutes. Le temps aléatoire minimum + doit être plus petit que le maximum. + </div> </div> </div> @@ -200,8 +218,12 @@ </div> </div> - <div class="row text-center"> - <div class="mt-4 pt-2"> + <div class="row"> + <div class="col-md-6 mb-4"> + <a href="resetSimulation"><button type="button" + class="btn btn-secondary btn-lg">Réinitialiser les paramètres</button></a> + </div> + <div class="col-md-6 mb-4"> <button type="submit" class="btn btn-primary btn-lg">Lancer la simulation</button> </div> </div> @@ -281,13 +303,11 @@ résultats</button> </div> <div class="col-sm mb-4 text-center"> - <!-- Button trigger modal --> <button type="button" class="btn btn-primary btn-lg" data-bs-toggle="modal" data-bs-target="#modalSimulation"> Démarrer une nouvelle simulation </button> - <!-- Modal --> <div class="modal fade" id="modalSimulation" tabindex="-1" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> @@ -396,23 +416,23 @@ const SMA = document.getElementById('rowSMA'); const random = document.getElementById('rowRandom'); const test = document.getElementById('rowTest'); - + var inProgress = {{ inProgress| lower }}; var chart = null; {% if fig != None %} - chart = {{ fig | safe }}; - Plotly.plot("chart", chart, {}); - showMoreInfo.addEventListener('click', function () { - if (moreResults.style.display === 'none') { - moreResults.style.display = 'block'; - showMoreInfo.innerHTML = 'Masquer les résultats'; - } else { - moreResults.style.display = 'none'; - showMoreInfo.innerHTML = 'Afficher plus de résultats'; - } - }); - exportSimulation.addEventListener('click', exportFile); + chart = {{ fig | safe }}; + Plotly.plot("chart", chart, {}); + showMoreInfo.addEventListener('click', function () { + if (moreResults.style.display === 'none') { + moreResults.style.display = 'block'; + showMoreInfo.innerHTML = 'Masquer les résultats'; + } else { + moreResults.style.display = 'none'; + showMoreInfo.innerHTML = 'Afficher plus de résultats'; + } + }); + exportSimulation.addEventListener('click', exportFile); {% endif %} @@ -455,29 +475,64 @@ } } - function exportFile() { - Plotly.toImage(document.getElementById('chart'), { format: 'png' }) - .then(function(dataUrl){ - const filename = "{{ res['market'] }}.json"; - const jsonData = { - image: dataUrl, - res: {{ res | tojson }}, - params: {{ stickyForm | tojson }} + async function exportFile() { + const chartElement = document.getElementById('chart'); + const currentData = chartElement.data; + const currentLayout = chartElement.layout; + + const newChartElement = document.createElement('div'); + Plotly.newPlot(newChartElement, currentData, currentLayout); + + const layout = { + margin: { + t: 20, + r: 20, + b: 20, + l: 20 + } + }; + + Plotly.relayout(newChartElement, { + showlegend: false, + title: { + text: '' } - const jsonStr = JSON.stringify(jsonData); - - let element = document.createElement('a'); - element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(jsonStr)); - element.setAttribute('download', filename); - - element.style.display = 'none'; - document.body.appendChild(element); - - element.click(); - - document.body.removeChild(element); - }) + }); + + Plotly.update(newChartElement, {}, layout); + + Plotly.toImage(newChartElement, { format: 'png' }) + .then(function (dataUrl) { + const filename = "{{ res['market'] }}.json"; + const jsonData = { + image: dataUrl, + }; + const jsonStr = JSON.stringify(jsonData); + + fetch('/signJSON', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: jsonStr + }) + .then(response => response.json()) + .then(signedJson => { + const signedJsonStr = JSON.stringify(signedJson); + let element = document.createElement('a'); + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(signedJsonStr)); + element.setAttribute('download', filename); + + element.style.display = 'none'; + document.body.appendChild(element); + + element.click(); + + document.body.removeChild(element); + }); + }); } + </script> {% endblock %} \ No newline at end of file diff --git a/main/templates/mySimulation.html b/main/templates/mySimulation.html new file mode 100644 index 0000000..925a336 --- /dev/null +++ b/main/templates/mySimulation.html @@ -0,0 +1,138 @@ +{% extends 'base.html' %} +{% block content %} +{% if lstSimulations != [] %} +<section class="gradient-custom p-5 h-100 align-items-center my-auto"> + <div class="album py-5"> + <div class="container" style="padding: 0%; margin: 0%;"> + {% for error in err %} + <div class="alert alert-danger alert-dismissible fade show" role="alert"> + {{ error }} + <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> + </div> + {% endfor %} + {% if doublons != 0 %} + <div class="alert alert-warning alert-dismissible fade show" role="alert"> + {{ doublons }} doublon(s) supprimé(s) + <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> + </div> + {% endif %} + <div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3"> + {% for simulation in lstSimulations %} + <div class="col"> + <div class="card shadow-sm"> + <h5 class="px-4 pt-4">Marché : {{ simulation['res']['market']}}</h5> + <p class="px-4">{{ simulation['filename'] }}</p> + <img src="{{ simulation['image']}}" class="bd-placeholder-img card-img-top" width="100%" + height="225"> + <div class="card-body px-0"> + <div class="container h-100"> + <div class="row"> + {% for symbol in simulation['res']['losses']|reverse %} + {% if symbol == "USDT" %} + <div class="col-md-6 mb-2"> + <span class="badge badge-pill bg-danger">{{ symbol }}</span><span + style="color:red"> + {{ simulation['res']['moneyLosses'][symbol]|round(2)|string + }}₮ ({{ simulation['res']['losses'][symbol]|round(2)|string+"%" }})</span> + </div> + {% elif symbol == "BTC" %} + <div class="col-md-6 mb-2" style="padding-right: 0%;"> + <span class="badge badge-pill bg-danger">{{ symbol }}</span><span + style="color:red"> + {{ + "{:.5f}".format(simulation['res']['moneyLosses'][symbol])|string + }}₿ ({{ simulation['res']['losses'][symbol]|round(2)|string+"%" }})</span> + </div> + {% endif %} + {% endfor %} + + {% for symbol in simulation['res']['profits']|reverse %} + {% if symbol == "USDT" %} + <div class="col-md-6 mb-2"> + <span class="badge badge-pill bg-success">{{ symbol }}</span><span + style="color:green"> +{{ + simulation['res']['moneyProfits'][symbol]|round(2)|string + }}₮ ({{ simulation['res']['profits'][symbol]|round(2)|string+"%" }})</span> + </div> + {% elif symbol == "BTC" %} + <div class="col-md-6 mb-2" style="padding-right: 0%;"> + <span class="badge badge-pill bg-success">{{ symbol }}</span><span + style="color:green"> +{{ + "{:.5f}".format(simulation['res']['moneyProfits'][symbol])|string + }}₿ ({{ simulation['res']['profits'][symbol]|round(2)|string+"%" }})</span> + </div> + {% endif %} + {% endfor %} + {% if simulation['res']['profits'] == {} and simulation['res']['losses'] == {} %} + <span class="text-center mb-2">Aucun profit ou perte</span> + {% endif %} + </div> + <div class="row"> + <div class="col-md-6 mb-2"> + <span class="badge badge-pill bg-warning">Take Profit</span><span> {{ + simulation['params']['takeProfit'] }}</span> + </div> + <div class="col-md-6 mb-2"> + <span class="badge badge-pill bg-warning">Stop Loss</span><span> {{ + simulation['params']['stopLoss'] }}</span> + </div> + </div> + <div class="row"> + <div class="col-md-6 mb-2"> + <span class="badge badge-pill bg-warning">Stratégie</span><span> {{ + simulation['params']['indicatorSelect'] }}</span> + </div> + <div class="col-md-6 mb-2"> + <span class="badge badge-pill bg-warning">Winrate</span><span> {{ + simulation['res']['winrate'] }}%</span> + </div> + </div> + <br> + <div class="row"> + <div class="col-md-6 mb-2"> + <a href="/deleteSimulation?index={{ loop.index-1 }}" + class="btn btn-sm btn-danger">Supprimer</a> + </div> + <div class="col-md-6 mb-2"> + <a href="/restartSimulationWithParamsFromIndex?index={{ loop.index-1 }}" + class="btn btn btn-sm btn-primary">Relancer la simulation</a> + </div> + </div> + </div> + </div> + </div> + </div> + {% endfor %} + {% if lstSimulations|length > 0 %} + <div class="col"> + <div class="card shadow-sm h-100"> + <div class="card-body px-0"> + <div class="container h-100"> + <div class="d-flex justify-content-center align-items-center" style="height: 100%;"> + <a href="importSimulation"><i + class="bi bi-plus-square" id="logo" style="font-size: 70px; color:green;"></i></a> + </div> + </div> + </div> + </div> + </div> + <script> + const logo = document.getElementById('logo'); + logo.addEventListener('mouseover', function () { + logo.classList.remove('bi-plus-square'); + logo.classList.add('bi-plus-square-fill'); + }); + + logo.addEventListener('mouseout', function () { + logo.classList.remove('bi-plus-square-fill'); + logo.classList.add('bi-plus-square'); + }); + </script> + {% endif %} + </div> + </div> + </div> + </div> +</section> +{% endif %} +{% endblock %} \ No newline at end of file -- GitLab