def computeCryptoFiatRate(self, crypto, fiat, dateStr=None): ''' :raise UnsupportedCryptoFiatPairError in case the crypto fiat exchange CSV file does not have the necessary information to compute the crypto/fiat pair rate. :raise AfterNowPriceRequestDateError in case the passed dateStr is after now. @param crypto: @param fiat: @param dateStr: if not None, means that an historical rate must be obtained. Otherwise, a current rate is returned. :return crypto/fiat pair current rate ''' if dateStr is not None: nowDateArrow = DateTimeUtil.localNow(LOCAL_TIME_ZONE) requestDateArrow = DateTimeUtil.dateTimeStringToArrowLocalDate(dateStr, LOCAL_TIME_ZONE, DATE_FORMAT) if DateTimeUtil.isAfter(requestDateArrow, nowDateArrow): raise AfterNowPriceRequestDateError(dateStr) intermediateExchangeRateRequestLst = self._getIntermediateExchangeRateRequests(crypto, fiat) rateRequestNumber = len(intermediateExchangeRateRequestLst) if rateRequestNumber == 1: exchange = intermediateExchangeRateRequestLst[0][2] if exchange == '1': # the case if the crypto/fiat pair is a stablecoin/ coin fiat pair, # like USDC/USD ! return 1 else: resultData = self._getCurrentOrHistoRate(crypto, dateStr, exchange, fiat) if not self._checkIfProblem(resultData): return resultData.getValue(resultData.RESULT_KEY_PRICE) elif rateRequestNumber == 2: crypto = intermediateExchangeRateRequestLst[0][0] unit = intermediateExchangeRateRequestLst[0][1] exchange = intermediateExchangeRateRequestLst[0][2] if exchange == '1': resultData = ResultData() resultData.setValue(resultData.RESULT_KEY_PRICE, 1) else: resultData = self._getCurrentOrHistoRate(crypto, dateStr, exchange, unit) if not self._checkIfProblem(resultData): firstRate = resultData.getValue(resultData.RESULT_KEY_PRICE) crypto = intermediateExchangeRateRequestLst[1][0] fiat = intermediateExchangeRateRequestLst[1][1] exchange = intermediateExchangeRateRequestLst[1][2] resultData = self._getCurrentOrHistoRate(crypto, dateStr, exchange, fiat) if not self._checkIfProblem(resultData): secondRate = resultData.getValue(resultData.RESULT_KEY_PRICE) return firstRate * secondRate raise UnsupportedCryptoFiatPairError(crypto, fiat, self.cryptoFiatCsvFilePathName)
def testIsAfterDateBefore(self): zhArrowDateTimeObjRef = DateTimeUtil.dateTimeStringToArrowLocalDate( "2017/09/30 02:00:00", 'Europe/Zurich', "YYYY/MM/DD HH:mm:ss") zhArrowDateTimeObjOneSecBefore = DateTimeUtil.dateTimeStringToArrowLocalDate( "2017/09/30 01:59:59", 'Europe/Zurich', "YYYY/MM/DD HH:mm:ss") self.assertFalse( DateTimeUtil.isAfter(zhArrowDateTimeObjOneSecBefore, zhArrowDateTimeObjRef))
def testIsAfterOneSecond(self): zhArrowDateTimeObjRef = DateTimeUtil.dateTimeStringToArrowLocalDate( "2017/09/30 02:00:00", 'Europe/Zurich', "YYYY/MM/DD HH:mm:ss") zhArrowDateTimeObjOneSecAfter = DateTimeUtil.dateTimeStringToArrowLocalDate( "2017/09/30 02:00:01", 'Europe/Zurich', "YYYY/MM/DD HH:mm:ss") self.assertTrue( DateTimeUtil.isAfter(zhArrowDateTimeObjOneSecAfter, zhArrowDateTimeObjRef))
def execute(self): ''' :seqdiag_return ResultData or False :return: ''' resultPriceOrBoolean = self._validateMandatoryData() if resultPriceOrBoolean != True: return resultPriceOrBoolean localTimezone = self.configManager.localTimeZone localNow = DateTimeUtil.localNow(localTimezone) resultPriceOrBoolean = self._validateDateTimeData(localNow) if resultPriceOrBoolean != True: return resultPriceOrBoolean cryptoUpper = self.parsedParmData[self.CRYPTO].upper() fiatUpper = self.parsedParmData[self.FIAT].upper() exchange = self.parsedParmData[self.EXCHANGE] dayStr = self.parsedParmData[self.DAY] if dayStr != None: day = int(dayStr) else: day = 0 monthStr = self.parsedParmData[self.MONTH] if monthStr != None: month = int(monthStr) else: month = localNow.month yearStr = self.parsedParmData[self.YEAR] if yearStr != None: if len(yearStr) == 2: year = 2000 + int(yearStr) elif len(yearStr) == 4: year = int(yearStr) elif yearStr == '0': # user entered -d0 ! year = 0 else: year = localNow.year hourStr = self.parsedParmData[self.HOUR] if hourStr != None: hour = int(hourStr) else: hour = 0 minuteStr = self.parsedParmData[self.MINUTE] if minuteStr != None: minute = int(minuteStr) else: minute = 0 #storing the parsed parm gata dicèionary before it #may be modified in case the user requested a RT #price. The initial dictionary wiLl be added to the #returned resultData so the client can have access #to the full command request, even if only a partial #request like -d or -c was entered. This is necessary #bcecause if the client is a GUI, it stores the list #of requests in order to be able to replay them ! initialParsedParmDataDic = self.parsedParmData.copy() wasDateInFutureSetToLastYear = False localRequestDateTime = None if day + month + year == 0: # asking for RT price here. Current date is stored in parsed parm data for possible # use in next request self._storeDateTimeDataForNextPartialRequest(localNow) else: try: localRequestDateTime = DateTimeUtil.dateTimeComponentsToArrowLocalDate( day, month, year, hour, minute, 0, localTimezone) except ValueError as e: # is the when the user specify only the day if he enters 31 and the current month # has no 31st or if he enters 30 or 29 and we are on February result = ResultData() result.setValue( ResultData.RESULT_KEY_ERROR_MSG, "ERROR - {}: day {}, month {}".format(str(e), day, month)) return result if DateTimeUtil.isAfter(localRequestDateTime, localNow): # request date is in the future ---> invalid. This happens for example in case # btc usd 31/12 bittrex entered sometime before 31/12. Then the request year is # forced to last year and a warning will be displayed. year = localNow.year - 1 wasDateInFutureSetToLastYear = True priceValueSymbol = self.parsedParmData[self.PRICE_VALUE_SYMBOL] priceValueAmount = self.parsedParmData[self.PRICE_VALUE_AMOUNT] if priceValueSymbol: priceValueSymbol = priceValueSymbol.upper() if priceValueAmount: priceValueAmount = float(priceValueAmount) priceValueSaveFlag = self.parsedParmData[self.PRICE_VALUE_SAVE] result = self.receiver.getCryptoPrice(cryptoUpper, fiatUpper, exchange, day, month, year, hour, minute, priceValueSymbol, priceValueAmount, priceValueSaveFlag, self.requestInputString) #the command components denoting the user request will be used to recreate #a full command request which will be stored in the command history list. #The historry list can be replayed, stored on disk, edited ... result.setValue(ResultData.RESULT_KEY_INITIAL_COMMAND_PARMS, initialParsedParmDataDic) result.setValue(ResultData.RESULT_KEY_PRICE_VALUE_SAVE, priceValueSaveFlag) if wasDateInFutureSetToLastYear: result.setWarning( ResultData.WARNING_TYPE_FUTURE_DATE, "Warning - request date {} can not be in the future and was shifted back to last year" .format( localRequestDateTime.format( self.configManager.dateTimeFormat))) unsupportedCommand = self.parsedParmData[self.UNSUPPORTED_COMMAND] if unsupportedCommand: result.setWarning( ResultData.WARNING_TYPE_UNSUPPORTED_COMMAND, "Warning - unsupported command {}{} in request {}".format( unsupportedCommand, self.parsedParmData[self.UNSUPPORTED_COMMAND_DATA], self.requestInputString)) return result
def _handleDateTimeRequestParms(self): ''' Complete missing request date elements with current date values and validate request date and time elements format. Then converts or computes date and time elements to int. In case the resulting date is in the future, its year is reduced by one and the returned localRequestDateTime is not None ! :return: day, month, year, hour, minute localRequestDateTime None, except if the (effective or completed) request date is in the future resultPriceOrBoolean ''' day = None month = None year = None hour = None minute = None localRequestDateTime = None localTimezone = self.configManager.localTimeZone localNow = DateTimeUtil.localNow(localTimezone) resultPriceOrBoolean = self._completeAndValidateDateTimeData(localNow) # storing the parsed parm data dictionary before it # may be modified in case the user requested a RT # price. The initial dictionary wiLl be added to the # returned resultData so the client can have access # to the full command request, even if only a partial # request like -d or -c was entered. This is necessary # because if the client is a GUI, it stores the list # of requests in order to be able to replay them ! initialParsedParmDataDic = self.parsedParmData.copy() if resultPriceOrBoolean == True: dayStr = self.parsedParmData[self.DAY] day = int(dayStr) monthStr = self.parsedParmData[self.MONTH] if monthStr != None: month = int(monthStr) else: month = localNow.month yearStr = self.parsedParmData[self.YEAR] if yearStr != None: if len(yearStr) == 2: year = 2000 + int(yearStr) elif len(yearStr) == 4: year = int(yearStr) elif yearStr == '0': # user entered -d0 ! year = 0 else: year = localNow.year hourStr = self.parsedParmData[self.HOUR] if hourStr != None: hour = int(hourStr) else: hour = 0 minuteStr = self.parsedParmData[self.MINUTE] if minuteStr != None: minute = int(minuteStr) else: minute = 0 localRequestDateTime = None if day + month + year == 0: # asking for RT price here. Current date is stored in parsed parm data for possible # use in next request self._storeDateTimeDataForNextPartialRequest(localNow) else: try: localRequestDateTime = DateTimeUtil.dateTimeComponentsToArrowLocalDate( day, month, year, hour, minute, 0, localTimezone) except ValueError as e: # is the case when the user specify only the day if he enters 31 and the current month # has no 31st or if he enters 30 or 29 and we are on February resultPriceOrBoolean = ResultData() resultPriceOrBoolean.setError( "ERROR - {}: day {}, month {}.".format( str(e), day, month)) if resultPriceOrBoolean == True: if DateTimeUtil.isAfter(localRequestDateTime, localNow): # request date is in the future ---> invalid. This happens for example in case # btc usd 31/12 bittrex entered sometime before 31/12. Then the request year is # forced to last year and a warning will be displayed. year = localNow.year - 1 else: localRequestDateTime = None return day, month, year, hour, minute, localRequestDateTime, resultPriceOrBoolean, initialParsedParmDataDic