implement backtest subcommand

This commit is contained in:
gcarq
2017-11-14 22:15:24 +01:00
parent 77887d6fbc
commit bb4a9ed20f
4 changed files with 94 additions and 14 deletions

View File

@@ -3,6 +3,7 @@ import json
from datetime import datetime
from unittest.mock import MagicMock
import os
import pytest
from jsonschema import validate
from telegram import Message, Chat, Update
@@ -67,11 +68,12 @@ def backtest_conf():
@pytest.fixture(scope="module")
def backdata():
path = os.path.abspath(os.path.dirname(__file__))
result = {}
for pair in ['btc-neo', 'btc-eth', 'btc-omg', 'btc-edg', 'btc-pay',
'btc-pivx', 'btc-qtum', 'btc-mtl', 'btc-etc', 'btc-ltc']:
with open('freqtrade/tests/testdata/' + pair + '.json') as data_file:
result[pair] = json.load(data_file)
with open('{abspath}/testdata/{pair}.json'.format(abspath=path, pair=pair)) as fp:
result[pair] = json.load(fp)
return result

View File

@@ -1,10 +1,13 @@
# pragma pylint: disable=missing-docstring
from typing import Dict
import json
import logging
import os
from typing import Tuple, Dict
import pytest
import arrow
import pytest
from arrow import Arrow
from pandas import DataFrame
from freqtrade import exchange
@@ -14,15 +17,20 @@ from freqtrade.exchange import Bittrex
from freqtrade.main import min_roi_reached
from freqtrade.persistence import Trade
logging.disable(logging.DEBUG) # disable debug logs that slow backtesting a lot
logger = logging.getLogger(__name__)
def format_results(results):
return 'Made {} buys. Average profit {:.2f}%. Total profit was {:.3f}. Average duration {:.1f} mins.'.format(
len(results.index), results.profit.mean() * 100.0, results.profit.sum(), results.duration.mean() * 5)
def format_results(results: DataFrame):
return 'Made {} buys. Average profit {:.2f}%. ' \
'Total profit was {:.3f}. Average duration {:.1f} mins.'.format(
len(results.index),
results.profit.mean() * 100.0,
results.profit.sum(),
results.duration.mean() * 5,
)
def print_pair_results(pair, results):
def print_pair_results(pair: str, results: DataFrame):
print('For currency {}:'.format(pair))
print(format_results(results[results.currency == pair]))
@@ -34,11 +42,21 @@ def preprocess(backdata) -> Dict[str, DataFrame]:
return processed
def get_timeframe(backdata: Dict[str, Dict]) -> Tuple[Arrow, Arrow]:
min_date, max_date = None, None
for values in backdata.values():
values = sorted(values, key=lambda d: arrow.get(d['T']))
if not min_date or values[0]['T'] < min_date:
min_date = values[0]['T']
if not max_date or values[-1]['T'] > max_date:
max_date = values[-1]['T']
return arrow.get(min_date), arrow.get(max_date)
def backtest(backtest_conf, processed, mocker):
trades = []
exchange._API = Bittrex({'key': '', 'secret': ''})
mocker.patch.dict('freqtrade.main._CONF', backtest_conf)
mocker.patch('arrow.utcnow', return_value=arrow.get('2017-08-20T14:50:00'))
for pair, pair_data in processed.items():
pair_data['buy'] = 0
pair_data['sell'] = 0
@@ -62,12 +80,35 @@ def backtest(backtest_conf, processed, mocker):
return DataFrame.from_records(trades, columns=labels)
@pytest.mark.skipif(not os.environ.get('BACKTEST', False), reason="BACKTEST not set")
def test_backtest(backtest_conf, backdata, mocker, report=True):
results = backtest(backtest_conf, preprocess(backdata), mocker)
@pytest.mark.skipif(not os.environ.get('BACKTEST'), reason="BACKTEST not set")
def test_backtest(backtest_conf, backdata, mocker):
print('')
config = None
conf_path = os.environ.get('BACKTEST_CONFIG')
if conf_path:
print('Using config: {} ...'.format(conf_path))
with open(conf_path, 'r') as fp:
config = json.load(fp)
livedata = {}
if os.environ.get('BACKTEST_LIVE'):
print('Downloading data for all pairs in whitelist ...'.format(conf_path))
exchange._API = Bittrex({'key': '', 'secret': ''})
for pair in config['exchange']['pair_whitelist']:
livedata[pair] = exchange.get_ticker_history(pair)
config = config or backtest_conf
data = livedata or backdata
min_date, max_date = get_timeframe(data)
print('Measuring data from {} up to {} ...'.format(
min_date.isoformat(), max_date.isoformat()
))
results = backtest(config, preprocess(data), mocker)
print('====================== BACKTESTING REPORT ================================')
for pair in backdata:
for pair in data:
print_pair_results(pair, results)
print('TOTAL OVER ALL TRADES:')
print(format_results(results))