Merge pull request #6632 from freqtrade/short_terminology

Short terminology
This commit is contained in:
Matthias 2022-04-03 11:10:16 +02:00 committed by GitHub
commit d054916439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 269 additions and 246 deletions

View File

@ -56,7 +56,7 @@ Currently, the arguments are:
* `results`: DataFrame containing the resulting trades.
The following columns are available in results (corresponds to the output-file of backtesting when used with `--export trades`):
`pair, profit_ratio, profit_abs, open_date, open_rate, fee_open, close_date, close_rate, fee_close, amount, trade_duration, is_open, sell_reason, stake_amount, min_rate, max_rate, stop_loss_ratio, stop_loss_abs`
`pair, profit_ratio, profit_abs, open_date, open_rate, fee_open, close_date, close_rate, fee_close, amount, trade_duration, is_open, exit_reason, stake_amount, min_rate, max_rate, stop_loss_ratio, stop_loss_abs`
* `trade_count`: Amount of trades (identical to `len(results)`)
* `min_date`: Start date of the timerange used
* `min_date`: End date of the timerange used

View File

@ -65,7 +65,7 @@ SET is_open=0,
close_rate=<close_rate>,
close_profit = close_rate / open_rate - 1,
close_profit_abs = (amount * <close_rate> * (1 - fee_close) - (amount * (open_rate * (1 - fee_open)))),
sell_reason=<sell_reason>
exit_reason=<exit_reason>
WHERE id=<trade_ID_to_update>;
```
@ -78,7 +78,7 @@ SET is_open=0,
close_rate=0.19638016,
close_profit=0.0496,
close_profit_abs = (amount * 0.19638016 * (1 - fee_close) - (amount * (open_rate * (1 - fee_open)))),
sell_reason='force_sell'
exit_reason='force_sell'
WHERE id=31;
```

View File

@ -898,7 +898,7 @@ Stoploss values returned from `custom_stoploss` must specify a percentage relati
!!! Note
Providing invalid input to `stoploss_from_open()` may produce "CustomStoploss function did not return valid stoploss" warnings.
This may happen if `current_profit` parameter is below specified `open_relative_stop`. Such situations may arise when closing trade
is blocked by `confirm_trade_exit()` method. Warnings can be solved by never blocking stop loss sells by checking `sell_reason` in
is blocked by `confirm_trade_exit()` method. Warnings can be solved by never blocking stop loss sells by checking `exit_reason` in
`confirm_trade_exit()`, or by using `return stoploss_from_open(...) or 1` idiom, which will request to not change stop loss when
`current_profit < open_relative_stop`.

View File

@ -129,7 +129,7 @@ print(stats['strategy_comparison'])
trades = load_backtest_data(backtest_dir)
# Show value-counts per pair
trades.groupby("pair")["sell_reason"].value_counts()
trades.groupby("pair")["exit_reason"].value_counts()
```
## Plotting daily profit / equity line
@ -182,7 +182,7 @@ from freqtrade.data.btanalysis import load_trades_from_db
trades = load_trades_from_db("sqlite:///tradesv3.sqlite")
# Display results
trades.groupby("pair")["sell_reason"].value_counts()
trades.groupby("pair")["exit_reason"].value_counts()
```
## Analyze the loaded trades for trade parallelism

View File

@ -24,7 +24,12 @@ You can use the quick summary as checklist. Please refer to the detailed section
* [`sell` -> `exit_long`](#populate_sell_trend)
* [`buy_tag` -> `enter_tag` (used for both long and short trades)](#populate_buy_trend)
* [New column `enter_short` and corresponding new column `exit_short`](#populate_sell_trend)
* trade-object now has the following new properties: `is_short`, `enter_side`, `exit_side` and `trade_direction`.
* trade-object now has the following new properties:
* `is_short`
* `enter_side`
* `exit_side`
* `trade_direction`
* renamed: `sell_reason` -> `exit_reason`
* [Renamed `trade.nr_of_successful_buys` to `trade.nr_of_successful_entries` (mostly relevant for `adjust_trade_position()`)](#adjust-trade-position-changes)
* Introduced new [`leverage` callback](strategy-callbacks.md#leverage-callback).
* Informative pairs can now pass a 3rd element in the Tuple, defining the candle type.

View File

@ -180,7 +180,9 @@ official commands. You can ask at any moment for help with `/help`.
| `/daily <n>` | Shows profit or loss per day, over the last n days (n defaults to 7)
| `/weekly <n>` | Shows profit or loss per week, over the last n weeks (n defaults to 8)
| `/monthly <n>` | Shows profit or loss per month, over the last n months (n defaults to 6)
| `/stats` | Shows Wins / losses by Sell reason as well as Avg. holding durations for buys and sells
| `/stats` | Shows Wins / losses by Exit reason as well as Avg. holding durations for buys and sells
| `/exits` | Shows Wins / losses by Exit reason as well as Avg. holding durations for buys and sells
| `/entries` | Shows Wins / losses by Exit reason as well as Avg. holding durations for buys and sells
| `/whitelist` | Show the current whitelist
| `/blacklist [pair]` | Show the current blacklist, or adds a pair to the blacklist.
| `/edge` | Show validated pairs by Edge if it is enabled.

View File

@ -178,7 +178,7 @@ Possible parameters are:
* `stake_currency`
* `base_currency`
* `fiat_currency`
* `sell_reason`
* `exit_reason`
* `order_type`
* `open_date`
* `close_date`
@ -203,7 +203,7 @@ Possible parameters are:
* `stake_currency`
* `base_currency`
* `fiat_currency`
* `sell_reason`
* `exit_reason`
* `order_type`
* `open_date`
* `close_date`
@ -228,7 +228,7 @@ Possible parameters are:
* `stake_currency`
* `base_currency`
* `fiat_currency`
* `sell_reason`
* `exit_reason`
* `order_type`
* `open_date`
* `close_date`

View File

@ -22,7 +22,7 @@ logger = logging.getLogger(__name__)
BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date',
'open_rate', 'close_rate',
'fee_open', 'fee_close', 'trade_duration',
'profit_ratio', 'profit_abs', 'sell_reason',
'profit_ratio', 'profit_abs', 'exit_reason',
'initial_stop_loss_abs', 'initial_stop_loss_ratio', 'stop_loss_abs',
'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', 'enter_tag',
'is_short'

View File

@ -1010,7 +1010,7 @@ class FreqtradeBot(LoggingMixin):
# We check if stoploss order is fulfilled
if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'):
trade.sell_reason = ExitType.STOPLOSS_ON_EXCHANGE.value
trade.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value
self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order,
stoploss_order=True)
# Lock pair for one candle to prevent immediate rebuys
@ -1286,7 +1286,7 @@ class FreqtradeBot(LoggingMixin):
trade.close_date = None
trade.is_open = True
trade.open_order_id = None
trade.sell_reason = None
trade.exit_reason = None
cancelled = True
else:
# TODO: figure out how to handle partially complete sell orders
@ -1416,7 +1416,7 @@ class FreqtradeBot(LoggingMixin):
trade.open_order_id = order['id']
trade.sell_order_status = ''
trade.close_rate_requested = limit
trade.sell_reason = exit_tag or exit_check.exit_reason
trade.exit_reason = exit_tag or exit_check.exit_reason
# Lock pair for one candle to prevent immediate re-trading
self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc),
@ -1461,7 +1461,8 @@ class FreqtradeBot(LoggingMixin):
'profit_ratio': profit_ratio,
'buy_tag': trade.enter_tag,
'enter_tag': trade.enter_tag,
'sell_reason': trade.sell_reason,
'sell_reason': trade.exit_reason, # Deprecated
'exit_reason': trade.exit_reason,
'open_date': trade.open_date,
'close_date': trade.close_date or datetime.utcnow(),
'stake_currency': self.config['stake_currency'],
@ -1509,7 +1510,8 @@ class FreqtradeBot(LoggingMixin):
'profit_ratio': profit_ratio,
'buy_tag': trade.enter_tag,
'enter_tag': trade.enter_tag,
'sell_reason': trade.sell_reason,
'sell_reason': trade.exit_reason, # Deprecated
'exit_reason': trade.exit_reason,
'open_date': trade.open_date,
'close_date': trade.close_date or datetime.now(timezone.utc),
'stake_currency': self.config['stake_currency'],

View File

@ -555,7 +555,7 @@ class Backtesting:
current_time=sell_candle_time):
return None
trade.sell_reason = sell.exit_reason
trade.exit_reason = sell.exit_reason
# Checks and adds an exit tag, after checking that the length of the
# sell_row has the length for an exit tag column
@ -564,7 +564,7 @@ class Backtesting:
and sell_row[EXIT_TAG_IDX] is not None
and len(sell_row[EXIT_TAG_IDX]) > 0
):
trade.sell_reason = sell_row[EXIT_TAG_IDX]
trade.exit_reason = sell_row[EXIT_TAG_IDX]
self.order_id_counter += 1
order = Order(
@ -810,7 +810,7 @@ class Backtesting:
sell_row = data[pair][-1]
trade.close_date = sell_row[DATE_IDX].to_pydatetime()
trade.sell_reason = ExitType.FORCE_SELL.value
trade.exit_reason = ExitType.FORCE_SELL.value
trade.close(sell_row[OPEN_IDX], show_msg=False)
LocalTrade.close_bt_trade(trade)
# Deepcopy object to have wallets update correctly

View File

@ -166,7 +166,7 @@ def generate_tag_metrics(tag_type: str,
return []
def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List[Dict]:
def generate_exit_reason_stats(max_open_trades: int, results: DataFrame) -> List[Dict]:
"""
Generate small table outlining Backtest results
:param max_open_trades: Max_open_trades parameter
@ -175,8 +175,8 @@ def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List
"""
tabular_data = []
for reason, count in results['sell_reason'].value_counts().iteritems():
result = results.loc[results['sell_reason'] == reason]
for reason, count in results['exit_reason'].value_counts().iteritems():
result = results.loc[results['exit_reason'] == reason]
profit_mean = result['profit_ratio'].mean()
profit_sum = result['profit_ratio'].sum()
@ -184,7 +184,7 @@ def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List
tabular_data.append(
{
'sell_reason': reason,
'exit_reason': reason,
'trades': count,
'wins': len(result[result['profit_abs'] > 0]),
'draws': len(result[result['profit_abs'] == 0]),
@ -382,7 +382,7 @@ def generate_strategy_stats(pairlist: List[str],
enter_tag_results = generate_tag_metrics("enter_tag", starting_balance=start_balance,
results=results, skip_nan=False)
exit_reason_stats = generate_sell_reason_stats(max_open_trades=max_open_trades,
exit_reason_stats = generate_exit_reason_stats(max_open_trades=max_open_trades,
results=results)
left_open_results = generate_pair_metrics(pairlist, stake_currency=stake_currency,
starting_balance=start_balance,
@ -406,7 +406,7 @@ def generate_strategy_stats(pairlist: List[str],
'worst_pair': worst_pair,
'results_per_pair': pair_results,
'results_per_enter_tag': enter_tag_results,
'sell_reason_summary': exit_reason_stats,
'exit_reason_summary': exit_reason_stats,
'left_open_trades': left_open_results,
# 'days_breakdown_stats': days_breakdown_stats,
@ -572,7 +572,7 @@ def text_table_bt_results(pair_results: List[Dict[str, Any]], stake_currency: st
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right")
def text_table_exit_reason(sell_reason_stats: List[Dict[str, Any]], stake_currency: str) -> str:
def text_table_exit_reason(exit_reason_stats: List[Dict[str, Any]], stake_currency: str) -> str:
"""
Generate small table outlining Backtest results
:param sell_reason_stats: Exit reason metrics
@ -590,12 +590,12 @@ def text_table_exit_reason(sell_reason_stats: List[Dict[str, Any]], stake_curren
]
output = [[
t['sell_reason'], t['trades'],
t.get('exit_reason', t.get('sell_reason')), t['trades'],
_generate_wins_draws_losses(t['wins'], t['draws'], t['losses']),
t['profit_mean_pct'], t['profit_sum_pct'],
round_coin_value(t['profit_total_abs'], stake_currency, False),
t['profit_total_pct'],
] for t in sell_reason_stats]
] for t in exit_reason_stats]
return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right")
@ -813,7 +813,8 @@ def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency:
print(' ENTER TAG STATS '.center(len(table.splitlines()[0]), '='))
print(table)
table = text_table_exit_reason(sell_reason_stats=results['sell_reason_summary'],
exit_reasons = results.get('exit_reason_summary', results.get('sell_reason_summary'))
table = text_table_exit_reason(exit_reason_stats=exit_reasons,
stake_currency=stake_currency)
if isinstance(table, str) and len(table) > 0:
print(' EXIT REASON STATS '.center(len(table.splitlines()[0]), '='))

View File

@ -74,7 +74,7 @@ def migrate_trades_and_orders_table(
stoploss_last_update = get_column_def(cols, 'stoploss_last_update', 'null')
max_rate = get_column_def(cols, 'max_rate', '0.0')
min_rate = get_column_def(cols, 'min_rate', 'null')
sell_reason = get_column_def(cols, 'sell_reason', 'null')
exit_reason = get_column_def(cols, 'sell_reason', get_column_def(cols, 'exit_reason', 'null'))
strategy = get_column_def(cols, 'strategy', 'null')
enter_tag = get_column_def(cols, 'buy_tag', get_column_def(cols, 'enter_tag', 'null'))
@ -136,7 +136,7 @@ def migrate_trades_and_orders_table(
stake_amount, amount, amount_requested, open_date, close_date, open_order_id,
stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct,
stoploss_order_id, stoploss_last_update,
max_rate, min_rate, sell_reason, sell_order_status, strategy, enter_tag,
max_rate, min_rate, exit_reason, sell_order_status, strategy, enter_tag,
timeframe, open_trade_value, close_profit_abs,
trading_mode, leverage, liquidation_price, is_short,
interest_rate, funding_fees
@ -152,7 +152,7 @@ def migrate_trades_and_orders_table(
{initial_stop_loss} initial_stop_loss,
{initial_stop_loss_pct} initial_stop_loss_pct,
{stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update,
{max_rate} max_rate, {min_rate} min_rate, {sell_reason} sell_reason,
{max_rate} max_rate, {min_rate} min_rate, {exit_reason} exit_reason,
{sell_order_status} sell_order_status,
{strategy} strategy, {enter_tag} enter_tag, {timeframe} timeframe,
{open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs,
@ -234,7 +234,7 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
# Migrates both trades and orders table!
# if ('orders' not in previous_tables
# or not has_column(cols_orders, 'leverage')):
if not has_column(cols, 'liquidation_price'):
if not has_column(cols, 'exit_reason'):
logger.info(f"Running database migration for trades - "
f"backup: {table_back_name}, {order_table_bak_name}")
migrate_trades_and_orders_table(

View File

@ -316,7 +316,7 @@ class LocalTrade():
max_rate: float = 0.0
# Lowest price reached
min_rate: float = 0.0
sell_reason: str = ''
exit_reason: str = ''
sell_order_status: str = ''
strategy: str = ''
enter_tag: Optional[str] = None
@ -459,7 +459,8 @@ class LocalTrade():
'profit_pct': round(self.close_profit * 100, 2) if self.close_profit else None,
'profit_abs': self.close_profit_abs,
'sell_reason': self.sell_reason,
'sell_reason': self.exit_reason, # Deprecated
'exit_reason': self.exit_reason,
'sell_order_status': self.sell_order_status,
'stop_loss_abs': self.stop_loss,
'stop_loss_ratio': self.stop_loss_pct if self.stop_loss_pct else None,
@ -618,7 +619,7 @@ class LocalTrade():
elif order.ft_order_side == 'stoploss':
self.stoploss_order_id = None
self.close_rate_requested = self.stop_loss
self.sell_reason = ExitType.STOPLOSS_ON_EXCHANGE.value
self.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value
if self.is_open:
logger.info(f'{order.order_type.upper()} is hit for {self}.')
self.close(order.safe_price)
@ -947,6 +948,11 @@ class LocalTrade():
"""
return len(self.select_filled_orders('sell'))
@property
def sell_reason(self) -> str:
""" DEPRECATED! Please use exit_reason instead."""
return self.exit_reason
@staticmethod
def get_trades_proxy(*, pair: str = None, is_open: bool = None,
open_date: datetime = None, close_date: datetime = None,
@ -1076,7 +1082,7 @@ class Trade(_DECL_BASE, LocalTrade):
max_rate = Column(Float, nullable=True, default=0.0)
# Lowest price reached
min_rate = Column(Float, nullable=True)
sell_reason = Column(String(100), nullable=True)
exit_reason = Column(String(100), nullable=True)
sell_order_status = Column(String(100), nullable=True)
strategy = Column(String(100), nullable=True)
enter_tag = Column(String(100), nullable=True)
@ -1283,7 +1289,7 @@ class Trade(_DECL_BASE, LocalTrade):
]
@staticmethod
def get_sell_reason_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
def get_exit_reason_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
"""
Returns List of dicts containing all Trades, based on sell reason performance
Can either be average for all pairs or a specific pair provided
@ -1295,30 +1301,30 @@ class Trade(_DECL_BASE, LocalTrade):
filters.append(Trade.pair == pair)
sell_tag_perf = Trade.query.with_entities(
Trade.sell_reason,
Trade.exit_reason,
func.sum(Trade.close_profit).label('profit_sum'),
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
func.count(Trade.pair).label('count')
).filter(*filters)\
.group_by(Trade.sell_reason) \
.group_by(Trade.exit_reason) \
.order_by(desc('profit_sum_abs')) \
.all()
return [
{
'sell_reason': sell_reason if sell_reason is not None else "Other",
'exit_reason': exit_reason if exit_reason is not None else "Other",
'profit_ratio': profit,
'profit_pct': round(profit * 100, 2),
'profit_abs': profit_abs,
'count': count
}
for sell_reason, profit, profit_abs, count in sell_tag_perf
for exit_reason, profit, profit_abs, count in sell_tag_perf
]
@staticmethod
def get_mix_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
"""
Returns List of dicts containing all Trades, based on buy_tag + sell_reason performance
Returns List of dicts containing all Trades, based on entry_tag + exit_reason performance
Can either be average for all pairs or a specific pair provided
NOTE: Not supported in Backtesting.
"""
@ -1330,7 +1336,7 @@ class Trade(_DECL_BASE, LocalTrade):
mix_tag_perf = Trade.query.with_entities(
Trade.id,
Trade.enter_tag,
Trade.sell_reason,
Trade.exit_reason,
func.sum(Trade.close_profit).label('profit_sum'),
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
func.count(Trade.pair).label('count')
@ -1340,12 +1346,12 @@ class Trade(_DECL_BASE, LocalTrade):
.all()
return_list: List[Dict] = []
for id, enter_tag, sell_reason, profit, profit_abs, count in mix_tag_perf:
for id, enter_tag, exit_reason, profit, profit_abs, count in mix_tag_perf:
enter_tag = enter_tag if enter_tag is not None else "Other"
sell_reason = sell_reason if sell_reason is not None else "Other"
exit_reason = exit_reason if exit_reason is not None else "Other"
if(sell_reason is not None and enter_tag is not None):
mix_tag = enter_tag + " " + sell_reason
if(exit_reason is not None and enter_tag is not None):
mix_tag = enter_tag + " " + exit_reason
i = 0
if not any(item["mix_tag"] == mix_tag for item in return_list):
return_list.append({'mix_tag': mix_tag,

View File

@ -240,7 +240,7 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
trades['desc'] = trades.apply(
lambda row: f"{row['profit_ratio']:.2%}, " +
(f"{row['enter_tag']}, " if row['enter_tag'] is not None else "") +
f"{row['sell_reason']}, " +
f"{row['exit_reason']}, " +
f"{row['trade_duration']} min",
axis=1)
trade_buys = go.Scatter(

View File

@ -41,19 +41,9 @@ class StoplossGuard(IProtection):
Evaluate recent trades
"""
look_back_until = date_now - timedelta(minutes=self._lookback_period)
# filters = [
# Trade.is_open.is_(False),
# Trade.close_date > look_back_until,
# or_(Trade.sell_reason == ExitType.STOP_LOSS.value,
# and_(Trade.sell_reason == ExitType.TRAILING_STOP_LOSS.value,
# Trade.close_profit < 0))
# ]
# if pair:
# filters.append(Trade.pair == pair)
# trades = Trade.get_trades(filters).all()
trades1 = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until)
trades = [trade for trade in trades1 if (str(trade.sell_reason) in (
trades = [trade for trade in trades1 if (str(trade.exit_reason) in (
ExitType.TRAILING_STOP_LOSS.value, ExitType.STOP_LOSS.value,
ExitType.STOPLOSS_ON_EXCHANGE.value)
and trade.close_profit and trade.close_profit < 0)]

View File

@ -113,7 +113,7 @@ class SellReason(BaseModel):
class Stats(BaseModel):
sell_reasons: Dict[str, SellReason]
exit_reasons: Dict[str, SellReason]
durations: Dict[str, Optional[float]]
@ -235,7 +235,8 @@ class TradeSchema(BaseModel):
profit_pct: Optional[float]
profit_abs: Optional[float]
profit_fiat: Optional[float]
sell_reason: Optional[str]
sell_reason: Optional[str] # Deprecated
exit_reason: Optional[str]
sell_order_status: Optional[str]
stop_loss_abs: Optional[float]
stop_loss_ratio: Optional[float]

View File

@ -428,13 +428,13 @@ class RPC:
return 'losses'
else:
return 'draws'
trades = trades = Trade.get_trades([Trade.is_open.is_(False)])
trades: List[Trade] = Trade.get_trades([Trade.is_open.is_(False)])
# Sell reason
sell_reasons = {}
exit_reasons = {}
for trade in trades:
if trade.sell_reason not in sell_reasons:
sell_reasons[trade.sell_reason] = {'wins': 0, 'losses': 0, 'draws': 0}
sell_reasons[trade.sell_reason][trade_win_loss(trade)] += 1
if trade.exit_reason not in exit_reasons:
exit_reasons[trade.exit_reason] = {'wins': 0, 'losses': 0, 'draws': 0}
exit_reasons[trade.exit_reason][trade_win_loss(trade)] += 1
# Duration
dur: Dict[str, List[int]] = {'wins': [], 'draws': [], 'losses': []}
@ -448,7 +448,7 @@ class RPC:
losses_dur = sum(dur['losses']) / len(dur['losses']) if len(dur['losses']) > 0 else None
durations = {'wins': wins_dur, 'draws': draws_dur, 'losses': losses_dur}
return {'sell_reasons': sell_reasons, 'durations': durations}
return {'exit_reasons': exit_reasons, 'durations': durations}
def _rpc_trade_statistics(
self, stake_currency: str, fiat_display_currency: str,
@ -848,16 +848,16 @@ class RPC:
"""
return Trade.get_enter_tag_performance(pair)
def _rpc_sell_reason_performance(self, pair: Optional[str]) -> List[Dict[str, Any]]:
def _rpc_exit_reason_performance(self, pair: Optional[str]) -> List[Dict[str, Any]]:
"""
Handler for sell reason performance.
Shows a performance statistic from finished trades
"""
return Trade.get_sell_reason_performance(pair)
return Trade.get_exit_reason_performance(pair)
def _rpc_mix_tag_performance(self, pair: Optional[str]) -> List[Dict[str, Any]]:
"""
Handler for mix tag (enter_tag + sell_reason) performance.
Handler for mix tag (enter_tag + exit_reason) performance.
Shows a performance statistic from finished trades
"""
mix_tags = Trade.get_mix_tag_performance(pair)

View File

@ -108,7 +108,8 @@ class Telegram(RPCHandler):
# this needs refactoring of the whole telegram module (same
# problem in _help()).
valid_keys: List[str] = [r'/start$', r'/stop$', r'/status$', r'/status table$',
r'/trades$', r'/performance$', r'/buys', r'/sells', r'/mix_tags',
r'/trades$', r'/performance$', r'/buys', r'/entries',
r'/sells', r'/exits', r'/mix_tags',
r'/daily$', r'/daily \d+$', r'/profit$', r'/profit \d+',
r'/stats$', r'/count$', r'/locks$', r'/balance$',
r'/stopbuy$', r'/reload_config$', r'/show_config$',
@ -161,7 +162,7 @@ class Telegram(RPCHandler):
CommandHandler('delete', self._delete_trade),
CommandHandler('performance', self._performance),
CommandHandler(['buys', 'entries'], self._enter_tag_performance),
CommandHandler('sells', self._sell_reason_performance),
CommandHandler(['sells', 'exits'], self._exit_reason_performance),
CommandHandler('mix_tags', self._mix_tag_performance),
CommandHandler('stats', self._stats),
CommandHandler('daily', self._daily),
@ -192,8 +193,8 @@ class Telegram(RPCHandler):
CallbackQueryHandler(self._performance, pattern='update_performance'),
CallbackQueryHandler(self._enter_tag_performance,
pattern='update_enter_tag_performance'),
CallbackQueryHandler(self._sell_reason_performance,
pattern='update_sell_reason_performance'),
CallbackQueryHandler(self._exit_reason_performance,
pattern='update_exit_reason_performance'),
CallbackQueryHandler(self._mix_tag_performance, pattern='update_mix_tag_performance'),
CallbackQueryHandler(self._count, pattern='update_count'),
CallbackQueryHandler(self._forceenter_inline),
@ -290,7 +291,7 @@ class Telegram(RPCHandler):
f"*{'Profit' if is_fill else 'Unrealized Profit'}:* "
f"`{msg['profit_ratio']:.2%}{msg['profit_extra']}`\n"
f"*Enter Tag:* `{msg['enter_tag']}`\n"
f"*Exit Reason:* `{msg['sell_reason']}`\n"
f"*Exit Reason:* `{msg['exit_reason']}`\n"
f"*Duration:* `{msg['duration']} ({msg['duration_min']:.1f} min)`\n"
f"*Direction:* `{msg['direction']}`\n"
f"{msg['leverage_text']}"
@ -361,7 +362,7 @@ class Telegram(RPCHandler):
if isinstance(sell_noti, str):
noti = sell_noti
else:
noti = sell_noti.get(str(msg['sell_reason']), default_noti)
noti = sell_noti.get(str(msg['exit_reason']), default_noti)
else:
noti = self._config['telegram'] \
.get('notification_settings', {}).get(str(msg_type), default_noti)
@ -384,7 +385,7 @@ class Telegram(RPCHandler):
return "\N{ROCKET}"
elif float(msg['profit_percent']) >= 0.0:
return "\N{EIGHT SPOKED ASTERISK}"
elif msg['sell_reason'] == "stop_loss":
elif msg['exit_reason'] == "stop_loss":
return "\N{WARNING SIGN}"
else:
return "\N{CROSS MARK}"
@ -466,7 +467,7 @@ class Telegram(RPCHandler):
for r in results:
r['open_date_hum'] = arrow.get(r['open_date']).humanize()
r['num_entries'] = len([o for o in r['orders'] if o['ft_is_entry']])
r['sell_reason'] = r.get('sell_reason', "")
r['exit_reason'] = r.get('exit_reason', "")
lines = [
"*Trade ID:* `{trade_id}`" +
("` (since {open_date_hum})`" if r['is_open'] else ""),
@ -475,7 +476,7 @@ class Telegram(RPCHandler):
"*Leverage:* `{leverage}`" if r.get('leverage') else "",
"*Amount:* `{amount} ({stake_amount} {base_currency})`",
"*Enter Tag:* `{enter_tag}`" if r['enter_tag'] else "",
"*Exit Reason:* `{sell_reason}`" if r['sell_reason'] else "",
"*Exit Reason:* `{exit_reason}`" if r['exit_reason'] else "",
]
if position_adjust:
@ -771,23 +772,23 @@ class Telegram(RPCHandler):
'force_sell': 'Forcesell',
'emergency_sell': 'Emergency Sell',
}
sell_reasons_tabulate = [
exit_reasons_tabulate = [
[
reason_map.get(reason, reason),
sum(count.values()),
count['wins'],
count['losses']
] for reason, count in stats['sell_reasons'].items()
] for reason, count in stats['exit_reasons'].items()
]
sell_reasons_msg = 'No trades yet.'
for reason in chunks(sell_reasons_tabulate, 25):
sell_reasons_msg = tabulate(
exit_reasons_msg = 'No trades yet.'
for reason in chunks(exit_reasons_tabulate, 25):
exit_reasons_msg = tabulate(
reason,
headers=['Sell Reason', 'Sells', 'Wins', 'Losses']
headers=['Exit Reason', 'Exits', 'Wins', 'Losses']
)
if len(sell_reasons_tabulate) > 25:
self._send_msg(sell_reasons_msg, ParseMode.MARKDOWN)
sell_reasons_msg = ''
if len(exit_reasons_tabulate) > 25:
self._send_msg(exit_reasons_msg, ParseMode.MARKDOWN)
exit_reasons_msg = ''
durations = stats['durations']
duration_msg = tabulate(
@ -799,7 +800,7 @@ class Telegram(RPCHandler):
],
headers=['', 'Avg. Duration']
)
msg = (f"""```\n{sell_reasons_msg}```\n```\n{duration_msg}```""")
msg = (f"""```\n{exit_reasons_msg}```\n```\n{duration_msg}```""")
self._send_msg(msg, ParseMode.MARKDOWN)
@ -1101,7 +1102,7 @@ class Telegram(RPCHandler):
pair = context.args[0]
trades = self._rpc._rpc_enter_tag_performance(pair)
output = "<b>Buy Tag Performance:</b>\n"
output = "<b>Entry Tag Performance:</b>\n"
for i, trade in enumerate(trades):
stat_line = (
f"{i+1}.\t <code>{trade['enter_tag']}\t"
@ -1122,7 +1123,7 @@ class Telegram(RPCHandler):
self._send_msg(str(e))
@authorized_only
def _sell_reason_performance(self, update: Update, context: CallbackContext) -> None:
def _exit_reason_performance(self, update: Update, context: CallbackContext) -> None:
"""
Handler for /sells.
Shows a performance statistic from finished trades
@ -1135,11 +1136,11 @@ class Telegram(RPCHandler):
if context.args and isinstance(context.args[0], str):
pair = context.args[0]
trades = self._rpc._rpc_sell_reason_performance(pair)
output = "<b>Sell Reason Performance:</b>\n"
trades = self._rpc._rpc_exit_reason_performance(pair)
output = "<b>Exit Reason Performance:</b>\n"
for i, trade in enumerate(trades):
stat_line = (
f"{i+1}.\t <code>{trade['sell_reason']}\t"
f"{i+1}.\t <code>{trade['exit_reason']}\t"
f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} "
f"({trade['profit_ratio']:.2%}) "
f"({trade['count']})</code>\n")
@ -1151,7 +1152,7 @@ class Telegram(RPCHandler):
output += stat_line
self._send_msg(output, parse_mode=ParseMode.HTML,
reload_able=True, callback_path="update_sell_reason_performance",
reload_able=True, callback_path="update_exit_reason_performance",
query=update.callback_query)
except RPCException as e:
self._send_msg(str(e))

View File

@ -187,7 +187,7 @@
"trades = load_backtest_data(backtest_dir)\n",
"\n",
"# Show value-counts per pair\n",
"trades.groupby(\"pair\")[\"sell_reason\"].value_counts()"
"trades.groupby(\"pair\")[\"exit_reason\"].value_counts()"
]
},
{
@ -257,7 +257,7 @@
"trades = load_trades_from_db(\"sqlite:///tradesv3.sqlite\")\n",
"\n",
"# Display results\n",
"trades.groupby(\"pair\")[\"sell_reason\"].value_counts()"
"trades.groupby(\"pair\")[\"exit_reason\"].value_counts()"
]
},
{

View File

@ -104,7 +104,7 @@ def mock_trade_2(fee, is_short: bool):
strategy='StrategyTestV3',
timeframe=5,
enter_tag='TEST1',
sell_reason='sell_signal',
exit_reason='sell_signal',
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2),
is_short=is_short
@ -164,7 +164,7 @@ def mock_trade_3(fee, is_short: bool):
is_open=False,
strategy='StrategyTestV3',
timeframe=5,
sell_reason='roi',
exit_reason='roi',
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
close_date=datetime.now(tz=timezone.utc),
is_short=is_short
@ -401,7 +401,7 @@ def short_trade(fee):
open_order_id='dry_run_exit_short_12345',
strategy='DefaultStrategy',
timeframe=5,
sell_reason='sell_signal',
exit_reason='sell_signal',
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
# close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2),
is_short=True
@ -490,7 +490,7 @@ def leverage_trade(fee):
open_order_id='dry_run_leverage_buy_12368',
strategy='DefaultStrategy',
timeframe=5,
sell_reason='sell_signal',
exit_reason='sell_signal',
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=300),
close_date=datetime.now(tz=timezone.utc),
interest_rate=0.0005

View File

@ -89,7 +89,7 @@ def mock_trade_usdt_2(fee):
open_order_id='dry_run_sell_12345',
strategy='StrategyTestV2',
timeframe=5,
sell_reason='sell_signal',
exit_reason='sell_signal',
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2),
)
@ -148,7 +148,7 @@ def mock_trade_usdt_3(fee):
is_open=False,
strategy='StrategyTestV2',
timeframe=5,
sell_reason='roi',
exit_reason='roi',
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
close_date=datetime.now(tz=timezone.utc),
)

View File

@ -95,8 +95,8 @@ tc1 = BTContainer(data=[
[6, 5000, 5025, 4975, 4987, 6172, 0, 0], # should sell
],
stop_loss=-0.99, roi={"0": float('inf')}, profit_perc=0.00,
trades=[BTrade(sell_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=2),
BTrade(sell_reason=ExitType.SELL_SIGNAL, open_tick=4, close_tick=6)]
trades=[BTrade(exit_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=2),
BTrade(exit_reason=ExitType.SELL_SIGNAL, open_tick=4, close_tick=6)]
)
# 3) Entered, sl 1%, candle drops 8% => Trade closed, 1% loss
@ -107,7 +107,7 @@ tc2 = BTContainer(data=[
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
],
stop_loss=-0.01, roi={"0": float('inf')}, profit_perc=-0.01,
trades=[BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)]
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)]
)
# 4) Entered, sl 3 %, candle drops 4%, recovers to 1 % = > Trade closed, 3 % loss
@ -118,7 +118,7 @@ tc3 = BTContainer(data=[
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
],
stop_loss=-0.03, roi={"0": float('inf')}, profit_perc=-0.03,
trades=[BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)]
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)]
)
# 5) Stoploss and sell are hit. should sell on stoploss
@ -129,7 +129,7 @@ tc4 = BTContainer(data=[
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
],
stop_loss=-0.03, roi={"0": float('inf')}, profit_perc=-0.03,
trades=[BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)]
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)]
)
TESTS = [
@ -162,7 +162,7 @@ def test_edge_results(edge_conf, mocker, caplog, data) -> None:
for c, trade in enumerate(data.trades):
res = results.iloc[c]
assert res.exit_type == trade.sell_reason
assert res.exit_type == trade.exit_reason
assert res.open_date == _get_frame_time_from_offset(trade.open_tick).replace(tzinfo=None)
assert res.close_date == _get_frame_time_from_offset(trade.close_tick).replace(tzinfo=None)

View File

@ -15,7 +15,7 @@ class BTrade(NamedTuple):
"""
Minimalistic Trade result used for functional backtesting
"""
sell_reason: ExitType
exit_reason: ExitType
open_tick: int
close_tick: int
enter_tag: Optional[str] = None

View File

@ -44,7 +44,7 @@ def hyperopt_results():
'profit_abs': [-0.2, 0.4, -0.2, 0.6],
'trade_duration': [10, 30, 10, 10],
'amount': [0.1, 0.1, 0.1, 0.1],
'sell_reason': [ExitType.STOP_LOSS, ExitType.ROI, ExitType.STOP_LOSS, ExitType.ROI],
'exit_reason': [ExitType.STOP_LOSS, ExitType.ROI, ExitType.STOP_LOSS, ExitType.ROI],
'open_date':
[
datetime(2019, 1, 1, 9, 15, 0),

View File

@ -23,7 +23,7 @@ tc0 = BTContainer(data=[
[4, 5010, 5011, 4977, 4995, 6172, 0, 0],
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True,
trades=[BTrade(sell_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=4)]
trades=[BTrade(exit_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=4)]
)
# Test 1: Stop-Loss Triggered 1% loss
@ -37,7 +37,7 @@ tc1 = BTContainer(data=[
[4, 4977, 4995, 4977, 4995, 6172, 0, 0],
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01,
trades=[BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)]
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)]
)
@ -52,7 +52,7 @@ tc2 = BTContainer(data=[
[4, 4962, 4987, 4937, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.03, roi={"0": 1}, profit_perc=-0.03,
trades=[BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=3)]
)
@ -72,8 +72,8 @@ tc3 = BTContainer(data=[
[5, 4962, 4987, 4000, 4000, 6172, 0, 0], # exit with stoploss hit
[6, 4950, 4975, 4950, 4950, 6172, 0, 0]],
stop_loss=-0.02, roi={"0": 1}, profit_perc=-0.04,
trades=[BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2),
BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=4, close_tick=5)]
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2),
BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=4, close_tick=5)]
)
# Test 4: Minus 3% / recovery +15%
@ -89,7 +89,7 @@ tc4 = BTContainer(data=[
[4, 4962, 4987, 4937, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.02, roi={"0": 0.06}, profit_perc=-0.02,
trades=[BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)]
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)]
)
# Test 5: Drops 0.5% Closes +20%, ROI triggers 3% Gain
@ -103,7 +103,7 @@ tc5 = BTContainer(data=[
[4, 4962, 4987, 4962, 4972, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.01, roi={"0": 0.03}, profit_perc=0.03,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
)
# Test 6: Drops 3% / Recovers 6% Positive / Closes 1% positve, Stop-Loss triggers 2% Loss
@ -117,7 +117,7 @@ tc6 = BTContainer(data=[
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.02, roi={"0": 0.05}, profit_perc=-0.02,
trades=[BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)]
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)]
)
# Test 7: 6% Positive / 1% Negative / Close 1% Positve, ROI Triggers 3% Gain
@ -131,7 +131,7 @@ tc7 = BTContainer(data=[
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.02, roi={"0": 0.03}, profit_perc=0.03,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=2)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)]
)
@ -145,7 +145,7 @@ tc8 = BTContainer(data=[
[3, 4850, 5050, 4650, 4750, 6172, 0, 0],
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.055, trailing_stop=True,
trades=[BTrade(sell_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
)
@ -159,7 +159,7 @@ tc9 = BTContainer(data=[
[3, 5000, 5200, 4550, 4850, 6172, 0, 0],
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.064, trailing_stop=True,
trades=[BTrade(sell_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
)
# Test 10: trailing_stop should raise so candle 3 causes a stoploss
@ -175,7 +175,7 @@ tc10 = BTContainer(data=[
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.1, trailing_stop=True,
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.10,
trailing_stop_positive=0.03,
trades=[BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=4)]
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=4)]
)
# Test 11: trailing_stop should raise so candle 3 causes a stoploss
@ -191,7 +191,7 @@ tc11 = BTContainer(data=[
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True,
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
trailing_stop_positive=0.03,
trades=[BTrade(sell_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
)
# Test 12: trailing_stop should raise in candle 2 and cause a stoploss in the same candle
@ -207,7 +207,7 @@ tc12 = BTContainer(data=[
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True,
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
trailing_stop_positive=0.03,
trades=[BTrade(sell_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)]
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)]
)
# Test 13: Buy and sell ROI on same candle
@ -220,7 +220,7 @@ tc13 = BTContainer(data=[
[3, 4850, 5050, 4750, 4750, 6172, 0, 0],
[4, 4750, 4950, 4750, 4750, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=1)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1)]
)
# Test 14 - Buy and Stoploss on same candle
@ -233,7 +233,7 @@ tc14 = BTContainer(data=[
[3, 4850, 5050, 4750, 4750, 6172, 0, 0],
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
stop_loss=-0.05, roi={"0": 0.10}, profit_perc=-0.05,
trades=[BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)]
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)]
)
@ -247,8 +247,8 @@ tc15 = BTContainer(data=[
[3, 4850, 5050, 4750, 4750, 6172, 0, 0],
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
stop_loss=-0.05, roi={"0": 0.01}, profit_perc=-0.04,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=1),
BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=2, close_tick=2)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1),
BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=2, close_tick=2)]
)
# Test 16: Buy, hold for 65 min, then forcesell using roi=-1
@ -263,7 +263,7 @@ tc16 = BTContainer(data=[
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.10, "65": -1}, profit_perc=-0.012,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
)
# Test 17: Buy, hold for 120 mins, then forcesell using roi=-1
@ -279,7 +279,7 @@ tc17 = BTContainer(data=[
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.10, "120": -1}, profit_perc=-0.004,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
)
@ -295,7 +295,7 @@ tc18 = BTContainer(data=[
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.04,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
)
# Test 19: Buy, hold for 119 mins, then drop ROI to 1%, causing a sell in candle 3.
@ -310,7 +310,7 @@ tc19 = BTContainer(data=[
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
[5, 4550, 4975, 4550, 4950, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.01,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
)
# Test 20: Buy, hold for 119 mins, then drop ROI to 1%, causing a sell in candle 3.
@ -325,7 +325,7 @@ tc20 = BTContainer(data=[
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
[5, 4925, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.10, "119": 0.01}, profit_perc=0.01,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
)
# Test 21: trailing_stop ROI collision.
@ -342,7 +342,7 @@ tc21 = BTContainer(data=[
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=ExitType.ROI, open_tick=1, close_tick=2)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)]
)
# Test 22: trailing_stop Raises in candle 2 - but ROI applies at the same time.
@ -358,7 +358,7 @@ tc22 = BTContainer(data=[
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=ExitType.ROI, open_tick=1, close_tick=2)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)]
)
@ -375,7 +375,7 @@ tc23 = BTContainer(data=[
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=ExitType.ROI, open_tick=1, close_tick=2, is_short=True)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2, is_short=True)]
)
# Test 24: trailing_stop Raises in candle 2 (does not trigger)
@ -394,7 +394,7 @@ tc24 = BTContainer(data=[
stop_loss=-0.10, roi={"0": 0.1, "119": 0.03}, 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=ExitType.ROI, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
)
# Test 25: Sell with signal sell in candle 3 (stoploss also triggers on this candle)
@ -409,7 +409,7 @@ tc25 = BTContainer(data=[
[4, 5010, 5010, 4977, 4995, 6172, 0, 0],
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, use_sell_signal=True,
trades=[BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=3)]
)
# Test 26: Sell with signal sell in candle 3 (stoploss also triggers on this candle)
@ -424,7 +424,7 @@ tc26 = BTContainer(data=[
[4, 5010, 5010, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True,
trades=[BTrade(sell_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=4)]
trades=[BTrade(exit_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=4)]
)
# Test 27: (copy of test26 with leverage)
@ -441,7 +441,7 @@ tc27 = BTContainer(data=[
[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=ExitType.SELL_SIGNAL, open_tick=1, close_tick=4)]
trades=[BTrade(exit_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=4)]
)
# Test 28: (copy of test26 with leverage and as short)
@ -458,7 +458,7 @@ tc28 = BTContainer(data=[
[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=ExitType.SELL_SIGNAL, open_tick=1, close_tick=4, is_short=True)]
trades=[BTrade(exit_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=4, is_short=True)]
)
# Test 29: Sell with signal sell in candle 3 (ROI at signal candle)
# Stoploss at 10% (irrelevant), ROI at 5% (will trigger)
@ -472,7 +472,7 @@ tc29 = BTContainer(data=[
[4, 5010, 5010, 4855, 4995, 6172, 0, 0],
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_sell_signal=True,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
)
# Test 30: Sell with signal sell in candle 3 (ROI at signal candle)
@ -486,7 +486,7 @@ tc30 = BTContainer(data=[
[4, 5010, 5251, 4855, 4995, 6172, 0, 0], # Triggers ROI, sell-signal acted on
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.002, use_sell_signal=True,
trades=[BTrade(sell_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=4)]
trades=[BTrade(exit_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=4)]
)
# Test 31: trailing_stop should raise so candle 3 causes a stoploss
@ -503,7 +503,7 @@ tc31 = BTContainer(data=[
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=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
)
# Test 32: (Short of test 31) trailing_stop should raise so candle 3 causes a stoploss
@ -521,7 +521,7 @@ tc32 = BTContainer(data=[
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
trailing_stop_positive=0.03,
trades=[
BTrade(sell_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3, is_short=True)
BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3, is_short=True)
]
)
@ -537,7 +537,7 @@ tc33 = BTContainer(data=[
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.02, trailing_stop=True,
trailing_stop_positive=0.03,
trades=[BTrade(sell_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)]
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)]
)
# Test 34: trailing_stop should be triggered immediately on trade open candle.
@ -551,7 +551,7 @@ tc34 = BTContainer(data=[
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True,
trailing_stop_positive=0.01,
trades=[BTrade(sell_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
)
# Test 35: trailing_stop should be triggered immediately on trade open candle.
@ -566,7 +566,7 @@ tc35 = BTContainer(data=[
stop_loss=-0.10, 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,
trades=[BTrade(sell_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
)
# Test 36: trailing_stop should be triggered immediately on trade open candle.
@ -581,7 +581,7 @@ tc36 = BTContainer(data=[
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=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
)
# Test 37: trailing_stop should be triggered immediately on trade open candle.
@ -597,7 +597,7 @@ tc37 = BTContainer(data=[
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=ExitType.TRAILING_STOP_LOSS,
exit_reason=ExitType.TRAILING_STOP_LOSS,
open_tick=1,
close_tick=1,
enter_tag='buy_signal_01'
@ -617,7 +617,7 @@ tc38 = BTContainer(data=[
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=ExitType.TRAILING_STOP_LOSS,
exit_reason=ExitType.TRAILING_STOP_LOSS,
open_tick=1,
close_tick=1,
enter_tag='short_signal_01',
@ -647,7 +647,7 @@ tc40 = BTContainer(data=[
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01,
custom_entry_price=7200, trades=[
BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)
BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)
])
# Test 41: Custom-entry-price above all candles should have rate adjusted to "entry candle high"
@ -661,7 +661,7 @@ tc41 = BTContainer(data=[
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01,
custom_entry_price=4000,
trades=[
BTrade(sell_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1, is_short=True)
BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1, is_short=True)
]
)
@ -678,7 +678,7 @@ tc42 = BTContainer(data=[
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01,
custom_entry_price=4952,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=2)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)]
)
# Test 43: Custom-entry-price around candle low
@ -693,7 +693,7 @@ tc43 = BTContainer(data=[
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01,
custom_entry_price=4952,
trades=[BTrade(sell_reason=ExitType.ROI, open_tick=1, close_tick=1)]
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1)]
)
# Test 44: Custom exit price below all candles
@ -708,7 +708,7 @@ tc44 = BTContainer(data=[
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.01,
use_sell_signal=True,
custom_exit_price=4552,
trades=[BTrade(sell_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=3)]
trades=[BTrade(exit_reason=ExitType.SELL_SIGNAL, open_tick=1, close_tick=3)]
)
# Test 45: Custom exit price above all candles
@ -723,7 +723,7 @@ tc45 = BTContainer(data=[
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0,
use_sell_signal=True,
custom_exit_price=6052,
trades=[BTrade(sell_reason=ExitType.FORCE_SELL, open_tick=1, close_tick=4)]
trades=[BTrade(exit_reason=ExitType.FORCE_SELL, open_tick=1, close_tick=4)]
)
# Test 46: (Short of tc45) Custom short exit price above below candles
@ -738,7 +738,7 @@ tc46 = BTContainer(data=[
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0,
use_sell_signal=True,
custom_exit_price=4700,
trades=[BTrade(sell_reason=ExitType.FORCE_SELL, open_tick=1, close_tick=4, is_short=True)]
trades=[BTrade(exit_reason=ExitType.FORCE_SELL, open_tick=1, close_tick=4, is_short=True)]
)
# Test 47: Colliding long and short signal
@ -861,7 +861,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
for c, trade in enumerate(data.trades):
res: BTrade = results.iloc[c]
assert res.sell_reason == trade.sell_reason.value
assert res.exit_reason == trade.exit_reason.value
assert res.enter_tag == trade.enter_tag
assert res.open_date == _get_frame_time_from_offset(trade.open_tick)
assert res.close_date == _get_frame_time_from_offset(trade.close_tick)

View File

@ -713,7 +713,7 @@ def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None:
# No data available.
res = backtesting._get_sell_trade_entry(trade, row_sell)
assert res is not None
assert res.sell_reason == ExitType.ROI.value
assert res.exit_reason == ExitType.ROI.value
assert res.close_date_utc == datetime(2020, 1, 1, 5, 0, tzinfo=timezone.utc)
# Enter new trade
@ -732,7 +732,7 @@ def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None:
res = backtesting._get_sell_trade_entry(trade, row_sell)
assert res is not None
assert res.sell_reason == ExitType.ROI.value
assert res.exit_reason == ExitType.ROI.value
# Sell at minute 3 (not available above!)
assert res.close_date_utc == datetime(2020, 1, 1, 5, 3, tzinfo=timezone.utc)
sell_order = res.select_order('sell', True)
@ -781,7 +781,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
'trade_duration': [235, 40],
'profit_ratio': [0.0, 0.0],
'profit_abs': [0.0, 0.0],
'sell_reason': [ExitType.ROI.value, ExitType.ROI.value],
'exit_reason': [ExitType.ROI.value, ExitType.ROI.value],
'initial_stop_loss_abs': [0.0940005, 0.09272236],
'initial_stop_loss_ratio': [-0.1, -0.1],
'stop_loss_abs': [0.0940005, 0.09272236],
@ -1178,7 +1178,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
text_table_bt_results=text_table_mock,
text_table_strategy=strattable_mock,
generate_pair_metrics=MagicMock(),
generate_sell_reason_stats=sell_reason_mock,
generate_exit_reason_stats=sell_reason_mock,
generate_strategy_comparison=strat_summary,
generate_daily_stats=MagicMock(),
)
@ -1249,7 +1249,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
'close_rate': [0.104969, 0.103541],
"is_short": [False, False],
'sell_reason': [ExitType.ROI, ExitType.ROI]
'exit_reason': [ExitType.ROI, ExitType.ROI]
})
result2 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'],
'profit_ratio': [0.03, 0.01, 0.1],
@ -1267,7 +1267,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
'open_rate': [0.104445, 0.10302485, 0.122541],
'close_rate': [0.104969, 0.103541, 0.123541],
"is_short": [False, False, False],
'sell_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
'exit_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
})
backtestmock = MagicMock(side_effect=[
{
@ -1367,7 +1367,7 @@ def test_backtest_start_nomock_futures(default_conf_usdt, mocker,
'stake_amount': [0.01, 0.01],
'open_rate': [0.104445, 0.10302485],
'close_rate': [0.104969, 0.103541],
'sell_reason': [ExitType.ROI, ExitType.ROI]
'exit_reason': [ExitType.ROI, ExitType.ROI]
})
result2 = pd.DataFrame({'pair': ['XRP/USDT', 'XRP/USDT', 'XRP/USDT'],
'profit_ratio': [0.03, 0.01, 0.1],
@ -1385,7 +1385,7 @@ def test_backtest_start_nomock_futures(default_conf_usdt, mocker,
'stake_amount': [0.01, 0.01, 0.01],
'open_rate': [0.104445, 0.10302485, 0.122541],
'close_rate': [0.104969, 0.103541, 0.123541],
'sell_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
'exit_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
})
backtestmock = MagicMock(side_effect=[
{
@ -1470,7 +1470,7 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
'stake_amount': [0.01, 0.01],
'open_rate': [0.104445, 0.10302485],
'close_rate': [0.104969, 0.103541],
'sell_reason': [ExitType.ROI, ExitType.ROI]
'exit_reason': [ExitType.ROI, ExitType.ROI]
})
result2 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'],
'profit_ratio': [0.03, 0.01, 0.1],
@ -1488,7 +1488,7 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
'stake_amount': [0.01, 0.01, 0.01],
'open_rate': [0.104445, 0.10302485, 0.122541],
'close_rate': [0.104969, 0.103541, 0.123541],
'sell_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
'exit_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
})
backtestmock = MagicMock(side_effect=[
{

View File

@ -60,7 +60,7 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
'trade_duration': [200, 40],
'profit_ratio': [0.0, 0.0],
'profit_abs': [0.0, 0.0],
'sell_reason': [ExitType.ROI.value, ExitType.ROI.value],
'exit_reason': [ExitType.ROI.value, ExitType.ROI.value],
'initial_stop_loss_abs': [0.0940005, 0.09272236],
'initial_stop_loss_ratio': [-0.1, -0.1],
'stop_loss_abs': [0.0940005, 0.09272236],

View File

@ -357,7 +357,7 @@ def test_hyperopt_format_results(hyperopt):
"is_open": [False, False, False, True],
"is_short": [False, False, False, False],
"stake_amount": [0.01, 0.01, 0.01, 0.01],
"sell_reason": [ExitType.ROI, ExitType.STOP_LOSS,
"exit_reason": [ExitType.ROI, ExitType.STOP_LOSS,
ExitType.ROI, ExitType.FORCE_SELL]
}),
'config': hyperopt.config,
@ -428,7 +428,7 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
"is_open": [False, False, False, True],
"is_short": [False, False, False, False],
"stake_amount": [0.01, 0.01, 0.01, 0.01],
"sell_reason": [ExitType.ROI, ExitType.STOP_LOSS,
"exit_reason": [ExitType.ROI, ExitType.STOP_LOSS,
ExitType.ROI, ExitType.FORCE_SELL]
}),
'config': hyperopt_conf,

View File

@ -15,9 +15,8 @@ from freqtrade.edge import PairInfo
from freqtrade.enums import ExitType
from freqtrade.optimize.optimize_reports import (_get_resample_from_period, generate_backtest_stats,
generate_daily_stats, generate_edge_table,
generate_pair_metrics,
generate_exit_reason_stats, generate_pair_metrics,
generate_periodic_breakdown_stats,
generate_sell_reason_stats,
generate_strategy_comparison,
generate_trading_stats, show_sorted_pairlist,
store_backtest_stats, text_table_bt_results,
@ -77,7 +76,7 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir):
"is_open": [False, False, False, True],
"is_short": [False, False, False, False],
"stake_amount": [0.01, 0.01, 0.01, 0.01],
"sell_reason": [ExitType.ROI, ExitType.STOP_LOSS,
"exit_reason": [ExitType.ROI, ExitType.STOP_LOSS,
ExitType.ROI, ExitType.FORCE_SELL]
}),
'config': default_conf,
@ -129,7 +128,7 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir):
"is_open": [False, False, False, True],
"is_short": [False, False, False, False],
"stake_amount": [0.01, 0.01, 0.01, 0.01],
"sell_reason": [ExitType.ROI, ExitType.ROI,
"exit_reason": [ExitType.ROI, ExitType.ROI,
ExitType.STOP_LOSS, ExitType.FORCE_SELL]
}),
'config': default_conf,
@ -265,7 +264,7 @@ def test_generate_trading_stats(testdatadir):
assert res['losses'] == 0
def test_text_table_sell_reason():
def test_text_table_exit_reason():
results = pd.DataFrame(
{
@ -276,7 +275,7 @@ def test_text_table_sell_reason():
'wins': [2, 0, 0],
'draws': [0, 0, 0],
'losses': [0, 0, 1],
'sell_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
'exit_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
}
)
@ -291,9 +290,9 @@ def test_text_table_sell_reason():
' -0.2 | -5 |'
)
sell_reason_stats = generate_sell_reason_stats(max_open_trades=2,
exit_reason_stats = generate_exit_reason_stats(max_open_trades=2,
results=results)
assert text_table_exit_reason(sell_reason_stats=sell_reason_stats,
assert text_table_exit_reason(exit_reason_stats=exit_reason_stats,
stake_currency='BTC') == result_str
@ -308,23 +307,23 @@ def test_generate_sell_reason_stats():
'wins': [2, 0, 0],
'draws': [0, 0, 0],
'losses': [0, 0, 1],
'sell_reason': [ExitType.ROI.value, ExitType.ROI.value, ExitType.STOP_LOSS.value]
'exit_reason': [ExitType.ROI.value, ExitType.ROI.value, ExitType.STOP_LOSS.value]
}
)
sell_reason_stats = generate_sell_reason_stats(max_open_trades=2,
exit_reason_stats = generate_exit_reason_stats(max_open_trades=2,
results=results)
roi_result = sell_reason_stats[0]
assert roi_result['sell_reason'] == 'roi'
roi_result = exit_reason_stats[0]
assert roi_result['exit_reason'] == 'roi'
assert roi_result['trades'] == 2
assert pytest.approx(roi_result['profit_mean']) == 0.15
assert roi_result['profit_mean_pct'] == round(roi_result['profit_mean'] * 100, 2)
assert pytest.approx(roi_result['profit_mean']) == 0.15
assert roi_result['profit_mean_pct'] == round(roi_result['profit_mean'] * 100, 2)
stop_result = sell_reason_stats[1]
stop_result = exit_reason_stats[1]
assert stop_result['sell_reason'] == 'stop_loss'
assert stop_result['exit_reason'] == 'stop_loss'
assert stop_result['trades'] == 1
assert pytest.approx(stop_result['profit_mean']) == -0.1
assert stop_result['profit_mean_pct'] == round(stop_result['profit_mean'] * 100, 2)

View File

@ -32,7 +32,7 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
trade.recalc_open_trade_value()
if not is_open:
trade.close(open_rate * profit_rate)
trade.sell_reason = sell_reason
trade.exit_reason = sell_reason
return trade

View File

@ -66,6 +66,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'open_trade_value': 0.0010025,
'close_rate_requested': ANY,
'sell_reason': ANY,
'exit_reason': ANY,
'sell_order_status': ANY,
'min_rate': ANY,
'max_rate': ANY,
@ -148,6 +149,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'open_trade_value': ANY,
'close_rate_requested': ANY,
'sell_reason': ANY,
'exit_reason': ANY,
'sell_order_status': ANY,
'min_rate': ANY,
'max_rate': ANY,
@ -1008,7 +1010,7 @@ def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee):
assert prec_satoshi(res[0]['profit_pct'], 0.5)
def test_sell_reason_performance_handle(default_conf, ticker, limit_buy_order, fee,
def test_exit_reason_performance_handle(default_conf, ticker, limit_buy_order, fee,
limit_sell_order, mocker) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
@ -1037,23 +1039,23 @@ def test_sell_reason_performance_handle(default_conf, ticker, limit_buy_order, f
trade.close_date = datetime.utcnow()
trade.is_open = False
res = rpc._rpc_sell_reason_performance(None)
res = rpc._rpc_exit_reason_performance(None)
assert len(res) == 1
assert res[0]['sell_reason'] == 'Other'
assert res[0]['exit_reason'] == 'Other'
assert res[0]['count'] == 1
assert prec_satoshi(res[0]['profit_pct'], 6.2)
trade.sell_reason = "TEST1"
res = rpc._rpc_sell_reason_performance(None)
trade.exit_reason = "TEST1"
res = rpc._rpc_exit_reason_performance(None)
assert len(res) == 1
assert res[0]['sell_reason'] == 'TEST1'
assert res[0]['exit_reason'] == 'TEST1'
assert res[0]['count'] == 1
assert prec_satoshi(res[0]['profit_pct'], 6.2)
def test_sell_reason_performance_handle_2(mocker, default_conf, markets, fee):
def test_exit_reason_performance_handle_2(mocker, default_conf, markets, fee):
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
@ -1064,21 +1066,21 @@ def test_sell_reason_performance_handle_2(mocker, default_conf, markets, fee):
create_mock_trades(fee)
rpc = RPC(freqtradebot)
res = rpc._rpc_sell_reason_performance(None)
res = rpc._rpc_exit_reason_performance(None)
assert len(res) == 2
assert res[0]['sell_reason'] == 'sell_signal'
assert res[0]['exit_reason'] == 'sell_signal'
assert res[0]['count'] == 1
assert prec_satoshi(res[0]['profit_pct'], 0.5)
assert res[1]['sell_reason'] == 'roi'
assert res[1]['exit_reason'] == 'roi'
assert res[1]['count'] == 1
assert prec_satoshi(res[1]['profit_pct'], 1.0)
# Test for a specific pair
res = rpc._rpc_sell_reason_performance('ETC/BTC')
res = rpc._rpc_exit_reason_performance('ETC/BTC')
assert len(res) == 1
assert res[0]['count'] == 1
assert res[0]['sell_reason'] == 'sell_signal'
assert res[0]['exit_reason'] == 'sell_signal'
assert prec_satoshi(res[0]['profit_pct'], 0.5)
@ -1119,7 +1121,7 @@ def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
assert prec_satoshi(res[0]['profit_pct'], 6.2)
trade.enter_tag = "TESTBUY"
trade.sell_reason = "TESTSELL"
trade.exit_reason = "TESTSELL"
res = rpc._rpc_mix_tag_performance(None)
assert len(res) == 1

View File

@ -822,14 +822,14 @@ def test_api_stats(botclient, mocker, ticker, fee, markets, is_short):
rc = client_get(client, f"{BASE_URI}/stats")
assert_response(rc, 200)
assert 'durations' in rc.json()
assert 'sell_reasons' in rc.json()
assert 'exit_reasons' in rc.json()
create_mock_trades(fee, is_short=is_short)
rc = client_get(client, f"{BASE_URI}/stats")
assert_response(rc, 200)
assert 'durations' in rc.json()
assert 'sell_reasons' in rc.json()
assert 'exit_reasons' in rc.json()
assert 'wins' in rc.json()['durations']
assert 'losses' in rc.json()['durations']
@ -962,6 +962,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short,
'open_rate_requested': ANY,
'open_trade_value': open_trade_value,
'sell_reason': None,
'exit_reason': None,
'sell_order_status': None,
'strategy': CURRENT_TEST_STRATEGY,
'buy_tag': None,
@ -1162,6 +1163,7 @@ def test_api_forceentry(botclient, mocker, fee, endpoint):
'open_rate_requested': None,
'open_trade_value': 0.24605460,
'sell_reason': None,
'exit_reason': None,
'sell_order_status': None,
'strategy': CURRENT_TEST_STRATEGY,
'buy_tag': None,

View File

@ -97,7 +97,7 @@ def test_telegram_init(default_conf, mocker, caplog) -> None:
"['balance'], ['start'], ['stop'], "
"['forcesell', 'forceexit'], ['forcebuy', 'forcelong'], ['forceshort'], "
"['trades'], ['delete'], ['performance'], "
"['buys', 'entries'], ['sells'], ['mix_tags'], "
"['buys', 'entries'], ['sells', 'exits'], ['mix_tags'], "
"['stats'], ['daily'], ['weekly'], ['monthly'], "
"['count'], ['locks'], ['unlock', 'delete_locks'], "
"['reload_config', 'reload_conf'], ['show_config', 'show_conf'], "
@ -837,7 +837,7 @@ def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee,
telegram._stats(update=update, context=MagicMock())
assert msg_mock.call_count == 1
assert 'Sell Reason' in msg_mock.call_args_list[-1][0][0]
assert 'Exit Reason' in msg_mock.call_args_list[-1][0][0]
assert 'ROI' in msg_mock.call_args_list[-1][0][0]
assert 'Avg. Duration' in msg_mock.call_args_list[-1][0][0]
msg_mock.reset_mock()
@ -1060,6 +1060,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
'buy_tag': ANY,
'enter_tag': ANY,
'sell_reason': ExitType.FORCE_SELL.value,
'exit_reason': ExitType.FORCE_SELL.value,
'open_date': ANY,
'close_date': ANY,
'close_rate': ANY,
@ -1128,6 +1129,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
'buy_tag': ANY,
'enter_tag': ANY,
'sell_reason': ExitType.FORCE_SELL.value,
'exit_reason': ExitType.FORCE_SELL.value,
'open_date': ANY,
'close_date': ANY,
'close_rate': ANY,
@ -1186,6 +1188,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
'buy_tag': ANY,
'enter_tag': ANY,
'sell_reason': ExitType.FORCE_SELL.value,
'exit_reason': ExitType.FORCE_SELL.value,
'open_date': ANY,
'close_date': ANY,
'close_rate': ANY,
@ -1334,8 +1337,8 @@ def test_telegram_performance_handle(default_conf, update, ticker, fee,
assert '<code>ETH/BTC\t0.00006217 BTC (6.20%) (1)</code>' in msg_mock.call_args_list[0][0][0]
def test_telegram_buy_tag_performance_handle(default_conf, update, ticker, fee,
limit_buy_order, limit_sell_order, mocker) -> None:
def test_telegram_entry_tag_performance_handle(
default_conf, update, ticker, fee, limit_buy_order, limit_sell_order, mocker) -> None:
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
@ -1363,7 +1366,7 @@ def test_telegram_buy_tag_performance_handle(default_conf, update, ticker, fee,
context = MagicMock()
telegram._enter_tag_performance(update=update, context=context)
assert msg_mock.call_count == 1
assert 'Buy Tag Performance' in msg_mock.call_args_list[0][0][0]
assert 'Entry Tag Performance' in msg_mock.call_args_list[0][0][0]
assert '<code>TESTBUY\t0.00006217 BTC (6.20%) (1)</code>' in msg_mock.call_args_list[0][0][0]
context.args = [trade.pair]
@ -1379,7 +1382,7 @@ def test_telegram_buy_tag_performance_handle(default_conf, update, ticker, fee,
assert "Error" in msg_mock.call_args_list[0][0][0]
def test_telegram_sell_reason_performance_handle(default_conf, update, ticker, fee,
def test_telegram_exit_reason_performance_handle(default_conf, update, ticker, fee,
limit_buy_order, limit_sell_order, mocker) -> None:
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
@ -1393,7 +1396,7 @@ def test_telegram_sell_reason_performance_handle(default_conf, update, ticker, f
freqtradebot.enter_positions()
trade = Trade.query.first()
assert trade
trade.sell_reason = 'TESTSELL'
trade.exit_reason = 'TESTSELL'
# Simulate fulfilled LIMIT_BUY order for trade
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
@ -1405,19 +1408,19 @@ def test_telegram_sell_reason_performance_handle(default_conf, update, ticker, f
trade.close_date = datetime.utcnow()
trade.is_open = False
context = MagicMock()
telegram._sell_reason_performance(update=update, context=context)
telegram._exit_reason_performance(update=update, context=context)
assert msg_mock.call_count == 1
assert 'Sell Reason Performance' in msg_mock.call_args_list[0][0][0]
assert 'Exit Reason Performance' in msg_mock.call_args_list[0][0][0]
assert '<code>TESTSELL\t0.00006217 BTC (6.20%) (1)</code>' in msg_mock.call_args_list[0][0][0]
context.args = [trade.pair]
telegram._sell_reason_performance(update=update, context=context)
telegram._exit_reason_performance(update=update, context=context)
assert msg_mock.call_count == 2
msg_mock.reset_mock()
mocker.patch('freqtrade.rpc.rpc.RPC._rpc_sell_reason_performance',
mocker.patch('freqtrade.rpc.rpc.RPC._rpc_exit_reason_performance',
side_effect=RPCException('Error'))
telegram._sell_reason_performance(update=update, context=MagicMock())
telegram._exit_reason_performance(update=update, context=MagicMock())
assert msg_mock.call_count == 1
assert "Error" in msg_mock.call_args_list[0][0][0]
@ -1439,7 +1442,7 @@ def test_telegram_mix_tag_performance_handle(default_conf, update, ticker, fee,
assert trade
trade.enter_tag = "TESTBUY"
trade.sell_reason = "TESTSELL"
trade.exit_reason = "TESTSELL"
# Simulate fulfilled LIMIT_BUY order for trade
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
@ -1932,7 +1935,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
'stake_currency': 'ETH',
'fiat_currency': 'USD',
'enter_tag': 'buy_signal1',
'sell_reason': ExitType.STOP_LOSS.value,
'exit_reason': ExitType.STOP_LOSS.value,
'open_date': arrow.utcnow().shift(hours=-1),
'close_date': arrow.utcnow(),
})
@ -1966,7 +1969,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
'profit_ratio': -0.57405275,
'stake_currency': 'ETH',
'enter_tag': 'buy_signal1',
'sell_reason': ExitType.STOP_LOSS.value,
'exit_reason': ExitType.STOP_LOSS.value,
'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30),
'close_date': arrow.utcnow(),
})
@ -2045,7 +2048,7 @@ def test_send_msg_sell_fill_notification(default_conf, mocker, direction,
'profit_ratio': -0.57405275,
'stake_currency': 'ETH',
'enter_tag': enter_signal,
'sell_reason': ExitType.STOP_LOSS.value,
'exit_reason': ExitType.STOP_LOSS.value,
'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30),
'close_date': arrow.utcnow(),
})
@ -2169,7 +2172,7 @@ def test_send_msg_sell_notification_no_fiat(
'stake_currency': 'ETH',
'fiat_currency': 'USD',
'enter_tag': enter_signal,
'sell_reason': ExitType.STOP_LOSS.value,
'exit_reason': ExitType.STOP_LOSS.value,
'open_date': arrow.utcnow().shift(hours=-2, minutes=-35, seconds=-3),
'close_date': arrow.utcnow(),
})
@ -2191,13 +2194,13 @@ def test_send_msg_sell_notification_no_fiat(
@pytest.mark.parametrize('msg,expected', [
({'profit_percent': 20.1, 'sell_reason': 'roi'}, "\N{ROCKET}"),
({'profit_percent': 5.1, 'sell_reason': 'roi'}, "\N{ROCKET}"),
({'profit_percent': 2.56, 'sell_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
({'profit_percent': 1.0, 'sell_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
({'profit_percent': 0.0, 'sell_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
({'profit_percent': -5.0, 'sell_reason': 'stop_loss'}, "\N{WARNING SIGN}"),
({'profit_percent': -2.0, 'sell_reason': 'sell_signal'}, "\N{CROSS MARK}"),
({'profit_percent': 20.1, 'exit_reason': 'roi'}, "\N{ROCKET}"),
({'profit_percent': 5.1, 'exit_reason': 'roi'}, "\N{ROCKET}"),
({'profit_percent': 2.56, 'exit_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
({'profit_percent': 1.0, 'exit_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
({'profit_percent': 0.0, 'exit_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
({'profit_percent': -5.0, 'exit_reason': 'stop_loss'}, "\N{WARNING SIGN}"),
({'profit_percent': -2.0, 'exit_reason': 'sell_signal'}, "\N{CROSS MARK}"),
])
def test__sell_emoji(default_conf, mocker, msg, expected):
del default_conf['fiat_display_currency']

View File

@ -236,6 +236,8 @@ def test_edge_overrides_stoploss(limit_order, fee, caplog, mocker,
assert freqtrade.handle_trade(trade) is not ignore_strat_sl
if not ignore_strat_sl:
assert log_has_re('Exit for NEO/BTC detected. Reason: stop_loss.*', caplog)
assert trade.exit_reason == ExitType.STOP_LOSS.value
# Test compatibility ...
assert trade.sell_reason == ExitType.STOP_LOSS.value
@ -1208,7 +1210,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_
assert freqtrade.handle_stoploss_on_exchange(trade) is False
assert trade.stoploss_order_id is None
assert trade.is_open is False
assert trade.sell_reason == str(ExitType.EMERGENCY_SELL)
assert trade.exit_reason == str(ExitType.EMERGENCY_SELL)
@pytest.mark.parametrize("is_short", [False, True])
@ -1291,7 +1293,7 @@ def test_create_stoploss_order_invalid_order(
caplog.clear()
freqtrade.create_stoploss_order(trade, 200)
assert trade.stoploss_order_id is None
assert trade.sell_reason == ExitType.EMERGENCY_SELL.value
assert trade.exit_reason == ExitType.EMERGENCY_SELL.value
assert log_has("Unable to place a stoploss order on exchange. ", caplog)
assert log_has("Exiting the trade forcefully", caplog)
@ -2150,7 +2152,7 @@ def test_handle_trade(
assert trade.close_profit == close_profit
assert trade.calc_profit() == 5.685
assert trade.close_date is not None
assert trade.sell_reason == 'sell_signal1'
assert trade.exit_reason == 'sell_signal1'
@pytest.mark.parametrize("is_short", [False, True])
@ -2985,7 +2987,7 @@ def test_handle_cancel_exit_limit(mocker, default_conf_usdt, fee) -> None:
fee_close=fee.return_value,
close_rate=0.555,
close_date=arrow.utcnow().datetime,
sell_reason="sell_reason_whatever",
exit_reason="sell_reason_whatever",
)
order = {'remaining': 1,
'amount': 1,
@ -2995,7 +2997,7 @@ def test_handle_cancel_exit_limit(mocker, default_conf_usdt, fee) -> None:
assert cancel_order_mock.call_count == 1
assert send_msg_mock.call_count == 1
assert trade.close_rate is None
assert trade.sell_reason is None
assert trade.exit_reason is None
send_msg_mock.reset_mock()
@ -3107,6 +3109,7 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_
'stake_currency': 'USDT',
'fiat_currency': 'USD',
'sell_reason': ExitType.ROI.value,
'exit_reason': ExitType.ROI.value,
'open_date': ANY,
'close_date': ANY,
'close_rate': ANY,
@ -3166,6 +3169,7 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd
'stake_currency': 'USDT',
'fiat_currency': 'USD',
'sell_reason': ExitType.STOP_LOSS.value,
'exit_reason': ExitType.STOP_LOSS.value,
'open_date': ANY,
'close_date': ANY,
'close_rate': ANY,
@ -3246,6 +3250,7 @@ def test_execute_trade_exit_custom_exit_price(
'stake_currency': 'USDT',
'fiat_currency': 'USD',
'sell_reason': ExitType.SELL_SIGNAL.value,
'exit_reason': ExitType.SELL_SIGNAL.value,
'open_date': ANY,
'close_date': ANY,
'close_rate': ANY,
@ -3313,6 +3318,7 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(
'stake_currency': 'USDT',
'fiat_currency': 'USD',
'sell_reason': ExitType.STOP_LOSS.value,
'exit_reason': ExitType.STOP_LOSS.value,
'open_date': ANY,
'close_date': ANY,
'close_rate': ANY,
@ -3479,7 +3485,7 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(
freqtrade.exit_positions(trades)
assert trade.stoploss_order_id is None
assert trade.is_open is False
assert trade.sell_reason == ExitType.STOPLOSS_ON_EXCHANGE.value
assert trade.exit_reason == ExitType.STOPLOSS_ON_EXCHANGE.value
assert rpc_mock.call_count == 3
if is_short:
assert rpc_mock.call_args_list[0][0][0]['type'] == RPCMessageType.SHORT
@ -3576,6 +3582,7 @@ def test_execute_trade_exit_market_order(
'stake_currency': 'USDT',
'fiat_currency': 'USD',
'sell_reason': ExitType.ROI.value,
'exit_reason': ExitType.ROI.value,
'open_date': ANY,
'close_date': ANY,
'close_rate': ANY,
@ -3843,7 +3850,7 @@ def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_order_op
else:
patch_get_signal(freqtrade, enter_long=False, exit_long=False)
assert freqtrade.handle_trade(trade) is True
assert trade.sell_reason == ExitType.ROI.value
assert trade.exit_reason == ExitType.ROI.value
@pytest.mark.parametrize("is_short,val1,val2", [
@ -3905,7 +3912,7 @@ def test_trailing_stop_loss(default_conf_usdt, limit_order_open,
f"stoploss is {(2.0 * val1 * stop_multi):6f}, "
f"initial stoploss was at {(2.0 * stop_multi):6f}, trade opened at 2.000000",
caplog)
assert trade.sell_reason == ExitType.TRAILING_STOP_LOSS.value
assert trade.exit_reason == ExitType.TRAILING_STOP_LOSS.value
@pytest.mark.parametrize('offset,trail_if_reached,second_sl,is_short', [
@ -4011,7 +4018,7 @@ def test_trailing_stop_loss_positive(
f"initial stoploss was at {'2.42' if is_short else '1.80'}0000, "
f"trade opened at {2.2 if is_short else 2.0}00000",
caplog)
assert trade.sell_reason == ExitType.TRAILING_STOP_LOSS.value
assert trade.exit_reason == ExitType.TRAILING_STOP_LOSS.value
@pytest.mark.parametrize("is_short", [False, True])
@ -4057,7 +4064,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_
# Test if entry-signal is absent
patch_get_signal(freqtrade)
assert freqtrade.handle_trade(trade) is True
assert trade.sell_reason == ExitType.ROI.value
assert trade.exit_reason == ExitType.ROI.value
def test_get_real_amount_quote(default_conf_usdt, trades_for_order, buy_order_fee, fee, caplog,

View File

@ -115,15 +115,15 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
assert wallets_mock.call_count == 4
trade = trades[0]
assert trade.sell_reason == ExitType.STOPLOSS_ON_EXCHANGE.value
assert trade.exit_reason == ExitType.STOPLOSS_ON_EXCHANGE.value
assert not trade.is_open
trade = trades[1]
assert not trade.sell_reason
assert not trade.exit_reason
assert trade.is_open
trade = trades[2]
assert trade.sell_reason == ExitType.SELL_SIGNAL.value
assert trade.exit_reason == ExitType.SELL_SIGNAL.value
assert not trade.is_open

View File

@ -1255,7 +1255,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
assert trade.min_rate is None
assert trade.stop_loss == 0.0
assert trade.initial_stop_loss == 0.0
assert trade.sell_reason is None
assert trade.exit_reason is None
assert trade.strategy is None
assert trade.timeframe == '5m'
assert trade.stoploss_order_id == 'stop_order_id222'
@ -1590,6 +1590,7 @@ def test_to_json(fee):
'profit_pct': None,
'profit_abs': None,
'sell_reason': None,
'exit_reason': None,
'sell_order_status': None,
'stop_loss_abs': None,
'stop_loss_ratio': None,
@ -1676,6 +1677,7 @@ def test_to_json(fee):
'open_rate_requested': None,
'open_trade_value': 12.33075,
'sell_reason': None,
'exit_reason': None,
'sell_order_status': None,
'strategy': None,
'buy_tag': 'buys_signal_001',
@ -2195,7 +2197,7 @@ def test_Trade_object_idem():
'get_open_trades_without_assigned_fees',
'get_open_order_trades',
'get_trades',
'get_sell_reason_performance',
'get_exit_reason_performance',
'get_enter_tag_performance',
'get_mix_tag_performance',

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long