Switched close_reason back to sell_reason
This commit is contained in:
parent
73dbc7a2f4
commit
e198d7f3d2
@ -55,7 +55,7 @@ Currently, the arguments are:
|
|||||||
|
|
||||||
* `results`: DataFrame containing the result
|
* `results`: DataFrame containing the result
|
||||||
The following columns are available in results (corresponds to the output-file of backtesting when used with `--export 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, close_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, sell_reason, stake_amount, min_rate, max_rate, stop_loss_ratio, stop_loss_abs`
|
||||||
* `trade_count`: Amount of trades (identical to `len(results)`)
|
* `trade_count`: Amount of trades (identical to `len(results)`)
|
||||||
* `min_date`: Start date of the timerange used
|
* `min_date`: Start date of the timerange used
|
||||||
* `min_date`: End date of the timerange used
|
* `min_date`: End date of the timerange used
|
||||||
|
@ -65,7 +65,7 @@ SET is_open=0,
|
|||||||
close_rate=<close_rate>,
|
close_rate=<close_rate>,
|
||||||
close_profit = close_rate / open_rate - 1,
|
close_profit = close_rate / open_rate - 1,
|
||||||
close_profit_abs = (amount * <close_rate> * (1 - fee_close) - (amount * (open_rate * (1 - fee_open)))),
|
close_profit_abs = (amount * <close_rate> * (1 - fee_close) - (amount * (open_rate * (1 - fee_open)))),
|
||||||
close_reason=<close_reason>
|
sell_reason=<sell_reason>
|
||||||
WHERE id=<trade_ID_to_update>;
|
WHERE id=<trade_ID_to_update>;
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ SET is_open=0,
|
|||||||
close_rate=0.19638016,
|
close_rate=0.19638016,
|
||||||
close_profit=0.0496,
|
close_profit=0.0496,
|
||||||
close_profit_abs = (amount * 0.19638016 * (1 - fee_close) - (amount * (open_rate * (1 - fee_open)))),
|
close_profit_abs = (amount * 0.19638016 * (1 - fee_close) - (amount * (open_rate * (1 - fee_open)))),
|
||||||
close_reason='force_sell'
|
sell_reason='force_sell'
|
||||||
WHERE id=31;
|
WHERE id=31;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ from freqtrade.exchange import timeframe_to_prev_date
|
|||||||
|
|
||||||
class AwesomeStrategy(IStrategy):
|
class AwesomeStrategy(IStrategy):
|
||||||
def confirm_trade_exit(self, pair: str, trade: 'Trade', order_type: str, amount: float,
|
def confirm_trade_exit(self, pair: str, trade: 'Trade', order_type: str, amount: float,
|
||||||
rate: float, time_in_force: str, close_reason: str,
|
rate: float, time_in_force: str, sell_reason: str,
|
||||||
current_time: 'datetime', **kwargs) -> bool:
|
current_time: 'datetime', **kwargs) -> bool:
|
||||||
# Obtain pair dataframe.
|
# Obtain pair dataframe.
|
||||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||||
@ -490,7 +490,7 @@ class AwesomeStrategy(IStrategy):
|
|||||||
# ... populate_* methods
|
# ... populate_* methods
|
||||||
|
|
||||||
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
|
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
|
||||||
rate: float, time_in_force: str, close_reason: str, **kwargs) -> bool:
|
rate: float, time_in_force: str, sell_reason: str, **kwargs) -> bool:
|
||||||
"""
|
"""
|
||||||
Called right before placing a regular sell order.
|
Called right before placing a regular sell order.
|
||||||
Timing for this function is critical, so avoid doing heavy computations or
|
Timing for this function is critical, so avoid doing heavy computations or
|
||||||
@ -505,14 +505,14 @@ class AwesomeStrategy(IStrategy):
|
|||||||
:param amount: Amount in quote currency.
|
:param amount: Amount in quote currency.
|
||||||
:param rate: Rate that's going to be used when using limit orders
|
:param rate: Rate that's going to be used when using limit orders
|
||||||
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
|
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
|
||||||
:param close_reason: Sell reason.
|
:param sell_reason: Sell reason.
|
||||||
Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss',
|
Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss',
|
||||||
'sell_signal', 'force_sell', 'emergency_sell']
|
'sell_signal', 'force_sell', 'emergency_sell']
|
||||||
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
||||||
:return bool: When True is returned, then the sell-order is placed on the exchange.
|
:return bool: When True is returned, then the sell-order is placed on the exchange.
|
||||||
False aborts the process
|
False aborts the process
|
||||||
"""
|
"""
|
||||||
if close_reason == 'force_sell' and trade.calc_profit_ratio(rate) < 0:
|
if sell_reason == 'force_sell' and trade.calc_profit_ratio(rate) < 0:
|
||||||
# Reject force-sells with negative profit
|
# Reject force-sells with negative profit
|
||||||
# This is just a sample, please adjust to your needs
|
# This is just a sample, please adjust to your needs
|
||||||
# (this does not necessarily make sense, assuming you know when you're force-selling)
|
# (this does not necessarily make sense, assuming you know when you're force-selling)
|
||||||
|
@ -127,7 +127,7 @@ print(stats['strategy_comparison'])
|
|||||||
trades = load_backtest_data(backtest_dir)
|
trades = load_backtest_data(backtest_dir)
|
||||||
|
|
||||||
# Show value-counts per pair
|
# Show value-counts per pair
|
||||||
trades.groupby("pair")["close_reason"].value_counts()
|
trades.groupby("pair")["sell_reason"].value_counts()
|
||||||
```
|
```
|
||||||
|
|
||||||
### Load live trading results into a pandas dataframe
|
### Load live trading results into a pandas dataframe
|
||||||
@ -142,7 +142,7 @@ from freqtrade.data.btanalysis import load_trades_from_db
|
|||||||
trades = load_trades_from_db("sqlite:///tradesv3.sqlite")
|
trades = load_trades_from_db("sqlite:///tradesv3.sqlite")
|
||||||
|
|
||||||
# Display results
|
# Display results
|
||||||
trades.groupby("pair")["close_reason"].value_counts()
|
trades.groupby("pair")["sell_reason"].value_counts()
|
||||||
```
|
```
|
||||||
|
|
||||||
## Analyze the loaded trades for trade parallelism
|
## Analyze the loaded trades for trade parallelism
|
||||||
|
@ -132,7 +132,7 @@ Possible parameters are:
|
|||||||
* `profit_ratio`
|
* `profit_ratio`
|
||||||
* `stake_currency`
|
* `stake_currency`
|
||||||
* `fiat_currency`
|
* `fiat_currency`
|
||||||
* `close_reason`
|
* `sell_reason`
|
||||||
* `order_type`
|
* `order_type`
|
||||||
* `open_date`
|
* `open_date`
|
||||||
* `close_date`
|
* `close_date`
|
||||||
@ -154,7 +154,7 @@ Possible parameters are:
|
|||||||
* `profit_ratio`
|
* `profit_ratio`
|
||||||
* `stake_currency`
|
* `stake_currency`
|
||||||
* `fiat_currency`
|
* `fiat_currency`
|
||||||
* `close_reason`
|
* `sell_reason`
|
||||||
* `order_type`
|
* `order_type`
|
||||||
* `open_date`
|
* `open_date`
|
||||||
* `close_date`
|
* `close_date`
|
||||||
@ -176,7 +176,7 @@ Possible parameters are:
|
|||||||
* `profit_ratio`
|
* `profit_ratio`
|
||||||
* `stake_currency`
|
* `stake_currency`
|
||||||
* `fiat_currency`
|
* `fiat_currency`
|
||||||
* `close_reason`
|
* `sell_reason`
|
||||||
* `order_type`
|
* `order_type`
|
||||||
* `open_date`
|
* `open_date`
|
||||||
* `close_date`
|
* `close_date`
|
||||||
|
@ -17,18 +17,18 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# Old format - maybe remove?
|
# Old format - maybe remove?
|
||||||
BT_DATA_COLUMNS_OLD = ["pair", "profit_percent", "open_date", "close_date", "index",
|
BT_DATA_COLUMNS_OLD = ["pair", "profit_percent", "open_date", "close_date", "index",
|
||||||
"trade_duration", "open_rate", "close_rate", "open_at_end", "close_reason"]
|
"trade_duration", "open_rate", "close_rate", "open_at_end", "sell_reason"]
|
||||||
|
|
||||||
# Mid-term format, crated by BacktestResult Named Tuple
|
# Mid-term format, crated by BacktestResult Named Tuple
|
||||||
BT_DATA_COLUMNS_MID = ['pair', 'profit_percent', 'open_date', 'close_date', 'trade_duration',
|
BT_DATA_COLUMNS_MID = ['pair', 'profit_percent', 'open_date', 'close_date', 'trade_duration',
|
||||||
'open_rate', 'close_rate', 'open_at_end', 'close_reason', 'fee_open',
|
'open_rate', 'close_rate', 'open_at_end', 'sell_reason', 'fee_open',
|
||||||
'fee_close', 'amount', 'profit_abs', 'profit_ratio']
|
'fee_close', 'amount', 'profit_abs', 'profit_ratio']
|
||||||
|
|
||||||
# Newest format
|
# Newest format
|
||||||
BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date',
|
BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date',
|
||||||
'open_rate', 'close_rate',
|
'open_rate', 'close_rate',
|
||||||
'fee_open', 'fee_close', 'trade_duration',
|
'fee_open', 'fee_close', 'trade_duration',
|
||||||
'profit_ratio', 'profit_abs', 'close_reason',
|
'profit_ratio', 'profit_abs', 'sell_reason',
|
||||||
'initial_stop_loss_abs', 'initial_stop_loss_ratio', 'stop_loss_abs',
|
'initial_stop_loss_abs', 'initial_stop_loss_ratio', 'stop_loss_abs',
|
||||||
'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', ]
|
'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', ]
|
||||||
|
|
||||||
|
@ -268,7 +268,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
# Updating open orders in dry-run does not make sense and will fail.
|
# Updating open orders in dry-run does not make sense and will fail.
|
||||||
return
|
return
|
||||||
|
|
||||||
trades: List[Trade] = Trade.get_closed_trades_without_assigned_fees()
|
trades: List[Trade] = Trade.get_sold_trades_without_assigned_fees()
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
|
|
||||||
if not trade.is_open and not trade.fee_updated('sell'):
|
if not trade.is_open and not trade.fee_updated('sell'):
|
||||||
@ -752,7 +752,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
trade.stoploss_order_id = None
|
trade.stoploss_order_id = None
|
||||||
logger.error(f'Unable to place a stoploss order on exchange. {e}')
|
logger.error(f'Unable to place a stoploss order on exchange. {e}')
|
||||||
logger.warning('Selling the trade forcefully')
|
logger.warning('Selling the trade forcefully')
|
||||||
self.execute_sell(trade, trade.stop_loss, close_reason=SellCheckTuple(
|
self.execute_sell(trade, trade.stop_loss, sell_reason=SellCheckTuple(
|
||||||
sell_type=SellType.EMERGENCY_SELL))
|
sell_type=SellType.EMERGENCY_SELL))
|
||||||
|
|
||||||
except ExchangeError:
|
except ExchangeError:
|
||||||
@ -783,7 +783,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
|
|
||||||
# We check if stoploss order is fulfilled
|
# We check if stoploss order is fulfilled
|
||||||
if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'):
|
if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'):
|
||||||
trade.close_reason = SellType.STOPLOSS_ON_EXCHANGE.value
|
trade.sell_reason = SellType.STOPLOSS_ON_EXCHANGE.value
|
||||||
self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order,
|
self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order,
|
||||||
stoploss_order=True)
|
stoploss_order=True)
|
||||||
# Lock pair for one candle to prevent immediate rebuys
|
# Lock pair for one candle to prevent immediate rebuys
|
||||||
@ -1071,16 +1071,16 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
raise DependencyException(
|
raise DependencyException(
|
||||||
f"Not enough amount to sell. Trade-amount: {amount}, Wallet: {wallet_amount}")
|
f"Not enough amount to sell. Trade-amount: {amount}, Wallet: {wallet_amount}")
|
||||||
|
|
||||||
def execute_sell(self, trade: Trade, limit: float, close_reason: SellCheckTuple) -> bool:
|
def execute_sell(self, trade: Trade, limit: float, sell_reason: SellCheckTuple) -> bool:
|
||||||
"""
|
"""
|
||||||
Executes a limit sell for the given trade and limit
|
Executes a limit sell for the given trade and limit
|
||||||
:param trade: Trade instance
|
:param trade: Trade instance
|
||||||
:param limit: limit rate for the sell order
|
:param limit: limit rate for the sell order
|
||||||
:param close_reason: Reason the sell was triggered
|
:param sell_reason: Reason the sell was triggered
|
||||||
:return: True if it succeeds (supported) False (not supported)
|
:return: True if it succeeds (supported) False (not supported)
|
||||||
"""
|
"""
|
||||||
sell_type = 'sell'
|
sell_type = 'sell'
|
||||||
if close_reason.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
|
if sell_reason.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
|
||||||
sell_type = 'stoploss'
|
sell_type = 'stoploss'
|
||||||
|
|
||||||
# if stoploss is on exchange and we are on dry_run mode,
|
# if stoploss is on exchange and we are on dry_run mode,
|
||||||
@ -1099,10 +1099,10 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}")
|
logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}")
|
||||||
|
|
||||||
order_type = self.strategy.order_types[sell_type]
|
order_type = self.strategy.order_types[sell_type]
|
||||||
if close_reason.sell_type == SellType.EMERGENCY_SELL:
|
if sell_reason.sell_type == SellType.EMERGENCY_SELL:
|
||||||
# Emergency sells (default to market!)
|
# Emergency sells (default to market!)
|
||||||
order_type = self.strategy.order_types.get("emergencysell", "market")
|
order_type = self.strategy.order_types.get("emergencysell", "market")
|
||||||
if close_reason.sell_type == SellType.FORCE_SELL:
|
if sell_reason.sell_type == SellType.FORCE_SELL:
|
||||||
# Force sells (default to the sell_type defined in the strategy,
|
# Force sells (default to the sell_type defined in the strategy,
|
||||||
# but we allow this value to be changed)
|
# but we allow this value to be changed)
|
||||||
order_type = self.strategy.order_types.get("forcesell", order_type)
|
order_type = self.strategy.order_types.get("forcesell", order_type)
|
||||||
@ -1112,7 +1112,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
|
|
||||||
if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)(
|
if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)(
|
||||||
pair=trade.pair, trade=trade, order_type=order_type, amount=amount, rate=limit,
|
pair=trade.pair, trade=trade, order_type=order_type, amount=amount, rate=limit,
|
||||||
time_in_force=time_in_force, close_reason=close_reason.close_reason,
|
time_in_force=time_in_force, sell_reason=sell_reason.sell_reason,
|
||||||
current_time=datetime.now(timezone.utc)):
|
current_time=datetime.now(timezone.utc)):
|
||||||
logger.info(f"User requested abortion of selling {trade.pair}")
|
logger.info(f"User requested abortion of selling {trade.pair}")
|
||||||
return False
|
return False
|
||||||
@ -1134,9 +1134,9 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
trade.orders.append(order_obj)
|
trade.orders.append(order_obj)
|
||||||
|
|
||||||
trade.open_order_id = order['id']
|
trade.open_order_id = order['id']
|
||||||
trade.close_order_status = ''
|
trade.sell_order_status = ''
|
||||||
trade.close_rate_requested = limit
|
trade.close_rate_requested = limit
|
||||||
trade.close_reason = close_reason.close_reason
|
trade.sell_reason = sell_reason.sell_reason
|
||||||
# In case of market sell orders the order can be closed immediately
|
# In case of market sell orders the order can be closed immediately
|
||||||
if order.get('status', 'unknown') == 'closed':
|
if order.get('status', 'unknown') == 'closed':
|
||||||
self.update_trade_state(trade, trade.open_order_id, order)
|
self.update_trade_state(trade, trade.open_order_id, order)
|
||||||
@ -1176,7 +1176,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
'current_rate': current_rate,
|
'current_rate': current_rate,
|
||||||
'profit_amount': profit_trade,
|
'profit_amount': profit_trade,
|
||||||
'profit_ratio': profit_ratio,
|
'profit_ratio': profit_ratio,
|
||||||
'close_reason': trade.close_reason,
|
'sell_reason': trade.sell_reason,
|
||||||
'open_date': trade.open_date,
|
'open_date': trade.open_date,
|
||||||
'close_date': trade.close_date or datetime.utcnow(),
|
'close_date': trade.close_date or datetime.utcnow(),
|
||||||
'stake_currency': self.config['stake_currency'],
|
'stake_currency': self.config['stake_currency'],
|
||||||
@ -1195,10 +1195,10 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
"""
|
"""
|
||||||
Sends rpc notification when a sell cancel occurred.
|
Sends rpc notification when a sell cancel occurred.
|
||||||
"""
|
"""
|
||||||
if trade.close_order_status == reason:
|
if trade.sell_order_status == reason:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
trade.close_order_status = reason
|
trade.sell_order_status = reason
|
||||||
|
|
||||||
profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested
|
profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested
|
||||||
profit_trade = trade.calc_profit(rate=profit_rate)
|
profit_trade = trade.calc_profit(rate=profit_rate)
|
||||||
@ -1219,7 +1219,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
'current_rate': current_rate,
|
'current_rate': current_rate,
|
||||||
'profit_amount': profit_trade,
|
'profit_amount': profit_trade,
|
||||||
'profit_ratio': profit_ratio,
|
'profit_ratio': profit_ratio,
|
||||||
'close_reason': trade.close_reason,
|
'sell_reason': trade.sell_reason,
|
||||||
'open_date': trade.open_date,
|
'open_date': trade.open_date,
|
||||||
'close_date': trade.close_date,
|
'close_date': trade.close_date,
|
||||||
'stake_currency': self.config['stake_currency'],
|
'stake_currency': self.config['stake_currency'],
|
||||||
|
@ -283,7 +283,7 @@ class Backtesting:
|
|||||||
|
|
||||||
if sell.sell_flag:
|
if sell.sell_flag:
|
||||||
trade.close_date = sell_row[DATE_IDX].to_pydatetime()
|
trade.close_date = sell_row[DATE_IDX].to_pydatetime()
|
||||||
trade.close_reason = sell.close_reason
|
trade.sell_reason = sell.sell_reason
|
||||||
trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60)
|
trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60)
|
||||||
closerate = self._get_close_rate(sell_row, trade, sell, trade_dur)
|
closerate = self._get_close_rate(sell_row, trade, sell, trade_dur)
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ class Backtesting:
|
|||||||
pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount,
|
pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount,
|
||||||
rate=closerate,
|
rate=closerate,
|
||||||
time_in_force=time_in_force,
|
time_in_force=time_in_force,
|
||||||
close_reason=sell.close_reason,
|
sell_reason=sell.sell_reason,
|
||||||
current_time=sell_row[DATE_IDX].to_pydatetime()):
|
current_time=sell_row[DATE_IDX].to_pydatetime()):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -345,7 +345,7 @@ class Backtesting:
|
|||||||
sell_row = data[pair][-1]
|
sell_row = data[pair][-1]
|
||||||
|
|
||||||
trade.close_date = sell_row[DATE_IDX].to_pydatetime()
|
trade.close_date = sell_row[DATE_IDX].to_pydatetime()
|
||||||
trade.close_reason = SellType.FORCE_SELL.value
|
trade.sell_reason = SellType.FORCE_SELL.value
|
||||||
trade.close(sell_row[OPEN_IDX], show_msg=False)
|
trade.close(sell_row[OPEN_IDX], show_msg=False)
|
||||||
LocalTrade.close_bt_trade(trade)
|
LocalTrade.close_bt_trade(trade)
|
||||||
# Deepcopy object to have wallets update correctly
|
# Deepcopy object to have wallets update correctly
|
||||||
|
@ -127,7 +127,7 @@ def generate_pair_metrics(data: Dict[str, Dict], stake_currency: str, starting_b
|
|||||||
return tabular_data
|
return tabular_data
|
||||||
|
|
||||||
|
|
||||||
def generate_close_reason_stats(max_open_trades: int, results: DataFrame) -> List[Dict]:
|
def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List[Dict]:
|
||||||
"""
|
"""
|
||||||
Generate small table outlining Backtest results
|
Generate small table outlining Backtest results
|
||||||
:param max_open_trades: Max_open_trades parameter
|
:param max_open_trades: Max_open_trades parameter
|
||||||
@ -136,8 +136,8 @@ def generate_close_reason_stats(max_open_trades: int, results: DataFrame) -> Lis
|
|||||||
"""
|
"""
|
||||||
tabular_data = []
|
tabular_data = []
|
||||||
|
|
||||||
for reason, count in results['close_reason'].value_counts().iteritems():
|
for reason, count in results['sell_reason'].value_counts().iteritems():
|
||||||
result = results.loc[results['close_reason'] == reason]
|
result = results.loc[results['sell_reason'] == reason]
|
||||||
|
|
||||||
profit_mean = result['profit_ratio'].mean()
|
profit_mean = result['profit_ratio'].mean()
|
||||||
profit_sum = result['profit_ratio'].sum()
|
profit_sum = result['profit_ratio'].sum()
|
||||||
@ -145,7 +145,7 @@ def generate_close_reason_stats(max_open_trades: int, results: DataFrame) -> Lis
|
|||||||
|
|
||||||
tabular_data.append(
|
tabular_data.append(
|
||||||
{
|
{
|
||||||
'close_reason': reason,
|
'sell_reason': reason,
|
||||||
'trades': count,
|
'trades': count,
|
||||||
'wins': len(result[result['profit_abs'] > 0]),
|
'wins': len(result[result['profit_abs'] > 0]),
|
||||||
'draws': len(result[result['profit_abs'] == 0]),
|
'draws': len(result[result['profit_abs'] == 0]),
|
||||||
@ -230,7 +230,7 @@ def generate_trading_stats(results: DataFrame) -> Dict[str, Any]:
|
|||||||
draw_trades = results.loc[results['profit_ratio'] == 0]
|
draw_trades = results.loc[results['profit_ratio'] == 0]
|
||||||
losing_trades = results.loc[results['profit_ratio'] < 0]
|
losing_trades = results.loc[results['profit_ratio'] < 0]
|
||||||
zero_duration_trades = len(results.loc[(results['trade_duration'] == 0) &
|
zero_duration_trades = len(results.loc[(results['trade_duration'] == 0) &
|
||||||
(results['close_reason'] == 'trailing_stop_loss')])
|
(results['sell_reason'] == 'trailing_stop_loss')])
|
||||||
|
|
||||||
holding_avg = (timedelta(minutes=round(results['trade_duration'].mean()))
|
holding_avg = (timedelta(minutes=round(results['trade_duration'].mean()))
|
||||||
if not results.empty else timedelta())
|
if not results.empty else timedelta())
|
||||||
@ -313,7 +313,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
|||||||
pair_results = generate_pair_metrics(btdata, stake_currency=stake_currency,
|
pair_results = generate_pair_metrics(btdata, stake_currency=stake_currency,
|
||||||
starting_balance=starting_balance,
|
starting_balance=starting_balance,
|
||||||
results=results, skip_nan=False)
|
results=results, skip_nan=False)
|
||||||
close_reason_stats = generate_close_reason_stats(max_open_trades=max_open_trades,
|
sell_reason_stats = generate_sell_reason_stats(max_open_trades=max_open_trades,
|
||||||
results=results)
|
results=results)
|
||||||
left_open_results = generate_pair_metrics(btdata, stake_currency=stake_currency,
|
left_open_results = generate_pair_metrics(btdata, stake_currency=stake_currency,
|
||||||
starting_balance=starting_balance,
|
starting_balance=starting_balance,
|
||||||
@ -335,7 +335,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
|||||||
'best_pair': best_pair,
|
'best_pair': best_pair,
|
||||||
'worst_pair': worst_pair,
|
'worst_pair': worst_pair,
|
||||||
'results_per_pair': pair_results,
|
'results_per_pair': pair_results,
|
||||||
'close_reason_summary': close_reason_stats,
|
'sell_reason_summary': sell_reason_stats,
|
||||||
'left_open_trades': left_open_results,
|
'left_open_trades': left_open_results,
|
||||||
'total_trades': len(results),
|
'total_trades': len(results),
|
||||||
'total_volume': float(results['stake_amount'].sum()),
|
'total_volume': float(results['stake_amount'].sum()),
|
||||||
@ -477,10 +477,10 @@ def text_table_bt_results(pair_results: List[Dict[str, Any]], stake_currency: st
|
|||||||
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right")
|
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right")
|
||||||
|
|
||||||
|
|
||||||
def text_table_close_reason(close_reason_stats: List[Dict[str, Any]], stake_currency: str) -> str:
|
def text_table_sell_reason(sell_reason_stats: List[Dict[str, Any]], stake_currency: str) -> str:
|
||||||
"""
|
"""
|
||||||
Generate small table outlining Backtest results
|
Generate small table outlining Backtest results
|
||||||
:param close_reason_stats: Sell reason metrics
|
:param sell_reason_stats: Sell reason metrics
|
||||||
:param stake_currency: Stakecurrency used
|
:param stake_currency: Stakecurrency used
|
||||||
:return: pretty printed table with tabulate as string
|
:return: pretty printed table with tabulate as string
|
||||||
"""
|
"""
|
||||||
@ -495,12 +495,12 @@ def text_table_close_reason(close_reason_stats: List[Dict[str, Any]], stake_curr
|
|||||||
]
|
]
|
||||||
|
|
||||||
output = [[
|
output = [[
|
||||||
t['close_reason'], t['trades'],
|
t['sell_reason'], t['trades'],
|
||||||
_generate_wins_draws_losses(t['wins'], t['draws'], t['losses']),
|
_generate_wins_draws_losses(t['wins'], t['draws'], t['losses']),
|
||||||
t['profit_mean_pct'], t['profit_sum_pct'],
|
t['profit_mean_pct'], t['profit_sum_pct'],
|
||||||
round_coin_value(t['profit_total_abs'], stake_currency, False),
|
round_coin_value(t['profit_total_abs'], stake_currency, False),
|
||||||
t['profit_total_pct'],
|
t['profit_total_pct'],
|
||||||
] for t in close_reason_stats]
|
] for t in sell_reason_stats]
|
||||||
return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right")
|
return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right")
|
||||||
|
|
||||||
|
|
||||||
@ -633,7 +633,7 @@ def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency:
|
|||||||
print(' BACKTESTING REPORT '.center(len(table.splitlines()[0]), '='))
|
print(' BACKTESTING REPORT '.center(len(table.splitlines()[0]), '='))
|
||||||
print(table)
|
print(table)
|
||||||
|
|
||||||
table = text_table_close_reason(close_reason_stats=results['close_reason_summary'],
|
table = text_table_sell_reason(sell_reason_stats=results['sell_reason_summary'],
|
||||||
stake_currency=stake_currency)
|
stake_currency=stake_currency)
|
||||||
if isinstance(table, str) and len(table) > 0:
|
if isinstance(table, str) and len(table) > 0:
|
||||||
print(' SELL REASON STATS '.center(len(table.splitlines()[0]), '='))
|
print(' SELL REASON STATS '.center(len(table.splitlines()[0]), '='))
|
||||||
|
@ -193,7 +193,7 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
|
|||||||
if trades is not None and len(trades) > 0:
|
if trades is not None and len(trades) > 0:
|
||||||
# Create description for sell summarizing the trade
|
# Create description for sell summarizing the trade
|
||||||
trades['desc'] = trades.apply(lambda row: f"{round(row['profit_ratio'] * 100, 1)}%, "
|
trades['desc'] = trades.apply(lambda row: f"{round(row['profit_ratio'] * 100, 1)}%, "
|
||||||
f"{row['close_reason']}, "
|
f"{row['sell_reason']}, "
|
||||||
f"{row['trade_duration']} min",
|
f"{row['trade_duration']} min",
|
||||||
axis=1)
|
axis=1)
|
||||||
trade_buys = go.Scatter(
|
trade_buys = go.Scatter(
|
||||||
|
@ -44,8 +44,8 @@ class StoplossGuard(IProtection):
|
|||||||
# filters = [
|
# filters = [
|
||||||
# Trade.is_open.is_(False),
|
# Trade.is_open.is_(False),
|
||||||
# Trade.close_date > look_back_until,
|
# Trade.close_date > look_back_until,
|
||||||
# or_(Trade.close_reason == SellType.STOP_LOSS.value,
|
# or_(Trade.sell_reason == SellType.STOP_LOSS.value,
|
||||||
# and_(Trade.close_reason == SellType.TRAILING_STOP_LOSS.value,
|
# and_(Trade.sell_reason == SellType.TRAILING_STOP_LOSS.value,
|
||||||
# Trade.close_profit < 0))
|
# Trade.close_profit < 0))
|
||||||
# ]
|
# ]
|
||||||
# if pair:
|
# if pair:
|
||||||
@ -53,7 +53,7 @@ class StoplossGuard(IProtection):
|
|||||||
# trades = Trade.get_trades(filters).all()
|
# trades = Trade.get_trades(filters).all()
|
||||||
|
|
||||||
trades1 = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until)
|
trades1 = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until)
|
||||||
trades = [trade for trade in trades1 if (str(trade.close_reason) in (
|
trades = [trade for trade in trades1 if (str(trade.sell_reason) in (
|
||||||
SellType.TRAILING_STOP_LOSS.value, SellType.STOP_LOSS.value,
|
SellType.TRAILING_STOP_LOSS.value, SellType.STOP_LOSS.value,
|
||||||
SellType.STOPLOSS_ON_EXCHANGE.value)
|
SellType.STOPLOSS_ON_EXCHANGE.value)
|
||||||
and trade.close_profit and trade.close_profit < 0)]
|
and trade.close_profit and trade.close_profit < 0)]
|
||||||
|
@ -94,7 +94,7 @@ class SellReason(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class Stats(BaseModel):
|
class Stats(BaseModel):
|
||||||
close_reasons: Dict[str, SellReason]
|
sell_reasons: Dict[str, SellReason]
|
||||||
durations: Dict[str, Union[str, float]]
|
durations: Dict[str, Union[str, float]]
|
||||||
|
|
||||||
|
|
||||||
@ -168,8 +168,8 @@ class TradeSchema(BaseModel):
|
|||||||
profit_pct: Optional[float]
|
profit_pct: Optional[float]
|
||||||
profit_abs: Optional[float]
|
profit_abs: Optional[float]
|
||||||
profit_fiat: Optional[float]
|
profit_fiat: Optional[float]
|
||||||
close_reason: Optional[str]
|
sell_reason: Optional[str]
|
||||||
close_order_status: Optional[str]
|
sell_order_status: Optional[str]
|
||||||
stop_loss_abs: Optional[float]
|
stop_loss_abs: Optional[float]
|
||||||
stop_loss_ratio: Optional[float]
|
stop_loss_ratio: Optional[float]
|
||||||
stop_loss_pct: Optional[float]
|
stop_loss_pct: Optional[float]
|
||||||
|
@ -315,11 +315,11 @@ class RPC:
|
|||||||
return 'draws'
|
return 'draws'
|
||||||
trades = trades = Trade.get_trades([Trade.is_open.is_(False)])
|
trades = trades = Trade.get_trades([Trade.is_open.is_(False)])
|
||||||
# Sell reason
|
# Sell reason
|
||||||
close_reasons = {}
|
sell_reasons = {}
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
if trade.close_reason not in close_reasons:
|
if trade.sell_reason not in sell_reasons:
|
||||||
close_reasons[trade.close_reason] = {'wins': 0, 'losses': 0, 'draws': 0}
|
sell_reasons[trade.sell_reason] = {'wins': 0, 'losses': 0, 'draws': 0}
|
||||||
close_reasons[trade.close_reason][trade_win_loss(trade)] += 1
|
sell_reasons[trade.sell_reason][trade_win_loss(trade)] += 1
|
||||||
|
|
||||||
# Duration
|
# Duration
|
||||||
dur: Dict[str, List[int]] = {'wins': [], 'draws': [], 'losses': []}
|
dur: Dict[str, List[int]] = {'wins': [], 'draws': [], 'losses': []}
|
||||||
@ -333,7 +333,7 @@ class RPC:
|
|||||||
losses_dur = sum(dur['losses']) / len(dur['losses']) if len(dur['losses']) > 0 else 'N/A'
|
losses_dur = sum(dur['losses']) / len(dur['losses']) if len(dur['losses']) > 0 else 'N/A'
|
||||||
|
|
||||||
durations = {'wins': wins_dur, 'draws': draws_dur, 'losses': losses_dur}
|
durations = {'wins': wins_dur, 'draws': draws_dur, 'losses': losses_dur}
|
||||||
return {'close_reasons': close_reasons, 'durations': durations}
|
return {'sell_reasons': sell_reasons, 'durations': durations}
|
||||||
|
|
||||||
def _rpc_trade_statistics(
|
def _rpc_trade_statistics(
|
||||||
self, stake_currency: str, fiat_display_currency: str,
|
self, stake_currency: str, fiat_display_currency: str,
|
||||||
@ -539,8 +539,8 @@ class RPC:
|
|||||||
if not fully_canceled:
|
if not fully_canceled:
|
||||||
# Get current rate and execute sell
|
# Get current rate and execute sell
|
||||||
current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False)
|
current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False)
|
||||||
close_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL)
|
sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL)
|
||||||
self._freqtrade.execute_sell(trade, current_rate, close_reason)
|
self._freqtrade.execute_sell(trade, current_rate, sell_reason)
|
||||||
# ---- EOF def _exec_forcesell ----
|
# ---- EOF def _exec_forcesell ----
|
||||||
|
|
||||||
if self._freqtrade.state != State.RUNNING:
|
if self._freqtrade.state != State.RUNNING:
|
||||||
|
@ -242,7 +242,7 @@ class Telegram(RPCHandler):
|
|||||||
|
|
||||||
message = ("{emoji} *{exchange}:* Selling {pair} (#{trade_id})\n"
|
message = ("{emoji} *{exchange}:* Selling {pair} (#{trade_id})\n"
|
||||||
"*Profit:* `{profit_percent:.2f}%{profit_extra}`\n"
|
"*Profit:* `{profit_percent:.2f}%{profit_extra}`\n"
|
||||||
"*Sell Reason:* `{close_reason}`\n"
|
"*Sell Reason:* `{sell_reason}`\n"
|
||||||
"*Duration:* `{duration} ({duration_min:.1f} min)`\n"
|
"*Duration:* `{duration} ({duration_min:.1f} min)`\n"
|
||||||
"*Amount:* `{amount:.8f}`\n"
|
"*Amount:* `{amount:.8f}`\n"
|
||||||
"*Open Rate:* `{open_rate:.8f}`\n"
|
"*Open Rate:* `{open_rate:.8f}`\n"
|
||||||
@ -265,7 +265,7 @@ class Telegram(RPCHandler):
|
|||||||
if isinstance(sell_noti, str):
|
if isinstance(sell_noti, str):
|
||||||
noti = sell_noti
|
noti = sell_noti
|
||||||
else:
|
else:
|
||||||
noti = sell_noti.get(str(msg['close_reason']), default_noti)
|
noti = sell_noti.get(str(msg['sell_reason']), default_noti)
|
||||||
else:
|
else:
|
||||||
noti = self._config['telegram'] \
|
noti = self._config['telegram'] \
|
||||||
.get('notification_settings', {}).get(str(msg_type), default_noti)
|
.get('notification_settings', {}).get(str(msg_type), default_noti)
|
||||||
@ -318,7 +318,7 @@ class Telegram(RPCHandler):
|
|||||||
return "\N{ROCKET}"
|
return "\N{ROCKET}"
|
||||||
elif float(msg['profit_percent']) >= 0.0:
|
elif float(msg['profit_percent']) >= 0.0:
|
||||||
return "\N{EIGHT SPOKED ASTERISK}"
|
return "\N{EIGHT SPOKED ASTERISK}"
|
||||||
elif msg['close_reason'] == "stop_loss":
|
elif msg['sell_reason'] == "stop_loss":
|
||||||
return"\N{WARNING SIGN}"
|
return"\N{WARNING SIGN}"
|
||||||
else:
|
else:
|
||||||
return "\N{CROSS MARK}"
|
return "\N{CROSS MARK}"
|
||||||
@ -372,8 +372,8 @@ class Telegram(RPCHandler):
|
|||||||
lines.append("*Stoploss distance:* `{stoploss_current_dist:.8f}` "
|
lines.append("*Stoploss distance:* `{stoploss_current_dist:.8f}` "
|
||||||
"`({stoploss_current_dist_pct:.2f}%)`")
|
"`({stoploss_current_dist_pct:.2f}%)`")
|
||||||
if r['open_order']:
|
if r['open_order']:
|
||||||
if r['close_order_status']:
|
if r['sell_order_status']:
|
||||||
lines.append("*Open Order:* `{open_order}` - `{close_order_status}`")
|
lines.append("*Open Order:* `{open_order}` - `{sell_order_status}`")
|
||||||
else:
|
else:
|
||||||
lines.append("*Open Order:* `{open_order}`")
|
lines.append("*Open Order:* `{open_order}`")
|
||||||
|
|
||||||
@ -554,16 +554,16 @@ class Telegram(RPCHandler):
|
|||||||
'force_sell': 'Forcesell',
|
'force_sell': 'Forcesell',
|
||||||
'emergency_sell': 'Emergency Sell',
|
'emergency_sell': 'Emergency Sell',
|
||||||
}
|
}
|
||||||
close_reasons_tabulate = [
|
sell_reasons_tabulate = [
|
||||||
[
|
[
|
||||||
reason_map.get(reason, reason),
|
reason_map.get(reason, reason),
|
||||||
sum(count.values()),
|
sum(count.values()),
|
||||||
count['wins'],
|
count['wins'],
|
||||||
count['losses']
|
count['losses']
|
||||||
] for reason, count in stats['close_reasons'].items()
|
] for reason, count in stats['sell_reasons'].items()
|
||||||
]
|
]
|
||||||
close_reasons_msg = tabulate(
|
sell_reasons_msg = tabulate(
|
||||||
close_reasons_tabulate,
|
sell_reasons_tabulate,
|
||||||
headers=['Sell Reason', 'Sells', 'Wins', 'Losses']
|
headers=['Sell Reason', 'Sells', 'Wins', 'Losses']
|
||||||
)
|
)
|
||||||
durations = stats['durations']
|
durations = stats['durations']
|
||||||
@ -575,7 +575,7 @@ class Telegram(RPCHandler):
|
|||||||
],
|
],
|
||||||
headers=['', 'Avg. Duration']
|
headers=['', 'Avg. Duration']
|
||||||
)
|
)
|
||||||
msg = (f"""```\n{close_reasons_msg}```\n```\n{duration_msg}```""")
|
msg = (f"""```\n{sell_reasons_msg}```\n```\n{duration_msg}```""")
|
||||||
|
|
||||||
self._send_msg(msg, ParseMode.MARKDOWN)
|
self._send_msg(msg, ParseMode.MARKDOWN)
|
||||||
|
|
||||||
|
@ -32,11 +32,11 @@ class SellCheckTuple(object):
|
|||||||
NamedTuple for Sell type + reason
|
NamedTuple for Sell type + reason
|
||||||
"""
|
"""
|
||||||
sell_type: SellType
|
sell_type: SellType
|
||||||
close_reason: str = ''
|
sell_reason: str = ''
|
||||||
|
|
||||||
def __init__(self, sell_type: SellType, close_reason: str = ''):
|
def __init__(self, sell_type: SellType, sell_reason: str = ''):
|
||||||
self.sell_type = sell_type
|
self.sell_type = sell_type
|
||||||
self.close_reason = close_reason or sell_type.value
|
self.sell_reason = sell_reason or sell_type.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sell_flag(self):
|
def sell_flag(self):
|
||||||
@ -225,7 +225,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
|
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
|
||||||
rate: float, time_in_force: str, close_reason: str,
|
rate: float, time_in_force: str, sell_reason: str,
|
||||||
current_time: datetime, **kwargs) -> bool:
|
current_time: datetime, **kwargs) -> bool:
|
||||||
"""
|
"""
|
||||||
Called right before placing a regular sell order.
|
Called right before placing a regular sell order.
|
||||||
@ -242,7 +242,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
:param amount: Amount in quote currency.
|
:param amount: Amount in quote currency.
|
||||||
:param rate: Rate that's going to be used when using limit orders
|
:param rate: Rate that's going to be used when using limit orders
|
||||||
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
|
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
|
||||||
:param close_reason: Sell reason.
|
:param sell_reason: Sell reason.
|
||||||
Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss',
|
Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss',
|
||||||
'sell_signal', 'force_sell', 'emergency_sell']
|
'sell_signal', 'force_sell', 'emergency_sell']
|
||||||
:param current_time: datetime object, containing the current datetime
|
:param current_time: datetime object, containing the current datetime
|
||||||
@ -592,7 +592,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
logger.debug(f"{trade.pair} - Sell signal received. "
|
logger.debug(f"{trade.pair} - Sell signal received. "
|
||||||
f"sell_type=SellType.{sell_signal.name}" +
|
f"sell_type=SellType.{sell_signal.name}" +
|
||||||
(f", custom_reason={custom_reason}" if custom_reason else ""))
|
(f", custom_reason={custom_reason}" if custom_reason else ""))
|
||||||
return SellCheckTuple(sell_type=sell_signal, close_reason=custom_reason)
|
return SellCheckTuple(sell_type=sell_signal, sell_reason=custom_reason)
|
||||||
|
|
||||||
if stoplossflag.sell_flag:
|
if stoplossflag.sell_flag:
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@
|
|||||||
"trades = load_backtest_data(backtest_dir)\n",
|
"trades = load_backtest_data(backtest_dir)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# Show value-counts per pair\n",
|
"# Show value-counts per pair\n",
|
||||||
"trades.groupby(\"pair\")[\"close_reason\"].value_counts()"
|
"trades.groupby(\"pair\")[\"sell_reason\"].value_counts()"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -209,7 +209,7 @@
|
|||||||
"trades = load_trades_from_db(\"sqlite:///tradesv3.sqlite\")\n",
|
"trades = load_trades_from_db(\"sqlite:///tradesv3.sqlite\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# Display results\n",
|
"# Display results\n",
|
||||||
"trades.groupby(\"pair\")[\"close_reason\"].value_counts()"
|
"trades.groupby(\"pair\")[\"sell_reason\"].value_counts()"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -62,7 +62,7 @@ def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: f
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def confirm_trade_exit(self, pair: str, trade: 'Trade', order_type: str, amount: float,
|
def confirm_trade_exit(self, pair: str, trade: 'Trade', order_type: str, amount: float,
|
||||||
rate: float, time_in_force: str, close_reason: str,
|
rate: float, time_in_force: str, sell_reason: str,
|
||||||
current_time: 'datetime', **kwargs) -> bool:
|
current_time: 'datetime', **kwargs) -> bool:
|
||||||
"""
|
"""
|
||||||
Called right before placing a regular sell order.
|
Called right before placing a regular sell order.
|
||||||
@ -79,7 +79,7 @@ def confirm_trade_exit(self, pair: str, trade: 'Trade', order_type: str, amount:
|
|||||||
:param amount: Amount in quote currency.
|
:param amount: Amount in quote currency.
|
||||||
:param rate: Rate that's going to be used when using limit orders
|
:param rate: Rate that's going to be used when using limit orders
|
||||||
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
|
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
|
||||||
:param close_reason: Sell reason.
|
:param sell_reason: Sell reason.
|
||||||
Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss',
|
Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss',
|
||||||
'sell_signal', 'force_sell', 'emergency_sell']
|
'sell_signal', 'force_sell', 'emergency_sell']
|
||||||
:param current_time: datetime object, containing the current datetime
|
:param current_time: datetime object, containing the current datetime
|
||||||
|
Loading…
Reference in New Issue
Block a user