from datetime import datetime import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui from PyQt5.QtCore import QTime, QTimer from PyQt5.QtWidgets import QFrame import numpy as np import time from threading import Timer, Lock import pytz from tzlocal import get_localzone from random import randint import TradingBotConfig as theConfig from UIWidgets import ButtonHoverStart from UIWidgets import ButtonHoverStart from UIWidgets import ButtonHoverPause from UIWidgets import ButtonHoverSettings from UIWidgets import ButtonHoverDonation from UIWidgets import ButtonHoverInfo from UIWidgets import RadioHoverSimulation from UIWidgets import RadioHoverTrading from UIWidgets import SliderHoverRiskLevel from UIWidgets import SliderHoverSensitivityLevel from UIWidgets import LabelClickable from UISettings import UISettings from UIDonation import UIDonation from UIInfo import UIInfo class TimeAxisItem(pg.AxisItem): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.localTimezone = pytz.timezone(str(get_localzone())) def tickStrings(self, values, scale, spacing): try: if (theConfig.CONFIG_INPUT_MODE_IS_REAL_MARKET == False): # valuesToReturn = [(datetime.fromtimestamp(value, self.localTimezone).strftime("%H:%M:%S\n%b%d")) for value in values] else: valuesToReturn = [(datetime.fromtimestamp(value, self.localTimezone).strftime("%H:%M:%S")) for value in values] except BaseException as e: print("UIGR - Exception in tick strings: %s" % str(e)) return valuesToReturn class UIGraph(): MAIN_WINDOW_WIDTH_IN_PX = 1120 MAIN_WINDOW_HEIGHT_IN_PX = 900 MAX_NB_POINTS_ON_PLOT = 2000 Y_AXIS_TOP_MARGIN_IN_EXTREMUM_PERCENT = 1.0001 Y_AXIS_BOTTOM_MARGIN_IN_EXTREMUM_PERCENT = 0.9999 PLOT1_DEFAULT_MINIMUM = 8000 PLOT1_DEFAULT_MAXIMUM = 10000 STR_RADIO_SIMULATION = 'Simulation mode' STR_RADIO_TRADING = 'Live trading mode' STR_BUTTON_START = 'Start' STR_BUTTON_PAUSE = 'Pause' STR_BUTTON_SETTINGS = 'Settings' STR_BUTTON_Donation = 'Donate' STR_BUTTON_INFO = 'Info' STR_LABEL_MONEY_MIDDLEMARKET_PRICE = 'MiddleMarket price : ' STR_LABEL_INFO = 'Info : ' STR_LABEL_CURRENT_STATE = 'Current State : ' STR_LABEL_TOTAL_GAINS = 'Total profit : ' STR_BORDER_BLOCK_STYLESHEET = "QWidget {background-color : #151f2b;}" STR_USER_BLOCK_STYLESHEET = "QWidget {background-color : #203044;}" STR_QLABEL_STYLESHEET = "QLabel { background-color : #203044; color : white; font: bold 13px;}" STR_QLABEL_PROFIT_GREEN_STYLESHEET = "QLabel { background-color : #203044; color : #24b62e; font: bold 14px;}" STR_QLABEL_PROFIT_RED_STYLESHEET = "QLabel { background-color : #203044; color : #FF2F2F; font: bold 14px;}" STR_QLABEL_CURRENT_STATE_LIVE_TRADING_STYLESHEET = "QLabel { background-color : #203044; color : #ff2e2e; font: bold 14px;}" STR_QLABEL_INFO_STYLESHEET = "QLabel { background-color : #203044; color : white; font: 14px;}" STR_QLABEL_INFO_ERROR_STYLESHEET = "QLabel { background-color : #203044; color : #FF2F2F; font: 14px;}" STR_QLABEL_INFO_GREEN_STYLESHEET = "QLabel { background-color : #203044; color : #29CF36; font: bold 14px; text-decoration: underline;}" STR_QLABEL_INFO_ORANGE_STYLESHEET = "QLabel { background-color : #203044; color : #FF8000; font: bold 14px; text-decoration: underline;}" STR_QLABEL_TOOLTIP_STYLESHEET = "QLabel { background-color : #151f2b; color : white; font: 10px;}" STR_QLABEL_CONNEXION_STATUS_STYLESHEET = "QLabel { background-color : #151f2b; color : green; font: 10px;}" STR_QLABEL_VERSION_STYLESHEET = "QLabel { background-color : #151f2b; color : #334c6b; font: 11px;}" STR_QLABEL_LIVE_DATA_STYLESHEET = "QLabel { background-color : #151f2b; color : #334c6b; font: 10px;}" STR_QRADIOBUTTON_STYLESHEET = "QRadioButton { background-color : #203044; color : white; font: 14px;} QRadioButton::indicator:checked {background-color: #007ad9; border: 1px solid white;} QRadioButton::indicator:unchecked {background-color: #203044; border: 1px solid white;}" STR_QRADIOBUTTON_DISABLED_STYLESHEET = "QRadioButton { background-color : #203044; color : white; font: 14px;} QRadioButton::indicator:checked {background-color: #007ad9; border: 1px solid #203044;} QRadioButton::indicator:unchecked {background-color: #203044; border: 1px solid #203044;}" STR_QBUTTON_START_STYLESHEET = "QPushButton {background-color: #23b42c; border-width: 2px; border-radius: 10px; border-color: white; font: bold 18px; color:white} QPushButton:pressed { background-color: #1d8d24 } QPushButton:hover { background-color: #1a821f }" STR_QBUTTON_SETTINGS_STYLESHEET = "QPushButton {background-color: #21435e; border-width: 1.5px; border-radius: 10px; border-color: white; font: bold 15px; color:white} QPushButton:pressed { background-color: #096fbf } QPushButton:hover { background-color: #1D3850 }" STR_QBUTTON_SETTINGS_DISABLED_STYLESHEET = "QPushButton {background-color: #183145; border-width: 1.5px; border-radius: 10px; border-color: #838fa7; font: bold 15px; color:#838fa7}" STR_QBUTTON_START_DISABLED_STYLESHEET = "QPushButton {background-color: #9f9f9f; border-width: 2px; border-radius: 10px; border-color: white; font: bold 18px; color:white}" STR_QBUTTON_LOADING_STYLESHEET = "QPushButton {background-color: #9f9f9f; border-width: 2px; border-radius: 10px; border-color: white; font: bold 15px; color:white}" STR_QBUTTON_STOP_STYLESHEET = "QPushButton {background-color: #ff1824; border-width: 2px; border-radius: 10px; border-color: white; font: bold 18px; color:white} QPushButton:pressed { background-color: #aa0009 } QPushButton:hover { background-color: #aa0009 }" STR_QBUTTON_PAUSE_STYLESHEET = "QPushButton {background-color: #ddbd00; border-width: 2px; border-radius: 10px; border-color: white; font: bold 18px; color:white} QPushButton:pressed { background-color: #b3af00 } QPushButton:hover { background-color: #b3af00 }" STR_QBUTTON_PAUSE_DISABLED_STYLESHEET = "QPushButton {background-color: #9f9f9f; border-width: 2px; border-radius: 10px; border-color: white; font: bold 18px; color:white}" STR_QFRAME_SEPARATOR_STYLESHEET = "background-color: rgb(20, 41, 58);" STR_QSLIDER_STYLESHEET = "QSlider::handle:hover {background-color: #C6D0FF;}" def __init__(self, QtApplication, Settings): print("UIGR - UIGraph Constructor") self.theSettings = Settings self.firstGraphDataInitIsDone = False # Settings-dependant variables init self.STR_LABEL_FIAT_BALANCE = str(self.theSettings.SETT_GetSettings()["strFiatType"]) + " Account Balance : " self.STR_LABEL_CRYPTO_BALANCE = str(self.theSettings.SETT_GetSettings()["strCryptoType"]) + " Account Balance : " # Window initialization self.theQtApp = QtApplication self.mainWidget = QtGui.QWidget() self.rootGrid = QtGui.QGridLayout() self.mainWidget.setWindowTitle('Astibot') self.mainWidget.resize(self.MAIN_WINDOW_WIDTH_IN_PX, self.MAIN_WINDOW_HEIGHT_IN_PX) self.mainWidget.setWindowIcon(QtGui.QIcon("AstibotIcon.png")) # Customize main widget (window) self.mainWidget.setStyleSheet("background-color:#203044;") self.mainWidget.setAutoFillBackground(True); # By default consider the data series will start now. This can be overridden self.MostRecentPointTimestamp = time.time() # Widget additional data initialization self.bStartButtonHasBeenClicked = False self.bPauseButtonHasBeenClicked = False self.timerBlinkWidgets = QtCore.QTimer() self.timerBlinkWidgets.timeout.connect(self.TimerRaisedBlinkWidgets) self.timerBlinkWidgets.start(1000) self.isLblCurrentStateBlinking = False self.currentRiskLineRawAvgValue = 0 self.currentSensitivitySliderValue = 3 self.sensitivitySliderValueHasChanged = True # Graph data initialization pg.setConfigOptions(antialias=True) nbPointsOnPlot = self.MAX_NB_POINTS_ON_PLOT self.UIGR_ResetAllGraphData(False, -1, nbPointsOnPlot) # Layouts and widgets init self.initializeTopWindowWidgets() self.initializeGraphWidgets() self.initializeRootLayout() self.mainWidget.setLayout(self.mainGridLayout) # Graph refresh and multithreading management self.isContinuousGraphRefreshEnabled = False self.areNewSamplesRequested = False self.timerUpdateGraph = QtCore.QTimer() self.timerUpdateGraph.timeout.connect(self.UIGR_updateGraphsSimuTimer) self.currentAppState = "" # True if a pending UI refresh has been requested from a background thread calling a UIGR API self.safeUIRefreshIsRequested = False # Necessary storage of safe UI updates self.lblInfoInErrorStyle = False self.lblInfoStr = "" self.realProfit = 0.0 self.theoricProfit = 0.0 self.percentageProfit = 0.0 self.displayProfitAsInSimulation = False self.priceLabelStr = "" self.strEURBalance = "" self.strCryptoBalance = "" self.strLiveData = "-" # Live data update timer self.timerUpdateLiveData = QtCore.QTimer() self.timerUpdateLiveData.timeout.connect(self.UIGR_updateLiveDataTimer) self.timerUpdateLiveData.start(150) # End if UI init, show window self.mainWidget.show() # Child windows self.theUISettings = UISettings(Settings) self.theUIDonation = UIDonation(Settings) self.theUIInfo = UIInfo() # Set child UIs to clickable label that can open them self.lblInfo.SetUIs(self.theUISettings, self.theUIDonation) print("UIGR - UIGraph init done!") # Argument startTimeStamp : # - Set to -1 if the graph will be batch updated with a number of data greater than nbPointsOnPlot : time # axis will be ok and next samples will be added on the left shifting the whole graph # - Set to the timestamp of the first sample that will be added to the graph. This function will then build a # retro time axis so that, during the period where added samples < nbPointsOnPlot, next samples will be added # on the left shifting the whole graph def UIGR_ResetAllGraphData(self, applyToGraphs, startTimeStamp, nbPointsOnPlot): print("UIGR - Reseting all graph data with applyToGraphs = %s, startTimeStamp = %s, nbPointsOnPlot = %s" % (applyToGraphs, startTimeStamp, nbPointsOnPlot)) self.totalNbIterations = 0 self.totalNbGraphUpdates = 0 self.timeOfLastSampleDisplayed = 0 self.nbPointsOnPlot = nbPointsOnPlot self.currentRiskLineRawAvgValue = 0 self.graphDataTime = [] self.graphDataBitcoinPrice = [] self.graphDataBitcoinPriceSmoothSlow = [] self.graphDataBitcoinPriceSmoothFast = [] self.graphDataBitcoinPriceMarker1 = [] self.graphDataBitcoinPriceMarker2 = [] self.graphDataBitcoinRiskLine = [] self.graphDataIndicatorMACD = [] self.minInPlot1 = self.PLOT1_DEFAULT_MINIMUM self.maxInPlot1 = self.PLOT1_DEFAULT_MAXIMUM self.graphDataTime = np.zeros(self.nbPointsOnPlot) # Y-Data vectors : put empty values (apprently set to zero) self.graphDataBitcoinPrice = np.zeros(self.nbPointsOnPlot) #For tests np.random.normal(size=self.nbPointsOnPlot) self.graphDataBitcoinPriceSmoothFast = np.zeros(self.nbPointsOnPlot) self.graphDataBitcoinPriceSmoothSlow = np.zeros(self.nbPointsOnPlot) self.graphDataBitcoinPriceMarker1 = np.zeros(self.nbPointsOnPlot) self.graphDataBitcoinPriceMarker2 = np.zeros(self.nbPointsOnPlot) self.graphDataBitcoinRiskLine = np.zeros(self.nbPointsOnPlot) self.graphDataIndicatorMACD = np.zeros(self.nbPointsOnPlot) if (startTimeStamp != -1): # Time vector : put old timestamps until now so that the "present time" will be located at the right of the graph self.graphDataTime = self.initInitialTimeVector(startTimeStamp) # Called from Main (UI) thread - OK def UIGR_StartContinuousGraphRefresh(self, refreshPeriodInMs): if (self.isContinuousGraphRefreshEnabled == False): print("UIGR - Starting continuous graph refresh") self.isContinuousGraphRefreshEnabled = True self.areNewSamplesRequested = True self.timerUpdateGraph.start(refreshPeriodInMs) # Called from Main (UI) thread - OK def UIGR_StopContinuousGraphRefresh(self): if (self.isContinuousGraphRefreshEnabled == True): print("UIGR - Stopping continuous graph refresh") self.isContinuousGraphRefreshEnabled = False self.areNewSamplesRequested = False self.timerUpdateGraph.stop() def UIGR_AreNewSamplesRequested(self): if (self.areNewSamplesRequested == True): self.areNewSamplesRequested = False return True else: return False def EventStartButtonClick(self): self.bStartButtonHasBeenClicked = True def EventPauseButtonClick(self): self.bPauseButtonHasBeenClicked = True def EventSettingsButtonClick(self): self.theUISettings.UIST_ShowWindow() def EventDonationButtonClick(self): self.theUIDonation.UIDO_ShowWindow() def EventInfoButtonClick(self): self.theUIInfo.UIFO_ShowWindow() def EventRadioModeToggle(self): theConfig.CONFIG_INPUT_MODE_IS_REAL_MARKET = not self.radioButtonSimulation.isChecked() def UIGR_IsStartButtonClicked(self): if (self.bStartButtonHasBeenClicked == True): self.bStartButtonHasBeenClicked = False return True else: return False def UIGR_IsPauseButtonClicked(self): if (self.bPauseButtonHasBeenClicked == True): self.bPauseButtonHasBeenClicked = False return True else: return False def UIGR_GetSelectedRadioMode(self): if (self.radioButtonSimulation.isChecked() == True): return "Simulation" else: return "Trading" def EventMovedSliderRiskLevel(self): newRiskLineValue = theConfig.CONFIG_RISK_LINE_PERCENTS_ABOVE_THRESHOLD_TO_BUY_MIN + ((self.sliderRiskLevel.value() / 100.0) * (theConfig.CONFIG_RISK_LINE_PERCENTS_ABOVE_THRESHOLD_TO_BUY_MAX - theConfig.CONFIG_RISK_LINE_PERCENTS_ABOVE_THRESHOLD_TO_BUY_MIN)) if (abs(newRiskLineValue - theConfig.CONFIG_RiskLinePercentsAboveThresholdToBuy) > 0.0005): theConfig.CONFIG_RiskLinePercentsAboveThresholdToBuy = newRiskLineValue riskPercent = round((theConfig.CONFIG_RiskLinePercentsAboveThresholdToBuy - 1) * 100, 1) self.lblRiskLevelSlider1.setText("Risk level: %s%%" % str(riskPercent)) # Refresh risk line plot data self.graphDataBitcoinRiskLine.fill(self.currentRiskLineRawAvgValue * theConfig.CONFIG_RiskLinePercentsAboveThresholdToBuy) # Update graph self.plot1GraphRiskLine.setData(x=self.graphDataTime, y=self.graphDataBitcoinRiskLine) if (theConfig.CONFIG_INPUT_MODE_IS_REAL_MARKET == True): # Force UI refresh. After a long running time, UI refresh is not automatic sometimes self.plot1.update() def EventMovedSliderSensitivityLevel(self): print("slider moved to %s" % self.sliderSensitivityLevel.value()) if (self.sliderSensitivityLevel.value() != self.currentSensitivitySliderValue): self.currentSensitivitySliderValue = self.sliderSensitivityLevel.value() self.lblSensitivityLevelSlider1.setText("Dips sensitivity: %s/6" % str(self.currentSensitivitySliderValue)) self.sensitivitySliderValueHasChanged = True def UIGR_hasSensitivityLevelValueChanged(self): return self.sensitivitySliderValueHasChanged def UIGR_getSensitivityLevelValue(self): self.sensitivitySliderValueHasChanged = False return self.currentSensitivitySliderValue def initializeRootLayout(self): print("UIGR - InitializeRootLayout") self.mainGridLayout.setContentsMargins(0, 0, 0, 0) self.rootBlockTop = QtGui.QWidget() self.rootBlockTop.setStyleSheet(self.STR_BORDER_BLOCK_STYLESHEET) # Top ==================================== self.buttonSettings = ButtonHoverSettings(self.lblToolTip, self.STR_BUTTON_SETTINGS) self.buttonSettings.setVisible(True) self.buttonSettings.clicked.connect(self.EventSettingsButtonClick) self.buttonSettings.setFixedWidth(110) self.buttonSettings.setFixedHeight(28) self.buttonSettings.setStyleSheet(self.STR_QBUTTON_SETTINGS_STYLESHEET) self.buttonSettings.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.buttonDonation = ButtonHoverDonation(self.lblToolTip, self.STR_BUTTON_Donation) self.buttonDonation.setVisible(True) self.buttonDonation.clicked.connect(self.EventDonationButtonClick) self.buttonDonation.setFixedWidth(110) self.buttonDonation.setFixedHeight(28) self.buttonDonation.setStyleSheet(self.STR_QBUTTON_SETTINGS_STYLESHEET) self.buttonDonation.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.buttonInfo = ButtonHoverInfo(self.lblToolTip, self.STR_BUTTON_INFO) self.buttonInfo.setVisible(True) self.buttonInfo.clicked.connect(self.EventInfoButtonClick) self.buttonInfo.setFixedWidth(110) self.buttonInfo.setFixedHeight(28) self.buttonInfo.setStyleSheet(self.STR_QBUTTON_SETTINGS_STYLESHEET) self.buttonInfo.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.lblVersion = QtGui.QLabel("Version " + str(theConfig.CONFIG_VERSION)) self.lblVersion.setStyleSheet(self.STR_QLABEL_VERSION_STYLESHEET); self.lblVersion.setAlignment(QtCore.Qt.AlignLeft) self.lblVersion.setFixedHeight(28) self.rootTopBlock = QtGui.QWidget() self.rootTopBlock.setStyleSheet(self.STR_BORDER_BLOCK_STYLESHEET) self.rootTopBlock.setFixedHeight(60) self.rootHboxTop = QtGui.QHBoxLayout() self.rootHboxTop.setContentsMargins(40, 0, 40, 0) # left, top, right, bottom self.lblLogo = QtGui.QLabel("lblLogo") pixmap = QtGui.QPixmap('AstibotLogo.png') self.lblLogo.setPixmap(pixmap) self.rootHboxTop.addWidget(self.lblLogo) self.rootHboxTop.addWidget(self.lblVersion) self.rootHboxTop.addWidget(self.buttonSettings, QtCore.Qt.AlignRight) self.rootHboxTop.addWidget(self.buttonDonation, QtCore.Qt.AlignRight) self.rootHboxTop.addWidget(self.buttonInfo, QtCore.Qt.AlignRight) self.rootTopBlock.setLayout(self.rootHboxTop) self.mainGridLayout.addWidget(self.rootTopBlock, 0, 0, 1, 4) # Bottom ================================== self.rootBottomBlock = QtGui.QWidget() self.rootBottomBlock.setStyleSheet(self.STR_BORDER_BLOCK_STYLESHEET) self.rootBottomBlock.setFixedHeight(40) self.rootHboxBottom = QtGui.QHBoxLayout() self.rootHboxBottom.setContentsMargins(40, 0, 40, 0) # left, top, right, bottom self.rootVboxBottomRight = QtGui.QVBoxLayout() self.lblConnexion = QtGui.QLabel("") self.lblConnexion.setAlignment(QtCore.Qt.AlignRight) self.lblToolTip.setStyleSheet(self.STR_QLABEL_TOOLTIP_STYLESHEET); self.lblToolTip.setWordWrap(True); self.lblToolTip.setFixedWidth((self.MAIN_WINDOW_WIDTH_IN_PX / 2)) self.lblToolTip.setFixedHeight(42) self.lblConnexion.setStyleSheet(self.STR_QLABEL_CONNEXION_STATUS_STYLESHEET); self.lblLiveData = QtGui.QLabel("") self.lblLiveData.setStyleSheet(self.STR_QLABEL_LIVE_DATA_STYLESHEET); self.lblLiveData.setAlignment(QtCore.Qt.AlignRight) self.lblConnexion.setAlignment(QtCore.Qt.AlignRight) self.rootHboxBottom.addWidget(self.lblToolTip, QtCore.Qt.AlignLeft) self.rootVboxBottomRight.addWidget(self.lblConnexion) self.rootVboxBottomRight.addWidget(self.lblLiveData) self.rootHboxBottom.addLayout(self.rootVboxBottomRight, QtCore.Qt.AlignRight) self.rootBottomBlock.setLayout(self.rootHboxBottom) self.mainGridLayout.addWidget(self.rootBottomBlock, 13, 0, 1, 4) # Left and Right self.rootLeftBlock = QtGui.QWidget() self.rootLeftBlock.setStyleSheet(self.STR_BORDER_BLOCK_STYLESHEET) self.rootLeftBlock.setFixedWidth(40) self.rootRightBlock = QtGui.QWidget() self.rootRightBlock.setStyleSheet(self.STR_BORDER_BLOCK_STYLESHEET) self.rootRightBlock.setFixedWidth(40) self.mainGridLayout.addWidget(self.rootLeftBlock, 0, 0, 14, 1) self.mainGridLayout.addWidget(self.rootRightBlock, 0, 3, 14, 1) def initializeTopWindowWidgets(self): # Pre requisite for further inits self.lblToolTip = QtGui.QLabel(""); self.rootMiddleBlock1 = QtGui.QWidget() self.rootMiddleBlock1.setStyleSheet(self.STR_BORDER_BLOCK_STYLESHEET) self.rootMiddleBlock1.setFixedHeight(15) self.rootMiddleBlock2 = QtGui.QWidget() self.rootMiddleBlock2.setStyleSheet(self.STR_BORDER_BLOCK_STYLESHEET) self.rootMiddleBlock2.setFixedHeight(15) # Part 1 self.hBox1 = QtGui.QHBoxLayout() self.vBoxRadioModeButtons = QtGui.QVBoxLayout() self.vBoxSliders = QtGui.QVBoxLayout() self.hBoxSliders1 = QtGui.QHBoxLayout() self.hBoxSliders2 = QtGui.QHBoxLayout() self.hBox1.setSpacing(10) self.radioButtonSimulation = RadioHoverSimulation(self.lblToolTip, self.STR_RADIO_SIMULATION) self.radioButtonSimulation.setChecked(False) self.radioButtonSimulation.setFixedWidth(200) self.radioButtonSimulation.setStyleSheet(self.STR_QRADIOBUTTON_STYLESHEET); self.radioButtonSimulation.toggled.connect(self.EventRadioModeToggle) self.radioButtonSimulation.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.radioButtonTrading = RadioHoverTrading(self.lblToolTip, self.STR_RADIO_TRADING) self.radioButtonTrading.setChecked(True) self.radioButtonTrading.setFixedWidth(200) self.radioButtonTrading.setStyleSheet(self.STR_QRADIOBUTTON_STYLESHEET); self.radioButtonTrading.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.buttonPause = ButtonHoverPause(self.lblToolTip, self.STR_BUTTON_PAUSE) self.buttonPause.setVisible(True) self.buttonPause.clicked.connect(self.EventPauseButtonClick) self.buttonPause.setFixedWidth(80) self.buttonPause.setFixedHeight(60) self.buttonPause.setStyleSheet(self.STR_QBUTTON_PAUSE_STYLESHEET) self.buttonPause.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.buttonStart = ButtonHoverStart(self.lblToolTip, self.STR_BUTTON_START) self.buttonStart.setVisible(True) self.buttonStart.clicked.connect(self.EventStartButtonClick) self.buttonStart.setFixedWidth(80) self.buttonStart.setFixedHeight(60) self.buttonStart.setStyleSheet(self.STR_QBUTTON_START_STYLESHEET) self.buttonStart.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.vBoxRadioModeButtons.addWidget(self.radioButtonSimulation) self.vBoxRadioModeButtons.addWidget(self.radioButtonTrading) self.hBox1.addLayout(self.vBoxRadioModeButtons, QtCore.Qt.AlignLeft) self.hBox1.addWidget(self.buttonPause, QtCore.Qt.AlignLeft) self.hBox1.addWidget(self.buttonStart, QtCore.Qt.AlignLeft) # Slider Risk level self.lblRiskLevelSlider1 = QtGui.QLabel("Risk level: "); self.lblRiskLevelSlider1.setFixedWidth(140) self.lblRiskLevelSlider2 = QtGui.QLabel("Low"); self.lblRiskLevelSlider2.setFixedWidth(30); self.lblRiskLevelSlider3 = QtGui.QLabel("High"); self.lblRiskLevelSlider3.setFixedWidth(30) self.sliderRiskLevel = SliderHoverRiskLevel(self.lblToolTip, QtCore.Qt.Horizontal) self.sliderRiskLevel.setMinimum(0) self.sliderRiskLevel.setMaximum(100) self.sliderRiskLevel.setFixedWidth(130) self.sliderRiskLevel.setValue(round((theConfig.CONFIG_RiskLinePercentsAboveThresholdToBuy - theConfig.CONFIG_RISK_LINE_PERCENTS_ABOVE_THRESHOLD_TO_BUY_MIN) * 100 / (theConfig.CONFIG_RISK_LINE_PERCENTS_ABOVE_THRESHOLD_TO_BUY_MAX - theConfig.CONFIG_RISK_LINE_PERCENTS_ABOVE_THRESHOLD_TO_BUY_MIN))) self.sliderRiskLevel.valueChanged.connect(self.EventMovedSliderRiskLevel) self.sliderRiskLevel.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.EventMovedSliderRiskLevel() # Refresh trading parameter according to slider initial position self.lblRiskLevelSlider1.setStyleSheet(self.STR_QLABEL_STYLESHEET) self.lblRiskLevelSlider2.setStyleSheet(self.STR_QLABEL_STYLESHEET) self.lblRiskLevelSlider3.setStyleSheet(self.STR_QLABEL_STYLESHEET) self.sliderRiskLevel.setStyleSheet(self.STR_QSLIDER_STYLESHEET) self.lblSensitivityLevelSlider1 = QtGui.QLabel("Dips sensitivity: "); self.lblSensitivityLevelSlider1.setFixedWidth(140) self.lblSensitivityLevelSlider2 = QtGui.QLabel("Low"); self.lblSensitivityLevelSlider2.setFixedWidth(30) self.lblSensitivityLevelSlider3 = QtGui.QLabel("High"); self.lblSensitivityLevelSlider3.setFixedWidth(30) self.sliderSensitivityLevel = SliderHoverSensitivityLevel(self.lblToolTip, QtCore.Qt.Horizontal) self.sliderSensitivityLevel.setMinimum(1) self.sliderSensitivityLevel.setMaximum(6) self.sliderSensitivityLevel.setTickInterval(1) self.sliderSensitivityLevel.setSingleStep(1) self.sliderSensitivityLevel.setFixedWidth(130) self.sliderSensitivityLevel.setValue(self.currentSensitivitySliderValue) self.sliderSensitivityLevel.setTickPosition(QtGui.QSlider.TicksBelow) self.sliderSensitivityLevel.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.sliderSensitivityLevel.valueChanged.connect(self.EventMovedSliderSensitivityLevel) self.lblSensitivityLevelSlider1.setStyleSheet(self.STR_QLABEL_STYLESHEET) self.lblSensitivityLevelSlider2.setStyleSheet(self.STR_QLABEL_STYLESHEET) self.lblSensitivityLevelSlider3.setStyleSheet(self.STR_QLABEL_STYLESHEET) self.sliderSensitivityLevel.setStyleSheet(self.STR_QSLIDER_STYLESHEET) self.vBoxSliders.addLayout(self.hBoxSliders1, QtCore.Qt.AlignLeft) self.vBoxSliders.addLayout(self.hBoxSliders2, QtCore.Qt.AlignLeft) self.hBoxSliders1.addWidget(self.lblRiskLevelSlider1, QtCore.Qt.AlignLeft) self.hBoxSliders1.addWidget(self.lblRiskLevelSlider2) self.hBoxSliders1.addWidget(self.sliderRiskLevel) self.hBoxSliders1.addWidget(self.lblRiskLevelSlider3) self.hBoxSliders2.addWidget(self.lblSensitivityLevelSlider1, QtCore.Qt.AlignLeft) self.hBoxSliders2.addWidget(self.lblSensitivityLevelSlider2) self.hBoxSliders2.addWidget(self.sliderSensitivityLevel) self.hBoxSliders2.addWidget(self.lblSensitivityLevelSlider3) # Part 2 self.lblLivePrice = QtGui.QLabel() self.STR_LABEL_MONEY_MIDDLEMARKET_PRICE = self.theSettings.SETT_GetSettings()["strCryptoType"] + str(" ") + str(self.STR_LABEL_MONEY_MIDDLEMARKET_PRICE) self.lblLivePrice.setText(self.STR_LABEL_MONEY_MIDDLEMARKET_PRICE) self.lblInfo = LabelClickable(self.STR_LABEL_INFO) self.lblInfo.setFixedHeight(24) self.lblCurrentState = QtGui.QLabel(self.STR_LABEL_CURRENT_STATE) self.lblFiatBalance = QtGui.QLabel(self.STR_LABEL_FIAT_BALANCE) self.lblCryptoMoneyBalance = QtGui.QLabel(self.STR_LABEL_CRYPTO_BALANCE) self.lblCryptoMoneyBalance.setFixedHeight(20) self.lblTotalGains = QtGui.QLabel(self.STR_LABEL_TOTAL_GAINS) self.lblLivePrice.setStyleSheet(self.STR_QLABEL_STYLESHEET); self.lblInfo.setStyleSheet(self.STR_QLABEL_STYLESHEET); self.lblCurrentState.setStyleSheet(self.STR_QLABEL_STYLESHEET); self.lblFiatBalance.setStyleSheet(self.STR_QLABEL_STYLESHEET); self.lblCryptoMoneyBalance.setStyleSheet(self.STR_QLABEL_STYLESHEET); self.lblTotalGains.setStyleSheet(self.STR_QLABEL_STYLESHEET); # Add widgets to layout self.mainGridLayout = QtGui.QGridLayout() self.mainGridLayout.addWidget(self.rootMiddleBlock1, 5, 0, 1, 4) self.mainGridLayout.addWidget(self.lblLivePrice, 1, 2) self.mainGridLayout.addWidget(self.lblCurrentState, 2, 2) self.mainGridLayout.addWidget(self.lblInfo, 4, 1, 1, 2) self.mainGridLayout.addWidget(self.lblFiatBalance, 1, 1, QtCore.Qt.AlignLeft) self.mainGridLayout.addWidget(self.lblCryptoMoneyBalance, 2, 1, QtCore.Qt.AlignLeft) self.mainGridLayout.addWidget(self.lblTotalGains, 3, 1, QtCore.Qt.AlignLeft) self.mainGridLayout.addLayout(self.hBox1, 6, 1, QtCore.Qt.AlignLeft) self.mainGridLayout.addLayout(self.vBoxSliders, 6, 2, QtCore.Qt.AlignLeft) self.mainGridLayout.addWidget(self.rootMiddleBlock2, 7, 0, 1, 4) # Each column of the grid layout has the same total width proportion self.mainGridLayout.setColumnStretch(1, 1) self.mainGridLayout.setColumnStretch(2, 1) self.mainGridLayout.setRowStretch(10, 2) def initializeGraphWidgets(self): pg.setConfigOption('foreground', 'w') pg.setConfigOption('background', (32, 48, 68)) pg.GraphicsLayout(border=(100,100,100)) self.strPlot1Title = str(self.theSettings.SETT_GetSettings()["strTradingPair"]) + ' Coinbase Pro Market Price (' + str(self.theSettings.SETT_GetSettings()["strFiatType"]) + ')' self.plot1 = pg.PlotWidget(title=self.strPlot1Title, axisItems={'bottom': TimeAxisItem(orientation='bottom')}) self.plot1.setYRange(self.minInPlot1, self.maxInPlot1) self.plot1.setMouseEnabled(False, False) # Mettre False, True pour release self.plot1.setMenuEnabled(False) axis = self.plot1.getAxis('bottom') # This is the trick axis.setStyle(textFillLimits = [(0, 0.7)]) #self.plot1.plotItem.vb.setBackgroundColor((15, 25, 34, 255)) self.plot2 = pg.PlotWidget(title='Astibot decision indicator (normalized)') self.plot2.showGrid(x=True,y=True,alpha=0.1) self.plot2.setYRange(-100, 100) self.plot2.setMouseEnabled(False, True) self.plot2.setMouseEnabled(False) self.plot2.hideAxis('bottom') # Graphs take one row but 2 columns self.mainGridLayout.addWidget(self.plot1, 9, 1, 1, 2) self.mainGridLayout.addWidget(self.plot2, 10, 1, 1, 2) # Graph curves initialization self.plot1GraphLivePrice = self.plot1.plot(x=self.graphDataTime, y=self.graphDataBitcoinPrice, name=' Price') # , clipToView=True self.plot1GraphLivePrice.setPen(color=(220,220,220), width=3) self.plot1GraphSmoothPriceFast = self.plot1.plot(x=self.graphDataTime, y=self.graphDataBitcoinPriceSmoothFast, name=' Price Fast MA') self.plot1GraphSmoothPriceFast.setPen(color=(3,86,243), width=2) self.plot1GraphSmoothPriceSlow = self.plot1.plot(x=self.graphDataTime, y=self.graphDataBitcoinPriceSmoothSlow, name=' Price Slow MA') self.plot1GraphSmoothPriceSlow.setPen(color=(230,79,6), width=2) self.plot1GraphRiskLine = self.plot1.plot(x=self.graphDataTime, y=self.graphDataBitcoinRiskLine, name=' Risk Line') self.plot1GraphRiskLine.setPen(color=(255,46,46), width=2, style=QtCore.Qt.DotLine) self.plot1Markers1 = self.plot1.plot(x=self.graphDataTime, y=self.graphDataBitcoinPriceMarker1, name=' Buy', pen=None, symbol='o', symbolPen=(43, 206, 55), symbolBrush=(43, 206, 55), symbolSize = 30) self.plot1Markers2 = self.plot1.plot(x=self.graphDataTime, y=self.graphDataBitcoinPriceMarker2, name=' Sell', pen=None, symbol='o', symbolPen=(255, 0, 0), symbolBrush=(255, 0, 0), symbolSize = 30) # Graph 2 (Indicators) curves initialization self.plot2GraphIndicatorMACD = self.plot2.plot(x=self.graphDataTime, y=self.graphDataIndicatorMACD, pen='y', name=' MACD') self.graphicObject = pg.GraphicsObject() def initInitialTimeVector(self, firstFutureSampleTimeStamp): np.set_printoptions(suppress=True) timeBetweenRetrievedSamplesInSec = 60 startTimeValue = firstFutureSampleTimeStamp - (self.nbPointsOnPlot * timeBetweenRetrievedSamplesInSec) tempTimeVector = np.linspace(startTimeValue, firstFutureSampleTimeStamp, self.nbPointsOnPlot) self.timeOfLastSampleDisplayed = startTimeValue return tempTimeVector def UIGR_updateNextIterationData(self, newTime, newSpotPrice, newSmoothPriceFast, newSmoothPriceSlow, newRiskLineRawAvgValue, newIndicatorMACD): # Don't append data that were before the oldest time in the graphs and that are older than the last sample displayed if (newTime > self.timeOfLastSampleDisplayed): self.graphDataTime[-1] = newTime self.timeOfLastSampleDisplayed = newTime self.graphDataBitcoinPrice[-1] = newSpotPrice self.graphDataBitcoinPriceSmoothFast[-1] = newSmoothPriceFast self.graphDataBitcoinPriceSmoothSlow[-1] = newSmoothPriceSlow self.graphDataBitcoinPriceMarker1[-1] = 0 self.graphDataBitcoinPriceMarker2[-1] = 0 self.currentRiskLineRawAvgValue = newRiskLineRawAvgValue self.graphDataBitcoinRiskLine.fill(newRiskLineRawAvgValue * theConfig.CONFIG_RiskLinePercentsAboveThresholdToBuy) self.graphDataIndicatorMACD[-1] = newIndicatorMACD # Shift data in the array one sample left (see also: np.roll) self.graphDataTime[:-1] = self.graphDataTime[1:] self.graphDataBitcoinPrice[:-1] = self.graphDataBitcoinPrice[1:] self.graphDataBitcoinPriceSmoothFast[:-1] = self.graphDataBitcoinPriceSmoothFast[1:] self.graphDataBitcoinPriceSmoothSlow[:-1] = self.graphDataBitcoinPriceSmoothSlow[1:] self.graphDataBitcoinPriceMarker1[:-1] = self.graphDataBitcoinPriceMarker1[1:] self.graphDataBitcoinPriceMarker2[:-1] = self.graphDataBitcoinPriceMarker2[1:] self.graphDataBitcoinRiskLine[:-1] = self.graphDataBitcoinRiskLine[1:] self.graphDataIndicatorMACD[:-1] = self.graphDataIndicatorMACD[1:] self.totalNbIterations = self.totalNbIterations + 1 # Experimentation pour live trading aussi def UIGR_updateGraphsSimuTimer(self): if (theConfig.CONFIG_INPUT_MODE_IS_REAL_MARKET == False): if (self.totalNbIterations > theConfig.CONFIG_NB_POINTS_INIT_SIMU_GRAPH): self.UIGR_updateGraphs() self.totalNbGraphUpdates = self.totalNbGraphUpdates + 1 # Perform UI apdates that were requested from the background thread self.UIGR_SAFE_updatePriceLbl() if (self.safeUIRefreshIsRequested == True): self.safeUIRefreshIsRequested = False self.UIGR_SAFE_updateInfoText() self.UIGR_SAFE_updateTotalProfit() self.UIGR_SAFE_updateAccountsBalance() else: self.UIGR_updateGraphs() # Perform UI updates that were requested from the background thread self.UIGR_SAFE_updatePriceLbl() if (self.safeUIRefreshIsRequested == True): self.safeUIRefreshIsRequested = False self.UIGR_SAFE_updateInfoText() self.UIGR_SAFE_updateTotalProfit() self.UIGR_SAFE_updateAccountsBalance() def UIGR_updateGraphs(self): self.plot1GraphLivePrice.setData(x=self.graphDataTime, y=self.graphDataBitcoinPrice) self.plot1GraphSmoothPriceFast.setData(x=self.graphDataTime, y=self.graphDataBitcoinPriceSmoothFast) self.plot1GraphSmoothPriceSlow.setData(x=self.graphDataTime, y=self.graphDataBitcoinPriceSmoothSlow) self.plot1Markers1.setData(x=self.graphDataTime, y=self.graphDataBitcoinPriceMarker1) self.plot1Markers2.setData(x=self.graphDataTime, y=self.graphDataBitcoinPriceMarker2) self.plot1GraphRiskLine.setData(x=self.graphDataTime, y=self.graphDataBitcoinRiskLine) self.plot2GraphIndicatorMACD.setData(x=self.graphDataTime, y=self.graphDataIndicatorMACD, fillLevel=0, brush=(255, 243, 20, 80)) # Update Y avis scale in live market mode if (theConfig.CONFIG_INPUT_MODE_IS_REAL_MARKET == True): if (self.totalNbIterations > 1): # Avoid computing min on a full of zeros array which throws an exception maxInPlot1 = np.amax(self.graphDataBitcoinPrice) * self.Y_AXIS_TOP_MARGIN_IN_EXTREMUM_PERCENT minInPlot1 = (min(i for i in self.graphDataBitcoinPrice if i > 0)) * self.Y_AXIS_BOTTOM_MARGIN_IN_EXTREMUM_PERCENT # Set larget Y scaling if chart amplitude is too weak if ((maxInPlot1 - minInPlot1) < (self.graphDataBitcoinPrice[-1] * 0.02)): maxInPlot1 = maxInPlot1 * 1.006 minInPlot1 = minInPlot1 * 0.994 else: minInPlot1 = self.PLOT1_DEFAULT_MINIMUM maxInPlot1 = self.PLOT1_DEFAULT_MAXIMUM # Y range update only on change to avoid permanent axis rescaling which affects the user experience when zomming if ((self.minInPlot1 != minInPlot1) or (self.maxInPlot1 != maxInPlot1)): self.minInPlot1 = minInPlot1 self.maxInPlot1 = maxInPlot1 self.plot1.setYRange(minInPlot1, maxInPlot1) # Force UI refresh. After a long running time, UI refresh is not automatic sometimes QtGui.QApplication.processEvents() self.plot1.update() else: # Simulation mode if (self.totalNbIterations > 1): # Avoid computing min on a full of zeros array which throws an exception if (self.totalNbGraphUpdates % 4 == 0): maxInPlot1 = np.amax(self.graphDataBitcoinPrice) * self.Y_AXIS_TOP_MARGIN_IN_EXTREMUM_PERCENT minInPlot1 = (min(i for i in self.graphDataBitcoinPrice if i > 0)) * self.Y_AXIS_BOTTOM_MARGIN_IN_EXTREMUM_PERCENT # Set larget Y scaling if chart amplitude is too weak if ((maxInPlot1 - minInPlot1) < (self.graphDataBitcoinPrice[-1] * 0.02)): maxInPlot1 = maxInPlot1 * 1.006 minInPlot1 = minInPlot1 * 0.994 # Y range update only on change to avoid permanent axis rescaling which affects the user experience when zomming if ((self.minInPlot1 != minInPlot1) or (self.maxInPlot1 != maxInPlot1)): self.minInPlot1 = minInPlot1 self.maxInPlot1 = maxInPlot1 self.plot1.setYRange(minInPlot1, maxInPlot1) # Every 3 refreshes is sufficient QtGui.QApplication.processEvents() self.plot1.update() # Shall be at the end self.areNewSamplesRequested = True def UIGR_performManualYRangeRefresh(self): if (self.totalNbIterations > 1): # Avoid computing min on a full of zeros array which throws an exception maxInPlot1 = np.amax(self.graphDataBitcoinPrice) * self.Y_AXIS_TOP_MARGIN_IN_EXTREMUM_PERCENT minInPlot1 = (min(i for i in self.graphDataBitcoinPrice if i > 0)) * self.Y_AXIS_BOTTOM_MARGIN_IN_EXTREMUM_PERCENT # Set larget Y scaling if chart amplitude is too weak if ((maxInPlot1 - minInPlot1) < (self.graphDataBitcoinPrice[-1] * 0.02)): maxInPlot1 = maxInPlot1 * 1.006 minInPlot1 = minInPlot1 * 0.994 else: minInPlot1 = self.PLOT1_DEFAULT_MINIMUM maxInPlot1 = self.PLOT1_DEFAULT_MAXIMUM # Y range update only on change to avoid permanent axis rescaling which affects the user experience when zomming if ((self.minInPlot1 != minInPlot1) or (self.maxInPlot1 != maxInPlot1)): self.minInPlot1 = minInPlot1 self.maxInPlot1 = maxInPlot1 self.plot1.setYRange(minInPlot1, maxInPlot1) def UIGR_updateAccountsBalance(self, EURBalance, CryptoBalance): self.strEURBalance = str(EURBalance) # Avoid display like 1e-7 if (CryptoBalance <= theConfig.CONFIG_CRYPTO_PRICE_QUANTUM): CryptoBalance = 0.0 self.strCryptoBalance = str(CryptoBalance) # If there's no risk from being called from a background thread, update UI here if (self.isContinuousGraphRefreshEnabled == False): if (theConfig.CONFIG_INPUT_MODE_IS_REAL_MARKET == True): strSimulationPrecision = "" else: strSimulationPrecision = " (Simulation)" self.lblFiatBalance.setText(self.STR_LABEL_FIAT_BALANCE + self.strEURBalance + ' ' + str(self.theSettings.SETT_GetSettings()["strFiatType"]) + strSimulationPrecision) self.lblCryptoMoneyBalance.setText(self.STR_LABEL_CRYPTO_BALANCE + self.strCryptoBalance + ' ' + str(self.theSettings.SETT_GetSettings()["strCryptoType"]) + strSimulationPrecision) else: self.safeUIRefreshIsRequested = True def UIGR_SAFE_updateAccountsBalance(self): if (theConfig.CONFIG_INPUT_MODE_IS_REAL_MARKET == True): strSimulationPrecision = "" else: strSimulationPrecision = " (Simulation)" self.lblFiatBalance.setText(self.STR_LABEL_FIAT_BALANCE + self.strEURBalance + ' ' + str(self.theSettings.SETT_GetSettings()["strFiatType"]) + strSimulationPrecision) self.lblCryptoMoneyBalance.setText(self.STR_LABEL_CRYPTO_BALANCE + self.strCryptoBalance + ' ' + str(self.theSettings.SETT_GetSettings()["strCryptoType"]) + strSimulationPrecision) def UIGR_updatePriceLbl(self, newPrice): self.priceLabelStr = str(newPrice) # If there's no risk from being called from a background thread, update UI here if (self.isContinuousGraphRefreshEnabled == False): self.lblLivePrice.setText(self.STR_LABEL_MONEY_MIDDLEMARKET_PRICE + self.priceLabelStr + " " + str(self.theSettings.SETT_GetSettings()["strFiatType"])) else: pass # No need to set a flag, price lbl update is automatic in background def UIGR_SAFE_updatePriceLbl(self): self.lblLivePrice.setText(self.STR_LABEL_MONEY_MIDDLEMARKET_PRICE + self.priceLabelStr + " " + str(self.theSettings.SETT_GetSettings()["strFiatType"])) def UIGR_updateCurrentState(self, newState, isLiveTrading, blink): if (blink == True): self.isLblCurrentStateBlinking = True else: self.isLblCurrentStateBlinking = False self.lblCurrentState.setVisible(True) self.lblCurrentState.setText(str(newState)) if (isLiveTrading == True): self.lblCurrentState.setStyleSheet(self.STR_QLABEL_CURRENT_STATE_LIVE_TRADING_STYLESHEET) else: self.lblCurrentState.setStyleSheet(self.STR_QLABEL_STYLESHEET) def UIGR_toogleStatus(self): self.lblCurrentState.setVisible(False); def UIGR_updateTotalProfit(self, realProfit, theoricProfit, percentageProfit, isSimulation): self.realProfit = realProfit self.theoricProfit = theoricProfit self.percentageProfit = percentageProfit self.displayProfitAsInSimulation = isSimulation # If there's no risk from being called from a background thread, update UI here if (self.isContinuousGraphRefreshEnabled == False): if (self.displayProfitAsInSimulation == True): self.lblTotalGains.setText(self.STR_LABEL_TOTAL_GAINS + str(self.theoricProfit) + " " + str(self.theSettings.SETT_GetSettings()["strFiatType"]) + " (" + str(self.percentageProfit) + "%) (Simulation)") else: self.lblTotalGains.setText(self.STR_LABEL_TOTAL_GAINS + str(self.realProfit) + " " + str(self.theSettings.SETT_GetSettings()["strFiatType"]) + " (Theoric: " + str(self.theoricProfit) + " " + str(self.theSettings.SETT_GetSettings()["strFiatType"]) + ")" + " (" + str(self.percentageProfit) + "%)") if (self.theoricProfit > 0): self.lblTotalGains.setStyleSheet(self.STR_QLABEL_PROFIT_GREEN_STYLESHEET) elif (self.theoricProfit < 0): self.lblTotalGains.setStyleSheet(self.STR_QLABEL_PROFIT_RED_STYLESHEET) else: self.lblTotalGains.setStyleSheet(self.STR_QLABEL_STYLESHEET) else: self.safeUIRefreshIsRequested = True def UIGR_SAFE_updateTotalProfit(self): if (self.displayProfitAsInSimulation == True): self.lblTotalGains.setText(self.STR_LABEL_TOTAL_GAINS + str(self.theoricProfit) + " " + str(self.theSettings.SETT_GetSettings()["strFiatType"]) + " (" + str(self.percentageProfit) + "%) (Simulation)") else: self.lblTotalGains.setText(self.STR_LABEL_TOTAL_GAINS + str(self.realProfit) + " " + str(self.theSettings.SETT_GetSettings()["strFiatType"]) + " (Theoric: " + str(self.theoricProfit) + " " + str(self.theSettings.SETT_GetSettings()["strFiatType"]) + ")" + " " + str(self.theSettings.SETT_GetSettings()["strFiatType"]) + ")" + " (" + str(self.percentageProfit) + "%)") if (self.theoricProfit > 0): self.lblTotalGains.setStyleSheet(self.STR_QLABEL_PROFIT_GREEN_STYLESHEET) elif (self.theoricProfit < 0): self.lblTotalGains.setStyleSheet(self.STR_QLABEL_PROFIT_RED_STYLESHEET) else: self.lblTotalGains.setStyleSheet(self.STR_QLABEL_STYLESHEET) def UIGR_updateInfoText(self, newInfoText, isError): self.lblInfoInErrorStyle = isError self.lblInfoStr = newInfoText # If there's no risk from being called from a background thread, update UI here if (self.isContinuousGraphRefreshEnabled == False): # Actual update self.lblInfo.setText(self.lblInfoStr) if (self.lblInfoInErrorStyle == True): self.lblInfo.setStyleSheet(self.STR_QLABEL_INFO_ERROR_STYLESHEET) else: self.lblInfo.setStyleSheet(self.STR_QLABEL_INFO_STYLESHEET); # Automatic style change if needed if ((("Welcome" in self.lblInfoStr) == True)): self.lblInfo.setStyleSheet(self.STR_QLABEL_INFO_GREEN_STYLESHEET) self.lblInfo.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) elif (("here to unlock" in self.lblInfoStr) == True): self.lblInfo.setStyleSheet(self.STR_QLABEL_INFO_ORANGE_STYLESHEET) self.lblInfo.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) else: # Set default cursor back self.lblInfo.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) else: self.safeUIRefreshIsRequested = True def UIGR_SAFE_updateInfoText(self): self.lblInfo.setText(self.lblInfoStr) if (self.lblInfoInErrorStyle == True): self.lblInfo.setStyleSheet(self.STR_QLABEL_INFO_ERROR_STYLESHEET); else: self.lblInfo.setStyleSheet(self.STR_QLABEL_INFO_STYLESHEET); pass def UIGR_updateLoadingDataProgress(self, progressInPercent): if (self.buttonStart.text() != "Start"): self.buttonStart.setText("Loading\nData...\n%s%%" % progressInPercent) def TimerRaisedBlinkWidgets(self): if (self.isLblCurrentStateBlinking == True): if (self.lblCurrentState.isVisible() == False): self.lblCurrentState.setVisible(True) else: self.lblCurrentState.setVisible(False) def UIGR_resetConnexionText(self): self.lblConnexion.setText("") def UIGR_updateConnexionText(self, newText): if (self.lblConnexion.text() == ""): self.lblConnexion.setText(newText) def UIGR_updateLiveData(self, newData): self.strLiveData = (str(newData)[:100]) def UIGR_updateLiveDataTimer(self): self.lblLiveData.setText(self.strLiveData) # Will be added to data on the next call to UIGR_updateNextIterationData() # Will be displayed on the next call to UIGR_updateGraphs # 1 => Buy marker # 2 => Sell marker # This function needs to be called after UIGR_updateNextIterationData to avoid the markers to be overwritten def UIGR_addMarker(self, markerNumber): print("UIGR - Marker added at %s" % self.graphDataBitcoinPrice[-1]) if (markerNumber == 1): # Added on the last-but-one sample in order to avoid last sample to be overwritten by UIGR_updateNextIterationData self.graphDataBitcoinPriceMarker1[-2] = self.graphDataBitcoinPrice[-1] pass elif (markerNumber == 2): # Added on the last-but-one sample in order to avoid last sample to be overwritten by UIGR_updateNextIterationData self.graphDataBitcoinPriceMarker2[-2] = self.graphDataBitcoinPrice[-1] pass def UIGR_getRadioButtonSimulation(self): return self.radioButtonSimulation; def UIGR_getRadioButtonTrading(self): return self.radioButtonTrading; def UIGR_SetRadioButtonsEnabled(self, bEnable): self.radioButtonSimulation.setEnabled(bEnable) self.radioButtonTrading.setEnabled(bEnable) if (bEnable == True): self.radioButtonSimulation.setStyleSheet(self.STR_QRADIOBUTTON_STYLESHEET) self.radioButtonTrading.setStyleSheet(self.STR_QRADIOBUTTON_STYLESHEET) else: self.radioButtonSimulation.setStyleSheet(self.STR_QRADIOBUTTON_DISABLED_STYLESHEET) self.radioButtonTrading.setStyleSheet(self.STR_QRADIOBUTTON_DISABLED_STYLESHEET) def UIGR_SetStartButtonEnabled(self, bEnable): self.buttonStart.setEnabled(bEnable) def UIGR_SetStartButtonAspect(self, aspect): if (aspect == "START"): self.buttonStart.setText("Start") self.buttonStart.setStyleSheet(self.STR_QBUTTON_START_STYLESHEET) if (aspect == "START_DISABLED"): self.buttonStart.setText("Start") self.buttonStart.setStyleSheet(self.STR_QBUTTON_START_DISABLED_STYLESHEET) elif (aspect == "STOP"): self.buttonStart.setText("Stop") self.buttonStart.setStyleSheet(self.STR_QBUTTON_STOP_STYLESHEET) elif (aspect == "LOADING"): self.buttonStart.setText("Loading\ndata...") self.buttonStart.setStyleSheet(self.STR_QBUTTON_LOADING_STYLESHEET) def UIGR_SetPauseButtonEnabled(self, bEnable): self.buttonPause.setEnabled(bEnable) self.buttonPause.setVisible(bEnable) def UIGR_SetPauseButtonAspect(self, aspect): if (aspect == "PAUSE"): self.buttonPause.setText("Pause") self.buttonPause.setStyleSheet(self.STR_QBUTTON_PAUSE_STYLESHEET) if (aspect == "PAUSE_DISABLED"): self.buttonPause.setText("Pause") self.buttonPause.setStyleSheet(self.STR_QBUTTON_PAUSE_DISABLED_STYLESHEET) elif (aspect == "RESUME"): self.buttonPause.setText("Resume") self.buttonPause.setStyleSheet(self.STR_QBUTTON_PAUSE_STYLESHEET) def UIGR_SetSettingsButtonsEnabled(self, bEnable): self.buttonSettings.setEnabled(bEnable) if (bEnable == True): self.buttonSettings.setStyleSheet(self.STR_QBUTTON_SETTINGS_STYLESHEET) else: self.buttonSettings.setStyleSheet(self.STR_QBUTTON_SETTINGS_DISABLED_STYLESHEET) def UIGR_SetDonationButtonsEnabled(self, bEnable): self.buttonDonation.setEnabled(bEnable) if (bEnable == True): self.buttonDonation.setStyleSheet(self.STR_QBUTTON_SETTINGS_STYLESHEET) else: self.buttonDonation.setStyleSheet(self.STR_QBUTTON_SETTINGS_DISABLED_STYLESHEET) def UIGR_SetCurrentAppState(self, appState): self.currentAppState = appState # Perform UI actions due to a trading pair change def UIGR_NotifyThatTradingPairHasChanged(self): self.STR_LABEL_MONEY_MIDDLEMARKET_PRICE = self.theSettings.SETT_GetSettings()["strCryptoType"] + str(" MiddleMarket price : ") self.lblLivePrice.setText(self.STR_LABEL_MONEY_MIDDLEMARKET_PRICE) # Non destructive "search and replace" because of the possible '(Simulation)' annotation self.lblFiatBalance.setText(self.lblFiatBalance.text().replace("USD", "---")) self.lblFiatBalance.setText(self.lblFiatBalance.text().replace("EUR", "---")) self.lblFiatBalance.setText(self.lblFiatBalance.text().replace("---", self.theSettings.SETT_GetSettings()["strFiatType"])) self.lblCryptoMoneyBalance.setText(self.lblCryptoMoneyBalance.text().replace("USD", "---")) self.lblCryptoMoneyBalance.setText(self.lblCryptoMoneyBalance.text().replace("EUR", "---")) self.lblCryptoMoneyBalance.setText(self.lblCryptoMoneyBalance.text().replace("---", self.theSettings.SETT_GetSettings()["strFiatType"])) self.lblTotalGains.setText(self.lblTotalGains.text().replace("USD", "---")) self.lblTotalGains.setText(self.lblTotalGains.text().replace("EUR", "---")) self.lblTotalGains.setText(self.lblTotalGains.text().replace("---", self.theSettings.SETT_GetSettings()["strFiatType"])) self.STR_LABEL_FIAT_BALANCE = str(self.theSettings.SETT_GetSettings()["strFiatType"]) + " Account Balance : " self.STR_LABEL_CRYPTO_BALANCE = str(self.theSettings.SETT_GetSettings()["strCryptoType"]) + " Account Balance : " self.strPlot1Title = str(self.theSettings.SETT_GetSettings()["strTradingPair"]) + ' Coinbase Pro Market Price (' + str(self.theSettings.SETT_GetSettings()["strFiatType"]) + ')' self.plot1.setTitle(self.strPlot1Title) self.UIGR_ResetAllGraphData(True, -1, 600) def UIGR_SetTransactionManager(self, transactionManager): self.theUIDonation.UIDO_SetTransactionManager(transactionManager) def UIGR_RequestDonationWindowDisplay(self): self.theUIDonation.UIDO_ShowWindow() def UIGR_closeBackgroundOperations(self): self.timerUpdateLiveData.stop()