From ce1ed76269370862f6f717c4d9ffe98b049d7caa Mon Sep 17 00:00:00 2001 From: Brook Miles Date: Wed, 17 Mar 2021 22:44:10 +0900 Subject: [PATCH] complete stoploss_from_open and associated test --- freqtrade/strategy/__init__.py | 3 +- freqtrade/strategy/strategy_helper.py | 15 ++++++++-- tests/strategy/test_strategy_helpers.py | 39 ++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/freqtrade/strategy/__init__.py b/freqtrade/strategy/__init__.py index 3de90666e..85148b6ea 100644 --- a/freqtrade/strategy/__init__.py +++ b/freqtrade/strategy/__init__.py @@ -2,5 +2,4 @@ from freqtrade.exchange import (timeframe_to_minutes, timeframe_to_msecs, timeframe_to_next_date, timeframe_to_prev_date, timeframe_to_seconds) from freqtrade.strategy.interface import IStrategy -from freqtrade.strategy.strategy_helper import merge_informative_pair -from freqtrade.strategy.strategy_helper import stoploss_from_open +from freqtrade.strategy.strategy_helper import merge_informative_pair, stoploss_from_open diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index f40fa285d..22b6f0be5 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -65,12 +65,21 @@ def stoploss_from_open(open_relative_stop: float, current_profit: float) -> floa return a stop loss value that is relative to the current price, and which can be returned from `custom_stoploss`. - :param open_relative_stop: Desired stop loss value relative to open price + The requested stop can be positive for a stop above the open price, or negative for + a stop below the open price. The return value is always >= 0. + + Returns 0 if the resulting stop price would be above the current price. + + :param open_relative_stop: Desired stop loss percentage relative to open price :param current_profit: The current profit percentage - :return: Stop loss value relative to current price + :return: Positive stop loss value relative to current price """ + # formula is undefined for current_profit -1, return maximum value if current_profit == -1: return 1 - return 1-((1+open_relative_stop)/(1+current_profit)) + stoploss = 1-((1+open_relative_stop)/(1+current_profit)) + + # negative stoploss values indicate the requested stop price is higher than the current price + return max(stoploss, 0.0) diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index 252288e2e..3b84fc254 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -1,8 +1,10 @@ +from math import isclose + import numpy as np import pandas as pd import pytest -from freqtrade.strategy import merge_informative_pair, timeframe_to_minutes +from freqtrade.strategy import merge_informative_pair, stoploss_from_open, timeframe_to_minutes def generate_test_data(timeframe: str, size: int): @@ -95,3 +97,38 @@ def test_merge_informative_pair_lower(): with pytest.raises(ValueError, match=r"Tried to merge a faster timeframe .*"): merge_informative_pair(data, informative, '1h', '15m', ffill=True) + + +def test_stoploss_from_open(): + open_price_ranges = [ + [0.01, 1.00, 30], + [1, 100, 30], + [100, 10000, 30], + ] + current_profit_range = [-0.99, 2, 30] + desired_stop_range = [-0.50, 0.50, 30] + + for open_range in open_price_ranges: + for open_price in np.linspace(*open_range): + for desired_stop in np.linspace(*desired_stop_range): + + # -1 is not a valid current_profit, should return 1 + assert stoploss_from_open(desired_stop, -1) == 1 + + for current_profit in np.linspace(*current_profit_range): + current_price = open_price * (1 + current_profit) + expected_stop_price = open_price * (1 + desired_stop) + + stoploss = stoploss_from_open(desired_stop, current_profit) + + assert stoploss >= 0 + assert stoploss <= 1 + + stop_price = current_price * (1 - stoploss) + + # there is no correct answer if the expected stop price is above + # the current price + if expected_stop_price > current_price: + assert stoploss == 0 + else: + assert isclose(stop_price, expected_stop_price, rel_tol=0.00001)