Merge branch 'feat/short' into fs_valid_leverage
This commit is contained in:
commit
9b756c4015
@ -24,7 +24,7 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and
|
|||||||
|
|
||||||
* Fetch open trades from persistence.
|
* Fetch open trades from persistence.
|
||||||
* Calculate current list of tradable pairs.
|
* Calculate current list of tradable pairs.
|
||||||
* Download ohlcv data for the pairlist including all [informative pairs](strategy-customization.md#get-data-for-non-tradeable-pairs)
|
* Download OHLCV data for the pairlist including all [informative pairs](strategy-customization.md#get-data-for-non-tradeable-pairs)
|
||||||
This step is only executed once per Candle to avoid unnecessary network traffic.
|
This step is only executed once per Candle to avoid unnecessary network traffic.
|
||||||
* Call `bot_loop_start()` strategy callback.
|
* Call `bot_loop_start()` strategy callback.
|
||||||
* Analyze strategy per pair.
|
* Analyze strategy per pair.
|
||||||
|
@ -210,6 +210,9 @@ OKX requires a passphrase for each api key, you will therefore need to add this
|
|||||||
|
|
||||||
## Gate.io
|
## Gate.io
|
||||||
|
|
||||||
|
!!! Tip "Stoploss on Exchange"
|
||||||
|
Gate.io supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange..
|
||||||
|
|
||||||
Gate.io allows the use of `POINT` to pay for fees. As this is not a tradable currency (no regular market available), automatic fee calculations will fail (and default to a fee of 0).
|
Gate.io allows the use of `POINT` to pay for fees. As this is not a tradable currency (no regular market available), automatic fee calculations will fail (and default to a fee of 0).
|
||||||
The configuration parameter `exchange.unknown_fee_rate` can be used to specify the exchange rate between Point and the stake currency. Obviously, changing the stake-currency will also require changes to this value.
|
The configuration parameter `exchange.unknown_fee_rate` can be used to specify the exchange rate between Point and the stake currency. Obviously, changing the stake-currency will also require changes to this value.
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ These modes can be configured with these values:
|
|||||||
```
|
```
|
||||||
|
|
||||||
!!! Note
|
!!! Note
|
||||||
Stoploss on exchange is only supported for Binance (stop-loss-limit), Huobi (stop-limit), Kraken (stop-loss-market, stop-loss-limit), FTX (stop limit and stop-market) and kucoin (stop-limit and stop-market) as of now.
|
Stoploss on exchange is only supported for Binance (stop-loss-limit), Huobi (stop-limit), Kraken (stop-loss-market, stop-loss-limit), FTX (stop limit and stop-market) Gateio (stop-limit), and Kucoin (stop-limit and stop-market) as of now.
|
||||||
<ins>Do not set too low/tight stoploss value if using stop loss on exchange!</ins>
|
<ins>Do not set too low/tight stoploss value if using stop loss on exchange!</ins>
|
||||||
If set to low/tight then you have greater risk of missing fill on the order and stoploss will not work.
|
If set to low/tight then you have greater risk of missing fill on the order and stoploss will not work.
|
||||||
|
|
||||||
|
@ -517,20 +517,25 @@ Requires a configuration with specified `pairlists` attribute.
|
|||||||
Can be used to generate static pairlists to be used during backtesting / hyperopt.
|
Can be used to generate static pairlists to be used during backtesting / hyperopt.
|
||||||
|
|
||||||
```
|
```
|
||||||
usage: freqtrade test-pairlist [-h] [-c PATH]
|
usage: freqtrade test-pairlist [-h] [-v] [-c PATH]
|
||||||
[--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]]
|
[--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]]
|
||||||
[-1] [--print-json]
|
[-1] [--print-json] [--exchange EXCHANGE]
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
|
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
|
||||||
-c PATH, --config PATH
|
-c PATH, --config PATH
|
||||||
Specify configuration file (default: `config.json`).
|
Specify configuration file (default:
|
||||||
Multiple --config options may be used. Can be set to
|
`userdir/config.json` or `config.json` whichever
|
||||||
`-` to read config from stdin.
|
exists). Multiple --config options may be used. Can be
|
||||||
|
set to `-` to read config from stdin.
|
||||||
--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]
|
--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]
|
||||||
Specify quote currency(-ies). Space-separated list.
|
Specify quote currency(-ies). Space-separated list.
|
||||||
-1, --one-column Print output in one column.
|
-1, --one-column Print output in one column.
|
||||||
--print-json Print list of pairs or market symbols in JSON format.
|
--print-json Print list of pairs or market symbols in JSON format.
|
||||||
|
--exchange EXCHANGE Exchange name (default: `bittrex`). Only valid if no
|
||||||
|
config is provided.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
@ -52,7 +52,7 @@ ARGS_LIST_PAIRS = ["exchange", "print_list", "list_pairs_print_json", "print_one
|
|||||||
"trading_mode"]
|
"trading_mode"]
|
||||||
|
|
||||||
ARGS_TEST_PAIRLIST = ["verbosity", "config", "quote_currencies", "print_one_column",
|
ARGS_TEST_PAIRLIST = ["verbosity", "config", "quote_currencies", "print_one_column",
|
||||||
"list_pairs_print_json"]
|
"list_pairs_print_json", "exchange"]
|
||||||
|
|
||||||
ARGS_CREATE_USERDIR = ["user_data_dir", "reset"]
|
ARGS_CREATE_USERDIR = ["user_data_dir", "reset"]
|
||||||
|
|
||||||
|
@ -226,9 +226,11 @@ class Edge:
|
|||||||
"""
|
"""
|
||||||
final = []
|
final = []
|
||||||
for pair, info in self._cached_pairs.items():
|
for pair, info in self._cached_pairs.items():
|
||||||
if info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) and \
|
if (
|
||||||
info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)) and \
|
info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2))
|
||||||
pair in pairs:
|
and info.winrate > float(self.edge_config.get('minimum_winrate', 0.60))
|
||||||
|
and pair in pairs
|
||||||
|
):
|
||||||
final.append(pair)
|
final.append(pair)
|
||||||
|
|
||||||
if self._final_pairs != final:
|
if self._final_pairs != final:
|
||||||
@ -253,8 +255,8 @@ class Edge:
|
|||||||
"""
|
"""
|
||||||
final = []
|
final = []
|
||||||
for pair, info in self._cached_pairs.items():
|
for pair, info in self._cached_pairs.items():
|
||||||
if info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) and \
|
if (info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) and
|
||||||
info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)):
|
info.winrate > float(self.edge_config.get('minimum_winrate', 0.60))):
|
||||||
final.append({
|
final.append({
|
||||||
'Pair': pair,
|
'Pair': pair,
|
||||||
'Winrate': info.winrate,
|
'Winrate': info.winrate,
|
||||||
|
@ -1124,11 +1124,11 @@ class Exchange:
|
|||||||
raise OperationalException(e) from e
|
raise OperationalException(e) from e
|
||||||
|
|
||||||
@retrier(retries=API_FETCH_ORDER_RETRY_COUNT)
|
@retrier(retries=API_FETCH_ORDER_RETRY_COUNT)
|
||||||
def fetch_order(self, order_id: str, pair: str) -> Dict:
|
def fetch_order(self, order_id: str, pair: str, params={}) -> Dict:
|
||||||
if self._config['dry_run']:
|
if self._config['dry_run']:
|
||||||
return self.fetch_dry_run_order(order_id)
|
return self.fetch_dry_run_order(order_id)
|
||||||
try:
|
try:
|
||||||
order = self._api.fetch_order(order_id, pair)
|
order = self._api.fetch_order(order_id, pair, params=params)
|
||||||
self._log_exchange_response('fetch_order', order)
|
self._log_exchange_response('fetch_order', order)
|
||||||
order = self._order_contracts_to_amount(order)
|
order = self._order_contracts_to_amount(order)
|
||||||
return order
|
return order
|
||||||
@ -1172,7 +1172,7 @@ class Exchange:
|
|||||||
and order.get('filled') == 0.0)
|
and order.get('filled') == 0.0)
|
||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def cancel_order(self, order_id: str, pair: str) -> Dict:
|
def cancel_order(self, order_id: str, pair: str, params={}) -> Dict:
|
||||||
if self._config['dry_run']:
|
if self._config['dry_run']:
|
||||||
try:
|
try:
|
||||||
order = self.fetch_dry_run_order(order_id)
|
order = self.fetch_dry_run_order(order_id)
|
||||||
@ -1183,7 +1183,7 @@ class Exchange:
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
order = self._api.cancel_order(order_id, pair)
|
order = self._api.cancel_order(order_id, pair, params=params)
|
||||||
self._log_exchange_response('cancel_order', order)
|
self._log_exchange_response('cancel_order', order)
|
||||||
order = self._order_contracts_to_amount(order)
|
order = self._order_contracts_to_amount(order)
|
||||||
return order
|
return order
|
||||||
|
@ -23,10 +23,10 @@ class Gateio(Exchange):
|
|||||||
_ft_has: Dict = {
|
_ft_has: Dict = {
|
||||||
"ohlcv_candle_limit": 1000,
|
"ohlcv_candle_limit": 1000,
|
||||||
"ohlcv_volume_currency": "quote",
|
"ohlcv_volume_currency": "quote",
|
||||||
|
"stoploss_order_types": {"limit": "limit"},
|
||||||
|
"stoploss_on_exchange": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
_headers = {'X-Gate-Channel-Id': 'freqtrade'}
|
|
||||||
|
|
||||||
_supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [
|
_supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [
|
||||||
# TradingMode.SPOT always supported and not required in this list
|
# TradingMode.SPOT always supported and not required in this list
|
||||||
# (TradingMode.MARGIN, MarginMode.CROSS),
|
# (TradingMode.MARGIN, MarginMode.CROSS),
|
||||||
@ -41,3 +41,25 @@ class Gateio(Exchange):
|
|||||||
if any(v == 'market' for k, v in order_types.items()):
|
if any(v == 'market' for k, v in order_types.items()):
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
f'Exchange {self.name} does not support market orders.')
|
f'Exchange {self.name} does not support market orders.')
|
||||||
|
|
||||||
|
def fetch_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict:
|
||||||
|
return self.fetch_order(
|
||||||
|
order_id=order_id,
|
||||||
|
pair=pair,
|
||||||
|
params={'stop': True}
|
||||||
|
)
|
||||||
|
|
||||||
|
def cancel_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict:
|
||||||
|
return self.cancel_order(
|
||||||
|
order_id=order_id,
|
||||||
|
pair=pair,
|
||||||
|
params={'stop': True}
|
||||||
|
)
|
||||||
|
|
||||||
|
def stoploss_adjust(self, stop_loss: float, order: Dict, side: str) -> bool:
|
||||||
|
"""
|
||||||
|
Verify stop_loss against stoploss-order value (limit or price)
|
||||||
|
Returns True if adjustment is necessary.
|
||||||
|
"""
|
||||||
|
return ((side == "sell" and stop_loss > float(order['stopPrice'])) or
|
||||||
|
(side == "buy" and stop_loss < float(order['stopPrice'])))
|
||||||
|
@ -359,10 +359,25 @@ class Backtesting:
|
|||||||
"""
|
"""
|
||||||
# Special handling if high or low hit STOP_LOSS or ROI
|
# Special handling if high or low hit STOP_LOSS or ROI
|
||||||
if sell.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
|
if sell.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
|
||||||
if trade.stop_loss > sell_row[HIGH_IDX]:
|
return self._get_close_rate_for_stoploss(sell_row, trade, sell, trade_dur)
|
||||||
# our stoploss was already higher than candle high,
|
elif sell.sell_type == (SellType.ROI):
|
||||||
|
return self._get_close_rate_for_roi(sell_row, trade, sell, trade_dur)
|
||||||
|
else:
|
||||||
|
return sell_row[OPEN_IDX]
|
||||||
|
|
||||||
|
def _get_close_rate_for_stoploss(self, sell_row: Tuple, trade: LocalTrade, sell: SellCheckTuple,
|
||||||
|
trade_dur: int) -> float:
|
||||||
|
# our stoploss was already lower than candle high,
|
||||||
# possibly due to a cancelled trade exit.
|
# possibly due to a cancelled trade exit.
|
||||||
# sell at open price.
|
# sell at open price.
|
||||||
|
is_short = trade.is_short or False
|
||||||
|
leverage = trade.leverage or 1.0
|
||||||
|
side_1 = -1 if is_short else 1
|
||||||
|
if is_short:
|
||||||
|
if trade.stop_loss < sell_row[LOW_IDX]:
|
||||||
|
return sell_row[OPEN_IDX]
|
||||||
|
else:
|
||||||
|
if trade.stop_loss > sell_row[HIGH_IDX]:
|
||||||
return sell_row[OPEN_IDX]
|
return sell_row[OPEN_IDX]
|
||||||
|
|
||||||
# Special case: trailing triggers within same candle as trade opened. Assume most
|
# Special case: trailing triggers within same candle as trade opened. Assume most
|
||||||
@ -377,19 +392,32 @@ class Backtesting:
|
|||||||
):
|
):
|
||||||
# Worst case: price reaches stop_positive_offset and dives down.
|
# Worst case: price reaches stop_positive_offset and dives down.
|
||||||
stop_rate = (sell_row[OPEN_IDX] *
|
stop_rate = (sell_row[OPEN_IDX] *
|
||||||
(1 + abs(self.strategy.trailing_stop_positive_offset) -
|
(1 + side_1 * abs(self.strategy.trailing_stop_positive_offset) -
|
||||||
abs(self.strategy.trailing_stop_positive)))
|
side_1 * abs(self.strategy.trailing_stop_positive / leverage)))
|
||||||
else:
|
else:
|
||||||
# Worst case: price ticks tiny bit above open and dives down.
|
# Worst case: price ticks tiny bit above open and dives down.
|
||||||
stop_rate = sell_row[OPEN_IDX] * (1 - abs(trade.stop_loss_pct))
|
stop_rate = sell_row[OPEN_IDX] * (1 -
|
||||||
|
side_1 * abs(trade.stop_loss_pct / leverage))
|
||||||
|
if is_short:
|
||||||
|
assert stop_rate > sell_row[LOW_IDX]
|
||||||
|
else:
|
||||||
assert stop_rate < sell_row[HIGH_IDX]
|
assert stop_rate < sell_row[HIGH_IDX]
|
||||||
|
|
||||||
# Limit lower-end to candle low to avoid sells below the low.
|
# Limit lower-end to candle low to avoid sells below the low.
|
||||||
# This still remains "worst case" - but "worst realistic case".
|
# This still remains "worst case" - but "worst realistic case".
|
||||||
|
if is_short:
|
||||||
|
return min(sell_row[HIGH_IDX], stop_rate)
|
||||||
|
else:
|
||||||
return max(sell_row[LOW_IDX], stop_rate)
|
return max(sell_row[LOW_IDX], stop_rate)
|
||||||
|
|
||||||
# Set close_rate to stoploss
|
# Set close_rate to stoploss
|
||||||
return trade.stop_loss
|
return trade.stop_loss
|
||||||
elif sell.sell_type == (SellType.ROI):
|
|
||||||
|
def _get_close_rate_for_roi(self, sell_row: Tuple, trade: LocalTrade, sell: SellCheckTuple,
|
||||||
|
trade_dur: int) -> float:
|
||||||
|
is_short = trade.is_short or False
|
||||||
|
leverage = trade.leverage or 1.0
|
||||||
|
side_1 = -1 if is_short else 1
|
||||||
roi_entry, roi = self.strategy.min_roi_reached_entry(trade_dur)
|
roi_entry, roi = self.strategy.min_roi_reached_entry(trade_dur)
|
||||||
if roi is not None and roi_entry is not None:
|
if roi is not None and roi_entry is not None:
|
||||||
if roi == -1 and roi_entry % self.timeframe_min == 0:
|
if roi == -1 and roi_entry % self.timeframe_min == 0:
|
||||||
@ -398,29 +426,44 @@ class Backtesting:
|
|||||||
# - we'll use open instead of close
|
# - we'll use open instead of close
|
||||||
return sell_row[OPEN_IDX]
|
return sell_row[OPEN_IDX]
|
||||||
|
|
||||||
# - (Expected abs profit + open_rate + open_fee) / (fee_close -1)
|
# - (Expected abs profit - open_rate - open_fee) / (fee_close -1)
|
||||||
close_rate = - (trade.open_rate * roi + trade.open_rate *
|
roi_rate = trade.open_rate * roi / leverage
|
||||||
(1 + trade.fee_open)) / (trade.fee_close - 1)
|
open_fee_rate = side_1 * trade.open_rate * (1 + side_1 * trade.fee_open)
|
||||||
|
close_rate = -(roi_rate + open_fee_rate) / (trade.fee_close - side_1 * 1)
|
||||||
|
if is_short:
|
||||||
|
is_new_roi = sell_row[OPEN_IDX] < close_rate
|
||||||
|
else:
|
||||||
|
is_new_roi = sell_row[OPEN_IDX] > close_rate
|
||||||
if (trade_dur > 0 and trade_dur == roi_entry
|
if (trade_dur > 0 and trade_dur == roi_entry
|
||||||
and roi_entry % self.timeframe_min == 0
|
and roi_entry % self.timeframe_min == 0
|
||||||
and sell_row[OPEN_IDX] > close_rate):
|
and is_new_roi):
|
||||||
# new ROI entry came into effect.
|
# new ROI entry came into effect.
|
||||||
# use Open rate if open_rate > calculated sell rate
|
# use Open rate if open_rate > calculated sell rate
|
||||||
return sell_row[OPEN_IDX]
|
return sell_row[OPEN_IDX]
|
||||||
|
|
||||||
if (
|
if (trade_dur == 0 and (
|
||||||
trade_dur == 0
|
(
|
||||||
# Red candle (for longs), TODO: green candle (for shorts)
|
is_short
|
||||||
and sell_row[OPEN_IDX] > sell_row[CLOSE_IDX] # Red candle
|
# Red candle (for longs)
|
||||||
|
and sell_row[OPEN_IDX] < sell_row[CLOSE_IDX] # Red candle
|
||||||
|
and trade.open_rate > sell_row[OPEN_IDX] # trade-open above open_rate
|
||||||
|
and close_rate < sell_row[CLOSE_IDX] # closes below close
|
||||||
|
)
|
||||||
|
or
|
||||||
|
(
|
||||||
|
not is_short
|
||||||
|
# green candle (for shorts)
|
||||||
|
and sell_row[OPEN_IDX] > sell_row[CLOSE_IDX] # green candle
|
||||||
and trade.open_rate < sell_row[OPEN_IDX] # trade-open below open_rate
|
and trade.open_rate < sell_row[OPEN_IDX] # trade-open below open_rate
|
||||||
and close_rate > sell_row[CLOSE_IDX]
|
and close_rate > sell_row[CLOSE_IDX] # closes above close
|
||||||
):
|
)
|
||||||
|
)):
|
||||||
# ROI on opening candles with custom pricing can only
|
# ROI on opening candles with custom pricing can only
|
||||||
# trigger if the entry was at Open or lower.
|
# trigger if the entry was at Open or lower wick.
|
||||||
# details: https: // github.com/freqtrade/freqtrade/issues/6261
|
# details: https: // github.com/freqtrade/freqtrade/issues/6261
|
||||||
# If open_rate is < open, only allow sells below the close on red candles.
|
# If open_rate is < open, only allow sells below the close on red candles.
|
||||||
raise ValueError("Opening candle ROI on red candles.")
|
raise ValueError("Opening candle ROI on red candles.")
|
||||||
|
|
||||||
# Use the maximum between close_rate and low as we
|
# Use the maximum between close_rate and low as we
|
||||||
# cannot sell outside of a candle.
|
# cannot sell outside of a candle.
|
||||||
# Applies when a new ROI setting comes in place and the whole candle is above that.
|
# Applies when a new ROI setting comes in place and the whole candle is above that.
|
||||||
@ -429,8 +472,6 @@ class Backtesting:
|
|||||||
else:
|
else:
|
||||||
# This should not be reached...
|
# This should not be reached...
|
||||||
return sell_row[OPEN_IDX]
|
return sell_row[OPEN_IDX]
|
||||||
else:
|
|
||||||
return sell_row[OPEN_IDX]
|
|
||||||
|
|
||||||
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple
|
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple
|
||||||
) -> LocalTrade:
|
) -> LocalTrade:
|
||||||
@ -501,6 +542,9 @@ class Backtesting:
|
|||||||
proposed_rate=closerate, current_profit=current_profit)
|
proposed_rate=closerate, current_profit=current_profit)
|
||||||
# We can't place orders lower than current low.
|
# We can't place orders lower than current low.
|
||||||
# freqtrade does not support this in live, and the order would fill immediately
|
# freqtrade does not support this in live, and the order would fill immediately
|
||||||
|
if trade.is_short:
|
||||||
|
closerate = min(closerate, sell_row[HIGH_IDX])
|
||||||
|
else:
|
||||||
closerate = max(closerate, sell_row[LOW_IDX])
|
closerate = max(closerate, sell_row[LOW_IDX])
|
||||||
# Confirm trade exit:
|
# Confirm trade exit:
|
||||||
time_in_force = self.strategy.order_time_in_force['exit']
|
time_in_force = self.strategy.order_time_in_force['exit']
|
||||||
@ -534,8 +578,8 @@ class Backtesting:
|
|||||||
ft_pair=trade.pair,
|
ft_pair=trade.pair,
|
||||||
order_id=str(self.order_id_counter),
|
order_id=str(self.order_id_counter),
|
||||||
symbol=trade.pair,
|
symbol=trade.pair,
|
||||||
ft_order_side="sell",
|
ft_order_side=trade.exit_side,
|
||||||
side="sell",
|
side=trade.exit_side,
|
||||||
order_type=order_type,
|
order_type=order_type,
|
||||||
status="open",
|
status="open",
|
||||||
price=closerate,
|
price=closerate,
|
||||||
@ -607,6 +651,9 @@ class Backtesting:
|
|||||||
proposed_rate=propose_rate, entry_tag=entry_tag) # default value is the open rate
|
proposed_rate=propose_rate, entry_tag=entry_tag) # default value is the open rate
|
||||||
# We can't place orders higher than current high (otherwise it'd be a stop limit buy)
|
# We can't place orders higher than current high (otherwise it'd be a stop limit buy)
|
||||||
# which freqtrade does not support in live.
|
# which freqtrade does not support in live.
|
||||||
|
if direction == "short":
|
||||||
|
propose_rate = max(propose_rate, row[LOW_IDX])
|
||||||
|
else:
|
||||||
propose_rate = min(propose_rate, row[HIGH_IDX])
|
propose_rate = min(propose_rate, row[HIGH_IDX])
|
||||||
|
|
||||||
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, propose_rate, -0.05) or 0
|
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, propose_rate, -0.05) or 0
|
||||||
@ -712,8 +759,8 @@ class Backtesting:
|
|||||||
ft_pair=trade.pair,
|
ft_pair=trade.pair,
|
||||||
order_id=str(self.order_id_counter),
|
order_id=str(self.order_id_counter),
|
||||||
symbol=trade.pair,
|
symbol=trade.pair,
|
||||||
ft_order_side="buy",
|
ft_order_side=trade.enter_side,
|
||||||
side="buy",
|
side=trade.enter_side,
|
||||||
order_type=order_type,
|
order_type=order_type,
|
||||||
status="open",
|
status="open",
|
||||||
order_date=current_time,
|
order_date=current_time,
|
||||||
@ -795,17 +842,17 @@ class Backtesting:
|
|||||||
|
|
||||||
timedout = self.strategy.ft_check_timed_out(order.side, trade, order, current_time)
|
timedout = self.strategy.ft_check_timed_out(order.side, trade, order, current_time)
|
||||||
if timedout:
|
if timedout:
|
||||||
if order.side == 'buy':
|
if order.side == trade.enter_side:
|
||||||
self.timedout_entry_orders += 1
|
self.timedout_entry_orders += 1
|
||||||
if trade.nr_of_successful_entries == 0:
|
if trade.nr_of_successful_entries == 0:
|
||||||
# Remove trade due to buy timeout expiration.
|
# Remove trade due to entry timeout expiration.
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
# Close additional buy order
|
# Close additional buy order
|
||||||
del trade.orders[trade.orders.index(order)]
|
del trade.orders[trade.orders.index(order)]
|
||||||
if order.side == 'sell':
|
if order.side == trade.exit_side:
|
||||||
self.timedout_exit_orders += 1
|
self.timedout_exit_orders += 1
|
||||||
# Close sell order and retry selling on next signal.
|
# Close exit order and retry exiting on next signal.
|
||||||
del trade.orders[trade.orders.index(order)]
|
del trade.orders[trade.orders.index(order)]
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -901,8 +948,8 @@ class Backtesting:
|
|||||||
open_trades[pair].append(trade)
|
open_trades[pair].append(trade)
|
||||||
|
|
||||||
for trade in list(open_trades[pair]):
|
for trade in list(open_trades[pair]):
|
||||||
# 2. Process buy orders.
|
# 2. Process entry orders.
|
||||||
order = trade.select_order('buy', is_open=True)
|
order = trade.select_order(trade.enter_side, is_open=True)
|
||||||
if order and self._get_order_filled(order.price, row):
|
if order and self._get_order_filled(order.price, row):
|
||||||
order.close_bt_order(current_time)
|
order.close_bt_order(current_time)
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
@ -914,7 +961,7 @@ class Backtesting:
|
|||||||
self._get_sell_trade_entry(trade, row) # Place sell order if necessary
|
self._get_sell_trade_entry(trade, row) # Place sell order if necessary
|
||||||
|
|
||||||
# 4. Process sell orders.
|
# 4. Process sell orders.
|
||||||
order = trade.select_order('sell', is_open=True)
|
order = trade.select_order(trade.exit_side, is_open=True)
|
||||||
if order and self._get_order_filled(order.price, row):
|
if order and self._get_order_filled(order.price, row):
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
trade.close_date = current_time
|
trade.close_date = current_time
|
||||||
|
@ -4,6 +4,7 @@ Spread pair list filter
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
|
|
||||||
|
|
||||||
@ -20,6 +21,12 @@ class SpreadFilter(IPairList):
|
|||||||
self._max_spread_ratio = pairlistconfig.get('max_spread_ratio', 0.005)
|
self._max_spread_ratio = pairlistconfig.get('max_spread_ratio', 0.005)
|
||||||
self._enabled = self._max_spread_ratio != 0
|
self._enabled = self._max_spread_ratio != 0
|
||||||
|
|
||||||
|
if not self._exchange.exchange_has('fetchTickers'):
|
||||||
|
raise OperationalException(
|
||||||
|
'Exchange does not support fetchTickers, therefore SpreadFilter cannot be used.'
|
||||||
|
'Please edit your config and restart the bot.'
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def needstickers(self) -> bool:
|
def needstickers(self) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -592,7 +592,7 @@ class RPC:
|
|||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
pair = self._freqtrade.exchange.get_valid_pair_combination(coin, stake_currency)
|
pair = self._freqtrade.exchange.get_valid_pair_combination(coin, stake_currency)
|
||||||
rate = tickers.get(pair, {}).get('bid', None)
|
rate = tickers.get(pair, {}).get('last', None)
|
||||||
if rate:
|
if rate:
|
||||||
if pair.startswith(stake_currency) and not pair.endswith(stake_currency):
|
if pair.startswith(stake_currency) and not pair.endswith(stake_currency):
|
||||||
rate = 1.0 / rate
|
rate = 1.0 / rate
|
||||||
|
@ -869,7 +869,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
force_stoploss=force_stoploss, low=low, high=high)
|
force_stoploss=force_stoploss, low=low, high=high)
|
||||||
|
|
||||||
# Set current rate to high for backtesting sell
|
# Set current rate to high for backtesting sell
|
||||||
current_rate = high or rate
|
current_rate = (low if trade.is_short else high) or rate
|
||||||
current_profit = trade.calc_profit_ratio(current_rate)
|
current_profit = trade.calc_profit_ratio(current_rate)
|
||||||
|
|
||||||
# if enter signal and ignore_roi is set, we don't need to evaluate min_roi.
|
# if enter signal and ignore_roi is set, we don't need to evaluate min_roi.
|
||||||
@ -961,9 +961,9 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
else:
|
else:
|
||||||
logger.warning("CustomStoploss function did not return valid stoploss")
|
logger.warning("CustomStoploss function did not return valid stoploss")
|
||||||
|
|
||||||
sl_lower_long = (trade.stop_loss < (low or current_rate) and not trade.is_short)
|
sl_lower_short = (trade.stop_loss < (low or current_rate) and not trade.is_short)
|
||||||
sl_higher_short = (trade.stop_loss > (high or current_rate) and trade.is_short)
|
sl_higher_long = (trade.stop_loss > (high or current_rate) and trade.is_short)
|
||||||
if self.trailing_stop and (sl_lower_long or sl_higher_short):
|
if self.trailing_stop and (sl_lower_short or sl_higher_long):
|
||||||
# trailing stoploss handling
|
# trailing stoploss handling
|
||||||
sl_offset = self.trailing_stop_positive_offset
|
sl_offset = self.trailing_stop_positive_offset
|
||||||
|
|
||||||
@ -981,12 +981,12 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
|
|
||||||
trade.adjust_stop_loss(bound or current_rate, stop_loss_value)
|
trade.adjust_stop_loss(bound or current_rate, stop_loss_value)
|
||||||
|
|
||||||
sl_higher_short = (trade.stop_loss >= (low or current_rate) and not trade.is_short)
|
sl_higher_long = (trade.stop_loss >= (low or current_rate) and not trade.is_short)
|
||||||
sl_lower_long = ((trade.stop_loss <= (high or current_rate) and trade.is_short))
|
sl_lower_short = (trade.stop_loss <= (high or current_rate) and trade.is_short)
|
||||||
# evaluate if the stoploss was hit if stoploss is not on exchange
|
# evaluate if the stoploss was hit if stoploss is not on exchange
|
||||||
# in Dry-Run, this handles stoploss logic as well, as the logic will not be different to
|
# in Dry-Run, this handles stoploss logic as well, as the logic will not be different to
|
||||||
# regular stoploss handling.
|
# regular stoploss handling.
|
||||||
if ((sl_higher_short or sl_lower_long) and
|
if ((sl_higher_long or sl_lower_short) and
|
||||||
(not self.order_types.get('stoploss_on_exchange') or self.config['dry_run'])):
|
(not self.order_types.get('stoploss_on_exchange') or self.config['dry_run'])):
|
||||||
|
|
||||||
sell_type = SellType.STOP_LOSS
|
sell_type = SellType.STOP_LOSS
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
coveralls==3.3.1
|
coveralls==3.3.1
|
||||||
flake8==4.0.1
|
flake8==4.0.1
|
||||||
flake8-tidy-imports==4.6.0
|
flake8-tidy-imports==4.6.0
|
||||||
mypy==0.931
|
mypy==0.940
|
||||||
pytest==7.0.1
|
pytest==7.1.0
|
||||||
pytest-asyncio==0.18.2
|
pytest-asyncio==0.18.2
|
||||||
pytest-cov==3.0.0
|
pytest-cov==3.0.0
|
||||||
pytest-mock==3.7.0
|
pytest-mock==3.7.0
|
||||||
@ -17,12 +17,12 @@ isort==5.10.1
|
|||||||
time-machine==2.6.0
|
time-machine==2.6.0
|
||||||
|
|
||||||
# Convert jupyter notebooks to markdown documents
|
# Convert jupyter notebooks to markdown documents
|
||||||
nbconvert==6.4.2
|
nbconvert==6.4.4
|
||||||
|
|
||||||
# mypy types
|
# mypy types
|
||||||
types-cachetools==4.2.10
|
types-cachetools==5.0.0
|
||||||
types-filelock==3.2.5
|
types-filelock==3.2.5
|
||||||
types-requests==2.27.11
|
types-requests==2.27.12
|
||||||
types-tabulate==0.8.5
|
types-tabulate==0.8.5
|
||||||
|
|
||||||
# Extensions to datetime library
|
# Extensions to datetime library
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
numpy==1.22.2
|
numpy==1.22.3
|
||||||
pandas==1.4.1
|
pandas==1.4.1
|
||||||
pandas-ta==0.3.14b
|
pandas-ta==0.3.14b
|
||||||
|
|
||||||
ccxt==1.75.12
|
ccxt==1.76.5
|
||||||
# Pin cryptography for now due to rust build errors with piwheels
|
# Pin cryptography for now due to rust build errors with piwheels
|
||||||
cryptography==36.0.1
|
cryptography==36.0.1
|
||||||
aiohttp==3.8.1
|
aiohttp==3.8.1
|
||||||
@ -32,7 +32,7 @@ sdnotify==0.3.2
|
|||||||
|
|
||||||
# API Server
|
# API Server
|
||||||
fastapi==0.75.0
|
fastapi==0.75.0
|
||||||
uvicorn==0.17.5
|
uvicorn==0.17.6
|
||||||
pyjwt==2.3.0
|
pyjwt==2.3.0
|
||||||
aiofiles==0.8.0
|
aiofiles==0.8.0
|
||||||
psutil==5.9.0
|
psutil==5.9.0
|
||||||
|
2
setup.py
2
setup.py
@ -42,7 +42,7 @@ setup(
|
|||||||
],
|
],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
# from requirements.txt
|
# from requirements.txt
|
||||||
'ccxt>=1.74.17',
|
'ccxt>=1.76.5',
|
||||||
'SQLAlchemy',
|
'SQLAlchemy',
|
||||||
'python-telegram-bot>=13.4',
|
'python-telegram-bot>=13.4',
|
||||||
'arrow>=0.17.0',
|
'arrow>=0.17.0',
|
||||||
|
@ -2720,37 +2720,36 @@ def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
|
|||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', return_value={'for': 123})
|
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', return_value={'for': 123})
|
||||||
mocker.patch('freqtrade.exchange.Ftx.fetch_stoploss_order', return_value={'for': 123})
|
mocker.patch('freqtrade.exchange.Ftx.fetch_stoploss_order', return_value={'for': 123})
|
||||||
|
mocker.patch('freqtrade.exchange.Gateio.fetch_stoploss_order', return_value={'for': 123})
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order',
|
res = {'fee': {}, 'status': 'canceled', 'amount': 1234}
|
||||||
return_value={'fee': {}, 'status': 'canceled', 'amount': 1234})
|
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', return_value=res)
|
||||||
mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order',
|
mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order', return_value=res)
|
||||||
return_value={'fee': {}, 'status': 'canceled', 'amount': 1234})
|
mocker.patch('freqtrade.exchange.Gateio.cancel_stoploss_order', return_value=res)
|
||||||
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
|
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
|
||||||
assert co == {'fee': {}, 'status': 'canceled', 'amount': 1234}
|
assert co == res
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order',
|
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', return_value='canceled')
|
||||||
return_value='canceled')
|
mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order', return_value='canceled')
|
||||||
mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order',
|
mocker.patch('freqtrade.exchange.Gateio.cancel_stoploss_order', return_value='canceled')
|
||||||
return_value='canceled')
|
|
||||||
# Fall back to fetch_stoploss_order
|
# Fall back to fetch_stoploss_order
|
||||||
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
|
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
|
||||||
assert co == {'for': 123}
|
assert co == {'for': 123}
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order',
|
exc = InvalidOrderException("")
|
||||||
side_effect=InvalidOrderException(""))
|
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', side_effect=exc)
|
||||||
mocker.patch('freqtrade.exchange.Ftx.fetch_stoploss_order',
|
mocker.patch('freqtrade.exchange.Ftx.fetch_stoploss_order', side_effect=exc)
|
||||||
side_effect=InvalidOrderException(""))
|
mocker.patch('freqtrade.exchange.Gateio.fetch_stoploss_order', side_effect=exc)
|
||||||
|
|
||||||
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
|
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
|
||||||
assert co['amount'] == 555
|
assert co['amount'] == 555
|
||||||
assert co == {'fee': {}, 'status': 'canceled', 'amount': 555, 'info': {}}
|
assert co == {'fee': {}, 'status': 'canceled', 'amount': 555, 'info': {}}
|
||||||
|
|
||||||
with pytest.raises(InvalidOrderException):
|
with pytest.raises(InvalidOrderException):
|
||||||
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order',
|
exc = InvalidOrderException("Did not find order")
|
||||||
side_effect=InvalidOrderException("Did not find order"))
|
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', side_effect=exc)
|
||||||
mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order',
|
mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order', side_effect=exc)
|
||||||
side_effect=InvalidOrderException("Did not find order"))
|
mocker.patch('freqtrade.exchange.Gateio.cancel_stoploss_order', side_effect=exc)
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||||
exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=123)
|
exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=123)
|
||||||
|
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import Gateio
|
from freqtrade.exchange import Gateio
|
||||||
from freqtrade.resolvers.exchange_resolver import ExchangeResolver
|
from freqtrade.resolvers.exchange_resolver import ExchangeResolver
|
||||||
|
from tests.conftest import get_patched_exchange
|
||||||
|
|
||||||
|
|
||||||
def test_validate_order_types_gateio(default_conf, mocker):
|
def test_validate_order_types_gateio(default_conf, mocker):
|
||||||
@ -26,3 +29,43 @@ def test_validate_order_types_gateio(default_conf, mocker):
|
|||||||
with pytest.raises(OperationalException,
|
with pytest.raises(OperationalException,
|
||||||
match=r'Exchange .* does not support market orders.'):
|
match=r'Exchange .* does not support market orders.'):
|
||||||
ExchangeResolver.load_exchange('gateio', default_conf, True)
|
ExchangeResolver.load_exchange('gateio', default_conf, True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_fetch_stoploss_order_gateio(default_conf, mocker):
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id='gateio')
|
||||||
|
|
||||||
|
fetch_order_mock = MagicMock()
|
||||||
|
exchange.fetch_order = fetch_order_mock
|
||||||
|
|
||||||
|
exchange.fetch_stoploss_order('1234', 'ETH/BTC')
|
||||||
|
assert fetch_order_mock.call_count == 1
|
||||||
|
assert fetch_order_mock.call_args_list[0][1]['order_id'] == '1234'
|
||||||
|
assert fetch_order_mock.call_args_list[0][1]['pair'] == 'ETH/BTC'
|
||||||
|
assert fetch_order_mock.call_args_list[0][1]['params'] == {'stop': True}
|
||||||
|
|
||||||
|
|
||||||
|
def test_cancel_stoploss_order_gateio(default_conf, mocker):
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id='gateio')
|
||||||
|
|
||||||
|
cancel_order_mock = MagicMock()
|
||||||
|
exchange.cancel_order = cancel_order_mock
|
||||||
|
|
||||||
|
exchange.cancel_stoploss_order('1234', 'ETH/BTC')
|
||||||
|
assert cancel_order_mock.call_count == 1
|
||||||
|
assert cancel_order_mock.call_args_list[0][1]['order_id'] == '1234'
|
||||||
|
assert cancel_order_mock.call_args_list[0][1]['pair'] == 'ETH/BTC'
|
||||||
|
assert cancel_order_mock.call_args_list[0][1]['params'] == {'stop': True}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('sl1,sl2,sl3,side', [
|
||||||
|
(1501, 1499, 1501, "sell"),
|
||||||
|
(1499, 1501, 1499, "buy")
|
||||||
|
])
|
||||||
|
def test_stoploss_adjust_gateio(mocker, default_conf, sl1, sl2, sl3, side):
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id='gateio')
|
||||||
|
order = {
|
||||||
|
'price': 1500,
|
||||||
|
'stopPrice': 1500,
|
||||||
|
}
|
||||||
|
assert exchange.stoploss_adjust(sl1, order, side)
|
||||||
|
assert not exchange.stoploss_adjust(sl2, order, side)
|
||||||
|
@ -19,6 +19,7 @@ class BTrade(NamedTuple):
|
|||||||
open_tick: int
|
open_tick: int
|
||||||
close_tick: int
|
close_tick: int
|
||||||
enter_tag: Optional[str] = None
|
enter_tag: Optional[str] = None
|
||||||
|
is_short: bool = False
|
||||||
|
|
||||||
|
|
||||||
class BTContainer(NamedTuple):
|
class BTContainer(NamedTuple):
|
||||||
|
@ -15,7 +15,7 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe,
|
|||||||
# Test 0: Sell with signal sell in candle 3
|
# Test 0: Sell with signal sell in candle 3
|
||||||
# Test with Stop-loss at 1%
|
# Test with Stop-loss at 1%
|
||||||
tc0 = BTContainer(data=[
|
tc0 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4987, 5012, 4986, 4986, 6172, 0, 0], # exit with stoploss hit
|
[2, 4987, 5012, 4986, 4986, 6172, 0, 0], # exit with stoploss hit
|
||||||
@ -29,7 +29,7 @@ tc0 = BTContainer(data=[
|
|||||||
# Test 1: Stop-Loss Triggered 1% loss
|
# Test 1: Stop-Loss Triggered 1% loss
|
||||||
# Test with Stop-loss at 1%
|
# Test with Stop-loss at 1%
|
||||||
tc1 = BTContainer(data=[
|
tc1 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit
|
[2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit
|
||||||
@ -44,7 +44,7 @@ tc1 = BTContainer(data=[
|
|||||||
# Test 2: Minus 4% Low, minus 1% close
|
# Test 2: Minus 4% Low, minus 1% close
|
||||||
# Test with Stop-Loss at 3%
|
# Test with Stop-Loss at 3%
|
||||||
tc2 = BTContainer(data=[
|
tc2 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4987, 5012, 4962, 4975, 6172, 0, 0],
|
[2, 4987, 5012, 4962, 4975, 6172, 0, 0],
|
||||||
@ -63,7 +63,7 @@ tc2 = BTContainer(data=[
|
|||||||
# Trade-A: Stop-Loss Triggered 2% Loss
|
# Trade-A: Stop-Loss Triggered 2% Loss
|
||||||
# Trade-B: Stop-Loss Triggered 2% Loss
|
# Trade-B: Stop-Loss Triggered 2% Loss
|
||||||
tc3 = BTContainer(data=[
|
tc3 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4987, 5012, 4800, 4975, 6172, 0, 0], # exit with stoploss hit
|
[2, 4987, 5012, 4800, 4975, 6172, 0, 0], # exit with stoploss hit
|
||||||
@ -81,7 +81,7 @@ tc3 = BTContainer(data=[
|
|||||||
# Test with Stop-loss at 2% ROI 6%
|
# Test with Stop-loss at 2% ROI 6%
|
||||||
# Stop-Loss Triggered 2% Loss
|
# Stop-Loss Triggered 2% Loss
|
||||||
tc4 = BTContainer(data=[
|
tc4 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit
|
[2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit
|
||||||
@ -95,7 +95,7 @@ tc4 = BTContainer(data=[
|
|||||||
# Test 5: Drops 0.5% Closes +20%, ROI triggers 3% Gain
|
# Test 5: Drops 0.5% Closes +20%, ROI triggers 3% Gain
|
||||||
# stop-loss: 1%, ROI: 3%
|
# stop-loss: 1%, ROI: 3%
|
||||||
tc5 = BTContainer(data=[
|
tc5 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4980, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4980, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4987, 5025, 4975, 4987, 6172, 0, 0],
|
[2, 4987, 5025, 4975, 4987, 6172, 0, 0],
|
||||||
@ -109,7 +109,7 @@ tc5 = BTContainer(data=[
|
|||||||
# Test 6: Drops 3% / Recovers 6% Positive / Closes 1% positve, Stop-Loss triggers 2% Loss
|
# Test 6: Drops 3% / Recovers 6% Positive / Closes 1% positve, Stop-Loss triggers 2% Loss
|
||||||
# stop-loss: 2% ROI: 5%
|
# stop-loss: 2% ROI: 5%
|
||||||
tc6 = BTContainer(data=[
|
tc6 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss
|
[2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss
|
||||||
@ -123,7 +123,7 @@ tc6 = BTContainer(data=[
|
|||||||
# Test 7: 6% Positive / 1% Negative / Close 1% Positve, ROI Triggers 3% Gain
|
# Test 7: 6% Positive / 1% Negative / Close 1% Positve, ROI Triggers 3% Gain
|
||||||
# stop-loss: 2% ROI: 3%
|
# stop-loss: 2% ROI: 3%
|
||||||
tc7 = BTContainer(data=[
|
tc7 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||||
[2, 4987, 5300, 4950, 5050, 6172, 0, 0],
|
[2, 4987, 5300, 4950, 5050, 6172, 0, 0],
|
||||||
@ -138,7 +138,7 @@ tc7 = BTContainer(data=[
|
|||||||
# Test 8: trailing_stop should raise so candle 3 causes a stoploss.
|
# Test 8: trailing_stop should raise so candle 3 causes a stoploss.
|
||||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 2
|
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 2
|
||||||
tc8 = BTContainer(data=[
|
tc8 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5050, 4950, 5000, 6172, 0, 0],
|
[1, 5000, 5050, 4950, 5000, 6172, 0, 0],
|
||||||
[2, 5000, 5250, 4750, 4850, 6172, 0, 0],
|
[2, 5000, 5250, 4750, 4850, 6172, 0, 0],
|
||||||
@ -152,7 +152,7 @@ tc8 = BTContainer(data=[
|
|||||||
# Test 9: trailing_stop should raise - high and low in same candle.
|
# Test 9: trailing_stop should raise - high and low in same candle.
|
||||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 3
|
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 3
|
||||||
tc9 = BTContainer(data=[
|
tc9 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5050, 4950, 5000, 6172, 0, 0],
|
[1, 5000, 5050, 4950, 5000, 6172, 0, 0],
|
||||||
[2, 5000, 5050, 4950, 5000, 6172, 0, 0],
|
[2, 5000, 5050, 4950, 5000, 6172, 0, 0],
|
||||||
@ -166,7 +166,7 @@ tc9 = BTContainer(data=[
|
|||||||
# without applying trailing_stop_positive since stoploss_offset is at 10%.
|
# without applying trailing_stop_positive since stoploss_offset is at 10%.
|
||||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
||||||
tc10 = BTContainer(data=[
|
tc10 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||||
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
||||||
@ -182,7 +182,7 @@ tc10 = BTContainer(data=[
|
|||||||
# applying a positive trailing stop of 3% since stop_positive_offset is reached.
|
# applying a positive trailing stop of 3% since stop_positive_offset is reached.
|
||||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
||||||
tc11 = BTContainer(data=[
|
tc11 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||||
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
||||||
@ -198,7 +198,7 @@ tc11 = BTContainer(data=[
|
|||||||
# applying a positive trailing stop of 3% since stop_positive_offset is reached.
|
# applying a positive trailing stop of 3% since stop_positive_offset is reached.
|
||||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
||||||
tc12 = BTContainer(data=[
|
tc12 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||||
[2, 5100, 5251, 4650, 5100, 6172, 0, 0],
|
[2, 5100, 5251, 4650, 5100, 6172, 0, 0],
|
||||||
@ -213,7 +213,7 @@ tc12 = BTContainer(data=[
|
|||||||
# Test 13: Buy and sell ROI on same candle
|
# Test 13: Buy and sell ROI on same candle
|
||||||
# stop-loss: 10% (should not apply), ROI: 1%
|
# stop-loss: 10% (should not apply), ROI: 1%
|
||||||
tc13 = BTContainer(data=[
|
tc13 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||||
[2, 5100, 5251, 4850, 5100, 6172, 0, 0],
|
[2, 5100, 5251, 4850, 5100, 6172, 0, 0],
|
||||||
@ -226,7 +226,7 @@ tc13 = BTContainer(data=[
|
|||||||
# Test 14 - Buy and Stoploss on same candle
|
# Test 14 - Buy and Stoploss on same candle
|
||||||
# stop-loss: 5%, ROI: 10% (should not apply)
|
# stop-loss: 5%, ROI: 10% (should not apply)
|
||||||
tc14 = BTContainer(data=[
|
tc14 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5100, 4600, 5100, 6172, 0, 0],
|
[1, 5000, 5100, 4600, 5100, 6172, 0, 0],
|
||||||
[2, 5100, 5251, 4850, 5100, 6172, 0, 0],
|
[2, 5100, 5251, 4850, 5100, 6172, 0, 0],
|
||||||
@ -240,7 +240,7 @@ tc14 = BTContainer(data=[
|
|||||||
# Test 15 - Buy and ROI on same candle, followed by buy and Stoploss on next candle
|
# Test 15 - Buy and ROI on same candle, followed by buy and Stoploss on next candle
|
||||||
# stop-loss: 5%, ROI: 10% (should not apply)
|
# stop-loss: 5%, ROI: 10% (should not apply)
|
||||||
tc15 = BTContainer(data=[
|
tc15 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5100, 4900, 5100, 6172, 1, 0],
|
[1, 5000, 5100, 4900, 5100, 6172, 1, 0],
|
||||||
[2, 5100, 5251, 4650, 5100, 6172, 0, 0],
|
[2, 5100, 5251, 4650, 5100, 6172, 0, 0],
|
||||||
@ -255,7 +255,7 @@ tc15 = BTContainer(data=[
|
|||||||
# Causes negative profit even though sell-reason is ROI.
|
# Causes negative profit even though sell-reason is ROI.
|
||||||
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 65 minutes (limits trade duration)
|
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 65 minutes (limits trade duration)
|
||||||
tc16 = BTContainer(data=[
|
tc16 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||||
[2, 4987, 5300, 4950, 5050, 6172, 0, 0],
|
[2, 4987, 5300, 4950, 5050, 6172, 0, 0],
|
||||||
@ -271,7 +271,7 @@ tc16 = BTContainer(data=[
|
|||||||
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
||||||
# Uses open as sell-rate (special case) - since the roi-time is a multiple of the timeframe.
|
# Uses open as sell-rate (special case) - since the roi-time is a multiple of the timeframe.
|
||||||
tc17 = BTContainer(data=[
|
tc17 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||||
[2, 4987, 5300, 4950, 5050, 6172, 0, 0],
|
[2, 4987, 5300, 4950, 5050, 6172, 0, 0],
|
||||||
@ -287,7 +287,7 @@ tc17 = BTContainer(data=[
|
|||||||
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
||||||
# uses open_rate as sell-price
|
# uses open_rate as sell-price
|
||||||
tc18 = BTContainer(data=[
|
tc18 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||||
[2, 4987, 5300, 4950, 5200, 6172, 0, 0],
|
[2, 4987, 5300, 4950, 5200, 6172, 0, 0],
|
||||||
@ -302,7 +302,7 @@ tc18 = BTContainer(data=[
|
|||||||
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
||||||
# uses calculated ROI (1%) as sell rate, otherwise identical to tc18
|
# uses calculated ROI (1%) as sell rate, otherwise identical to tc18
|
||||||
tc19 = BTContainer(data=[
|
tc19 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||||
[2, 4987, 5300, 4950, 5200, 6172, 0, 0],
|
[2, 4987, 5300, 4950, 5200, 6172, 0, 0],
|
||||||
@ -317,7 +317,7 @@ tc19 = BTContainer(data=[
|
|||||||
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
||||||
# uses calculated ROI (1%) as sell rate, otherwise identical to tc18
|
# uses calculated ROI (1%) as sell rate, otherwise identical to tc18
|
||||||
tc20 = BTContainer(data=[
|
tc20 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||||
[2, 4987, 5300, 4950, 5200, 6172, 0, 0],
|
[2, 4987, 5300, 4950, 5200, 6172, 0, 0],
|
||||||
@ -333,7 +333,7 @@ tc20 = BTContainer(data=[
|
|||||||
# which cannot happen in reality
|
# which cannot happen in reality
|
||||||
# stop-loss: 10%, ROI: 4%, Trailing stop adjusted at the sell candle
|
# stop-loss: 10%, ROI: 4%, Trailing stop adjusted at the sell candle
|
||||||
tc21 = BTContainer(data=[
|
tc21 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||||
[2, 5100, 5251, 4650, 5100, 6172, 0, 0],
|
[2, 5100, 5251, 4650, 5100, 6172, 0, 0],
|
||||||
@ -349,7 +349,7 @@ tc21 = BTContainer(data=[
|
|||||||
# applying a positive trailing stop of 3% - ROI should apply before trailing stop.
|
# applying a positive trailing stop of 3% - ROI should apply before trailing stop.
|
||||||
# stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2
|
# stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2
|
||||||
tc22 = BTContainer(data=[
|
tc22 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||||
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
||||||
@ -361,6 +361,23 @@ tc22 = BTContainer(data=[
|
|||||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)]
|
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Test 22s: trailing_stop Raises in candle 2 - but ROI applies at the same time.
|
||||||
|
# applying a positive trailing stop of 3% - ROI should apply before trailing stop.
|
||||||
|
# stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2
|
||||||
|
tc22s = BTContainer(data=[
|
||||||
|
# D O H L C V EL XL ES Xs BT
|
||||||
|
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0],
|
||||||
|
[1, 5000, 5050, 4900, 4900, 6172, 0, 0, 0, 0],
|
||||||
|
[2, 4900, 4900, 4749, 4900, 6172, 0, 0, 0, 0],
|
||||||
|
[3, 4850, 5050, 4650, 4750, 6172, 0, 0, 0, 0],
|
||||||
|
[4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]],
|
||||||
|
stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True,
|
||||||
|
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
|
||||||
|
trailing_stop_positive=0.03,
|
||||||
|
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2, is_short=True)]
|
||||||
|
)
|
||||||
|
|
||||||
# Test 23: trailing_stop Raises in candle 2 (does not trigger)
|
# Test 23: trailing_stop Raises in candle 2 (does not trigger)
|
||||||
# applying a positive trailing stop of 3% since stop_positive_offset is reached.
|
# applying a positive trailing stop of 3% since stop_positive_offset is reached.
|
||||||
# ROI is changed after this to 4%, dropping ROI below trailing_stop_positive, causing a sell
|
# ROI is changed after this to 4%, dropping ROI below trailing_stop_positive, causing a sell
|
||||||
@ -368,7 +385,7 @@ tc22 = BTContainer(data=[
|
|||||||
# Stoploss would trigger in this candle too, but it's no longer relevant.
|
# Stoploss would trigger in this candle too, but it's no longer relevant.
|
||||||
# stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2, ROI adjusted in candle 3 (causing the sell)
|
# stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2, ROI adjusted in candle 3 (causing the sell)
|
||||||
tc23 = BTContainer(data=[
|
tc23 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||||
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
||||||
@ -384,7 +401,7 @@ tc23 = BTContainer(data=[
|
|||||||
# Stoploss at 1%.
|
# Stoploss at 1%.
|
||||||
# Stoploss wins over Sell-signal (because sell-signal is acted on in the next candle)
|
# Stoploss wins over Sell-signal (because sell-signal is acted on in the next candle)
|
||||||
tc24 = BTContainer(data=[
|
tc24 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4987, 5012, 4986, 4986, 6172, 0, 0],
|
[2, 4987, 5012, 4986, 4986, 6172, 0, 0],
|
||||||
@ -399,7 +416,7 @@ tc24 = BTContainer(data=[
|
|||||||
# Stoploss at 1%.
|
# Stoploss at 1%.
|
||||||
# Sell-signal wins over stoploss
|
# Sell-signal wins over stoploss
|
||||||
tc25 = BTContainer(data=[
|
tc25 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4987, 5012, 4986, 4986, 6172, 0, 0],
|
[2, 4987, 5012, 4986, 4986, 6172, 0, 0],
|
||||||
@ -410,11 +427,44 @@ tc25 = BTContainer(data=[
|
|||||||
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)]
|
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Test 25l: (copy of test25 with leverage)
|
||||||
|
# Sell with signal sell in candle 3 (stoploss also triggers on this candle)
|
||||||
|
# Stoploss at 1%.
|
||||||
|
# Sell-signal wins over stoploss
|
||||||
|
tc25l = BTContainer(data=[
|
||||||
|
# D O H L C V EL XL ES Xs BT
|
||||||
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
|
[2, 4987, 5012, 4986, 4986, 6172, 0, 0],
|
||||||
|
[3, 5010, 5010, 4986, 5010, 6172, 0, 1],
|
||||||
|
[4, 5010, 5010, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on
|
||||||
|
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
|
||||||
|
stop_loss=-0.05, roi={"0": 1}, profit_perc=0.002 * 5.0, use_sell_signal=True,
|
||||||
|
leverage=5.0,
|
||||||
|
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test 25s: (copy of test25 with leverage and as short)
|
||||||
|
# Sell with signal sell in candle 3 (stoploss also triggers on this candle)
|
||||||
|
# Stoploss at 1%.
|
||||||
|
# Sell-signal wins over stoploss
|
||||||
|
tc25s = BTContainer(data=[
|
||||||
|
# D O H L C V EL XL ES Xs BT
|
||||||
|
[0, 5000, 5025, 4975, 4987, 6172, 0, 0, 1, 0],
|
||||||
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0, 0, 0], # enter trade (signal on last candle)
|
||||||
|
[2, 4987, 5012, 4986, 4986, 6172, 0, 0, 0, 0],
|
||||||
|
[3, 5010, 5010, 4986, 5010, 6172, 0, 0, 0, 1],
|
||||||
|
[4, 4990, 5010, 4855, 4995, 6172, 0, 0, 0, 0], # Triggers stoploss + sellsignal acted on
|
||||||
|
[5, 4995, 4995, 4950, 4950, 6172, 0, 0, 0, 0]],
|
||||||
|
stop_loss=-0.05, roi={"0": 1}, profit_perc=0.002 * 5.0, use_sell_signal=True,
|
||||||
|
leverage=5.0,
|
||||||
|
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4, is_short=True)]
|
||||||
|
)
|
||||||
# Test 26: Sell with signal sell in candle 3 (ROI at signal candle)
|
# Test 26: Sell with signal sell in candle 3 (ROI at signal candle)
|
||||||
# Stoploss at 10% (irrelevant), ROI at 5% (will trigger)
|
# Stoploss at 10% (irrelevant), ROI at 5% (will trigger)
|
||||||
# Sell-signal wins over stoploss
|
# Sell-signal wins over stoploss
|
||||||
tc26 = BTContainer(data=[
|
tc26 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4987, 5012, 4986, 4986, 6172, 0, 0],
|
[2, 4987, 5012, 4986, 4986, 6172, 0, 0],
|
||||||
@ -428,7 +478,7 @@ tc26 = BTContainer(data=[
|
|||||||
# Test 27: Sell with signal sell in candle 3 (ROI at signal candle)
|
# Test 27: Sell with signal sell in candle 3 (ROI at signal candle)
|
||||||
# Stoploss at 10% (irrelevant), ROI at 5% (will trigger) - Wins over Sell-signal
|
# Stoploss at 10% (irrelevant), ROI at 5% (will trigger) - Wins over Sell-signal
|
||||||
tc27 = BTContainer(data=[
|
tc27 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4987, 5012, 4986, 4986, 6172, 0, 0],
|
[2, 4987, 5012, 4986, 4986, 6172, 0, 0],
|
||||||
@ -444,7 +494,7 @@ tc27 = BTContainer(data=[
|
|||||||
# therefore "open" will be used
|
# therefore "open" will be used
|
||||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
||||||
tc28 = BTContainer(data=[
|
tc28 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||||
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
||||||
@ -456,11 +506,30 @@ tc28 = BTContainer(data=[
|
|||||||
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
|
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Test 28s: trailing_stop should raise so candle 3 causes a stoploss
|
||||||
|
# Same case than tc11 - but candle 3 "gaps down" - the stoploss will be above the candle,
|
||||||
|
# therefore "open" will be used
|
||||||
|
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
||||||
|
tc28s = BTContainer(data=[
|
||||||
|
# D O H L C V EL XL ES Xs BT
|
||||||
|
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0],
|
||||||
|
[1, 5000, 5050, 4890, 4890, 6172, 0, 0, 0, 0],
|
||||||
|
[2, 4890, 4890, 4749, 4890, 6172, 0, 0, 0, 0],
|
||||||
|
[3, 5150, 5350, 4950, 4950, 6172, 0, 0, 0, 0],
|
||||||
|
[4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]],
|
||||||
|
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.03, trailing_stop=True,
|
||||||
|
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
|
||||||
|
trailing_stop_positive=0.03,
|
||||||
|
trades=[
|
||||||
|
BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3, is_short=True)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# Test 29: trailing_stop should be triggered by low of next candle, without adjusting stoploss using
|
# Test 29: trailing_stop should be triggered by low of next candle, without adjusting stoploss using
|
||||||
# high of stoploss candle.
|
# high of stoploss candle.
|
||||||
# stop-loss: 10%, ROI: 10% (should not apply)
|
# stop-loss: 10%, ROI: 10% (should not apply)
|
||||||
tc29 = BTContainer(data=[
|
tc29 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5050, 5000, 5000, 6172, 0, 0], # enter trade (signal on last candle)
|
[1, 5000, 5050, 5000, 5000, 6172, 0, 0], # enter trade (signal on last candle)
|
||||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Triggers trailing-stoploss
|
[2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Triggers trailing-stoploss
|
||||||
@ -474,7 +543,7 @@ tc29 = BTContainer(data=[
|
|||||||
# Test 30: trailing_stop should be triggered immediately on trade open candle.
|
# Test 30: trailing_stop should be triggered immediately on trade open candle.
|
||||||
# stop-loss: 10%, ROI: 10% (should not apply)
|
# stop-loss: 10%, ROI: 10% (should not apply)
|
||||||
tc30 = BTContainer(data=[
|
tc30 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5500, 4900, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop
|
[1, 5000, 5500, 4900, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop
|
||||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||||
@ -488,7 +557,7 @@ tc30 = BTContainer(data=[
|
|||||||
# Test 31: trailing_stop should be triggered immediately on trade open candle.
|
# Test 31: trailing_stop should be triggered immediately on trade open candle.
|
||||||
# stop-loss: 10%, ROI: 10% (should not apply)
|
# stop-loss: 10%, ROI: 10% (should not apply)
|
||||||
tc31 = BTContainer(data=[
|
tc31 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5500, 4900, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop
|
[1, 5000, 5500, 4900, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop
|
||||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||||
@ -503,7 +572,7 @@ tc31 = BTContainer(data=[
|
|||||||
# Test 32: trailing_stop should be triggered immediately on trade open candle.
|
# Test 32: trailing_stop should be triggered immediately on trade open candle.
|
||||||
# stop-loss: 1%, ROI: 10% (should not apply)
|
# stop-loss: 1%, ROI: 10% (should not apply)
|
||||||
tc32 = BTContainer(data=[
|
tc32 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0], # enter trade and stop
|
[1, 5000, 5500, 4951, 5000, 6172, 0, 0], # enter trade and stop
|
||||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||||
@ -534,10 +603,31 @@ tc33 = BTContainer(data=[
|
|||||||
enter_tag='buy_signal_01'
|
enter_tag='buy_signal_01'
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
|
# Test 33s: trailing_stop should be triggered immediately on trade open candle.
|
||||||
|
# copy of Test33 using shorts.
|
||||||
|
# stop-loss: 1%, ROI: 10% (should not apply)
|
||||||
|
tc33s = BTContainer(data=[
|
||||||
|
# D O H L C V EL XL ES Xs BT
|
||||||
|
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0, 'short_signal_01'],
|
||||||
|
[1, 5000, 5049, 4500, 5000, 6172, 0, 0, 0, 0, None], # enter trade and stop
|
||||||
|
[2, 4900, 5250, 4500, 5100, 6172, 0, 0, 0, 0, None],
|
||||||
|
[3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0, None],
|
||||||
|
[4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0, None]],
|
||||||
|
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True,
|
||||||
|
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02,
|
||||||
|
trailing_stop_positive=0.01, use_custom_stoploss=True,
|
||||||
|
trades=[BTrade(
|
||||||
|
sell_reason=SellType.TRAILING_STOP_LOSS,
|
||||||
|
open_tick=1,
|
||||||
|
close_tick=1,
|
||||||
|
enter_tag='short_signal_01',
|
||||||
|
is_short=True,
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
|
||||||
# Test 34: Custom-entry-price below all candles should timeout - so no trade happens.
|
# Test 34: Custom-entry-price below all candles should timeout - so no trade happens.
|
||||||
tc34 = BTContainer(data=[
|
tc34 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0], # timeout
|
[1, 5000, 5500, 4951, 5000, 6172, 0, 0], # timeout
|
||||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||||
@ -549,7 +639,7 @@ tc34 = BTContainer(data=[
|
|||||||
|
|
||||||
# Test 35: Custom-entry-price above all candles should have rate adjusted to "entry candle high"
|
# Test 35: Custom-entry-price above all candles should have rate adjusted to "entry candle high"
|
||||||
tc35 = BTContainer(data=[
|
tc35 = BTContainer(data=[
|
||||||
# D O H L C V B S
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0], # Timeout
|
[1, 5000, 5500, 4951, 5000, 6172, 0, 0], # Timeout
|
||||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||||
@ -558,6 +648,20 @@ tc35 = BTContainer(data=[
|
|||||||
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01,
|
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01,
|
||||||
custom_entry_price=7200, trades=[
|
custom_entry_price=7200, trades=[
|
||||||
BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)
|
BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)
|
||||||
|
])
|
||||||
|
|
||||||
|
# Test 35s: Custom-entry-price above all candles should have rate adjusted to "entry candle high"
|
||||||
|
tc35s = BTContainer(data=[
|
||||||
|
# D O H L C V EL XL ES Xs BT
|
||||||
|
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0],
|
||||||
|
[1, 5000, 5500, 4951, 5000, 6172, 0, 0, 0, 0], # Timeout
|
||||||
|
[2, 4900, 5250, 4500, 5100, 6172, 0, 0, 0, 0],
|
||||||
|
[3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0],
|
||||||
|
[4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]],
|
||||||
|
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01,
|
||||||
|
custom_entry_price=4000,
|
||||||
|
trades=[
|
||||||
|
BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1, is_short=True)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -566,7 +670,7 @@ tc35 = BTContainer(data=[
|
|||||||
# below open, we treat this as cheating, and delay the sell by 1 candle.
|
# below open, we treat this as cheating, and delay the sell by 1 candle.
|
||||||
# details: https://github.com/freqtrade/freqtrade/issues/6261
|
# details: https://github.com/freqtrade/freqtrade/issues/6261
|
||||||
tc36 = BTContainer(data=[
|
tc36 = BTContainer(data=[
|
||||||
# D O H L C V B S BT
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5500, 4951, 4999, 6172, 0, 0], # Enter and immediate ROI
|
[1, 5000, 5500, 4951, 4999, 6172, 0, 0], # Enter and immediate ROI
|
||||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||||
@ -581,7 +685,7 @@ tc36 = BTContainer(data=[
|
|||||||
# Would cause immediate ROI exit below close
|
# Would cause immediate ROI exit below close
|
||||||
# details: https://github.com/freqtrade/freqtrade/issues/6261
|
# details: https://github.com/freqtrade/freqtrade/issues/6261
|
||||||
tc37 = BTContainer(data=[
|
tc37 = BTContainer(data=[
|
||||||
# D O H L C V B S BT
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5400, 5500, 4951, 5100, 6172, 0, 0], # Enter and immediate ROI
|
[1, 5400, 5500, 4951, 5100, 6172, 0, 0], # Enter and immediate ROI
|
||||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||||
@ -595,7 +699,7 @@ tc37 = BTContainer(data=[
|
|||||||
# Test 38: Custom exit price below all candles
|
# Test 38: Custom exit price below all candles
|
||||||
# Price adjusted to candle Low.
|
# Price adjusted to candle Low.
|
||||||
tc38 = BTContainer(data=[
|
tc38 = BTContainer(data=[
|
||||||
# D O H L C V B S BT
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0],
|
[1, 5000, 5500, 4951, 5000, 6172, 0, 0],
|
||||||
[2, 4900, 5250, 4900, 5100, 6172, 0, 1], # exit - but timeout
|
[2, 4900, 5250, 4900, 5100, 6172, 0, 1], # exit - but timeout
|
||||||
@ -610,10 +714,10 @@ tc38 = BTContainer(data=[
|
|||||||
# Test 39: Custom exit price above all candles
|
# Test 39: Custom exit price above all candles
|
||||||
# causes sell signal timeout
|
# causes sell signal timeout
|
||||||
tc39 = BTContainer(data=[
|
tc39 = BTContainer(data=[
|
||||||
# D O H L C V B S BT
|
# D O H L C V EL XL ES Xs BT
|
||||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0],
|
[1, 5000, 5500, 4951, 5000, 6172, 0, 0],
|
||||||
[2, 4900, 5250, 4900, 5100, 6172, 0, 1], # exit - but timeout
|
[2, 4950, 5250, 4900, 5100, 6172, 0, 1], # exit - entry timeout
|
||||||
[3, 5100, 5100, 4950, 4950, 6172, 0, 0],
|
[3, 5100, 5100, 4950, 4950, 6172, 0, 0],
|
||||||
[4, 5000, 5100, 4950, 4950, 6172, 0, 0]],
|
[4, 5000, 5100, 4950, 4950, 6172, 0, 0]],
|
||||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0,
|
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0,
|
||||||
@ -622,21 +726,32 @@ tc39 = BTContainer(data=[
|
|||||||
trades=[BTrade(sell_reason=SellType.FORCE_SELL, open_tick=1, close_tick=4)]
|
trades=[BTrade(sell_reason=SellType.FORCE_SELL, open_tick=1, close_tick=4)]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Test 39: (copy of test25 with leverage)
|
# Test 39: Custom short exit price above below candles
|
||||||
# Sell with signal sell in candle 3 (stoploss also triggers on this candle)
|
# causes sell signal timeout
|
||||||
# Stoploss at 1%.
|
tc39a = BTContainer(data=[
|
||||||
# Sell-signal wins over stoploss
|
# D O H L C V EL XL ES Xs BT
|
||||||
tc39 = BTContainer(data=[
|
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0],
|
||||||
# D O H L C V B S
|
[1, 5000, 5000, 4951, 5000, 6172, 0, 0, 0, 0],
|
||||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
[2, 4910, 5150, 4910, 5100, 6172, 0, 0, 0, 1], # exit - entry timeout
|
||||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
[3, 5100, 5100, 4950, 4950, 6172, 0, 0, 0, 0],
|
||||||
[2, 4987, 5012, 4986, 4986, 6172, 0, 0],
|
[4, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 0]],
|
||||||
[3, 5010, 5010, 4986, 5010, 6172, 0, 1],
|
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0,
|
||||||
[4, 5010, 5010, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on
|
use_sell_signal=True,
|
||||||
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
|
custom_exit_price=4700,
|
||||||
stop_loss=-0.05, roi={"0": 1}, profit_perc=0.002 * 5.0, use_sell_signal=True,
|
trades=[BTrade(sell_reason=SellType.FORCE_SELL, open_tick=1, close_tick=4, is_short=True)]
|
||||||
leverage=5.0,
|
)
|
||||||
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)]
|
|
||||||
|
# Test 40: Colliding long and short signal
|
||||||
|
tc40 = BTContainer(data=[
|
||||||
|
# D O H L C V EL XL ES Xs BT
|
||||||
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0, 1, 0],
|
||||||
|
[1, 5000, 5500, 4951, 5000, 6172, 0, 0, 0, 0],
|
||||||
|
[2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 0],
|
||||||
|
[3, 5100, 5100, 4950, 4950, 6172, 0, 0, 0, 0],
|
||||||
|
[4, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 0]],
|
||||||
|
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0,
|
||||||
|
use_sell_signal=True,
|
||||||
|
trades=[]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -664,24 +779,31 @@ TESTS = [
|
|||||||
tc20,
|
tc20,
|
||||||
tc21,
|
tc21,
|
||||||
tc22,
|
tc22,
|
||||||
|
tc22s,
|
||||||
tc23,
|
tc23,
|
||||||
tc24,
|
tc24,
|
||||||
tc25,
|
tc25,
|
||||||
|
tc25l,
|
||||||
|
tc25s,
|
||||||
tc26,
|
tc26,
|
||||||
tc27,
|
tc27,
|
||||||
tc28,
|
tc28,
|
||||||
|
tc28s,
|
||||||
tc29,
|
tc29,
|
||||||
tc30,
|
tc30,
|
||||||
tc31,
|
tc31,
|
||||||
tc32,
|
tc32,
|
||||||
tc33,
|
tc33,
|
||||||
|
tc33s,
|
||||||
tc34,
|
tc34,
|
||||||
tc35,
|
tc35,
|
||||||
|
tc35s,
|
||||||
tc36,
|
tc36,
|
||||||
tc37,
|
tc37,
|
||||||
tc38,
|
tc38,
|
||||||
tc39,
|
tc39,
|
||||||
# TODO-lev: Add tests for short here
|
tc39a,
|
||||||
|
tc40,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -708,11 +830,10 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
|
|||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
frame = _build_backtest_dataframe(data.data)
|
frame = _build_backtest_dataframe(data.data)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
backtesting._set_strategy(backtesting.strategylist[0])
|
|
||||||
backtesting.required_startup = 0
|
|
||||||
if data.leverage > 1.0:
|
|
||||||
# TODO: Should we initialize this properly??
|
# TODO: Should we initialize this properly??
|
||||||
backtesting._can_short = True
|
backtesting._can_short = True
|
||||||
|
backtesting._set_strategy(backtesting.strategylist[0])
|
||||||
|
backtesting.required_startup = 0
|
||||||
backtesting.strategy.advise_entry = lambda a, m: frame
|
backtesting.strategy.advise_entry = lambda a, m: frame
|
||||||
backtesting.strategy.advise_exit = lambda a, m: frame
|
backtesting.strategy.advise_exit = lambda a, m: frame
|
||||||
if data.custom_entry_price:
|
if data.custom_entry_price:
|
||||||
@ -739,8 +860,9 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
|
|||||||
assert round(results["profit_ratio"].sum(), 3) == round(data.profit_perc, 3)
|
assert round(results["profit_ratio"].sum(), 3) == round(data.profit_perc, 3)
|
||||||
|
|
||||||
for c, trade in enumerate(data.trades):
|
for c, trade in enumerate(data.trades):
|
||||||
res = results.iloc[c]
|
res: BTrade = results.iloc[c]
|
||||||
assert res.sell_reason == trade.sell_reason.value
|
assert res.sell_reason == trade.sell_reason.value
|
||||||
assert res.enter_tag == trade.enter_tag
|
assert res.enter_tag == trade.enter_tag
|
||||||
assert res.open_date == _get_frame_time_from_offset(trade.open_tick)
|
assert res.open_date == _get_frame_time_from_offset(trade.open_tick)
|
||||||
assert res.close_date == _get_frame_time_from_offset(trade.close_tick)
|
assert res.close_date == _get_frame_time_from_offset(trade.close_tick)
|
||||||
|
assert res.is_short == trade.is_short
|
||||||
|
@ -582,13 +582,17 @@ def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None:
|
|||||||
pair = 'UNITTEST/USDT:USDT'
|
pair = 'UNITTEST/USDT:USDT'
|
||||||
row = [
|
row = [
|
||||||
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0),
|
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0),
|
||||||
1, # Buy
|
|
||||||
0.001, # Open
|
0.001, # Open
|
||||||
0.0011, # Close
|
|
||||||
0, # Sell
|
|
||||||
0.00099, # Low
|
|
||||||
0.0012, # High
|
0.0012, # High
|
||||||
'', # Buy Signal Name
|
0.00099, # Low
|
||||||
|
0.0011, # Close
|
||||||
|
1, # enter_long
|
||||||
|
0, # exit_long
|
||||||
|
1, # enter_short
|
||||||
|
0, # exit_hsort
|
||||||
|
'', # Long Signal Name
|
||||||
|
'', # Short Signal Name
|
||||||
|
'', # Exit Signal Name
|
||||||
]
|
]
|
||||||
|
|
||||||
backtesting.strategy.leverage = MagicMock(return_value=5.0)
|
backtesting.strategy.leverage = MagicMock(return_value=5.0)
|
||||||
|
@ -782,6 +782,19 @@ def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None
|
|||||||
get_patched_freqtradebot(mocker, default_conf)
|
get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pair_whitelist_not_supported_Spread(mocker, default_conf, tickers) -> None:
|
||||||
|
default_conf['pairlists'] = [{'method': 'StaticPairList'}, {'method': 'SpreadFilter'}]
|
||||||
|
|
||||||
|
mocker.patch.multiple('freqtrade.exchange.Exchange',
|
||||||
|
get_tickers=tickers,
|
||||||
|
exchange_has=MagicMock(return_value=False),
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(OperationalException,
|
||||||
|
match=r'Exchange does not support fetchTickers, .*'):
|
||||||
|
get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("pairlist", AVAILABLE_PAIRLISTS)
|
@pytest.mark.parametrize("pairlist", AVAILABLE_PAIRLISTS)
|
||||||
def test_pairlist_class(mocker, whitelist_conf, markets, pairlist):
|
def test_pairlist_class(mocker, whitelist_conf, markets, pairlist):
|
||||||
whitelist_conf['pairlists'][0]['method'] = pairlist
|
whitelist_conf['pairlists'][0]['method'] = pairlist
|
||||||
|
@ -650,8 +650,8 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
|
|||||||
rpc._fiat_converter = CryptoToFiatConverter()
|
rpc._fiat_converter = CryptoToFiatConverter()
|
||||||
|
|
||||||
result = rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
|
result = rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
|
||||||
assert prec_satoshi(result['total'], 30.309096315)
|
assert prec_satoshi(result['total'], 30.30909624)
|
||||||
assert prec_satoshi(result['value'], 454636.44472997)
|
assert prec_satoshi(result['value'], 454636.44360691)
|
||||||
assert tickers.call_count == 1
|
assert tickers.call_count == 1
|
||||||
assert tickers.call_args_list[0][1]['cached'] is True
|
assert tickers.call_args_list[0][1]['cached'] is True
|
||||||
assert 'USD' == result['symbol']
|
assert 'USD' == result['symbol']
|
||||||
@ -685,7 +685,7 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
|
|||||||
'free': 5.0,
|
'free': 5.0,
|
||||||
'balance': 10.0,
|
'balance': 10.0,
|
||||||
'currency': 'USDT',
|
'currency': 'USDT',
|
||||||
'est_stake': 0.0011563153318162476,
|
'est_stake': 0.0011562404610161968,
|
||||||
'used': 5.0,
|
'used': 5.0,
|
||||||
'stake': 'BTC',
|
'stake': 'BTC',
|
||||||
'is_position': False,
|
'is_position': False,
|
||||||
|
@ -26,7 +26,9 @@ def test_ttl_cache():
|
|||||||
assert 'a' in cache1h
|
assert 'a' in cache1h
|
||||||
|
|
||||||
t.move_to("2021-09-01 05:59:59 +00:00")
|
t.move_to("2021-09-01 05:59:59 +00:00")
|
||||||
|
assert 'a' not in cache
|
||||||
assert 'a' in cache1h
|
assert 'a' in cache1h
|
||||||
|
|
||||||
t.move_to("2021-09-01 06:00:00 +00:00")
|
t.move_to("2021-09-01 06:00:00 +00:00")
|
||||||
|
assert 'a' not in cache
|
||||||
assert 'a' not in cache1h
|
assert 'a' not in cache1h
|
||||||
|
Loading…
Reference in New Issue
Block a user