示例#1
0
    def initialize_lower_interval_trading(self, caller, interval: str):
        """
        Initializes lower interval trading data object.
        :param caller: Caller that determines whether lower interval is for simulation or live bot.
        :param interval: Current interval for simulation or live bot.
        """
        sortedIntervals = ('1m', '3m', '5m', '15m', '30m', '1h', '2h', '12h', '4h', '6h', '8h', '1d', '3d')
        gui = self.gui
        symbol = self.trader.symbol

        if interval != '1m':
            lowerInterval = sortedIntervals[sortedIntervals.index(interval) - 1]
            intervalString = helpers.convert_small_interval(lowerInterval)
            self.lowerIntervalNotification = True
            self.signals.activity.emit(caller, f'Retrieving {symbol} data for {intervalString.lower()} intervals...')

            if caller == LIVE:
                gui.lowerIntervalData = Data(interval=lowerInterval, symbol=symbol, updateData=False)
                gui.lowerIntervalData.custom_get_new_data(progress_callback=self.signals.progress, removeFirst=True,
                                                          caller=LIVE)
            elif caller == SIMULATION:
                gui.simulationLowerIntervalData = Data(interval=lowerInterval, symbol=symbol, updateData=False)
                gui.simulationLowerIntervalData.custom_get_new_data(progress_callback=self.signals.progress,
                                                                    removeFirst=True, caller=SIMULATION)
            else:
                raise TypeError("Invalid type of caller specified.")

            lowerData = gui.lowerIntervalData if caller == LIVE else gui.simulationLowerIntervalData
            if not lowerData or not lowerData.downloadCompleted:
                raise RuntimeError("Lower interval download failed.")
            self.signals.activity.emit(caller, "Retrieved lower interval data successfully.")
        else:
            self.signals.activity.emit(caller, "There is no lower interval than 1 minute intervals.")
示例#2
0
def test_create_csv_file(data_object: Data, descending, army_time, start_date,
                         expected_file_to_match):
    """
    Test create CSV file functionality.
    :param data_object: Data object to leverage to test this function.
    :param descending: Boolean that decides whether values in CSV are in descending format or not.
    :param army_time: Boolean that dictates whether dates will be written in army-time format or not.
    :param start_date: Date to have CSV data from.
    """
    remove_test_data()
    data_object.create_table()

    insert_test_data_to_database()
    data_object.data = data_object.get_data_from_database()

    start_date = parser.parse(start_date).date()

    path = data_object.create_csv_file(descending=descending,
                                       army_time=army_time,
                                       start_date=start_date)
    path_to_equal = os.path.join(ROOT_DIR, 'tests', 'data',
                                 'test_create_csv_file_data',
                                 expected_file_to_match)

    assert filecmp.cmp(path, path_to_equal)
示例#3
0
def test_dump_to_table(data_object: Data):
    """
    Testing dumping to table functionality.
    :param data_object: Data object to leverage to test this function.
    """
    remove_test_data()
    data_object.create_table()

    normalized_csv_data = get_normalized_csv_data()
    result = data_object.dump_to_table(normalized_csv_data)
    assert result is True, "Expected all data to dump successfully."

    def get_rows():
        with closing(sqlite3.connect(DATABASE_FILE_PATH)) as connection:
            with closing(connection.cursor()) as cursor:
                db_rows = cursor.execute(
                    f"SELECT * FROM {DATABASE_TABLE} ORDER BY date_utc"
                ).fetchall()
                return [
                    get_normalized_data(row, parse_date=True)
                    for row in db_rows
                ]

    rows = get_rows()
    assert normalized_csv_data == rows, "Values entered are not equal."

    # Dumping several, duplicate values multiple times. Nothing should be dumped due to duplicates.
    data_object.dump_to_table(normalized_csv_data)
    data_object.dump_to_table(normalized_csv_data)

    assert len(rows) == len(
        get_rows()), "Expected count of data to be the same."
示例#4
0
def test_create_table(data_object: Data):
    """
    Test to ensure create table works as intended.
    """
    remove_test_data()
    assert os.path.exists(
        DATABASE_FILE_PATH
    ) is False, f"Expected {DATABASE_FILE_PATH} to not exist for testing."

    data_object.create_table()
    with closing(sqlite3.connect(data_object.database_file)) as connection:
        with closing(connection.cursor()) as cursor:
            table_info = cursor.execute(
                f'PRAGMA TABLE_INFO({data_object.database_table})').fetchall()

    # Each tuple in table_info contains one column's information like this: (0, 'date_utc', 'TEXT', 0, None, 1)
    expected_columns = {
        'date_utc', 'open_price', 'high_price', 'low_price', 'close_price',
        'volume', 'quote_asset_volume', 'number_of_trades',
        'taker_buy_base_asset', 'taker_buy_quote_asset'
    }

    table_columns = {col[1] for col in table_info}
    assert table_columns == expected_columns, f"Expected: {expected_columns}. Got: {table_columns}"
    assert all(col[2] == 'TEXT' for col in
               table_info), "Expected all columns to have the TEXT data type."
示例#5
0
 def run(self):
     """
     Initialise the runner function with passed args, kwargs.
     """
     self.signals.started.emit()
     try:
         self.client = Data(interval=self.interval,
                            symbol=self.symbol,
                            updateData=False)
         data = self.client.custom_get_new_data(
             progress_callback=self.signals.progress,
             locked=self.signals.locked)
         if data:
             if self.descending is None and self.armyTime is None:
                 self.signals.finished.emit(data)
             else:  # This means the CSV generator called this thread.
                 self.signals.progress.emit(100, "Creating CSV file...", -1)
                 savedPath = self.client.create_csv_file(
                     descending=self.descending,
                     armyTime=self.armyTime,
                     startDate=self.startDate)
                 self.signals.csv_finished.emit(savedPath)
     except Exception as e:
         print(f'Error: {e}')
         traceback.print_exc()
         self.signals.error.emit(str(e))
     finally:
         self.signals.restore.emit()
示例#6
0
 def run(self):
     """
     Initialise the runner function with passed args, kwargs.
     """
     self.signals.started.emit()
     try:
         self.client = Data(interval=self.interval,
                            symbol=self.symbol,
                            update=False)
         data = self.client.custom_get_new_data(
             progress_callback=self.signals.progress,
             locked=self.signals.locked,
             caller=self.caller)
         if data:
             if self.descending is None and self.armyTime is None:
                 self.signals.finished.emit(data, self.caller)
             else:  # This means the CSV generator called this thread.
                 self.signals.progress.emit(100, "Creating CSV file...", '')
                 savedPath = self.client.create_csv_file(
                     descending=self.descending,
                     army_time=self.armyTime,
                     start_date=self.startDate)
                 self.signals.csv_finished.emit(savedPath)
     except Exception as e:
         algobot.MAIN_LOGGER.exception(repr(e))
         self.signals.error.emit(str(e), self.caller)
     finally:
         self.signals.restore.emit(self.caller)
示例#7
0
class DownloadThread(QRunnable):
    """
    Thread to use for downloads.
    """
    def __init__(self,
                 interval,
                 symbol,
                 descending=None,
                 armyTime=None,
                 startDate=None,
                 caller=None,
                 logger=None):
        super(DownloadThread, self).__init__()
        self.caller = caller
        self.signals = DownloadSignals()
        self.symbol = symbol
        self.interval = interval
        self.descending = descending
        self.armyTime = armyTime
        self.startDate = startDate
        self.logger = logger
        self.client: Data or None = None

    @pyqtSlot()
    def run(self):
        """
        Initialise the runner function with passed args, kwargs.
        """
        self.signals.started.emit()
        try:
            self.client = Data(interval=self.interval,
                               symbol=self.symbol,
                               update=False)
            data = self.client.custom_get_new_data(
                progress_callback=self.signals.progress,
                locked=self.signals.locked,
                caller=self.caller)
            if data:
                if self.descending is None and self.armyTime is None:
                    self.signals.finished.emit(data, self.caller)
                else:  # This means the CSV generator called this thread.
                    self.signals.progress.emit(100, "Creating CSV file...", '')
                    savedPath = self.client.create_csv_file(
                        descending=self.descending,
                        army_time=self.armyTime,
                        start_date=self.startDate)
                    self.signals.csv_finished.emit(savedPath)
        except Exception as e:
            algobot.MAIN_LOGGER.exception(repr(e))
            self.signals.error.emit(str(e), self.caller)
        finally:
            self.signals.restore.emit(self.caller)

    def stop(self):
        """
        Stop the download loop if it's running.
        """
        if self.client is not None:
            self.client.download_loop = False
示例#8
0
def test_validate_interval(data_object: Data, interval: str,
                           expectation: Callable):
    """
    Test validate interval function.
    :param data_object: Data object to leverage to check interval validation.
    :param interval: Interval to check if function handles correctly.
    :param expectation: Expectation of function.
    """
    with expectation:
        data_object.validate_interval(interval)
示例#9
0
def test_validate_symbol(data_object: Data, symbol: str,
                         expectation: Callable):
    """
    Test validate symbol function.
    :param data_object: Data object to leverage to check symbol validation.
    :param symbol: Symbol to check if function handles correctly.
    :param expectation: Expectation of function.
    """
    with expectation:
        data_object.validate_symbol(symbol)
示例#10
0
class DownloadThread(QRunnable):
    def __init__(self,
                 interval,
                 symbol,
                 descending=None,
                 armyTime=None,
                 startDate=None):
        super(DownloadThread, self).__init__()
        self.signals = DownloadSignals()
        self.symbol = symbol
        self.interval = interval
        self.descending = descending
        self.armyTime = armyTime
        self.startDate = startDate
        self.client: Data or None = None

    @pyqtSlot()
    def run(self):
        """
        Initialise the runner function with passed args, kwargs.
        """
        self.signals.started.emit()
        try:
            self.client = Data(interval=self.interval,
                               symbol=self.symbol,
                               updateData=False)
            data = self.client.custom_get_new_data(
                progress_callback=self.signals.progress,
                locked=self.signals.locked)
            if data:
                if self.descending is None and self.armyTime is None:
                    self.signals.finished.emit(data)
                else:  # This means the CSV generator called this thread.
                    self.signals.progress.emit(100, "Creating CSV file...", -1)
                    savedPath = self.client.create_csv_file(
                        descending=self.descending,
                        armyTime=self.armyTime,
                        startDate=self.startDate)
                    self.signals.csv_finished.emit(savedPath)
        except Exception as e:
            print(f'Error: {e}')
            traceback.print_exc()
            self.signals.error.emit(str(e))
        finally:
            self.signals.restore.emit()

    def stop(self):
        """
        Stop the download loop if it's running.
        """
        if self.client is not None:
            self.client.downloadLoop = False
示例#11
0
def test_get_data_from_database(data_object: Data):
    """
    Test to ensure get data from database works appropriately.
    :param data_object: Data object to leverage to test this function.
    """
    normalized_csv_data = get_normalized_csv_data()

    remove_test_data()
    data_object.create_table()
    data_object.dump_to_table(normalized_csv_data)
    result = data_object.get_data_from_database()

    # Reverse because data is in ascending order whereas CSV data is not.
    assert normalized_csv_data == result, "Expected data to equal."
示例#12
0
def test_get_database_file(data_object: Data):
    """
    Test to ensure get database file works as intended.
    :param data_object: Data object to leverage to test get database file.
    """
    result = data_object.get_database_file()
    assert result == DATABASE_FILE_PATH, f"Expected: {DATABASE_FILE_PATH}. Got: {result}"
示例#13
0
def get_data_object() -> Data:
    """
    Fixture to get a data object with a mocked Binance client.
    :return: Data object.
    """
    with mock.patch('binance.client.Client', BinanceMockClient):
        return Data(interval=INTERVAL, symbol=TICKER, load_data=False)
示例#14
0
def test_is_valid_symbol(data_object: Data, symbol: str, expected: bool):
    """
    Test to ensure is_valid_symbol works as intended.
    :param data_object: Data object to leverage to check symbol validation.
    :param symbol: Symbol value.
    :param expected: Expected value.
    """
    result = data_object.is_valid_symbol(symbol)
    assert result is expected, f"Expected: {expected} | Got: {result}"
示例#15
0
    def __init__(self,
                 startingBalance: float = 1000,
                 interval: str = '1h',
                 symbol: str = 'BTCUSDT',
                 loadData: bool = True,
                 updateData: bool = True,
                 logFile: str = 'simulation',
                 precision: int = 2,
                 addTradeCallback=None):
        """
        SimulationTrader object that will mimic real live market trades.
        :param startingBalance: Balance to start simulation trader with.
        :param interval: Interval to start trading on.
        :param symbol: Symbol to start trading with.
        :param loadData: Boolean whether we load data from data object or not.
        :param updateData: Boolean for whether data will be updated if it is loaded.
        :param logFile: Filename that logger will log to.
        :param precision: Precision to round data to.
        :param addTradeCallback: Callback signal to emit to (if provided) to reflect a new transaction.
        """
        super().__init__(precision=precision,
                         symbol=symbol,
                         startingBalance=startingBalance)
        self.logger = get_logger(logFile=logFile,
                                 loggerName=logFile)  # Get logger.
        self.dataView: Data = Data(interval=interval,
                                   symbol=symbol,
                                   loadData=loadData,
                                   updateData=updateData,
                                   logObject=self.logger,
                                   precision=precision)
        self.binanceClient = self.dataView.binanceClient  # Retrieve Binance client.
        self.symbol = self.dataView.symbol  # Retrieve symbol from data-view object.

        self.previousNet = self.balance  # Our previous net will just be the starting balance in the beginning.
        self.coinName = self.get_coin_name()  # Retrieve primary coin to trade.
        self.commissionPaid = 0  # Total commission paid to broker.
        self.dailyChangeNets = [
        ]  # Daily change net list. Will contain list of all nets.

        self.completedLoop = True  # Loop that'll keep track of bot. We wait for this to turn False before some action.
        self.lock = Lock(
        )  # Lock to ensure a transaction doesn't occur when another one is taking place.
        self.addTradeCallback = addTradeCallback

        self.customStopLoss = None  # Custom stop loss to use if we want to exit trade before trailing or stop loss.
        self.stopLoss = None  # Price at which bot will exit trade due to stop loss limits.
        self.smartStopLossEnter = False  # Boolean that'll determine whether current position is from a smart stop loss.
        self.scheduledSafetyTimer = None  # Next time to check if it's a true stop loss.

        self.inHumanControl = False  # Boolean that keeps track of whether human or bot controls transactions.
        self.trend = None

        self.optionDetails = [
        ]  # Current option values. Holds most recent option values.
        self.lowerOptionDetails = [
        ]  # Lower option values. Holds lower interval option values (if exist).
示例#16
0
def test_verify_integrity(data_object: Data, data,
                          expected: List[Dict[str, float]]):
    """
    Test verify integrity functionality.
    :param data_object: Data object to leverage to test this function.
    :param data: Data to use to check integrity.
    :param expected: Errored data.
    """
    result = data_object.verify_integrity(data)
    assert result == expected, f"Expected: {expected}. Got: {result}."
示例#17
0
    def get_start_date_for_csv(self):
        symbol = self.csvGenerationTicker.currentText()
        interval = helpers.convert_long_interval(self.csvGenerationDataInterval.currentText())

        ts = Data(loadData=False, log=False).binanceClient._get_earliest_valid_timestamp(symbol, interval)
        startDate = datetime.fromtimestamp(int(ts) / 1000, tz=timezone.utc)
        qStart = QDate(startDate.year, startDate.month, startDate.day)

        endDate = datetime.now(tz=timezone.utc)
        qEnd = QDate(endDate.year, endDate.month, endDate.day)

        return [qStart, qEnd]
示例#18
0
    def get_start_date_for_csv(self) -> List[QDate]:
        """
        Find start date by instantiating a Data object and fetching the Binance API.
        """
        symbol = self.csvGenerationTicker.text()
        interval = helpers.convert_long_interval(self.csvGenerationDataInterval.currentText())

        ts = Data(loadData=False, log=False).binanceClient._get_earliest_valid_timestamp(symbol, interval)
        startDate = datetime.fromtimestamp(int(ts) / 1000, tz=timezone.utc)
        qStart = QDate(startDate.year, startDate.month, startDate.day)

        endDate = datetime.now(tz=timezone.utc)
        qEnd = QDate(endDate.year, endDate.month, endDate.day)
        return [qStart, qEnd]
示例#19
0
    def get_average(self, movingAverage: str, parameter: str, value: int, dataObject: Data = None,
                    update: bool = True, round_value=False) -> float:
        """
        Returns the moving average with parameter and value provided
        :param round_value: Boolean for whether returned value should be rounded or not.
        :param update: Boolean for whether average will call the API to get latest values or not.
        :param dataObject: Data object to be used to get moving averages.
        :param movingAverage: Moving average to get the average from the data view.
        :param parameter: Parameter for the data view to use in the moving average.
        :param value: Value for the moving average to use in the moving average.
        :return: A float value representing the moving average.
        """
        if not dataObject:
            dataObject = self.dataView

        if movingAverage == 'SMA':
            return dataObject.get_sma(value, parameter, update=update, round_value=round_value)
        elif movingAverage == 'WMA':
            return dataObject.get_wma(value, parameter, update=update, round_value=round_value)
        elif movingAverage == 'EMA':
            return dataObject.get_ema(value, parameter, update=update, round_value=round_value)
        else:
            raise ValueError(f'Unknown moving average {movingAverage}.')
示例#20
0
 def __init__(self,
              startingBalance: float = 1000,
              interval: str = '1h',
              symbol: str = 'BTCUSDT',
              loadData: bool = True,
              updateData: bool = True,
              isSpot: bool = False,
              inSpot: bool = False,
              logFile: str = 'simulation',
              precision: int = 2,
              addTradeCallback=None):
     """
     SimulationTrader object that will mimic real live market trades.
     :param startingBalance: Balance to start simulation trader with.
     :param interval: Interval to start trading on.
     :param symbol: Symbol to start trading with.
     :param loadData: Boolean whether we load data from data object or not.
     :param updateData: Boolean for whether data will be updated if it is loaded.
     :param logFile: Filename that logger will log to.
     :param precision: Precision to round data to.
     :param addTradeCallback: Callback signal to emit to (if provided) to reflect a new transaction.
     """
     super().__init__(precision=precision,
                      symbol=symbol,
                      startingBalance=startingBalance)
     self.logger = get_logger(log_file=logFile,
                              logger_name=logFile)  # Get logger.
     self.dataView: Data = Data(interval=interval,
                                symbol=symbol,
                                loadData=loadData,
                                updateData=updateData,
                                logObject=self.logger,
                                precision=precision)
     self.binanceClient = self.dataView.binanceClient  # Retrieve Binance client.
     self.symbol = self.dataView.symbol  # Retrieve symbol from data-view object.
     self.coinName = self.get_coin_name()  # Retrieve primary coin to trade.
     self.commissionPaid = 0  # Total commission paid to broker.
     self.completedLoop = True  # Loop that'll keep track of bot. We wait for this to turn False before some action.
     self.inHumanControl = False  # Boolean that keeps track of whether human or bot controls transactions.
     self.lock = Lock(
     )  # Lock to ensure a transaction doesn't occur when another one is taking place.
     self.addTradeCallback = addTradeCallback  # Callback for GUI to add trades.
     self.dailyChangeNets = [
     ]  # Daily change net list. Will contain list of all nets.
     self.optionDetails = [
     ]  # Current option values. Holds most recent option values.
     self.lowerOptionDetails = [
     ]  # Lower option values. Holds lower interval option values (if exist).
     self.spot = isSpot
     self.inSpot = inSpot
示例#21
0
def test_get_latest_database_row(data_object: Data):
    """
    Test get latest database row functionality.
    :param data_object: Data object to leverage to test this function.
    """
    data_object.create_table()
    result = data_object.get_latest_database_row()
    assert result == {}, "Expected an empty dictionary."

    insert_test_data_to_database()
    result = data_object.get_latest_database_row()
    expected = {
        'close': 3.7763,
        'date_utc': convert_str_to_utc_datetime('03/06/2021 01:43 AM'),
        'high': 3.7763,
        'low': 3.7729,
        'number_of_trades': 6192.345082,
        'open': 3.7729,
        'quote_asset_volume': 1614995039999.0,
        'taker_buy_base_asset': 25.0,
        'taker_buy_quote_asset': 1635.85,
        'volume': 1640.75
    }
    assert result == expected, f'Expected: {expected}. Got: {result}'
示例#22
0
class MyTestCase(unittest.TestCase):
    dataObject = Data(interval='1h', symbol='YFIUSDT', loadData=True)

    def test_initialization(self):
        self.assertTrue(self.dataObject.data, 'Data initialization test.')

    def test_validate_interval(self):
        self.dataObject.validate_interval('15m')
        self.dataObject.validate_interval('30m')
        self.assertRaises(ValueError, self.dataObject.validate_interval, '51m')

    def test_validate_symbol(self):
        self.dataObject.validate_symbol('BTCUSDT')
        self.dataObject.validate_symbol('YFIUSDT')
        self.assertRaises(ValueError, self.dataObject.validate_symbol, 'BAD')

    def test_get_latest_row(self):
        self.assertTrue(self.dataObject.get_latest_database_row())