Merge branch 'develop' into feat/short

This commit is contained in:
Matthias 2021-12-02 06:53:15 +01:00
commit f71b7a4e76
6 changed files with 45 additions and 50 deletions

View File

@ -104,7 +104,8 @@ Possible parameters are:
* `trade_id` * `trade_id`
* `exchange` * `exchange`
* `pair` * `pair`
* `limit` * ~~`limit` # Deprecated - should no longer be used.~~
* `open_rate`
* `amount` * `amount`
* `open_date` * `open_date`
* `stake_amount` * `stake_amount`
@ -146,6 +147,8 @@ Possible parameters are:
* `stake_amount` * `stake_amount`
* `stake_currency` * `stake_currency`
* `fiat_currency` * `fiat_currency`
* `order_type`
* `current_rate`
* `enter_tag` * `enter_tag`
### Webhooksell ### Webhooksell

View File

@ -61,10 +61,10 @@ class HDF5DataHandler(IDataHandler):
filename = self._pair_data_filename(self._datadir, pair, timeframe) filename = self._pair_data_filename(self._datadir, pair, timeframe)
ds = pd.HDFStore(filename, mode='a', complevel=9, complib='blosc') _data.loc[:, self._columns].to_hdf(
ds.put(key, _data.loc[:, self._columns], format='table', data_columns=['date']) filename, key, mode='a', complevel=9, complib='blosc',
format='table', data_columns=['date']
ds.close() )
def _ohlcv_load(self, pair: str, timeframe: str, def _ohlcv_load(self, pair: str, timeframe: str,
timerange: Optional[TimeRange] = None) -> pd.DataFrame: timerange: Optional[TimeRange] = None) -> pd.DataFrame:
@ -142,11 +142,11 @@ class HDF5DataHandler(IDataHandler):
""" """
key = self._pair_trades_key(pair) key = self._pair_trades_key(pair)
ds = pd.HDFStore(self._pair_trades_filename(self._datadir, pair), pd.DataFrame(data, columns=DEFAULT_TRADES_COLUMNS).to_hdf(
mode='a', complevel=9, complib='blosc') self._pair_trades_filename(self._datadir, pair), key,
ds.put(key, pd.DataFrame(data, columns=DEFAULT_TRADES_COLUMNS), mode='a', complevel=9, complib='blosc',
format='table', data_columns=['timestamp']) format='table', data_columns=['timestamp']
ds.close() )
def trades_append(self, pair: str, data: TradeList): def trades_append(self, pair: str, data: TradeList):
""" """

View File

@ -322,7 +322,8 @@ class FreqtradeBot(LoggingMixin):
f"for order {order.order_id}." f"for order {order.order_id}."
) )
self.update_trade_state(trade, order.order_id, self.update_trade_state(trade, order.order_id,
stoploss_order=order.ft_order_side == 'stoploss') stoploss_order=order.ft_order_side == 'stoploss',
send_msg=False)
trades: List[Trade] = Trade.get_open_trades_without_assigned_fees() trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
for trade in trades: for trade in trades:
@ -333,7 +334,7 @@ class FreqtradeBot(LoggingMixin):
f"Updating {trade.enter_side}-fee on trade {trade}" f"Updating {trade.enter_side}-fee on trade {trade}"
f"for order {order.order_id}." f"for order {order.order_id}."
) )
self.update_trade_state(trade, order.order_id) self.update_trade_state(trade, order.order_id, send_msg=False)
def handle_insufficient_funds(self, trade: Trade): def handle_insufficient_funds(self, trade: Trade):
""" """
@ -356,7 +357,7 @@ class FreqtradeBot(LoggingMixin):
if order: if order:
logger.info( logger.info(
f"Updating {trade.enter_side}-fee on trade {trade} for order {order.order_id}.") f"Updating {trade.enter_side}-fee on trade {trade} for order {order.order_id}.")
self.update_trade_state(trade, order.order_id) self.update_trade_state(trade, order.order_id, send_msg=False)
def refind_lost_order(self, trade): def refind_lost_order(self, trade):
""" """
@ -743,10 +744,6 @@ class FreqtradeBot(LoggingMixin):
) )
trade.orders.append(order_obj) trade.orders.append(order_obj)
# Update fees if order is closed
if order_status == 'closed':
self.update_trade_state(trade, order_id, order)
Trade.query.session.add(trade) Trade.query.session.add(trade)
Trade.commit() Trade.commit()
@ -755,20 +752,31 @@ class FreqtradeBot(LoggingMixin):
self._notify_enter(trade, order_type) self._notify_enter(trade, order_type)
# Update fees if order is closed
if order_status == 'closed':
self.update_trade_state(trade, order_id, order)
return True return True
def _notify_enter(self, trade: Trade, order_type: str) -> None: def _notify_enter(self, trade: Trade, order_type: Optional[str] = None,
fill: bool = False) -> None:
""" """
Sends rpc notification when a entry order occurred. Sends rpc notification when a entry order occurred.
""" """
if fill:
msg_type = RPCMessageType.SHORT_FILL if trade.is_short else RPCMessageType.BUY_FILL
else:
msg_type = RPCMessageType.SHORT if trade.is_short else RPCMessageType.BUY
msg = { msg = {
'trade_id': trade.id, 'trade_id': trade.id,
'type': RPCMessageType.SHORT if trade.is_short else RPCMessageType.BUY, 'type': msg_type,
'buy_tag': trade.enter_tag, 'buy_tag': trade.enter_tag,
'enter_tag': trade.enter_tag, 'enter_tag': trade.enter_tag,
'exchange': self.exchange.name.capitalize(), 'exchange': self.exchange.name.capitalize(),
'pair': trade.pair, 'pair': trade.pair,
'limit': trade.open_rate, 'limit': trade.open_rate, # Deprecated (?)
'open_rate': trade.open_rate,
'order_type': order_type, 'order_type': order_type,
'stake_amount': trade.stake_amount, 'stake_amount': trade.stake_amount,
'stake_currency': self.config['stake_currency'], 'stake_currency': self.config['stake_currency'],
@ -808,24 +816,6 @@ class FreqtradeBot(LoggingMixin):
# Send the message # Send the message
self.rpc.send_msg(msg) self.rpc.send_msg(msg)
def _notify_enter_fill(self, trade: Trade) -> None:
msg_type = RPCMessageType.SHORT_FILL if trade.is_short else RPCMessageType.BUY_FILL
msg = {
'trade_id': trade.id,
'type': msg_type,
'buy_tag': trade.enter_tag,
'enter_tag': trade.enter_tag,
'exchange': self.exchange.name.capitalize(),
'pair': trade.pair,
'open_rate': trade.open_rate,
'stake_amount': trade.stake_amount,
'stake_currency': self.config['stake_currency'],
'fiat_currency': self.config.get('fiat_display_currency', None),
'amount': trade.amount,
'open_date': trade.open_date,
}
self.rpc.send_msg(msg)
# #
# SELL / exit positions / close trades logic and methods # SELL / exit positions / close trades logic and methods
# #
@ -1355,16 +1345,16 @@ class FreqtradeBot(LoggingMixin):
trade.sell_order_status = '' trade.sell_order_status = ''
trade.close_rate_requested = limit trade.close_rate_requested = limit
trade.sell_reason = exit_tag or sell_reason.sell_reason trade.sell_reason = exit_tag or sell_reason.sell_reason
# In case of market sell orders the order can be closed immediately
if order.get('status', 'unknown') in ('closed', 'expired'):
self.update_trade_state(trade, trade.open_order_id, order)
Trade.commit()
# Lock pair for one candle to prevent immediate re-trading # Lock pair for one candle to prevent immediate re-trading
self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc),
reason='Auto lock') reason='Auto lock')
self._notify_exit(trade, order_type) self._notify_exit(trade, order_type)
# In case of market sell orders the order can be closed immediately
if order.get('status', 'unknown') in ('closed', 'expired'):
self.update_trade_state(trade, trade.open_order_id, order)
Trade.commit()
return True return True
@ -1463,13 +1453,14 @@ class FreqtradeBot(LoggingMixin):
# #
def update_trade_state(self, trade: Trade, order_id: str, action_order: Dict[str, Any] = None, def update_trade_state(self, trade: Trade, order_id: str, action_order: Dict[str, Any] = None,
stoploss_order: bool = False) -> bool: stoploss_order: bool = False, send_msg: bool = True) -> bool:
""" """
Checks trades with open orders and updates the amount if necessary Checks trades with open orders and updates the amount if necessary
Handles closing both buy and sell orders. Handles closing both buy and sell orders.
:param trade: Trade object of the trade we're analyzing :param trade: Trade object of the trade we're analyzing
:param order_id: Order-id of the order we're analyzing :param order_id: Order-id of the order we're analyzing
:param action_order: Already acquired order object :param action_order: Already acquired order object
:param send_msg: Send notification - should always be True except in "recovery" methods
:return: True if order has been cancelled without being filled partially, False otherwise :return: True if order has been cancelled without being filled partially, False otherwise
""" """
if not order_id: if not order_id:
@ -1509,13 +1500,13 @@ class FreqtradeBot(LoggingMixin):
# Updating wallets when order is closed # Updating wallets when order is closed
if not trade.is_open: if not trade.is_open:
if not stoploss_order and not trade.open_order_id: if send_msg and not stoploss_order and not trade.open_order_id:
self._notify_exit(trade, '', True) self._notify_exit(trade, '', True)
self.handle_protections(trade.pair) self.handle_protections(trade.pair)
self.wallets.update() self.wallets.update()
elif not trade.open_order_id: elif send_msg and not trade.open_order_id:
# Buy fill # Buy fill
self._notify_enter_fill(trade) self._notify_enter(trade, fill=True)
return False return False

View File

@ -87,4 +87,5 @@ markdown_extensions:
alternate_style: true alternate_style: true
- pymdownx.tasklist: - pymdownx.tasklist:
custom_checkbox: true custom_checkbox: true
- pymdownx.tilde
- mdx_truly_sane_lists - mdx_truly_sane_lists

View File

@ -939,7 +939,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
telegram._forcesell(update=update, context=context) telegram._forcesell(update=update, context=context)
assert msg_mock.call_count == 4 assert msg_mock.call_count == 4
last_msg = msg_mock.call_args_list[-1][0][0] last_msg = msg_mock.call_args_list[-2][0][0]
assert { assert {
'type': RPCMessageType.SELL, 'type': RPCMessageType.SELL,
'trade_id': 1, 'trade_id': 1,
@ -1004,7 +1004,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
assert msg_mock.call_count == 4 assert msg_mock.call_count == 4
last_msg = msg_mock.call_args_list[-1][0][0] last_msg = msg_mock.call_args_list[-2][0][0]
assert { assert {
'type': RPCMessageType.SELL, 'type': RPCMessageType.SELL,
'trade_id': 1, 'trade_id': 1,
@ -1059,7 +1059,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
# Called for each trade 2 times # Called for each trade 2 times
assert msg_mock.call_count == 8 assert msg_mock.call_count == 8
msg = msg_mock.call_args_list[1][0][0] msg = msg_mock.call_args_list[0][0][0]
assert { assert {
'type': RPCMessageType.SELL, 'type': RPCMessageType.SELL,
'trade_id': 1, 'trade_id': 1,

View File

@ -3312,7 +3312,7 @@ def test_execute_trade_exit_market_order(
assert trade.close_profit == profit_ratio assert trade.close_profit == profit_ratio
assert rpc_mock.call_count == 3 assert rpc_mock.call_count == 3
last_msg = rpc_mock.call_args_list[-1][0][0] last_msg = rpc_mock.call_args_list[-2][0][0]
assert { assert {
'type': RPCMessageType.SELL, 'type': RPCMessageType.SELL,
'trade_id': 1, 'trade_id': 1,