Merge branch 'develop' into improve_cancel_order_handling

This commit is contained in:
Matthias
2020-08-11 15:25:47 +02:00
18 changed files with 388 additions and 66 deletions

View File

@@ -281,8 +281,8 @@ class Edge:
#
# Removing Pumps
if self.edge_config.get('remove_pumps', False):
results = results.groupby(['pair', 'stoploss']).apply(
lambda x: x[x['profit_abs'] < 2 * x['profit_abs'].std() + x['profit_abs'].mean()])
results = results[results['profit_abs'] < 2 * results['profit_abs'].std()
+ results['profit_abs'].mean()]
##########################################################################
# Removing trades having a duration more than X minutes (set in config)

View File

@@ -258,8 +258,8 @@ class Exchange:
api.urls['api'] = api.urls['test']
logger.info("Enabled Sandbox API on %s", name)
else:
logger.warning(name, "No Sandbox URL in CCXT, exiting. "
"Please check your config.json")
logger.warning(
f"No Sandbox URL in CCXT for {name}, exiting. Please check your config.json")
raise OperationalException(f'Exchange {name} does not provide a sandbox api')
def _load_async_markets(self, reload: bool = False) -> None:

View File

@@ -56,7 +56,7 @@ def require_login(func: Callable[[Any, Any], Any]):
# Type should really be Callable[[ApiServer], Any], but that will create a circular dependency
def rpc_catch_errors(func: Callable[[Any], Any]):
def rpc_catch_errors(func: Callable[..., Any]):
def func_wrapper(obj, *args, **kwargs):
@@ -200,6 +200,8 @@ class ApiServer(RPC):
view_func=self._ping, methods=['GET'])
self.app.add_url_rule(f'{BASE_URI}/trades', 'trades',
view_func=self._trades, methods=['GET'])
self.app.add_url_rule(f'{BASE_URI}/trades/<int:tradeid>', 'trades_delete',
view_func=self._trades_delete, methods=['DELETE'])
# Combined actions and infos
self.app.add_url_rule(f'{BASE_URI}/blacklist', 'blacklist', view_func=self._blacklist,
methods=['GET', 'POST'])
@@ -424,6 +426,19 @@ class ApiServer(RPC):
results = self._rpc_trade_history(limit)
return self.rest_dump(results)
@require_login
@rpc_catch_errors
def _trades_delete(self, tradeid):
"""
Handler for DELETE /trades/<tradeid> endpoint.
Removes the trade from the database (tries to cancel open orders first!)
get:
param:
tradeid: Numeric trade-id assigned to the trade.
"""
result = self._rpc_delete(tradeid)
return self.rest_dump(result)
@require_login
@rpc_catch_errors
def _whitelist(self):

View File

@@ -6,14 +6,14 @@ from abc import abstractmethod
from datetime import date, datetime, timedelta
from enum import Enum
from math import isnan
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Optional, Tuple, Union
import arrow
from numpy import NAN, mean
from freqtrade.exceptions import ExchangeError, PricingError
from freqtrade.exchange import timeframe_to_msecs, timeframe_to_minutes
from freqtrade.exceptions import (ExchangeError, InvalidOrderException,
PricingError)
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_msecs
from freqtrade.misc import shorten_date
from freqtrade.persistence import Trade
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
@@ -538,6 +538,46 @@ class RPC:
else:
return None
def _rpc_delete(self, trade_id: str) -> Dict[str, Union[str, int]]:
"""
Handler for delete <id>.
Delete the given trade and close eventually existing open orders.
"""
with self._freqtrade._sell_lock:
c_count = 0
trade = Trade.get_trades(trade_filter=[Trade.id == trade_id]).first()
if not trade:
logger.warning('delete trade: Invalid argument received')
raise RPCException('invalid argument')
# Try cancelling regular order if that exists
if trade.open_order_id:
try:
self._freqtrade.exchange.cancel_order(trade.open_order_id, trade.pair)
c_count += 1
except (ExchangeError, InvalidOrderException):
pass
# cancel stoploss on exchange ...
if (self._freqtrade.strategy.order_types.get('stoploss_on_exchange')
and trade.stoploss_order_id):
try:
self._freqtrade.exchange.cancel_stoploss_order(trade.stoploss_order_id,
trade.pair)
c_count += 1
except (ExchangeError, InvalidOrderException):
pass
Trade.session.delete(trade)
Trade.session.flush()
self._freqtrade.wallets.update()
return {
'result': 'success',
'trade_id': trade_id,
'result_msg': f'Deleted trade {trade_id}. Closed {c_count} open orders.',
'cancel_order_count': c_count,
}
def _rpc_performance(self) -> List[Dict[str, Any]]:
"""
Handler for performance.

View File

@@ -94,6 +94,7 @@ class Telegram(RPC):
CommandHandler('forcesell', self._forcesell),
CommandHandler('forcebuy', self._forcebuy),
CommandHandler('trades', self._trades),
CommandHandler('delete', self._delete_trade),
CommandHandler('performance', self._performance),
CommandHandler('daily', self._daily),
CommandHandler('count', self._count),
@@ -533,6 +534,27 @@ class Telegram(RPC):
except RPCException as e:
self._send_msg(str(e))
@authorized_only
def _delete_trade(self, update: Update, context: CallbackContext) -> None:
"""
Handler for /delete <id>.
Delete the given trade
:param bot: telegram bot
:param update: message update
:return: None
"""
trade_id = context.args[0] if len(context.args) > 0 else None
try:
msg = self._rpc_delete(trade_id)
self._send_msg((
'`{result_msg}`\n'
'Please make sure to take care of this asset on the exchange manually.'
).format(**msg))
except RPCException as e:
self._send_msg(str(e))
@authorized_only
def _performance(self, update: Update, context: CallbackContext) -> None:
"""
@@ -651,6 +673,7 @@ class Telegram(RPC):
"*/forcesell <trade_id>|all:* `Instantly sells the given trade or all trades, "
"regardless of profit`\n"
f"{forcebuy_text if self._config.get('forcebuy_enable', False) else ''}"
"*/delete <trade_id>:* `Instantly delete the given trade in the database`\n"
"*/performance:* `Show performance of each finished trade grouped by pair`\n"
"*/daily <n>:* `Shows profit or loss per day, over the last n days`\n"
"*/count:* `Show number of trades running compared to allowed number of trades`"

View File

@@ -34,7 +34,7 @@ def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: f
"""
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, sell_reason: str, **kwargs) -> bool:
"""
Called right before placing a regular sell order.