Merge branch 'develop' of https://github.com/gcarq/freqtrade into pair_blacklist
This commit is contained in:
commit
1adbeb5f06
@ -179,18 +179,21 @@ def handle_trade(trade: Trade) -> bool:
|
||||
current_rate = exchange.get_ticker(trade.pair)['bid']
|
||||
|
||||
# Check if minimal roi has been reached
|
||||
if not min_roi_reached(trade, current_rate, datetime.utcnow()):
|
||||
return False
|
||||
if min_roi_reached(trade, current_rate, datetime.utcnow()):
|
||||
logger.debug('Executing sell due to ROI ...')
|
||||
execute_sell(trade, current_rate)
|
||||
return True
|
||||
|
||||
# Check if sell signal has been enabled and triggered
|
||||
if _CONF.get('experimental', {}).get('use_sell_signal'):
|
||||
logger.debug('Checking sell_signal ...')
|
||||
if not get_signal(trade.pair, SignalType.SELL):
|
||||
return False
|
||||
|
||||
if get_signal(trade.pair, SignalType.SELL):
|
||||
logger.debug('Executing sell due to sell signal ...')
|
||||
execute_sell(trade, current_rate)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_target_bid(ticker: Dict[str, float]) -> float:
|
||||
""" Calculates bid target between current ask price and last price """
|
||||
|
@ -4,11 +4,9 @@ import logging
|
||||
import json
|
||||
import os
|
||||
from typing import Optional, List, Dict
|
||||
from pandas import DataFrame
|
||||
from freqtrade.exchange import get_ticker_history
|
||||
from freqtrade.optimize.hyperopt_conf import hyperopt_optimize_conf
|
||||
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.analyze import populate_indicators, parse_ticker_dataframe
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -50,10 +48,8 @@ def load_data(ticker_interval: int = 5, pairs: Optional[List[str]] = None,
|
||||
|
||||
def preprocess(tickerdata: Dict[str, List]) -> Dict[str, DataFrame]:
|
||||
"""Creates a dataframe and populates indicators for given ticker data"""
|
||||
processed = {}
|
||||
for pair, pair_data in tickerdata.items():
|
||||
processed[pair] = populate_indicators(parse_ticker_dataframe(pair_data))
|
||||
return processed
|
||||
return {pair: populate_indicators(parse_ticker_dataframe(pair_data))
|
||||
for pair, pair_data in tickerdata.items()}
|
||||
|
||||
|
||||
def testdata_path() -> str:
|
||||
@ -91,17 +87,17 @@ def download_backtesting_testdata(pair: str, interval: int = 5) -> bool:
|
||||
))
|
||||
|
||||
filepair = pair.replace("-", "_")
|
||||
filename = os.path.join(path, '{}-{}.json'.format(
|
||||
filepair,
|
||||
interval,
|
||||
filename = os.path.join(path, '{pair}-{interval}.json'.format(
|
||||
pair=filepair,
|
||||
interval=interval,
|
||||
))
|
||||
filename = filename.replace('USDT_BTC', 'BTC_FAKEBULL')
|
||||
|
||||
if os.path.isfile(filename):
|
||||
with open(filename, "rt") as fp:
|
||||
data = json.load(fp)
|
||||
logger.debug("Current Start:", data[1]['T'])
|
||||
logger.debug("Current End: ", data[-1:][0]['T'])
|
||||
logger.debug("Current Start: {}".format(data[1]['T']))
|
||||
logger.debug("Current End: {}".format(data[-1:][0]['T']))
|
||||
else:
|
||||
data = []
|
||||
logger.debug("Current Start: None")
|
||||
@ -111,8 +107,8 @@ def download_backtesting_testdata(pair: str, interval: int = 5) -> bool:
|
||||
for row in new_data:
|
||||
if row not in data:
|
||||
data.append(row)
|
||||
logger.debug("New Start:", data[1]['T'])
|
||||
logger.debug("New End: ", data[-1:][0]['T'])
|
||||
logger.debug("New Start: {}".format(data[1]['T']))
|
||||
logger.debug("New End: {}".format(data[-1:][0]['T']))
|
||||
data = sorted(data, key=lambda data: data['T'])
|
||||
|
||||
with open(filename, "wt") as fp:
|
||||
|
@ -111,14 +111,14 @@ def backtest(stake_amount: float, processed: Dict[str, DataFrame],
|
||||
|
||||
if min_roi_reached(trade, row2.close, row2.date) or row2.sell == 1:
|
||||
current_profit_percent = trade.calc_profit_percent(rate=row2.close)
|
||||
current_profit_BTC = trade.calc_profit(rate=row2.close)
|
||||
current_profit_btc = trade.calc_profit(rate=row2.close)
|
||||
lock_pair_until = row2.Index
|
||||
|
||||
trades.append(
|
||||
(
|
||||
pair,
|
||||
current_profit_percent,
|
||||
current_profit_BTC,
|
||||
current_profit_btc,
|
||||
row2.Index - row.Index
|
||||
)
|
||||
)
|
||||
|
@ -25,12 +25,10 @@ logging.getLogger('hyperopt.tpe').setLevel(logging.WARNING)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# set TARGET_TRADES to suit your number concurrent trades so its realistic to 20days of data
|
||||
TARGET_TRADES = 1100
|
||||
TOTAL_TRIES = None
|
||||
_CURRENT_TRIES = 0
|
||||
|
||||
CURRENT_BEST_LOSS = 100
|
||||
|
||||
# this is expexted avg profit * expected trade count
|
||||
@ -111,6 +109,13 @@ def log_results(results):
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def calculate_loss(total_profit: float, trade_count: int):
|
||||
""" objective function, returns smaller number for more optimal results """
|
||||
trade_loss = 1 - 0.35 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.2)
|
||||
profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT)
|
||||
return trade_loss + profit_loss
|
||||
|
||||
|
||||
def optimizer(params):
|
||||
global _CURRENT_TRIES
|
||||
|
||||
@ -118,37 +123,33 @@ def optimizer(params):
|
||||
backtesting.populate_buy_trend = buy_strategy_generator(params)
|
||||
|
||||
results = backtest(OPTIMIZE_CONFIG['stake_amount'], PROCESSED)
|
||||
|
||||
result = format_results(results)
|
||||
result_explanation = format_results(results)
|
||||
|
||||
total_profit = results.profit_percent.sum()
|
||||
trade_count = len(results.index)
|
||||
|
||||
if trade_count == 0:
|
||||
print('.', end='')
|
||||
return {
|
||||
'status': STATUS_FAIL,
|
||||
'loss': float('inf')
|
||||
}
|
||||
|
||||
trade_loss = 1 - 0.35 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.2)
|
||||
profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT)
|
||||
loss = trade_loss + profit_loss
|
||||
loss = calculate_loss(total_profit, trade_count)
|
||||
|
||||
_CURRENT_TRIES += 1
|
||||
|
||||
result_data = {
|
||||
log_results({
|
||||
'loss': loss,
|
||||
'current_tries': _CURRENT_TRIES,
|
||||
'total_tries': TOTAL_TRIES,
|
||||
'result': result,
|
||||
}
|
||||
log_results(result_data)
|
||||
'result': result_explanation,
|
||||
})
|
||||
|
||||
return {
|
||||
'loss': loss,
|
||||
'status': STATUS_OK,
|
||||
'result': result,
|
||||
'total_profit': total_profit,
|
||||
'avg_profit': results.profit_percent.mean() * 100.0,
|
||||
'result': result_explanation,
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
import logging
|
||||
import re
|
||||
from decimal import Decimal
|
||||
from datetime import timedelta, date, datetime
|
||||
from datetime import timedelta, datetime
|
||||
from typing import Callable, Any
|
||||
|
||||
import arrow
|
||||
from pandas import DataFrame
|
||||
from sqlalchemy import and_, func, text, between
|
||||
from sqlalchemy import and_, func, text
|
||||
from tabulate import tabulate
|
||||
from telegram import ParseMode, Bot, Update, ReplyKeyboardMarkup
|
||||
from telegram.error import NetworkError, TelegramError
|
||||
@ -220,29 +220,28 @@ def _daily(bot: Bot, update: Update) -> None:
|
||||
:param update: message update
|
||||
:return: None
|
||||
"""
|
||||
today = datetime.utcnow().toordinal()
|
||||
today = datetime.utcnow().date()
|
||||
profit_days = {}
|
||||
|
||||
try:
|
||||
timescale = int(update.message.text.replace('/daily', '').strip())
|
||||
except (TypeError, ValueError):
|
||||
timescale = 5
|
||||
timescale = 7
|
||||
|
||||
if not (isinstance(timescale, int) and timescale > 0):
|
||||
send_msg('*Daily [n]:* `must be an integer greater than 0`', bot=bot)
|
||||
return
|
||||
|
||||
for day in range(0, timescale):
|
||||
# need to query between day+1 and day-1
|
||||
nextdate = date.fromordinal(today - day + 1)
|
||||
prevdate = date.fromordinal(today - day - 1)
|
||||
profitday = today - timedelta(days=day)
|
||||
trades = Trade.query \
|
||||
.filter(Trade.is_open.is_(False)) \
|
||||
.filter(between(Trade.close_date, prevdate, nextdate)) \
|
||||
.filter(Trade.close_date >= profitday)\
|
||||
.filter(Trade.close_date < (profitday + timedelta(days=1)))\
|
||||
.order_by(Trade.close_date)\
|
||||
.all()
|
||||
curdayprofit = sum(trade.calc_profit() for trade in trades)
|
||||
profit_days[date.fromordinal(today - day)] = format(curdayprofit, '.8f')
|
||||
profit_days[profitday] = format(curdayprofit, '.8f')
|
||||
|
||||
stats = [
|
||||
[
|
||||
|
48
freqtrade/tests/optimize/test_backtesting.py
Normal file
48
freqtrade/tests/optimize/test_backtesting.py
Normal file
@ -0,0 +1,48 @@
|
||||
# pragma pylint: disable=missing-docstring,W0212
|
||||
|
||||
import pandas as pd
|
||||
from freqtrade import exchange, optimize
|
||||
from freqtrade.exchange import Bittrex
|
||||
from freqtrade.optimize.backtesting import backtest, generate_text_table, get_timeframe
|
||||
|
||||
|
||||
def test_generate_text_table():
|
||||
results = pd.DataFrame(
|
||||
{
|
||||
'currency': ['BTC_ETH', 'BTC_ETH'],
|
||||
'profit_percent': [0.1, 0.2],
|
||||
'profit_BTC': [0.2, 0.4],
|
||||
'duration': [10, 30]
|
||||
}
|
||||
)
|
||||
assert generate_text_table({'BTC_ETH': {}}, results, 'BTC', 5) == (
|
||||
'pair buy count avg profit total profit avg duration\n'
|
||||
'------- ----------- ------------ -------------- --------------\n'
|
||||
'BTC_ETH 2 15.00% 0.60000000 BTC 100\n'
|
||||
'TOTAL 2 15.00% 0.60000000 BTC 100')
|
||||
|
||||
|
||||
def test_get_timeframe():
|
||||
data = optimize.load_data(ticker_interval=1, pairs=['BTC_UNITEST'])
|
||||
min_date, max_date = get_timeframe(data)
|
||||
assert min_date.isoformat() == '2017-11-04T23:02:00+00:00'
|
||||
assert max_date.isoformat() == '2017-11-14T22:59:00+00:00'
|
||||
|
||||
|
||||
def test_backtest(default_conf, mocker):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
data = optimize.load_data(ticker_interval=5, pairs=['BTC_ETH'])
|
||||
results = backtest(default_conf['stake_amount'], optimize.preprocess(data), 10, True)
|
||||
assert not results.empty
|
||||
|
||||
|
||||
def test_backtest_1min_ticker_interval(default_conf, mocker):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
# Run a backtesting for an exiting 5min ticker_interval
|
||||
data = optimize.load_data(ticker_interval=1, pairs=['BTC_UNITEST'])
|
||||
results = backtest(default_conf['stake_amount'], optimize.preprocess(data), 1, True)
|
||||
assert not results.empty
|
79
freqtrade/tests/optimize/test_hyperopt.py
Normal file
79
freqtrade/tests/optimize/test_hyperopt.py
Normal file
@ -0,0 +1,79 @@
|
||||
# pragma pylint: disable=missing-docstring,W0212,C0103
|
||||
|
||||
from freqtrade.optimize.hyperopt import calculate_loss, TARGET_TRADES, EXPECTED_MAX_PROFIT, start, \
|
||||
log_results
|
||||
|
||||
|
||||
def test_loss_calculation_prefer_correct_trade_count():
|
||||
correct = calculate_loss(1, TARGET_TRADES)
|
||||
over = calculate_loss(1, TARGET_TRADES + 100)
|
||||
under = calculate_loss(1, TARGET_TRADES - 100)
|
||||
assert over > correct
|
||||
assert under > correct
|
||||
|
||||
|
||||
def test_loss_calculation_has_limited_profit():
|
||||
correct = calculate_loss(EXPECTED_MAX_PROFIT, TARGET_TRADES)
|
||||
over = calculate_loss(EXPECTED_MAX_PROFIT * 2, TARGET_TRADES)
|
||||
under = calculate_loss(EXPECTED_MAX_PROFIT / 2, TARGET_TRADES)
|
||||
assert over == correct
|
||||
assert under > correct
|
||||
|
||||
|
||||
def create_trials(mocker):
|
||||
return mocker.Mock(
|
||||
results=[{
|
||||
'loss': 1,
|
||||
'result': 'foo'
|
||||
}]
|
||||
)
|
||||
|
||||
|
||||
def test_start_calls_fmin(mocker):
|
||||
mocker.patch('freqtrade.optimize.hyperopt.Trials', return_value=create_trials(mocker))
|
||||
mocker.patch('freqtrade.optimize.preprocess')
|
||||
mocker.patch('freqtrade.optimize.load_data')
|
||||
mock_fmin = mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
|
||||
|
||||
args = mocker.Mock(epochs=1, config='config.json.example', mongodb=False)
|
||||
start(args)
|
||||
|
||||
mock_fmin.assert_called_once()
|
||||
|
||||
|
||||
def test_start_uses_mongotrials(mocker):
|
||||
mock_mongotrials = mocker.patch('freqtrade.optimize.hyperopt.MongoTrials',
|
||||
return_value=create_trials(mocker))
|
||||
mocker.patch('freqtrade.optimize.preprocess')
|
||||
mocker.patch('freqtrade.optimize.load_data')
|
||||
mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
|
||||
|
||||
args = mocker.Mock(epochs=1, config='config.json.example', mongodb=True)
|
||||
start(args)
|
||||
|
||||
mock_mongotrials.assert_called_once()
|
||||
|
||||
|
||||
def test_log_results_if_loss_improves(mocker):
|
||||
logger = mocker.patch('freqtrade.optimize.hyperopt.logger.info')
|
||||
global CURRENT_BEST_LOSS
|
||||
CURRENT_BEST_LOSS = 2
|
||||
log_results({
|
||||
'loss': 1,
|
||||
'current_tries': 1,
|
||||
'total_tries': 2,
|
||||
'result': 'foo'
|
||||
})
|
||||
|
||||
logger.assert_called_once()
|
||||
|
||||
|
||||
def test_no_log_if_loss_does_not_improve(mocker):
|
||||
logger = mocker.patch('freqtrade.optimize.hyperopt.logger.info')
|
||||
global CURRENT_BEST_LOSS
|
||||
CURRENT_BEST_LOSS = 2
|
||||
log_results({
|
||||
'loss': 3,
|
||||
})
|
||||
|
||||
assert not logger.called
|
166
freqtrade/tests/optimize/test_optimize.py
Normal file
166
freqtrade/tests/optimize/test_optimize.py
Normal file
@ -0,0 +1,166 @@
|
||||
# pragma pylint: disable=missing-docstring,W0212
|
||||
|
||||
import os
|
||||
import logging
|
||||
from shutil import copyfile
|
||||
from freqtrade import exchange, optimize
|
||||
from freqtrade.exchange import Bittrex
|
||||
from freqtrade.optimize.__init__ import testdata_path, download_pairs, download_backtesting_testdata
|
||||
|
||||
|
||||
def _backup_file(file: str, copy_file: bool = False) -> None:
|
||||
"""
|
||||
Backup existing file to avoid deleting the user file
|
||||
:param file: complete path to the file
|
||||
:param touch_file: create an empty file in replacement
|
||||
:return: None
|
||||
"""
|
||||
file_swp = file + '.swp'
|
||||
if os.path.isfile(file):
|
||||
os.rename(file, file_swp)
|
||||
|
||||
if copy_file:
|
||||
copyfile(file_swp, file)
|
||||
|
||||
|
||||
def _clean_test_file(file: str) -> None:
|
||||
"""
|
||||
Backup existing file to avoid deleting the user file
|
||||
:param file: complete path to the file
|
||||
:return: None
|
||||
"""
|
||||
file_swp = file + '.swp'
|
||||
# 1. Delete file from the test
|
||||
if os.path.isfile(file):
|
||||
os.remove(file)
|
||||
|
||||
# 2. Rollback to the initial file
|
||||
if os.path.isfile(file_swp):
|
||||
os.rename(file_swp, file)
|
||||
|
||||
|
||||
def test_load_data_5min_ticker(default_conf, ticker_history, mocker, caplog):
|
||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
file = 'freqtrade/tests/testdata/BTC_ETH-5.json'
|
||||
_backup_file(file, copy_file=True)
|
||||
optimize.load_data(pairs=['BTC_ETH'])
|
||||
assert os.path.isfile(file) is True
|
||||
assert ('freqtrade.optimize',
|
||||
logging.INFO,
|
||||
'Download the pair: "BTC_ETH", Interval: 5 min'
|
||||
) not in caplog.record_tuples
|
||||
_clean_test_file(file)
|
||||
|
||||
|
||||
def test_load_data_1min_ticker(default_conf, ticker_history, mocker, caplog):
|
||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
file = 'freqtrade/tests/testdata/BTC_ETH-1.json'
|
||||
_backup_file(file, copy_file=True)
|
||||
optimize.load_data(ticker_interval=1, pairs=['BTC_ETH'])
|
||||
assert os.path.isfile(file) is True
|
||||
assert ('freqtrade.optimize',
|
||||
logging.INFO,
|
||||
'Download the pair: "BTC_ETH", Interval: 1 min'
|
||||
) not in caplog.record_tuples
|
||||
_clean_test_file(file)
|
||||
|
||||
|
||||
def test_load_data_with_new_pair_1min(default_conf, ticker_history, mocker, caplog):
|
||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
file = 'freqtrade/tests/testdata/BTC_MEME-1.json'
|
||||
_backup_file(file)
|
||||
optimize.load_data(ticker_interval=1, pairs=['BTC_MEME'])
|
||||
assert os.path.isfile(file) is True
|
||||
assert ('freqtrade.optimize',
|
||||
logging.INFO,
|
||||
'Download the pair: "BTC_MEME", Interval: 1 min'
|
||||
) in caplog.record_tuples
|
||||
_clean_test_file(file)
|
||||
|
||||
|
||||
def test_testdata_path():
|
||||
assert os.path.join('freqtrade', 'tests', 'testdata') in testdata_path()
|
||||
|
||||
|
||||
def test_download_pairs(default_conf, ticker_history, mocker):
|
||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
file1_1 = 'freqtrade/tests/testdata/BTC_MEME-1.json'
|
||||
file1_5 = 'freqtrade/tests/testdata/BTC_MEME-5.json'
|
||||
file2_1 = 'freqtrade/tests/testdata/BTC_CFI-1.json'
|
||||
file2_5 = 'freqtrade/tests/testdata/BTC_CFI-5.json'
|
||||
|
||||
_backup_file(file1_1)
|
||||
_backup_file(file1_5)
|
||||
_backup_file(file2_1)
|
||||
_backup_file(file2_5)
|
||||
|
||||
assert download_pairs(pairs=['BTC-MEME', 'BTC-CFI']) is True
|
||||
|
||||
assert os.path.isfile(file1_1) is True
|
||||
assert os.path.isfile(file1_5) is True
|
||||
assert os.path.isfile(file2_1) is True
|
||||
assert os.path.isfile(file2_5) is True
|
||||
|
||||
# clean files freshly downloaded
|
||||
_clean_test_file(file1_1)
|
||||
_clean_test_file(file1_5)
|
||||
_clean_test_file(file2_1)
|
||||
_clean_test_file(file2_5)
|
||||
|
||||
|
||||
def test_download_pairs_exception(default_conf, ticker_history, mocker, caplog):
|
||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch('freqtrade.optimize.__init__.download_backtesting_testdata',
|
||||
side_effect=BaseException('File Error'))
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
file1_1 = 'freqtrade/tests/testdata/BTC_MEME-1.json'
|
||||
file1_5 = 'freqtrade/tests/testdata/BTC_MEME-5.json'
|
||||
_backup_file(file1_1)
|
||||
_backup_file(file1_5)
|
||||
|
||||
download_pairs(pairs=['BTC-MEME'])
|
||||
# clean files freshly downloaded
|
||||
_clean_test_file(file1_1)
|
||||
_clean_test_file(file1_5)
|
||||
assert ('freqtrade.optimize.__init__',
|
||||
logging.INFO,
|
||||
'Failed to download the pair: "BTC-MEME", Interval: 1 min'
|
||||
) in caplog.record_tuples
|
||||
|
||||
|
||||
def test_download_backtesting_testdata(default_conf, ticker_history, mocker):
|
||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
# Download a 1 min ticker file
|
||||
file1 = 'freqtrade/tests/testdata/BTC_XEL-1.json'
|
||||
_backup_file(file1)
|
||||
download_backtesting_testdata(pair="BTC-XEL", interval=1)
|
||||
assert os.path.isfile(file1) is True
|
||||
_clean_test_file(file1)
|
||||
|
||||
# Download a 5 min ticker file
|
||||
file2 = 'freqtrade/tests/testdata/BTC_STORJ-5.json'
|
||||
_backup_file(file2)
|
||||
|
||||
download_backtesting_testdata(pair="BTC-STORJ", interval=5)
|
||||
assert os.path.isfile(file2) is True
|
||||
_clean_test_file(file2)
|
@ -217,7 +217,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker):
|
||||
assert trade.close_date is not None
|
||||
|
||||
|
||||
def test_handle_trade_experimental(default_conf, ticker, limit_buy_order, mocker, caplog):
|
||||
def test_handle_trade_roi(default_conf, ticker, limit_buy_order, mocker, caplog):
|
||||
default_conf.update({'experimental': {'use_sell_signal': True}})
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
|
||||
@ -235,10 +235,46 @@ def test_handle_trade_experimental(default_conf, ticker, limit_buy_order, mocker
|
||||
trade = Trade.query.first()
|
||||
trade.is_open = True
|
||||
|
||||
# FIX: sniffing logs, suggest handle_trade should not execute_sell
|
||||
# instead that responsibility should be moved out of handle_trade(),
|
||||
# we might just want to check if we are in a sell condition without
|
||||
# executing
|
||||
# if ROI is reached we must sell
|
||||
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: False)
|
||||
assert handle_trade(trade)
|
||||
assert ('freqtrade', logging.DEBUG, 'Executing sell due to ROI ...') in caplog.record_tuples
|
||||
# if ROI is reached we must sell even if sell-signal is not signalled
|
||||
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
||||
assert handle_trade(trade)
|
||||
assert ('freqtrade', logging.DEBUG, 'Executing sell due to ROI ...') in caplog.record_tuples
|
||||
|
||||
|
||||
def test_handle_trade_experimental(default_conf, ticker, limit_buy_order, mocker, caplog):
|
||||
default_conf.update({'experimental': {'use_sell_signal': True}})
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
|
||||
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
||||
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
||||
mocker.patch.multiple('freqtrade.main.exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
buy=MagicMock(return_value='mocked_limit_buy'))
|
||||
mocker.patch('freqtrade.main.min_roi_reached', return_value=False)
|
||||
|
||||
init(default_conf, create_engine('sqlite://'))
|
||||
create_trade(0.001)
|
||||
|
||||
trade = Trade.query.first()
|
||||
trade.is_open = True
|
||||
|
||||
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: False)
|
||||
value_returned = handle_trade(trade)
|
||||
assert ('freqtrade', logging.DEBUG, 'Checking sell_signal ...') in caplog.record_tuples
|
||||
assert value_returned is False
|
||||
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
||||
assert handle_trade(trade)
|
||||
s = 'Executing sell due to sell signal ...'
|
||||
assert ('freqtrade', logging.DEBUG, s) in caplog.record_tuples
|
||||
|
||||
|
||||
def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, mocker):
|
||||
|
@ -1,98 +0,0 @@
|
||||
# pragma pylint: disable=missing-docstring,W0212
|
||||
|
||||
import os
|
||||
from freqtrade import exchange, optimize
|
||||
from freqtrade.exchange import Bittrex
|
||||
from freqtrade.optimize.backtesting import backtest
|
||||
from freqtrade.optimize.__init__ import testdata_path, download_pairs, download_backtesting_testdata
|
||||
|
||||
|
||||
def test_backtest(default_conf, mocker):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
data = optimize.load_data(ticker_interval=5, pairs=['BTC_ETH'])
|
||||
results = backtest(default_conf['stake_amount'], optimize.preprocess(data), 10, True)
|
||||
assert not results.empty
|
||||
|
||||
|
||||
def test_1min_ticker_interval(default_conf, mocker):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
# Run a backtesting for an exiting 5min ticker_interval
|
||||
data = optimize.load_data(ticker_interval=1, pairs=['BTC_UNITEST'])
|
||||
results = backtest(default_conf['stake_amount'], optimize.preprocess(data), 1, True)
|
||||
assert not results.empty
|
||||
|
||||
|
||||
def test_backtest_with_new_pair(default_conf, ticker_history, mocker):
|
||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
optimize.load_data(ticker_interval=1, pairs=['BTC_MEME'])
|
||||
file = 'freqtrade/tests/testdata/BTC_MEME-1.json'
|
||||
assert os.path.isfile(file) is True
|
||||
|
||||
# delete file freshly downloaded
|
||||
if os.path.isfile(file):
|
||||
os.remove(file)
|
||||
|
||||
|
||||
def test_testdata_path():
|
||||
assert os.path.join('freqtrade', 'tests', 'testdata') in testdata_path()
|
||||
|
||||
|
||||
def test_download_pairs(default_conf, ticker_history, mocker):
|
||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
file1_1 = 'freqtrade/tests/testdata/BTC_MEME-1.json'
|
||||
file1_5 = 'freqtrade/tests/testdata/BTC_MEME-5.json'
|
||||
file2_1 = 'freqtrade/tests/testdata/BTC_CFI-1.json'
|
||||
file2_5 = 'freqtrade/tests/testdata/BTC_CFI-5.json'
|
||||
|
||||
assert download_pairs(pairs=['BTC-MEME', 'BTC-CFI']) is True
|
||||
|
||||
assert os.path.isfile(file1_1) is True
|
||||
assert os.path.isfile(file1_5) is True
|
||||
assert os.path.isfile(file2_1) is True
|
||||
assert os.path.isfile(file2_5) is True
|
||||
|
||||
# delete files freshly downloaded
|
||||
if os.path.isfile(file1_1):
|
||||
os.remove(file1_1)
|
||||
|
||||
if os.path.isfile(file1_5):
|
||||
os.remove(file1_5)
|
||||
|
||||
if os.path.isfile(file2_1):
|
||||
os.remove(file2_1)
|
||||
|
||||
if os.path.isfile(file2_5):
|
||||
os.remove(file2_5)
|
||||
|
||||
|
||||
def test_download_backtesting_testdata(default_conf, ticker_history, mocker):
|
||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
exchange._API = Bittrex({'key': '', 'secret': ''})
|
||||
|
||||
# Download a 1 min ticker file
|
||||
file1 = 'freqtrade/tests/testdata/BTC_XEL-1.json'
|
||||
download_backtesting_testdata(pair="BTC-XEL", interval=1)
|
||||
assert os.path.isfile(file1) is True
|
||||
|
||||
if os.path.isfile(file1):
|
||||
os.remove(file1)
|
||||
|
||||
# Download a 5 min ticker file
|
||||
file2 = 'freqtrade/tests/testdata/BTC_STORJ-5.json'
|
||||
download_backtesting_testdata(pair="BTC-STORJ", interval=5)
|
||||
assert os.path.isfile(file2) is True
|
||||
|
||||
if os.path.isfile(file2):
|
||||
os.remove(file2)
|
@ -1,6 +0,0 @@
|
||||
# pragma pylint: disable=missing-docstring,W0212
|
||||
|
||||
|
||||
def test_optimizer(default_conf, mocker):
|
||||
# TODO: implement test
|
||||
pass
|
@ -1,5 +1,5 @@
|
||||
python-bittrex==0.2.2
|
||||
SQLAlchemy==1.1.15
|
||||
SQLAlchemy==1.2.0
|
||||
python-telegram-bot==9.0.0
|
||||
arrow==0.12.0
|
||||
cachetools==2.0.1
|
||||
|
Loading…
Reference in New Issue
Block a user