commit
b7686d06a7
@ -405,7 +405,7 @@ class Arguments(object):
|
||||
raise Exception('Incorrect syntax for timerange "%s"' % text)
|
||||
|
||||
@staticmethod
|
||||
def check_int_positive(value) -> int:
|
||||
def check_int_positive(value: str) -> int:
|
||||
try:
|
||||
uint = int(value)
|
||||
if uint <= 0:
|
||||
|
@ -10,10 +10,11 @@ import numpy as np
|
||||
import pytest
|
||||
from pandas import DataFrame, to_datetime
|
||||
|
||||
from freqtrade import OperationalException
|
||||
from freqtrade.data.converter import parse_ticker_dataframe
|
||||
from freqtrade.edge import Edge, PairInfo
|
||||
from freqtrade.strategy.interface import SellType
|
||||
from freqtrade.tests.conftest import get_patched_freqtradebot
|
||||
from freqtrade.tests.conftest import get_patched_freqtradebot, log_has
|
||||
from freqtrade.tests.optimize import (BTContainer, BTrade,
|
||||
_build_backtest_dataframe,
|
||||
_get_frame_time_from_offset)
|
||||
@ -30,7 +31,50 @@ ticker_start_time = arrow.get(2018, 10, 3)
|
||||
ticker_interval_in_minute = 60
|
||||
_ohlc = {'date': 0, 'buy': 1, 'open': 2, 'high': 3, 'low': 4, 'close': 5, 'sell': 6, 'volume': 7}
|
||||
|
||||
# Helpers for this test file
|
||||
|
||||
|
||||
def _validate_ohlc(buy_ohlc_sell_matrice):
|
||||
for index, ohlc in enumerate(buy_ohlc_sell_matrice):
|
||||
# if not high < open < low or not high < close < low
|
||||
if not ohlc[3] >= ohlc[2] >= ohlc[4] or not ohlc[3] >= ohlc[5] >= ohlc[4]:
|
||||
raise Exception('Line ' + str(index + 1) + ' of ohlc has invalid values!')
|
||||
return True
|
||||
|
||||
|
||||
def _build_dataframe(buy_ohlc_sell_matrice):
|
||||
_validate_ohlc(buy_ohlc_sell_matrice)
|
||||
tickers = []
|
||||
for ohlc in buy_ohlc_sell_matrice:
|
||||
ticker = {
|
||||
'date': ticker_start_time.shift(
|
||||
minutes=(
|
||||
ohlc[0] *
|
||||
ticker_interval_in_minute)).timestamp *
|
||||
1000,
|
||||
'buy': ohlc[1],
|
||||
'open': ohlc[2],
|
||||
'high': ohlc[3],
|
||||
'low': ohlc[4],
|
||||
'close': ohlc[5],
|
||||
'sell': ohlc[6]}
|
||||
tickers.append(ticker)
|
||||
|
||||
frame = DataFrame(tickers)
|
||||
frame['date'] = to_datetime(frame['date'],
|
||||
unit='ms',
|
||||
utc=True,
|
||||
infer_datetime_format=True)
|
||||
|
||||
return frame
|
||||
|
||||
|
||||
def _time_on_candle(number):
|
||||
return np.datetime64(ticker_start_time.shift(
|
||||
minutes=(number * ticker_interval_in_minute)).timestamp * 1000, 'ms')
|
||||
|
||||
|
||||
# End helper functions
|
||||
# Open trade should be removed from the end
|
||||
tc0 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
@ -203,46 +247,6 @@ def test_nonexisting_stake_amount(mocker, edge_conf):
|
||||
assert edge.stake_amount('N/O', 1, 2, 1) == 0.15
|
||||
|
||||
|
||||
def _validate_ohlc(buy_ohlc_sell_matrice):
|
||||
for index, ohlc in enumerate(buy_ohlc_sell_matrice):
|
||||
# if not high < open < low or not high < close < low
|
||||
if not ohlc[3] >= ohlc[2] >= ohlc[4] or not ohlc[3] >= ohlc[5] >= ohlc[4]:
|
||||
raise Exception('Line ' + str(index + 1) + ' of ohlc has invalid values!')
|
||||
return True
|
||||
|
||||
|
||||
def _build_dataframe(buy_ohlc_sell_matrice):
|
||||
_validate_ohlc(buy_ohlc_sell_matrice)
|
||||
tickers = []
|
||||
for ohlc in buy_ohlc_sell_matrice:
|
||||
ticker = {
|
||||
'date': ticker_start_time.shift(
|
||||
minutes=(
|
||||
ohlc[0] *
|
||||
ticker_interval_in_minute)).timestamp *
|
||||
1000,
|
||||
'buy': ohlc[1],
|
||||
'open': ohlc[2],
|
||||
'high': ohlc[3],
|
||||
'low': ohlc[4],
|
||||
'close': ohlc[5],
|
||||
'sell': ohlc[6]}
|
||||
tickers.append(ticker)
|
||||
|
||||
frame = DataFrame(tickers)
|
||||
frame['date'] = to_datetime(frame['date'],
|
||||
unit='ms',
|
||||
utc=True,
|
||||
infer_datetime_format=True)
|
||||
|
||||
return frame
|
||||
|
||||
|
||||
def _time_on_candle(number):
|
||||
return np.datetime64(ticker_start_time.shift(
|
||||
minutes=(number * ticker_interval_in_minute)).timestamp * 1000, 'ms')
|
||||
|
||||
|
||||
def test_edge_heartbeat_calculate(mocker, edge_conf):
|
||||
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
|
||||
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
@ -298,6 +302,40 @@ def test_edge_process_downloaded_data(mocker, edge_conf):
|
||||
assert edge._last_updated <= arrow.utcnow().timestamp + 2
|
||||
|
||||
|
||||
def test_edge_process_no_data(mocker, edge_conf, caplog):
|
||||
edge_conf['datadir'] = None
|
||||
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.001))
|
||||
mocker.patch('freqtrade.data.history.load_data', MagicMock(return_value={}))
|
||||
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
|
||||
assert not edge.calculate()
|
||||
assert len(edge._cached_pairs) == 0
|
||||
assert log_has("No data found. Edge is stopped ...", caplog.record_tuples)
|
||||
assert edge._last_updated == 0
|
||||
|
||||
|
||||
def test_edge_process_no_trades(mocker, edge_conf, caplog):
|
||||
edge_conf['datadir'] = None
|
||||
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.001))
|
||||
mocker.patch('freqtrade.data.history.load_data', mocked_load_data)
|
||||
# Return empty
|
||||
mocker.patch('freqtrade.edge.Edge._find_trades_for_stoploss_range', MagicMock(return_value=[]))
|
||||
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
|
||||
assert not edge.calculate()
|
||||
assert len(edge._cached_pairs) == 0
|
||||
assert log_has("No trades found.", caplog.record_tuples)
|
||||
|
||||
|
||||
def test_edge_init_error(mocker, edge_conf,):
|
||||
edge_conf['stake_amount'] = 0.5
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.001))
|
||||
with pytest.raises(OperationalException, match='Edge works only with unlimited stake amount'):
|
||||
get_patched_freqtradebot(mocker, edge_conf)
|
||||
|
||||
|
||||
def test_process_expectancy(mocker, edge_conf):
|
||||
edge_conf['edge']['min_trade_number'] = 2
|
||||
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
|
||||
@ -360,3 +398,11 @@ def test_process_expectancy(mocker, edge_conf):
|
||||
assert round(final['TEST/BTC'].risk_reward_ratio, 10) == 306.5384615384
|
||||
assert round(final['TEST/BTC'].required_risk_reward, 10) == 2.0
|
||||
assert round(final['TEST/BTC'].expectancy, 10) == 101.5128205128
|
||||
|
||||
# Pop last item so no trade is profitable
|
||||
trades.pop()
|
||||
trades_df = DataFrame(trades)
|
||||
trades_df = edge._fill_calculable_fields(trades_df)
|
||||
final = edge._process_expectancy(trades_df)
|
||||
assert len(final) == 0
|
||||
assert isinstance(final, dict)
|
||||
|
@ -1,5 +1,4 @@
|
||||
# pragma pylint: disable=missing-docstring, C0103
|
||||
|
||||
import argparse
|
||||
|
||||
import pytest
|
||||
@ -185,3 +184,22 @@ def test_testdata_dl_options() -> None:
|
||||
assert args.export == 'export/folder'
|
||||
assert args.days == 30
|
||||
assert args.exchange == 'binance'
|
||||
|
||||
|
||||
def test_check_int_positive() -> None:
|
||||
|
||||
assert Arguments.check_int_positive("3") == 3
|
||||
assert Arguments.check_int_positive("1") == 1
|
||||
assert Arguments.check_int_positive("100") == 100
|
||||
|
||||
with pytest.raises(argparse.ArgumentTypeError):
|
||||
Arguments.check_int_positive("-2")
|
||||
|
||||
with pytest.raises(argparse.ArgumentTypeError):
|
||||
Arguments.check_int_positive("0")
|
||||
|
||||
with pytest.raises(argparse.ArgumentTypeError):
|
||||
Arguments.check_int_positive("3.5")
|
||||
|
||||
with pytest.raises(argparse.ArgumentTypeError):
|
||||
Arguments.check_int_positive("DeadBeef")
|
||||
|
Loading…
Reference in New Issue
Block a user