Exemple #1
0
 def _validate_stop_loss(self) -> None:
     if self.stop_loss is None:
         raise exceptions.InvalidStrategy(
             'You forgot to set self.stop_loss. example [qty, price]')
     elif type(self.stop_loss) not in [tuple, list, np.ndarray]:
         raise exceptions.InvalidStrategy(
             'self.stop_loss must be either a list or a tuple. example: [qty, price]'
         )
Exemple #2
0
 def _validate_take_profit(self) -> None:
     if self.take_profit is None:
         raise exceptions.InvalidStrategy(
             'You forgot to set self.take_profit. example [qty, price]')
     elif type(self.take_profit) not in [tuple, list, np.ndarray]:
         raise exceptions.InvalidStrategy(
             'self.take_profit must be either a list or a tuple. example: [qty, price]'
         )
Exemple #3
0
    def _on_open_position(self, order: Order) -> None:
        self.increased_count = 1

        self._broadcast('route-open-position')

        if self.take_profit is not None:
            for o in self._take_profit:
                # validation: make sure take-profit will exit with profit
                if self.is_long:
                    if o[1] <= self.position.entry_price:
                        raise exceptions.InvalidStrategy(
                            f'take-profit({o[1]}) must be above entry-price({self.position.entry_price}) in a long position'
                        )
                elif self.is_short:
                    if o[1] >= self.position.entry_price:
                        raise exceptions.InvalidStrategy(
                            f'take-profit({o[1]}) must be below entry-price({self.position.entry_price}) in a short position'
                        )

                # submit take-profit
                submitted_order: Order = self.broker.reduce_position_at(
                    o[0],
                    o[1],
                    order_roles.CLOSE_POSITION
                )
                if submitted_order:
                    submitted_order.submitted_via = 'take-profit'
                    self._close_position_orders.append(submitted_order)

        if self.stop_loss is not None:
            for o in self._stop_loss:
                # validation
                if self.is_long:
                    if o[1] >= self.position.entry_price:
                        raise exceptions.InvalidStrategy(
                            f'stop-loss({o[1]}) must be below entry-price({self.position.entry_price}) in a long position'
                        )
                elif self.is_short:
                    if o[1] <= self.position.entry_price:
                        raise exceptions.InvalidStrategy(
                            f'stop-loss({o[1]}) must be above entry-price({self.position.entry_price}) in a short position'
                        )

                # submit stop-loss
                submitted_order: Order = self.broker.reduce_position_at(
                    o[0],
                    o[1],
                    order_roles.CLOSE_POSITION
                )
                if submitted_order:
                    submitted_order.submitted_via = 'stop-loss'
                    self._close_position_orders.append(submitted_order)

        self._open_position_orders = []
        self.on_open_position(order)
        self._detect_and_handle_entry_and_exit_modifications()
Exemple #4
0
    def _execute_long(self) -> None:
        self.go_long()

        # validation
        if self.buy is None:
            raise exceptions.InvalidStrategy(
                'You forgot to set self.buy. example [qty, price]')
        elif type(self.buy) not in [tuple, list]:
            raise exceptions.InvalidStrategy(
                'self.buy must be either a list or a tuple. example: [qty, price]'
            )

        self._prepare_buy()

        if self.take_profit is not None:
            # validate
            self._validate_take_profit()

            self._prepare_take_profit()

        if self.stop_loss is not None:
            # validate
            self._validate_stop_loss()

            self._prepare_stop_loss()

        # filters
        passed = self._execute_filters()
        if not passed:
            return

        for o in self._buy:
            # MARKET order
            if abs(o[1] - self.price) < 0.0001:
                submitted_order = self.broker.buy_at_market(
                    o[0], order_roles.OPEN_POSITION)
            # STOP order
            elif o[1] > self.price:
                submitted_order = self.broker.start_profit_at(
                    sides.BUY, o[0], o[1], order_roles.OPEN_POSITION)
            # LIMIT order
            elif o[1] < self.price:
                submitted_order = self.broker.buy_at(o[0], o[1],
                                                     order_roles.OPEN_POSITION)
            else:
                raise ValueError(
                    f'Invalid order price: o[1]:{o[1]}, self.price:{self.price}'
                )

            if submitted_order:
                self._entry_orders.append(submitted_order)
Exemple #5
0
    def _execute_long(self):
        self.go_long()

        # validation
        if self.buy is None:
            raise exceptions.InvalidStrategy(
                'You forgot to set self.buy. example [qty, price]')
        elif type(self.buy) not in [tuple, list]:
            raise exceptions.InvalidStrategy(
                'self.buy must be either a list or a tuple. example: [qty, price]'
            )

        self._prepare_buy()

        if self.take_profit is not None:
            # validate
            self._validate_take_profit()

            self._prepare_take_profit()

        if self.stop_loss is not None:
            # validate
            self._validate_stop_loss()

            self._prepare_stop_loss()

        # filters
        for f in self.filters():
            passed = f()
            if passed is False:
                logger.info(f.__name__)
                self._reset()
                return

        for o in self._buy:
            # STOP order
            if o[1] > self.price:
                self._open_position_orders.append(
                    self.broker.start_profit_at(sides.BUY, o[0], o[1],
                                                order_roles.OPEN_POSITION))
            # LIMIT order
            elif o[1] < self.price:
                self._open_position_orders.append(
                    self.broker.buy_at(o[0], o[1], order_roles.OPEN_POSITION))
            # MARKET order
            elif o[1] == self.price:
                self._open_position_orders.append(
                    self.broker.buy_at_market(o[0], order_roles.OPEN_POSITION))
Exemple #6
0
    def _execute_filters(self):
        for f in self.filters():
            try:
                passed = f()
            except TypeError:
                raise exceptions.InvalidStrategy(
                    "Invalid filter format. You need to pass filter methods WITHOUT calling them "
                    "(no parentheses must be present at the end)"
                    "\n\n"
                    u"\u274C " + "Incorrect Example:\n"
                    "return [\n"
                    "    self.filter_1()\n"
                    "]\n\n"
                    u"\u2705 " + "Correct Example:\n"
                    "return [\n"
                    "    self.filter_1\n"
                    "]\n"
                )

            if passed == False:
                logger.info(f.__name__)
                self._reset()
                return False
        
        return True
Exemple #7
0
    def average_take_profit(self) -> float:
        if self._take_profit is None:
            raise exceptions.InvalidStrategy(
                'You cannot access self.average_take_profit before setting self.take_profit')

        arr = self._take_profit
        return (np.abs(arr[:, 0] * arr[:, 1])).sum() / np.abs(arr[:, 0]).sum()
Exemple #8
0
    def __init__(self, training_candles: ndarray, testing_candles: ndarray, optimal_total: int, cpu_cores: int, csv: bool,
                 json: bool, start_date: str, finish_date: str) -> None:
        if len(router.routes) != 1:
            raise NotImplementedError('optimize_mode mode only supports one route at the moment')

        self.strategy_name = router.routes[0].strategy_name
        self.optimal_total = optimal_total
        self.exchange = router.routes[0].exchange
        self.symbol = router.routes[0].symbol
        self.timeframe = router.routes[0].timeframe
        StrategyClass = jh.get_strategy_class(self.strategy_name)
        self.strategy_hp = StrategyClass.hyperparameters(None)
        solution_len = len(self.strategy_hp)

        if solution_len == 0:
            raise exceptions.InvalidStrategy('Targeted strategy does not implement a valid hyperparameters() method.')

        super().__init__(
            iterations=2000 * solution_len,
            population_size=solution_len * 100,
            solution_len=solution_len,
            options={
                'strategy_name': self.strategy_name,
                'exchange': self.exchange,
                'symbol': self.symbol,
                'timeframe': self.timeframe,
                'strategy_hp': self.strategy_hp,
                'csv': csv,
                'json': json,
                'start_date': start_date,
                'finish_date': finish_date,
            }
        )

        if cpu_cores > cpu_count():
            raise ValueError(f'Entered cpu cores number is more than available on this machine which is {cpu_count()}')
        elif cpu_cores == 0:
            self.cpu_cores = cpu_count()
        else:
            self.cpu_cores = cpu_cores

        self.training_candles = training_candles
        self.testing_candles = testing_candles

        key = jh.key(self.exchange, self.symbol)
        training_candles_start_date = jh.timestamp_to_time(self.training_candles[key]['candles'][0][0]).split('T')[0]
        training_candles_finish_date = jh.timestamp_to_time(self.training_candles[key]['candles'][-1][0]).split('T')[0]
        testing_candles_start_date = jh.timestamp_to_time(self.testing_candles[key]['candles'][0][0]).split('T')[0]
        testing_candles_finish_date = jh.timestamp_to_time(self.testing_candles[key]['candles'][-1][0]).split('T')[0]

        self.training_initial_candles = []
        self.testing_initial_candles = []

        for c in config['app']['considering_candles']:
            self.training_initial_candles.append(
                required_candles.load_required_candles(c[0], c[1], training_candles_start_date,
                                                       training_candles_finish_date))
            self.testing_initial_candles.append(
                required_candles.load_required_candles(c[0], c[1], testing_candles_start_date,
                                                       testing_candles_finish_date))
Exemple #9
0
    def _on_open_position(self):
        logger.info('Detected open position')
        self._broadcast('route-open-position')

        if self.take_profit is not None:
            for o in self._take_profit:
                # validation: make sure take-profit will exit with profit
                if self.is_long:
                    if o[1] <= self.position.entry_price:
                        raise exceptions.InvalidStrategy(
                            'take-profit({}) must be above entry-price({}) in a long position'
                            .format(o[1], self.position.entry_price))
                elif self.is_short:
                    if o[1] >= self.position.entry_price:
                        raise exceptions.InvalidStrategy(
                            'take-profit({}) must be below entry-price({}) in a short position'
                            .format(o[1], self.position.entry_price))

                # submit take-profit
                self._take_profit_orders.append(
                    self.broker.reduce_position_at(o[0], o[1],
                                                   order_roles.CLOSE_POSITION))

        if self.stop_loss is not None:
            for o in self._stop_loss:
                # validation
                if self.is_long:
                    if o[1] >= self.position.entry_price:
                        raise exceptions.InvalidStrategy(
                            'stop-loss({}) must be below entry-price({}) in a long position'
                            .format(o[1], self.position.entry_price))
                elif self.is_short:
                    if o[1] <= self.position.entry_price:
                        raise exceptions.InvalidStrategy(
                            'stop-loss({}) must be above entry-price({}) in a short position'
                            .format(o[1], self.position.entry_price))

                # submit stop-loss
                self._stop_loss_orders.append(
                    self.broker.stop_loss_at(o[0], o[1],
                                             order_roles.CLOSE_POSITION))

        self._open_position_orders = []
        self._initial_qty = self.position.qty
        self.on_open_position()
        self._detect_and_handle_entry_and_exit_modifications()
Exemple #10
0
    def average_stop_loss(self) -> float:
        if self._stop_loss is None:
            raise exceptions.InvalidStrategy(
                'You cannot access self.average_stop_loss before setting self.stop_loss'
            )

        arr = self._stop_loss
        return (np.abs(arr[:, 0] * arr[:, 1])).sum() / np.abs(arr[:, 0]).sum()
Exemple #11
0
    def _execute_short(self) -> None:
        self.go_short()

        # validation
        if self.sell is None:
            raise exceptions.InvalidStrategy(
                'You forgot to set self.sell. example [qty, price]')
        elif type(self.sell) not in [tuple, list]:
            raise exceptions.InvalidStrategy(
                'self.sell must be either a list or a tuple. example: [qty, price]'
            )

        self._prepare_sell()

        if self.take_profit is not None:
            self._validate_take_profit()
            self._prepare_take_profit()

        if self.stop_loss is not None:
            self._validate_stop_loss()
            self._prepare_stop_loss()

        # filters
        passed = self._execute_filters()
        if not passed:
            return

        for o in self._sell:
            # STOP order
            if o[1] < self.price:
                self._open_position_orders.append(
                    self.broker.start_profit_at(sides.SELL, o[0], o[1],
                                                order_roles.OPEN_POSITION))
            # LIMIT order
            elif o[1] > self.price:
                self._open_position_orders.append(
                    self.broker.sell_at(o[0], o[1], order_roles.OPEN_POSITION))
            # MARKET order
            elif o[1] == self.price:
                self._open_position_orders.append(
                    self.broker.sell_at_market(o[0],
                                               order_roles.OPEN_POSITION))
Exemple #12
0
    def __init__(self, training_candles, testing_candles, optimal_total):
        if len(router.routes) != 1:
            raise NotImplementedError(
                'optimize_mode mode only supports one route at the moment')

        self.strategy_name = router.routes[0].strategy_name
        self.optimal_total = optimal_total
        self.exchange = router.routes[0].exchange
        self.symbol = router.routes[0].symbol
        self.timeframe = router.routes[0].timeframe
        StrategyClass = jh.get_strategy_class(self.strategy_name)
        self.strategy_hp = StrategyClass.hyperparameters(None)
        solution_len = len(self.strategy_hp)

        if solution_len == 0:
            raise exceptions.InvalidStrategy(
                'Targeted strategy does not implement a valid hyperparameters() method.'
            )

        super().__init__(iterations=2000 * solution_len,
                         population_size=solution_len * 100,
                         solution_len=solution_len,
                         options={
                             'strategy_name': self.strategy_name,
                             'exchange': self.exchange,
                             'symbol': self.symbol,
                             'timeframe': self.timeframe
                         })

        self.training_candles = training_candles
        self.testing_candles = testing_candles

        key = jh.key(self.exchange, self.symbol)
        training_candles_start_date = jh.timestamp_to_time(
            self.training_candles[key]['candles'][0][0]).split('T')[0]
        training_candles_finish_date = jh.timestamp_to_time(
            self.training_candles[key]['candles'][-1][0]).split('T')[0]
        testing_candles_start_date = jh.timestamp_to_time(
            self.testing_candles[key]['candles'][0][0]).split('T')[0]
        testing_candles_finish_date = jh.timestamp_to_time(
            self.testing_candles[key]['candles'][-1][0]).split('T')[0]

        self.training_initial_candles = []
        self.testing_initial_candles = []

        for c in config['app']['considering_candles']:
            self.training_initial_candles.append(
                required_candles.load_required_candles(
                    c[0], c[1], training_candles_start_date,
                    training_candles_finish_date))
            self.testing_initial_candles.append(
                required_candles.load_required_candles(
                    c[0], c[1], testing_candles_start_date,
                    testing_candles_finish_date))
Exemple #13
0
def simulator(candles: Dict[str, Dict[str, Union[str, np.ndarray]]],
              hyperparameters=None) -> None:
    begin_time_track = time.time()
    key = '{}-{}'.format(config['app']['considering_candles'][0][0],
                         config['app']['considering_candles'][0][1])
    first_candles_set = candles[key]['candles']
    length = len(first_candles_set)
    # to preset the array size for performance
    store.app.starting_time = first_candles_set[0][0]
    store.app.time = first_candles_set[0][0]

    # initiate strategies
    for r in router.routes:
        StrategyClass = jh.get_strategy_class(r.strategy_name)

        try:
            r.strategy = StrategyClass()
        except TypeError:
            raise exceptions.InvalidStrategy(
                "Looks like the structure of your strategy directory is incorrect. Make sure to include the strategy INSIDE the __init__.py file."
                "\nIf you need working examples, check out: https://github.com/jesse-ai/example-strategies"
            )
        except:
            raise

        r.strategy.name = r.strategy_name
        r.strategy.exchange = r.exchange
        r.strategy.symbol = r.symbol
        r.strategy.timeframe = r.timeframe

        # inject hyper parameters (used for optimize_mode)
        # convert DNS string into hyperparameters
        if r.dna and hyperparameters is None:
            hyperparameters = jh.dna_to_hp(r.strategy.hyperparameters(), r.dna)

        # inject hyperparameters sent within the optimize mode
        if hyperparameters is not None:
            r.strategy.hp = hyperparameters

        # init few objects that couldn't be initiated in Strategy __init__
        # it also injects hyperparameters into self.hp in case the route does not uses any DNAs
        r.strategy._init_objects()

        selectors.get_position(r.exchange, r.symbol).strategy = r.strategy

    # add initial balance
    save_daily_portfolio_balance()

    with click.progressbar(length=length,
                           label='Executing simulation...') as progressbar:
        for i in range(length):
            # update time
            store.app.time = first_candles_set[i][0] + 60_000

            # add candles
            for j in candles:
                short_candle = candles[j]['candles'][i]
                if i != 0:
                    previous_short_candle = candles[j]['candles'][i - 1]
                    short_candle = _get_fixed_jumped_candle(
                        previous_short_candle, short_candle)
                exchange = candles[j]['exchange']
                symbol = candles[j]['symbol']

                store.candles.add_candle(short_candle,
                                         exchange,
                                         symbol,
                                         '1m',
                                         with_execution=False,
                                         with_generation=False)

                # print short candle
                if jh.is_debuggable('shorter_period_candles'):
                    print_candle(short_candle, True, symbol)

                _simulate_price_change_effect(short_candle, exchange, symbol)

                # generate and add candles for bigger timeframes
                for timeframe in config['app']['considering_timeframes']:
                    # for 1m, no work is needed
                    if timeframe == '1m':
                        continue

                    count = jh.timeframe_to_one_minutes(timeframe)
                    until = count - ((i + 1) % count)

                    if (i + 1) % count == 0:
                        generated_candle = generate_candle_from_one_minutes(
                            timeframe,
                            candles[j]['candles'][(i - (count - 1)):(i + 1)])
                        store.candles.add_candle(generated_candle,
                                                 exchange,
                                                 symbol,
                                                 timeframe,
                                                 with_execution=False,
                                                 with_generation=False)

            # update progressbar
            if not jh.is_debugging() and not jh.should_execute_silently(
            ) and i % 60 == 0:
                progressbar.update(60)

            # now that all new generated candles are ready, execute
            for r in router.routes:
                count = jh.timeframe_to_one_minutes(r.timeframe)
                # 1m timeframe
                if r.timeframe == timeframes.MINUTE_1:
                    r.strategy._execute()
                elif (i + 1) % count == 0:
                    # print candle
                    if jh.is_debuggable('trading_candles'):
                        print_candle(
                            store.candles.get_current_candle(
                                r.exchange, r.symbol, r.timeframe), False,
                            r.symbol)
                    r.strategy._execute()

            # now check to see if there's any MARKET orders waiting to be executed
            store.orders.execute_pending_market_orders()

            if i != 0 and i % 1440 == 0:
                save_daily_portfolio_balance()

    if not jh.should_execute_silently():
        if jh.is_debuggable('trading_candles') or jh.is_debuggable(
                'shorter_period_candles'):
            print('\n')

        # print executed time for the backtest session
        finish_time_track = time.time()
        print(
            'Executed backtest simulation in: ',
            '{} seconds'.format(round(finish_time_track - begin_time_track,
                                      2)))

    for r in router.routes:
        r.strategy._terminate()
        store.orders.execute_pending_market_orders()

    # now that backtest is finished, add finishing balance
    save_daily_portfolio_balance()
Exemple #14
0
def simulator(
        candles: dict, run_silently: bool, hyperparameters: dict = None
) -> None:
    begin_time_track = time.time()
    key = f"{config['app']['considering_candles'][0][0]}-{config['app']['considering_candles'][0][1]}"
    first_candles_set = candles[key]['candles']
    length = len(first_candles_set)
    # to preset the array size for performance
    try:
        store.app.starting_time = first_candles_set[0][0]
    except IndexError:
        raise IndexError('Check your "warm_up_candles" config value')
    store.app.time = first_candles_set[0][0]

    # initiate strategies
    for r in router.routes:
        # if the r.strategy is str read it from file
        if isinstance(r.strategy_name, str):
            StrategyClass = jh.get_strategy_class(r.strategy_name)
        # else it is a class object so just use it
        else:
            StrategyClass = r.strategy_name

        try:
            r.strategy = StrategyClass()
        except TypeError:
            raise exceptions.InvalidStrategy(
                "Looks like the structure of your strategy directory is incorrect. Make sure to include the strategy INSIDE the __init__.py file."
                "\nIf you need working examples, check out: https://github.com/jesse-ai/example-strategies"
            )
        except:
            raise

        r.strategy.name = r.strategy_name
        r.strategy.exchange = r.exchange
        r.strategy.symbol = r.symbol
        r.strategy.timeframe = r.timeframe

        # read the dna from strategy's dna() and use it for injecting inject hyperparameters
        # first convert DNS string into hyperparameters
        if len(r.strategy.dna()) > 0 and hyperparameters is None:
            hyperparameters = jh.dna_to_hp(r.strategy.hyperparameters(), r.strategy.dna())

        # inject hyperparameters sent within the optimize mode
        if hyperparameters is not None:
            r.strategy.hp = hyperparameters

        # init few objects that couldn't be initiated in Strategy __init__
        # it also injects hyperparameters into self.hp in case the route does not uses any DNAs
        r.strategy._init_objects()

        selectors.get_position(r.exchange, r.symbol).strategy = r.strategy

    # add initial balance
    save_daily_portfolio_balance()

    progressbar = Progressbar(length, step=60)
    for i in range(length):
        # update time
        store.app.time = first_candles_set[i][0] + 60_000

        # add candles
        for j in candles:
            short_candle = candles[j]['candles'][i]
            if i != 0:
                previous_short_candle = candles[j]['candles'][i - 1]
                short_candle = _get_fixed_jumped_candle(previous_short_candle, short_candle)
            exchange = candles[j]['exchange']
            symbol = candles[j]['symbol']

            store.candles.add_candle(short_candle, exchange, symbol, '1m', with_execution=False,
                                     with_generation=False)

            # print short candle
            if jh.is_debuggable('shorter_period_candles'):
                print_candle(short_candle, True, symbol)

            _simulate_price_change_effect(short_candle, exchange, symbol)

            # generate and add candles for bigger timeframes
            for timeframe in config['app']['considering_timeframes']:
                # for 1m, no work is needed
                if timeframe == '1m':
                    continue

                count = jh.timeframe_to_one_minutes(timeframe)
                # until = count - ((i + 1) % count)

                if (i + 1) % count == 0:
                    generated_candle = generate_candle_from_one_minutes(
                        timeframe,
                        candles[j]['candles'][(i - (count - 1)):(i + 1)])
                    store.candles.add_candle(generated_candle, exchange, symbol, timeframe, with_execution=False,
                                             with_generation=False)

        # update progressbar
        if not run_silently and i % 60 == 0:
            progressbar.update()
            sync_publish('progressbar', {
                'current': progressbar.current,
                'estimated_remaining_seconds': progressbar.estimated_remaining_seconds
            })

        # now that all new generated candles are ready, execute
        for r in router.routes:
            count = jh.timeframe_to_one_minutes(r.timeframe)
            # 1m timeframe
            if r.timeframe == timeframes.MINUTE_1:
                r.strategy._execute()
            elif (i + 1) % count == 0:
                # print candle
                if jh.is_debuggable('trading_candles'):
                    print_candle(store.candles.get_current_candle(r.exchange, r.symbol, r.timeframe), False,
                                 r.symbol)
                r.strategy._execute()

        # now check to see if there's any MARKET orders waiting to be executed
        store.orders.execute_pending_market_orders()

        if i != 0 and i % 1440 == 0:
            save_daily_portfolio_balance()

    if not run_silently:
        # print executed time for the backtest session
        finish_time_track = time.time()
        sync_publish('alert', {
            'message': f'Successfully executed backtest simulation in: {round(finish_time_track - begin_time_track, 2)} seconds',
            'type': 'success'
        })

    for r in router.routes:
        r.strategy._terminate()
        store.orders.execute_pending_market_orders()

    # now that backtest is finished, add finishing balance
    save_daily_portfolio_balance()
Exemple #15
0
    def __init__(
            self,
            training_candles: ndarray, testing_candles: ndarray,
            optimal_total: int, cpu_cores: int,
            csv: bool,
            export_json: bool,
            start_date: str, finish_date: str,
            charset: str = r'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvw',
            fitness_goal: float = 1,
    ) -> None:
        if len(router.routes) != 1:
            raise NotImplementedError('optimize_mode mode only supports one route at the moment')

        self.strategy_name = router.routes[0].strategy_name
        self.exchange = router.routes[0].exchange
        self.symbol = router.routes[0].symbol
        self.timeframe = router.routes[0].timeframe
        strategy_class = jh.get_strategy_class(self.strategy_name)
        self.strategy_hp = strategy_class.hyperparameters(None)
        solution_len = len(self.strategy_hp)

        if solution_len == 0:
            raise exceptions.InvalidStrategy('Targeted strategy does not implement a valid hyperparameters() method.')

        self.started_index = 0
        self.start_time = jh.now_to_timestamp()
        self.population = []
        self.iterations = 2000 * solution_len
        self.population_size = solution_len * 100
        self.solution_len = solution_len
        self.charset = charset
        self.fitness_goal = fitness_goal
        self.cpu_cores = 0
        self.optimal_total = optimal_total
        self.training_candles = training_candles
        self.testing_candles = testing_candles
        self.average_execution_seconds = 0

        # check for termination event once per second
        tl_0 = Timeloop()
        @tl_0.job(interval=timedelta(seconds=1))
        def check_for_termination():
            if process_status() != 'started':
                raise exceptions.Termination
        tl_0.start()

        options = {
            'strategy_name': self.strategy_name,
            'exchange': self.exchange,
            'symbol': self.symbol,
            'timeframe': self.timeframe,
            'strategy_hp': self.strategy_hp,
            'csv': csv,
            'json': export_json,
            'start_date': start_date,
            'finish_date': finish_date,
        }

        self.options = {} if options is None else options
        os.makedirs('./storage/temp/optimize', exist_ok=True)
        self.temp_path = f"./storage/temp/optimize/{self.options['strategy_name']}-{self.options['exchange']}-{self.options['symbol']}-{self.options['timeframe']}-{self.options['start_date']}-{self.options['finish_date']}.pickle"

        if fitness_goal > 1 or fitness_goal < 0:
            raise ValueError('fitness scores must be between 0 and 1')

        if not optimal_total > 0:
            raise ValueError('optimal_total must be bigger than 0')

        # # if temp file exists, load data to resume previous session
        # if jh.file_exists(self.temp_path) and click.confirm(
        #         'Previous session detected. Do you want to resume?', default=True
        # ):
        #     self.load_progress()

        if cpu_cores > cpu_count():
            raise ValueError(f'Entered cpu cores number is more than available on this machine which is {cpu_count()}')
        elif cpu_cores == 0:
            self.cpu_cores = cpu_count()
        else:
            self.cpu_cores = cpu_cores
Exemple #16
0
    def evolve(self) -> list:
        """
        the main method, that runs the evolutionary algorithm
        """
        # clear the logs to start from a clean slate
        jh.clear_file('storage/logs/optimize-mode.txt')

        logger.log_optimize_mode('Optimization session started')

        if self.started_index == 0:
            logger.log_optimize_mode(
                f"Generating {self.population_size} population size (random DNAs) using {self.cpu_cores} CPU cores"
            )
            self.generate_initial_population()

            if len(self.population) < 0.5 * self.population_size:
                msg = f'Too many errors! less than half of the expected population size could be generated. Only {len(self.population)} indviduals from planned {self.population_size} are usable.'
                logger.log_optimize_mode(msg)
                raise ValueError(msg)

            # if even our best individual is too weak, then we better not continue
            if self.population[0]['fitness'] == 0.0001:
                msg = 'Cannot continue because no individual with the minimum fitness-score was found. Your strategy seems to be flawed or maybe it requires modifications. '
                logger.log_optimize_mode(msg)
                raise exceptions.InvalidStrategy(msg)

        loop_length = int(self.iterations / self.cpu_cores)

        i = self.started_index
        progressbar = Progressbar(loop_length)
        while i < loop_length:
            with Manager() as manager:
                people_bucket = manager.list([])
                workers = []

                try:
                    for _ in range(self.cpu_cores):
                        mommy = self.select_person()
                        daddy = self.select_person()
                        w = Process(
                            target=create_baby,
                            args=(
                                people_bucket, mommy, daddy, self.solution_len, self.charset,
                                jh.get_config('env.optimization'), router.formatted_routes,
                                router.formatted_extra_routes,
                                self.strategy_hp, self.training_candles, self.testing_candles,
                                self.optimal_total
                            )
                        )
                        w.start()
                        workers.append(w)

                    for w in workers:
                        w.join()
                        if w.exitcode > 0:
                            logger.error(f'a process exited with exitcode: {w.exitcode}')
                except exceptions.Termination:
                    self._handle_termination(manager, workers)

                # update dashboard
                click.clear()
                self.update_progressbar(progressbar)
                # general_info streams
                general_info = {
                    'started_at': jh.timestamp_to_arrow(self.start_time).humanize(),
                    'index': f'{(i + 1) * self.cpu_cores}/{self.iterations}',
                    'errors_info_count': f'{len(store.logs.errors)}/{len(store.logs.info)}',
                    'trading_route': f'{router.routes[0].exchange}, {router.routes[0].symbol}, {router.routes[0].timeframe}, {router.routes[0].strategy_name}',
                    'average_execution_seconds': self.average_execution_seconds
                }
                if jh.is_debugging():
                    general_info['population_size'] = self.population_size
                    general_info['iterations'] = self.iterations
                    general_info['solution_length'] = self.solution_len
                sync_publish('general_info', general_info)

                if self.population_size > 50:
                    number_of_ind_to_show = 40
                elif self.population_size > 20:
                    number_of_ind_to_show = 15
                elif self.population_size > 9:
                    number_of_ind_to_show = 9
                else:
                    raise ValueError('self.population_size cannot be less than 10')

                best_candidates = [{
                        'rank': j + 1,
                        'dna': self.population[j]['dna'],
                        'fitness': round(self.population[j]['fitness'], 4),
                        'training_win_rate': self.population[j]['training_log']['win-rate'],
                        'training_total_trades': self.population[j]['training_log']['total'],
                        'training_pnl': self.population[j]['training_log']['PNL'],
                        'testing_win_rate': self.population[j]['testing_log']['win-rate'],
                        'testing_total_trades': self.population[j]['testing_log']['total'],
                        'testing_pnl': self.population[j]['testing_log']['PNL'],
                    } for j in range(number_of_ind_to_show)]
                sync_publish('best_candidates', best_candidates)

                # one person has to die and be replaced with the newborn baby
                for baby in people_bucket:
                    # never kill our best performer
                    random_index = randint(1, len(self.population) - 1)
                    self.population[random_index] = baby
                    self.population = list(sorted(self.population, key=lambda x: x['fitness'], reverse=True))

                    # reaching the fitness goal could also end the process
                    if baby['fitness'] >= self.fitness_goal:
                        self.update_progressbar(progressbar, finished=True)
                        sync_publish('alert', {
                            'message': f'Fitness goal reached after iteration {i*self.cpu_cores}',
                            'type': 'success'
                        })
                        return baby

                # TODO: bring back progress resumption
                # # save progress after every n iterations
                # if i != 0 and int(i * self.cpu_cores) % 50 == 0:
                #     self.save_progress(i)

                # TODO: bring back
                # # store a take_snapshot of the fittest individuals of the population
                # if i != 0 and i % int(100 / self.cpu_cores) == 0:
                #     self.take_snapshot(i * self.cpu_cores)

                i += 1

        sync_publish('alert', {
            'message': f"Finished {self.iterations} iterations. Check your best DNA candidates, "
                       f"if you don't like any of them, try modifying your strategy.",
            'type': 'success'
        })
        return self.population
Exemple #17
0
    def _detect_and_handle_entry_and_exit_modifications(self) -> None:
        if self.position.is_close:
            return

        try:
            if self.is_long:
                # prepare format
                self._prepare_buy(make_copies=False)

                # if entry has been modified
                if not np.array_equal(self.buy, self._buy):
                    self._buy = self.buy.copy()

                    # cancel orders
                    for o in self._open_position_orders:
                        if o.is_active or o.is_queued:
                            self.broker.cancel_order(o.id)

                    # clean orders array but leave executed ones
                    self._open_position_orders = [
                        o for o in self._open_position_orders if o.is_executed
                    ]
                    for o in self._buy:
                        # STOP order
                        if o[1] > self.price:
                            self._open_position_orders.append(
                                self.broker.start_profit_at(
                                    sides.BUY, o[0], o[1],
                                    order_roles.OPEN_POSITION))
                        # LIMIT order
                        elif o[1] < self.price:
                            self._open_position_orders.append(
                                self.broker.buy_at(o[0], o[1],
                                                   order_roles.OPEN_POSITION))
                        # MARKET order
                        elif o[1] == self.price:
                            self._open_position_orders.append(
                                self.broker.buy_at_market(
                                    o[0], order_roles.OPEN_POSITION))

            elif self.is_short:
                # prepare format
                self._prepare_sell(make_copies=False)

                # if entry has been modified
                if not np.array_equal(self.sell, self._sell):
                    self._sell = self.sell.copy()

                    # cancel orders
                    for o in self._open_position_orders:
                        if o.is_active or o.is_queued:
                            self.broker.cancel_order(o.id)

                    # clean orders array but leave executed ones
                    self._open_position_orders = [
                        o for o in self._open_position_orders if o.is_executed
                    ]

                    for o in self._sell:
                        # STOP order
                        if o[1] < self.price:
                            self._open_position_orders.append(
                                self.broker.start_profit_at(
                                    sides.SELL, o[0], o[1],
                                    order_roles.OPEN_POSITION))
                        # LIMIT order
                        elif o[1] > self.price:
                            self._open_position_orders.append(
                                self.broker.sell_at(o[0], o[1],
                                                    order_roles.OPEN_POSITION))
                        # MARKET order
                        elif o[1] == self.price:
                            self._open_position_orders.append(
                                self.broker.sell_at_market(
                                    o[0], order_roles.OPEN_POSITION))

            if self.position.is_open and self.take_profit is not None:
                self._validate_take_profit()
                self._prepare_take_profit(False)

                # if _take_profit has been modified
                if not np.array_equal(self.take_profit, self._take_profit):
                    self._take_profit = self.take_profit.copy()

                    # cancel orders
                    for o in self._take_profit_orders:
                        if o.is_active or o.is_queued:
                            self.broker.cancel_order(o.id)

                    # clean orders array but leave executed ones
                    self._take_profit_orders = [
                        o for o in self._take_profit_orders if o.is_executed
                    ]
                    self._log_take_profit = []
                    for s in self._take_profit_orders:
                        self._log_take_profit.append((abs(s.qty), s.price))
                    for o in self._take_profit:
                        self._log_take_profit.append(o)

                        self._take_profit_orders.append(
                            self.broker.reduce_position_at(
                                o[0], o[1], order_roles.CLOSE_POSITION))

            if self.position.is_open and self.stop_loss is not None:
                self._validate_stop_loss()
                self._prepare_stop_loss(False)

                # if stop_loss has been modified
                if not np.array_equal(self.stop_loss, self._stop_loss):
                    # prepare format
                    self._stop_loss = self.stop_loss.copy()

                    # cancel orders
                    for o in self._stop_loss_orders:
                        if o.is_active or o.is_queued:
                            self.broker.cancel_order(o.id)

                    # clean orders array but leave executed ones
                    self._stop_loss_orders = [
                        o for o in self._stop_loss_orders if o.is_executed
                    ]
                    self._log_stop_loss = []
                    for s in self._stop_loss_orders:
                        self._log_stop_loss.append((abs(s.qty), s.price))
                    for o in self._stop_loss:
                        self._log_stop_loss.append(o)

                        self._stop_loss_orders.append(
                            self.broker.reduce_position_at(
                                o[0], o[1], order_roles.CLOSE_POSITION))
        except TypeError:
            raise exceptions.InvalidStrategy(
                'Something odd is going on with your strategy. '
                'Try running it with "--debug" to see what was going on near the end, and fix it.'
            )
        except:
            raise

        # validations: stop-loss and take-profit should not be the same
        if self.position.is_open:
            if (self.stop_loss is not None
                    and self.take_profit is not None) and np.array_equal(
                        self.stop_loss, self.take_profit):
                raise exceptions.InvalidStrategy(
                    'stop-loss and take-profit should not be exactly the same. Just use either one of them and it will do.'
                )
Exemple #18
0
    def _detect_and_handle_entry_and_exit_modifications(self) -> None:
        if self.position.is_close:
            return

        try:
            if self.is_long:
                # prepare format
                self._prepare_buy(make_copies=False)

                # if entry has been modified
                if not np.array_equal(self.buy, self._buy):
                    self._buy = self.buy.copy()

                    # cancel orders
                    for o in self._entry_orders:
                        if o.is_active or o.is_queued:
                            self.broker.cancel_order(o.id)
                    self._entry_orders = [
                        o for o in self._entry_orders if o.is_executed
                    ]
                    for o in self._buy:
                        # MARKET order
                        if abs(o[1] - self.price) < 0.0001:
                            submitted_order = self.broker.buy_at_market(
                                o[0], order_roles.OPEN_POSITION)
                        # STOP order
                        elif o[1] > self.price:
                            submitted_order = self.broker.start_profit_at(
                                sides.BUY, o[0], o[1],
                                order_roles.OPEN_POSITION)
                        # LIMIT order
                        elif o[1] < self.price:
                            submitted_order = self.broker.buy_at(
                                o[0], o[1], order_roles.OPEN_POSITION)
                        else:
                            raise ValueError(
                                f'Invalid order price: o[1]:{o[1]}, self.price:{self.price}'
                            )

                        if submitted_order:
                            self._entry_orders.append(submitted_order)

            elif self.is_short:
                # prepare format
                self._prepare_sell(make_copies=False)

                # if entry has been modified
                if not np.array_equal(self.sell, self._sell):
                    self._sell = self.sell.copy()

                    # cancel orders
                    for o in self._entry_orders:
                        if o.is_active or o.is_queued:
                            self.broker.cancel_order(o.id)
                    self._entry_orders = [
                        o for o in self._entry_orders if o.is_executed
                    ]
                    for o in self._sell:
                        # MARKET order
                        if abs(o[1] - self.price) < 0.0001:
                            submitted_order = self.broker.sell_at_market(
                                o[0], order_roles.OPEN_POSITION)
                        # STOP order
                        elif o[1] < self.price:
                            submitted_order = self.broker.start_profit_at(
                                sides.SELL, o[0], o[1],
                                order_roles.OPEN_POSITION)
                        # LIMIT order
                        elif o[1] > self.price:
                            submitted_order = self.broker.sell_at(
                                o[0], o[1], order_roles.OPEN_POSITION)
                        else:
                            raise ValueError(
                                f'Invalid order price: o[1]:{o[1]}, self.price:{self.price}'
                            )

                        if submitted_order:
                            self._entry_orders.append(submitted_order)

            if self.position.is_open and self.take_profit is not None:
                self._validate_take_profit()
                self._prepare_take_profit(False)

                # if _take_profit has been modified
                if not np.array_equal(self.take_profit, self._take_profit):
                    self._take_profit = self.take_profit.copy()

                    # cancel orders
                    for o in self._exit_orders:
                        if o.submitted_via == 'take-profit' and o.is_active or o.is_queued:
                            self.broker.cancel_order(o.id)
                    # remove canceled orders to optimize the loop
                    self._exit_orders = [
                        o for o in self._exit_orders if not o.is_canceled
                    ]
                    for o in self._take_profit:
                        submitted_order: Order = self.broker.reduce_position_at(
                            o[0], o[1], order_roles.CLOSE_POSITION)
                        if submitted_order:
                            submitted_order.submitted_via = 'take-profit'
                            self._exit_orders.append(submitted_order)

            if self.position.is_open and self.stop_loss is not None:
                self._validate_stop_loss()
                self._prepare_stop_loss(False)

                # if stop_loss has been modified
                if not np.array_equal(self.stop_loss, self._stop_loss):
                    # prepare format
                    self._stop_loss = self.stop_loss.copy()

                    # cancel orders
                    for o in self._exit_orders:
                        if o.submitted_via == 'stop-loss' and o.is_active or o.is_queued:
                            self.broker.cancel_order(o.id)
                    # remove canceled orders to optimize the loop
                    self._exit_orders = [
                        o for o in self._exit_orders if not o.is_canceled
                    ]
                    for o in self._stop_loss:
                        submitted_order: Order = self.broker.reduce_position_at(
                            o[0], o[1], order_roles.CLOSE_POSITION)
                        if submitted_order:
                            submitted_order.submitted_via = 'stop-loss'
                            self._exit_orders.append(submitted_order)
        except TypeError:
            raise exceptions.InvalidStrategy(
                'Something odd is going on within your strategy causing a TypeError exception. '
                'Try running it with "--debug" in a backtest to see what was going on near the end, and fix it.'
            )
        except:
            raise

        # validations: stop-loss and take-profit should not be the same
        if (self.position.is_open and
            (self.stop_loss is not None and self.take_profit is not None)
                and np.array_equal(self.stop_loss, self.take_profit)):
            raise exceptions.InvalidStrategy(
                'stop-loss and take-profit should not be exactly the same. Just use either one of them and it will do.'
            )
Exemple #19
0
    def _detect_and_handle_entry_and_exit_modifications(self):
        if self.position.is_close:
            return

        if self.is_long:
            # prepare format
            if type(self.buy[0]) not in [list, tuple, np.ndarray]:
                self.buy = [self.buy]
            self.buy = np.array(self.buy, dtype=float)

            # if entry has been modified
            if not np.array_equal(self.buy, self._buy):
                self._buy = self.buy.copy()

                # cancel orders
                for o in self._open_position_orders:
                    if o.is_active or o.is_queued:
                        self.broker.cancel_order(o.id)

                # clean orders array but leave executed ones
                self._open_position_orders = [o for o in self._open_position_orders if o.is_executed]
                for o in self._buy:
                    # STOP order
                    if o[1] > self.price:
                        self._open_position_orders.append(
                            self.broker.start_profit_at(sides.BUY, o[0], o[1], order_roles.OPEN_POSITION)
                        )
                    # LIMIT order
                    elif o[1] < self.price:
                        self._open_position_orders.append(
                            self.broker.buy_at(o[0], o[1], order_roles.OPEN_POSITION)
                        )
                    # MARKET order
                    elif o[1] == self.price:
                        self._open_position_orders.append(
                            self.broker.buy_at_market(o[0], order_roles.OPEN_POSITION)
                        )

        elif self.is_short:
            # prepare format
            if type(self.sell[0]) not in [list, tuple, np.ndarray]:
                self.sell = [self.sell]
            self.sell = np.array(self.sell, dtype=float)

            # if entry has been modified
            if not np.array_equal(self.sell, self._sell):
                self._sell = self.sell.copy()

                # cancel orders
                for o in self._open_position_orders:
                    if o.is_active or o.is_queued:
                        self.broker.cancel_order(o.id)

                # clean orders array but leave executed ones
                self._open_position_orders = [o for o in self._open_position_orders if o.is_executed]

                for o in self._sell:
                    # STOP order
                    if o[1] > self.price:
                        self._open_position_orders.append(
                            self.broker.start_profit_at(sides.BUY, o[0], o[1], order_roles.OPEN_POSITION)
                        )
                    # LIMIT order
                    elif o[1] < self.price:
                        self._open_position_orders.append(
                            self.broker.sell_at(o[0], o[1], order_roles.OPEN_POSITION)
                        )
                    # MARKET order
                    elif o[1] == self.price:
                        self._open_position_orders.append(
                            self.broker.sell_at_market(o[0], order_roles.OPEN_POSITION)
                        )

        if self.position.is_open and self.take_profit is not None:
            self._validate_take_profit()
            self._prepare_take_profit(False)

            # if _take_profit has been modified
            if not np.array_equal(self.take_profit, self._take_profit):
                self._take_profit = self.take_profit.copy()

                # cancel orders
                for o in self._take_profit_orders:
                    if o.is_active or o.is_queued:
                        self.broker.cancel_order(o.id)

                # clean orders array but leave executed ones
                self._take_profit_orders = [o for o in self._take_profit_orders if o.is_executed]
                self._log_take_profit = []
                for s in self._take_profit_orders:
                    self._log_take_profit.append(
                        (abs(s.qty), s.price)
                    )
                for o in self._take_profit:
                    self._log_take_profit.append(o)

                    if o[1] == self.price:
                        if self.is_long:
                            self._take_profit_orders.append(
                                self.broker.sell_at_market(o[0], role=order_roles.CLOSE_POSITION)
                            )
                        elif self.is_short:
                            self._take_profit_orders.append(
                                self.broker.buy_at_market(o[0], role=order_roles.CLOSE_POSITION)
                            )
                    else:
                        if (self.is_long and o[1] > self.price) or (self.is_short and o[1] < self.price):

                            self._take_profit_orders.append(
                                self.broker.reduce_position_at(
                                    o[0],
                                    o[1],
                                    order_roles.CLOSE_POSITION
                                )
                            )
                        elif (self.is_long and o[1] < self.price) or (self.is_short and o[1] > self.price):
                            self._take_profit_orders.append(
                                self.broker.stop_loss_at(
                                    o[0],
                                    o[1],
                                    order_roles.CLOSE_POSITION
                                )
                            )

        if self.position.is_open and self.stop_loss is not None:
            self._validate_stop_loss()
            self._prepare_stop_loss(False)

            # if stop_loss has been modified
            if not np.array_equal(self.stop_loss, self._stop_loss):
                # prepare format
                self._stop_loss = self.stop_loss.copy()

                # cancel orders
                for o in self._stop_loss_orders:
                    if o.is_active or o.is_queued:
                        self.broker.cancel_order(o.id)

                # clean orders array but leave executed ones
                self._stop_loss_orders = [o for o in self._stop_loss_orders if o.is_executed]
                self._log_stop_loss = []
                for s in self._stop_loss_orders:
                    self._log_stop_loss.append(
                        (abs(s.qty), s.price)
                    )
                for o in self._stop_loss:
                    self._log_stop_loss.append(o)

                    if o[1] == self.price:
                        if self.is_long:
                            self._stop_loss_orders.append(
                                self.broker.sell_at_market(o[0], role=order_roles.CLOSE_POSITION)
                            )
                        elif self.is_short:
                            self._stop_loss_orders.append(
                                self.broker.buy_at_market(o[0], role=order_roles.CLOSE_POSITION)
                            )
                    else:
                        self._stop_loss_orders.append(
                            self.broker.stop_loss_at(
                                o[0],
                                o[1],
                                order_roles.CLOSE_POSITION
                            )
                        )

        # validations: stop-loss and take-profit should not be the same
        if self.position.is_open:
            if (self.stop_loss is not None and self.take_profit is not None) and np.array_equal(self.stop_loss,
                                                                                                self.take_profit):
                raise exceptions.InvalidStrategy(
                    'stop-loss and take-profit should not be exactly the same. Just use either one of them and it will do.')