Ejemplo n.º 1
0
 def _rpc_show_config(config, botstate: Union[State,
                                              str]) -> Dict[str, Any]:
     """
     Return a dict of config options.
     Explicitly does NOT return the full config to avoid leakage of sensitive
     information via rpc.
     """
     val = {
         'dry_run':
         config['dry_run'],
         'stake_currency':
         config['stake_currency'],
         'stake_currency_decimals':
         decimals_per_coin(config['stake_currency']),
         'stake_amount':
         config['stake_amount'],
         'max_open_trades':
         (config['max_open_trades']
          if config['max_open_trades'] != float('inf') else -1),
         'minimal_roi':
         config['minimal_roi'].copy() if 'minimal_roi' in config else {},
         'stoploss':
         config.get('stoploss'),
         'trailing_stop':
         config.get('trailing_stop'),
         'trailing_stop_positive':
         config.get('trailing_stop_positive'),
         'trailing_stop_positive_offset':
         config.get('trailing_stop_positive_offset'),
         'trailing_only_offset_is_reached':
         config.get('trailing_only_offset_is_reached'),
         'use_custom_stoploss':
         config.get('use_custom_stoploss'),
         'bot_name':
         config.get('bot_name', 'freqtrade'),
         'timeframe':
         config.get('timeframe'),
         'timeframe_ms':
         timeframe_to_msecs(config['timeframe'])
         if 'timeframe' in config else '',
         'timeframe_min':
         timeframe_to_minutes(config['timeframe'])
         if 'timeframe' in config else '',
         'exchange':
         config['exchange']['name'],
         'strategy':
         config['strategy'],
         'forcebuy_enabled':
         config.get('forcebuy_enable', False),
         'ask_strategy':
         config.get('ask_strategy', {}),
         'bid_strategy':
         config.get('bid_strategy', {}),
         'state':
         str(botstate),
         'runmode':
         config['runmode'].value
     }
     return val
Ejemplo n.º 2
0
 def _rpc_show_config(config, botstate: Union[State, str],
                      strategy_version: Optional[str] = None) -> Dict[str, Any]:
     """
     Return a dict of config options.
     Explicitly does NOT return the full config to avoid leakage of sensitive
     information via rpc.
     """
     val = {
         'version': __version__,
         'strategy_version': strategy_version,
         'dry_run': config['dry_run'],
         'trading_mode': config.get('trading_mode', 'spot'),
         'short_allowed': config.get('trading_mode', 'spot') != 'spot',
         'stake_currency': config['stake_currency'],
         'stake_currency_decimals': decimals_per_coin(config['stake_currency']),
         'stake_amount': str(config['stake_amount']),
         'available_capital': config.get('available_capital'),
         'max_open_trades': (config['max_open_trades']
                             if config['max_open_trades'] != float('inf') else -1),
         'minimal_roi': config['minimal_roi'].copy() if 'minimal_roi' in config else {},
         'stoploss': config.get('stoploss'),
         'trailing_stop': config.get('trailing_stop'),
         'trailing_stop_positive': config.get('trailing_stop_positive'),
         'trailing_stop_positive_offset': config.get('trailing_stop_positive_offset'),
         'trailing_only_offset_is_reached': config.get('trailing_only_offset_is_reached'),
         'unfilledtimeout': config.get('unfilledtimeout'),
         'use_custom_stoploss': config.get('use_custom_stoploss'),
         'order_types': config.get('order_types'),
         'bot_name': config.get('bot_name', 'freqtrade'),
         'timeframe': config.get('timeframe'),
         'timeframe_ms': timeframe_to_msecs(config['timeframe']
                                            ) if 'timeframe' in config else 0,
         'timeframe_min': timeframe_to_minutes(config['timeframe']
                                               ) if 'timeframe' in config else 0,
         'exchange': config['exchange']['name'],
         'strategy': config['strategy'],
         'force_entry_enable': config.get('force_entry_enable', False),
         'exit_pricing': config.get('exit_pricing', {}),
         'entry_pricing': config.get('entry_pricing', {}),
         'state': str(botstate),
         'runmode': config['runmode'].value,
         'position_adjustment_enable': config.get('position_adjustment_enable', False),
         'max_entry_position_adjustment': (
             config.get('max_entry_position_adjustment', -1)
             if config.get('max_entry_position_adjustment') != float('inf')
             else -1)
     }
     return val
Ejemplo n.º 3
0
def generate_strategy_stats(pairlist: List[str], strategy: str,
                            content: Dict[str, Any], min_date: datetime,
                            max_date: datetime,
                            market_change: float) -> Dict[str, Any]:
    """
    :param pairlist: List of pairs to backtest
    :param strategy: Strategy name
    :param content: Backtest result data in the format:
                    {'results: results, 'config: config}}.
    :param min_date: Backtest start date
    :param max_date: Backtest end date
    :param market_change: float indicating the market change
    :return: Dictionary containing results per strategy and a strategy summary.
    """
    results: Dict[str, DataFrame] = content['results']
    if not isinstance(results, DataFrame):
        return {}
    config = content['config']
    max_open_trades = min(config['max_open_trades'], len(pairlist))
    start_balance = config['dry_run_wallet']
    stake_currency = config['stake_currency']

    pair_results = generate_pair_metrics(pairlist,
                                         stake_currency=stake_currency,
                                         starting_balance=start_balance,
                                         results=results,
                                         skip_nan=False)

    enter_tag_results = generate_tag_metrics("enter_tag",
                                             starting_balance=start_balance,
                                             results=results,
                                             skip_nan=False)

    exit_reason_stats = generate_exit_reason_stats(
        max_open_trades=max_open_trades, results=results)
    left_open_results = generate_pair_metrics(
        pairlist,
        stake_currency=stake_currency,
        starting_balance=start_balance,
        results=results.loc[results['is_open']],
        skip_nan=True)
    daily_stats = generate_daily_stats(results)
    trade_stats = generate_trading_stats(results)
    best_pair = max(
        [pair for pair in pair_results if pair['key'] != 'TOTAL'],
        key=lambda x: x['profit_sum']) if len(pair_results) > 1 else None
    worst_pair = min(
        [pair for pair in pair_results if pair['key'] != 'TOTAL'],
        key=lambda x: x['profit_sum']) if len(pair_results) > 1 else None
    if not results.empty:
        results['open_timestamp'] = results['open_date'].view(int64) // 1e6
        results['close_timestamp'] = results['close_date'].view(int64) // 1e6

    backtest_days = (max_date - min_date).days or 1
    strat_stats = {
        'trades':
        results.to_dict(orient='records'),
        'locks': [lock.to_json() for lock in content['locks']],
        'best_pair':
        best_pair,
        'worst_pair':
        worst_pair,
        'results_per_pair':
        pair_results,
        'results_per_enter_tag':
        enter_tag_results,
        'exit_reason_summary':
        exit_reason_stats,
        'left_open_trades':
        left_open_results,
        # 'days_breakdown_stats': days_breakdown_stats,
        'total_trades':
        len(results),
        'trade_count_long':
        len(results.loc[~results['is_short']]),
        'trade_count_short':
        len(results.loc[results['is_short']]),
        'total_volume':
        float(results['stake_amount'].sum()),
        'avg_stake_amount':
        results['stake_amount'].mean() if len(results) > 0 else 0,
        'profit_mean':
        results['profit_ratio'].mean() if len(results) > 0 else 0,
        'profit_median':
        results['profit_ratio'].median() if len(results) > 0 else 0,
        'profit_total':
        results['profit_abs'].sum() / start_balance,
        'profit_total_long':
        results.loc[~results['is_short'], 'profit_abs'].sum() / start_balance,
        'profit_total_short':
        results.loc[results['is_short'], 'profit_abs'].sum() / start_balance,
        'profit_total_abs':
        results['profit_abs'].sum(),
        'profit_total_long_abs':
        results.loc[~results['is_short'], 'profit_abs'].sum(),
        'profit_total_short_abs':
        results.loc[results['is_short'], 'profit_abs'].sum(),
        'backtest_start':
        min_date.strftime(DATETIME_PRINT_FORMAT),
        'backtest_start_ts':
        int(min_date.timestamp() * 1000),
        'backtest_end':
        max_date.strftime(DATETIME_PRINT_FORMAT),
        'backtest_end_ts':
        int(max_date.timestamp() * 1000),
        'backtest_days':
        backtest_days,
        'backtest_run_start_ts':
        content['backtest_start_time'],
        'backtest_run_end_ts':
        content['backtest_end_time'],
        'trades_per_day':
        round(len(results) / backtest_days, 2),
        'market_change':
        market_change,
        'pairlist':
        pairlist,
        'stake_amount':
        config['stake_amount'],
        'stake_currency':
        config['stake_currency'],
        'stake_currency_decimals':
        decimals_per_coin(config['stake_currency']),
        'starting_balance':
        start_balance,
        'dry_run_wallet':
        start_balance,
        'final_balance':
        content['final_balance'],
        'rejected_signals':
        content['rejected_signals'],
        'timedout_entry_orders':
        content['timedout_entry_orders'],
        'timedout_exit_orders':
        content['timedout_exit_orders'],
        'max_open_trades':
        max_open_trades,
        'max_open_trades_setting':
        (config['max_open_trades']
         if config['max_open_trades'] != float('inf') else -1),
        'timeframe':
        config['timeframe'],
        'timeframe_detail':
        config.get('timeframe_detail', ''),
        'timerange':
        config.get('timerange', ''),
        'enable_protections':
        config.get('enable_protections', False),
        'strategy_name':
        strategy,
        # Parameters relevant for backtesting
        'stoploss':
        config['stoploss'],
        'trailing_stop':
        config.get('trailing_stop', False),
        'trailing_stop_positive':
        config.get('trailing_stop_positive'),
        'trailing_stop_positive_offset':
        config.get('trailing_stop_positive_offset', 0.0),
        'trailing_only_offset_is_reached':
        config.get('trailing_only_offset_is_reached', False),
        'use_custom_stoploss':
        config.get('use_custom_stoploss', False),
        'minimal_roi':
        config['minimal_roi'],
        'use_exit_signal':
        config['use_exit_signal'],
        'exit_profit_only':
        config['exit_profit_only'],
        'exit_profit_offset':
        config['exit_profit_offset'],
        'ignore_roi_if_entry_signal':
        config['ignore_roi_if_entry_signal'],
        **daily_stats,
        **trade_stats
    }

    try:
        max_drawdown_legacy, _, _, _, _, _ = calculate_max_drawdown(
            results, value_col='profit_ratio')
        (drawdown_abs, drawdown_start, drawdown_end, high_val, low_val,
         max_drawdown) = calculate_max_drawdown(results,
                                                value_col='profit_abs',
                                                starting_balance=start_balance)
        strat_stats.update({
            'max_drawdown':
            max_drawdown_legacy,  # Deprecated - do not use
            'max_drawdown_account':
            max_drawdown,
            'max_drawdown_abs':
            drawdown_abs,
            'drawdown_start':
            drawdown_start.strftime(DATETIME_PRINT_FORMAT),
            'drawdown_start_ts':
            drawdown_start.timestamp() * 1000,
            'drawdown_end':
            drawdown_end.strftime(DATETIME_PRINT_FORMAT),
            'drawdown_end_ts':
            drawdown_end.timestamp() * 1000,
            'max_drawdown_low':
            low_val,
            'max_drawdown_high':
            high_val,
        })

        csum_min, csum_max = calculate_csum(results, start_balance)
        strat_stats.update({'csum_min': csum_min, 'csum_max': csum_max})

    except ValueError:
        strat_stats.update({
            'max_drawdown':
            0.0,
            'max_drawdown_account':
            0.0,
            'max_drawdown_abs':
            0.0,
            'max_drawdown_low':
            0.0,
            'max_drawdown_high':
            0.0,
            'drawdown_start':
            datetime(1970, 1, 1, tzinfo=timezone.utc),
            'drawdown_start_ts':
            0,
            'drawdown_end':
            datetime(1970, 1, 1, tzinfo=timezone.utc),
            'drawdown_end_ts':
            0,
            'csum_min':
            0,
            'csum_max':
            0
        })

    return strat_stats
Ejemplo n.º 4
0
def test_decimals_per_coin():
    assert decimals_per_coin('USDT') == 3
    assert decimals_per_coin('EUR') == 3
    assert decimals_per_coin('BTC') == 8
    assert decimals_per_coin('ETH') == 5
Ejemplo n.º 5
0
def generate_backtest_stats(btdata: Dict[str, DataFrame],
                            all_results: Dict[str, Dict[str, Union[DataFrame,
                                                                   Dict]]],
                            min_date: Arrow,
                            max_date: Arrow) -> Dict[str, Any]:
    """
    :param btdata: Backtest data
    :param all_results: backtest result - dictionary in the form:
                     { Strategy: {'results: results, 'config: config}}.
    :param min_date: Backtest start date
    :param max_date: Backtest end date
    :return:
    Dictionary containing results per strategy and a stratgy summary.
    """
    result: Dict[str, Any] = {'strategy': {}}
    market_change = calculate_market_change(btdata, 'close')

    for strategy, content in all_results.items():
        results: Dict[str, DataFrame] = content['results']
        if not isinstance(results, DataFrame):
            continue
        config = content['config']
        max_open_trades = min(config['max_open_trades'], len(btdata.keys()))
        starting_balance = config['dry_run_wallet']
        stake_currency = config['stake_currency']

        pair_results = generate_pair_metrics(btdata,
                                             stake_currency=stake_currency,
                                             starting_balance=starting_balance,
                                             results=results,
                                             skip_nan=False)
        sell_reason_stats = generate_sell_reason_stats(
            max_open_trades=max_open_trades, results=results)
        left_open_results = generate_pair_metrics(
            btdata,
            stake_currency=stake_currency,
            starting_balance=starting_balance,
            results=results.loc[results['is_open']],
            skip_nan=True)
        daily_stats = generate_daily_stats(results)
        best_pair = max(
            [pair for pair in pair_results if pair['key'] != 'TOTAL'],
            key=lambda x: x['profit_sum']) if len(pair_results) > 1 else None
        worst_pair = min(
            [pair for pair in pair_results if pair['key'] != 'TOTAL'],
            key=lambda x: x['profit_sum']) if len(pair_results) > 1 else None
        results['open_timestamp'] = results['open_date'].astype(int64) // 1e6
        results['close_timestamp'] = results['close_date'].astype(int64) // 1e6

        backtest_days = (max_date - min_date).days
        strat_stats = {
            'trades':
            results.to_dict(orient='records'),
            'locks': [lock.to_json() for lock in content['locks']],
            'best_pair':
            best_pair,
            'worst_pair':
            worst_pair,
            'results_per_pair':
            pair_results,
            'sell_reason_summary':
            sell_reason_stats,
            'left_open_trades':
            left_open_results,
            'total_trades':
            len(results),
            'total_volume':
            float(results['stake_amount'].sum()),
            'avg_stake_amount':
            results['stake_amount'].mean() if len(results) > 0 else 0,
            'profit_mean':
            results['profit_ratio'].mean() if len(results) > 0 else 0,
            'profit_total':
            results['profit_abs'].sum() / starting_balance,
            'profit_total_abs':
            results['profit_abs'].sum(),
            'backtest_start':
            min_date.datetime,
            'backtest_start_ts':
            min_date.int_timestamp * 1000,
            'backtest_end':
            max_date.datetime,
            'backtest_end_ts':
            max_date.int_timestamp * 1000,
            'backtest_days':
            backtest_days,
            'backtest_run_start_ts':
            content['backtest_start_time'],
            'backtest_run_end_ts':
            content['backtest_end_time'],
            'trades_per_day':
            round(len(results) / backtest_days, 2) if backtest_days > 0 else 0,
            'market_change':
            market_change,
            'pairlist':
            list(btdata.keys()),
            'stake_amount':
            config['stake_amount'],
            'stake_currency':
            config['stake_currency'],
            'stake_currency_decimals':
            decimals_per_coin(config['stake_currency']),
            'starting_balance':
            starting_balance,
            'dry_run_wallet':
            starting_balance,
            'final_balance':
            content['final_balance'],
            'max_open_trades':
            max_open_trades,
            'max_open_trades_setting':
            (config['max_open_trades']
             if config['max_open_trades'] != float('inf') else -1),
            'timeframe':
            config['timeframe'],
            'timerange':
            config.get('timerange', ''),
            'enable_protections':
            config.get('enable_protections', False),
            'strategy_name':
            strategy,
            # Parameters relevant for backtesting
            'stoploss':
            config['stoploss'],
            'trailing_stop':
            config.get('trailing_stop', False),
            'trailing_stop_positive':
            config.get('trailing_stop_positive'),
            'trailing_stop_positive_offset':
            config.get('trailing_stop_positive_offset', 0.0),
            'trailing_only_offset_is_reached':
            config.get('trailing_only_offset_is_reached', False),
            'use_custom_stoploss':
            config.get('use_custom_stoploss', False),
            'minimal_roi':
            config['minimal_roi'],
            'use_sell_signal':
            config['ask_strategy']['use_sell_signal'],
            'sell_profit_only':
            config['ask_strategy']['sell_profit_only'],
            'sell_profit_offset':
            config['ask_strategy']['sell_profit_offset'],
            'ignore_roi_if_buy_signal':
            config['ask_strategy']['ignore_roi_if_buy_signal'],
            **daily_stats,
        }
        result['strategy'][strategy] = strat_stats

        try:
            max_drawdown, _, _, _, _ = calculate_max_drawdown(
                results, value_col='profit_ratio')
            drawdown_abs, drawdown_start, drawdown_end, high_val, low_val = calculate_max_drawdown(
                results, value_col='profit_abs')
            strat_stats.update({
                'max_drawdown':
                max_drawdown,
                'max_drawdown_abs':
                drawdown_abs,
                'drawdown_start':
                drawdown_start,
                'drawdown_start_ts':
                drawdown_start.timestamp() * 1000,
                'drawdown_end':
                drawdown_end,
                'drawdown_end_ts':
                drawdown_end.timestamp() * 1000,
                'max_drawdown_low':
                low_val,
                'max_drawdown_high':
                high_val,
            })

            csum_min, csum_max = calculate_csum(results, starting_balance)
            strat_stats.update({'csum_min': csum_min, 'csum_max': csum_max})

        except ValueError:
            strat_stats.update({
                'max_drawdown':
                0.0,
                'max_drawdown_abs':
                0.0,
                'max_drawdown_low':
                0.0,
                'max_drawdown_high':
                0.0,
                'drawdown_start':
                datetime(1970, 1, 1, tzinfo=timezone.utc),
                'drawdown_start_ts':
                0,
                'drawdown_end':
                datetime(1970, 1, 1, tzinfo=timezone.utc),
                'drawdown_end_ts':
                0,
                'csum_min':
                0,
                'csum_max':
                0
            })

    strategy_results = generate_strategy_metrics(all_results=all_results)

    result['strategy_comparison'] = strategy_results

    return result