mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
Merge branch 'develop' into feat/short
This commit is contained in:
commit
f71b7a4e76
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user