Example #1
0
def test_metrics_for_trades_without_fee():
    single_route_backtest('TestMetrics1')

    trades = store.completed_trades.trades
    assert len(trades) == 1
    stats = metrics.trades(store.completed_trades.trades, store.app.daily_balance)

    assert stats['total'] == 1
    assert stats['starting_balance'] == 10000
    assert stats['finishing_balance'] == 10050
    assert stats['win_rate'] == 1
    assert stats['ratio_avg_win_loss'] is np.nan
    assert stats['longs_count'] == 1
    assert stats['shorts_count'] == 0
    assert stats['longs_percentage'] == 100
    assert stats['short_percentage'] == 0
    assert stats['fee'] == 0
    assert stats['net_profit'] == 50
    assert stats['net_profit_percentage'] == 0.5
    assert stats['average_win'] == 50
    assert stats['average_loss'] is np.nan
    assert stats['expectancy'] == 50
    assert stats['expectancy_percentage'] == 0.5
    assert stats['expected_net_profit_every_100_trades'] == 50
    assert stats['average_holding_period'] == 300
    assert stats['average_losing_holding_period'] is np.nan
    assert stats['average_winning_holding_period'] == 300
    assert stats['gross_loss'] == 0
    assert stats['gross_profit'] == 50
    assert stats['open_pl'] == 0
    assert stats['largest_losing_trade'] == 0
    assert stats['largest_winning_trade'] == 50
Example #2
0
 def metrics(self) -> dict:
     """
     Returns all the metrics of the strategy.
     """
     if self.trades_count not in self._cached_metrics:
         self._cached_metrics[self.trades_count] = metrics.trades(
             store.completed_trades.trades, store.app.daily_balance)
     return self._cached_metrics[self.trades_count]
Example #3
0
def portfolio_metrics() -> List[
    Union[Union[List[Union[str, Any]], List[str], List[Union[Union[str, float], Any]]], Any]]:
    data = stats.trades(store.completed_trades.trades, store.app.daily_balance)

    metrics = [
        ['Total Closed Trades', data['total']],
        ['Total Net Profit',
         '{} ({})'.format(jh.format_currency(round(data['net_profit'], 4)),
                          str(round(data['net_profit_percentage'], 2)) + '%')],
        ['Starting => Finishing Balance',
         '{} => {}'.format(jh.format_currency(round(data['starting_balance'], 2)),
                           jh.format_currency(round(data['finishing_balance'], 2)))],
        ['Total Open Trades', data['total_open_trades']],
        ['Open PL', jh.format_currency(round(data['open_pl'], 2))],
        ['Total Paid Fees', jh.format_currency(round(data['fee'], 2))],
        ['Max Drawdown', '{}%'.format(round(data['max_drawdown'], 2))],
        ['Annual Return', '{}%'.format(round(data['annual_return'], 2))],
        ['Expectancy',
         '{} ({})'.format(jh.format_currency(round(data['expectancy'], 2)),
                          str(round(data['expectancy_percentage'], 2)) + '%')],
        ['Avg Win | Avg Loss', '{} | {}'.format(jh.format_currency(round(data['average_win'], 2)),
                                                jh.format_currency(round(data['average_loss'], 2)))],
        ['Ratio Avg Win / Avg Loss', round(data['ratio_avg_win_loss'], 2)],
        ['Percent Profitable', str(round(data['win_rate'] * 100)) + '%'],
        ['Longs | Shorts', '{}% | {}%'.format(round(data['longs_percentage']), round(data['short_percentage']))],
        ['Avg Holding Time', jh.readable_duration(data['average_holding_period'], 3)],
        ['Winning Trades Avg Holding Time',
         np.nan if np.isnan(data['average_winning_holding_period']) else jh.readable_duration(
             data['average_winning_holding_period'], 3)],
        ['Losing Trades Avg Holding Time',
         np.nan if np.isnan(data['average_losing_holding_period']) else jh.readable_duration(
             data['average_losing_holding_period'], 3)]
    ]

    if jh.get_config('env.metrics.sharpe_ratio', True):
        metrics.append(['Sharpe Ratio', round(data['sharpe_ratio'], 2)])
    if jh.get_config('env.metrics.calmar_ratio', False):
        metrics.append(['Calmar Ratio', round(data['calmar_ratio'], 2)])
    if jh.get_config('env.metrics.sortino_ratio', False):
        metrics.append(['Sortino Ratio', round(data['sortino_ratio'], 2)])
    if jh.get_config('env.metrics.omega_ratio', False):
        metrics.append(['Omega Ratio', round(data['omega_ratio'], 2)])
    if jh.get_config('env.metrics.winning_streak', False):
        metrics.append(['Winning Streak', data['winning_streak']])
    if jh.get_config('env.metrics.losing_streak', False):
        metrics.append(['Losing Streak', data['losing_streak']])
    if jh.get_config('env.metrics.largest_winning_trade', False):
        metrics.append(['Largest Winning Trade', jh.format_currency(round(data['largest_winning_trade'], 2))])
    if jh.get_config('env.metrics.largest_losing_trade', False):
        metrics.append(['Largest Losing Trade', jh.format_currency(round(data['largest_losing_trade'], 2))])
    if jh.get_config('env.metrics.total_winning_trades', False):
        metrics.append(['Total Winning Trades', data['total_winning_trades']])
    if jh.get_config('env.metrics.total_losing_trades', False):
        metrics.append(['Total Losing Trades', data['total_losing_trades']])

    return metrics
Example #4
0
 def metrics(self) -> dict:
     """
     Returns all the metrics of the strategy.
     """
     if self.index in self._cached_metrics:
         return self._cached_metrics[self.index]
     else:
         self._cached_metrics[self.index] = metrics.trades(
             store.completed_trades.trades, store.app.daily_balance)
         return self._cached_metrics[self.index]
Example #5
0
    def _on_take_profit(self, order: Order) -> None:
        if not jh.should_execute_silently() or jh.is_debugging():
            logger.info("Take-profit order has been executed.")

        self._broadcast('route-take-profit')
        self._execute_cancel()

        # set metrics only after a trade happens and they actually change
        self.metrics = metrics.trades(store.completed_trades.trades,
                                      store.app.daily_balance)

        self.on_take_profit(order)

        self._detect_and_handle_entry_and_exit_modifications()
Example #6
0
    def fitness(self, dna: str) -> tuple:
        hp = jh.dna_to_hp(self.strategy_hp, dna)

        # init candle store
        store.candles.init_storage(5000)
        # inject required TRAINING candles to the candle store

        for num, c in enumerate(config['app']['considering_candles']):
            required_candles.inject_required_candles_to_store(
                self.training_initial_candles[num],
                c[0],
                c[1]
            )

        # run backtest simulation
        simulator(self.training_candles, hp)

        training_data = {'win_rate': None, 'total': None,
                        'net_profit_percentage': None}
        testing_data = {'win_rate': None, 'total': None,
                       'net_profit_percentage': None}

        # TODO: some of these have to be dynamic based on how many days it's trading for like for example "total"
        # I'm guessing we should accept "optimal" total from command line
        if store.completed_trades.count > 5:
            training_data = stats.trades(store.completed_trades.trades, store.app.daily_balance)
            total_effect_rate = log10(training_data['total']) / log10(self.optimal_total)
            total_effect_rate = min(total_effect_rate, 1)
            ratio_config = jh.get_config('env.optimization.ratio', 'sharpe')
            if ratio_config == 'sharpe':
                ratio = training_data['sharpe_ratio']
                ratio_normalized = jh.normalize(ratio, -.5, 5)
            elif ratio_config == 'calmar':
                ratio = training_data['calmar_ratio']
                ratio_normalized = jh.normalize(ratio, -.5, 30)
            elif ratio_config == 'sortino':
                ratio = training_data['sortino_ratio']
                ratio_normalized = jh.normalize(ratio, -.5, 15)
            elif ratio_config == 'omega':
                ratio = training_data['omega_ratio']
                ratio_normalized = jh.normalize(ratio, -.5, 5)
            elif ratio_config == 'serenity':
                ratio = training_data['serenity_index']
                ratio_normalized = jh.normalize(ratio, -.5, 15)
            elif ratio_config == 'smart sharpe':
                ratio = training_data['smart_sharpe']
                ratio_normalized = jh.normalize(ratio, -.5, 5)
            elif ratio_config == 'smart sortino':
                ratio = training_data['smart_sortino']
                ratio_normalized = jh.normalize(ratio, -.5, 15)
            else:
                raise ValueError(
                    f'The entered ratio configuration `{ratio_config}` for the optimization is unknown. Choose between sharpe, calmar, sortino, serenity, smart shapre, smart sortino and omega.')

            if ratio < 0:
                score = 0.0001
                # reset store
                store.reset()
                return score, training_data, testing_data

            score = total_effect_rate * ratio_normalized

            # perform backtest with testing data. this is using data
            # model hasn't trained for. if it works well, there is
            # high change it will do good with future data too.
            store.reset()
            store.candles.init_storage(5000)
            # inject required TESTING candles to the candle store

            for num, c in enumerate(config['app']['considering_candles']):
                required_candles.inject_required_candles_to_store(
                    self.testing_initial_candles[num],
                    c[0],
                    c[1]
                )

            # run backtest simulation
            simulator(self.testing_candles, hp)

            # log for debugging/monitoring
            if store.completed_trades.count > 0:
                testing_data = stats.trades(store.completed_trades.trades, store.app.daily_balance)

        else:
            score = 0.0001

        # reset store
        store.reset()

        return score, training_data, testing_data
Example #7
0
 def metrics(self) -> dict:
     """
     Returns all the metrics of the strategy.
     """
     return metrics.trades(store.completed_trades.trades,
                           store.app.daily_balance)
Example #8
0
def portfolio_metrics() -> dict:
    return stats.trades(store.completed_trades.trades, store.app.daily_balance)
Example #9
0
def _isolated_backtest(config: dict,
                       routes: List[Dict[str, str]],
                       extra_routes: List[Dict[str, str]],
                       candles: dict,
                       run_silently: bool = True,
                       hyperparameters: dict = None) -> dict:
    from jesse.services.validators import validate_routes
    from jesse.modes.backtest_mode import simulator
    from jesse.config import config as jesse_config, reset_config
    from jesse.routes import router
    from jesse.store import store
    from jesse.config import set_config
    from jesse.services import metrics
    from jesse.services import required_candles
    import jesse.helpers as jh

    jesse_config['app']['trading_mode'] = 'backtest'

    # inject (formatted) configuration values
    set_config(_format_config(config))

    # set routes
    router.initiate(routes, extra_routes)

    # register_custom_exception_handler()

    validate_routes(router)
    # TODO: further validate routes and allow only one exchange
    # TODO: validate the name of the exchange in the config and the route? or maybe to make sure it's a supported exchange

    # initiate candle store
    store.candles.init_storage(5000)

    # divide candles into warm_up_candles and trading_candles and then inject warm_up_candles

    max_timeframe = jh.max_timeframe(
        jesse_config['app']['considering_timeframes'])
    warm_up_num = config['warm_up_candles'] * jh.timeframe_to_one_minutes(
        max_timeframe)
    trading_candles = candles
    if warm_up_num != 0:
        for c in jesse_config['app']['considering_candles']:
            key = jh.key(c[0], c[1])
            # update trading_candles
            trading_candles[key]['candles'] = candles[key]['candles'][
                warm_up_num:]
            # inject warm-up candles
            required_candles.inject_required_candles_to_store(
                candles[key]['candles'][:warm_up_num], c[0], c[1])

    # run backtest simulation
    simulator(trading_candles, run_silently, hyperparameters)

    result = {
        'metrics': {
            'total': 0,
            'win_rate': 0,
            'net_profit_percentage': 0
        },
        'charts': None,
        'logs': None,
    }
    if store.completed_trades.count > 0:
        # add metrics
        result['metrics'] = metrics.trades(store.completed_trades.trades,
                                           store.app.daily_balance)
        # add charts
        result['charts'] = charts.portfolio_vs_asset_returns()
        # add logs
        result['logs'] = store.logs.info

    # reset store and config so rerunning would be flawlessly possible
    reset_config()
    store.reset()

    return result