diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 321c0ebd7..2a8207ecf 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -120,7 +120,7 @@ class Order(_DECL_BASE): ft_pair: str = Column(String(25), nullable=False) ft_is_open = Column(Boolean, nullable=False, default=True, index=True) - order_id = Column(String(255), nullable=False, index=True) + order_id: str = Column(String(255), nullable=False, index=True) status = Column(String(255), nullable=True) symbol = Column(String(25), nullable=True) order_type: str = Column(String(50), nullable=True) @@ -193,6 +193,9 @@ class Order(_DECL_BASE): def to_json(self) -> Dict[str, Any]: return { + 'pair': self.ft_pair, + 'order_id': self.order_id, + 'status': self.status, 'amount': self.amount, 'average': round(self.average, 8) if self.average else 0, 'safe_price': self.safe_price, @@ -209,10 +212,8 @@ class Order(_DECL_BASE): 'order_filled_timestamp': int(self.order_filled_date.replace( tzinfo=timezone.utc).timestamp() * 1000) if self.order_filled_date else None, 'order_type': self.order_type, - 'pair': self.ft_pair, 'price': self.price, 'remaining': self.remaining, - 'status': self.status, } def close_bt_order(self, close_date: datetime): @@ -339,14 +340,7 @@ class LocalTrade(): def to_json(self) -> Dict[str, Any]: filled_orders = self.select_filled_orders() - filled_entries = [] - filled_exits = [] - if len(filled_orders) > 0: - for order in filled_orders: - if order.ft_order_side == 'buy': - filled_entries.append(order.to_json()) - if order.ft_order_side == 'sell': - filled_exits.append(order.to_json()) + orders = [order.to_json() for order in filled_orders] return { 'trade_id': self.id, @@ -411,8 +405,7 @@ class LocalTrade(): 'max_rate': self.max_rate, 'open_order_id': self.open_order_id, - 'filled_entry_orders': filled_entries, - 'filled_exit_orders': filled_exits, + 'orders': orders, } @staticmethod diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index e22cf82b3..32c7e9214 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -177,6 +177,22 @@ class ShowConfig(BaseModel): max_entry_position_adjustment: int +class OrderSchema(BaseModel): + pair: str + order_id: str + status: str + remaining: float + amount: float + safe_price: float + cost: float + filled: float + ft_order_side: str + order_type: str + is_open: bool + order_timestamp: Optional[int] + order_filled_timestamp: Optional[int] + + class TradeSchema(BaseModel): trade_id: int pair: str @@ -224,6 +240,7 @@ class TradeSchema(BaseModel): min_rate: Optional[float] max_rate: Optional[float] open_order_id: Optional[str] + orders: List[OrderSchema] class OpenTradeSchema(TradeSchema): diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index f072e2b14..6379150ee 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -32,7 +32,8 @@ logger = logging.getLogger(__name__) # 1.11: forcebuy and forcesell accept ordertype # 1.12: add blacklist delete endpoint # 1.13: forcebuy supports stake_amount -API_VERSION = 1.13 +# 1.14: Add entry/exit orders to trade response +API_VERSION = 1.14 # Public API, requires no auth. router_public = APIRouter() diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 7bae5ab76..ef18a065c 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -406,6 +406,8 @@ class Telegram(RPCHandler): first_avg = filled_orders[0]["safe_price"] for x, order in enumerate(filled_orders): + if order['ft_order_side'] != 'buy': + continue cur_entry_datetime = arrow.get(order["order_filled_date"]) cur_entry_amount = order["filled"] or order["amount"] cur_entry_average = order["safe_price"] @@ -472,7 +474,7 @@ class Telegram(RPCHandler): messages = [] for r in results: r['open_date_hum'] = arrow.get(r['open_date']).humanize() - r['num_entries'] = len(r['filled_entry_orders']) + r['num_entries'] = len([o for o in r['orders'] if o['ft_order_side'] == 'buy']) r['sell_reason'] = r.get('sell_reason', "") lines = [ "*Trade ID:* `{trade_id}`" + @@ -516,8 +518,8 @@ class Telegram(RPCHandler): lines.append("*Open Order:* `{open_order}`") lines_detail = self._prepare_entry_details( - r['filled_entry_orders'], r['base_currency'], r['is_open']) - lines.extend((lines_detail if (len(r['filled_entry_orders']) > 1) else "")) + r['orders'], r['base_currency'], r['is_open']) + lines.extend(lines_detail if lines_detail else "") # Filter empty lines using list-comprehension messages.append("\n".join([line for line in lines if line]).format(**r)) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 1776bc7cf..951c7d5ad 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -109,14 +109,13 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'stoploss_entry_dist_ratio': -0.10448878, 'open_order': None, 'exchange': 'binance', - 'filled_entry_orders': [{ + 'orders': [{ 'amount': 91.07468123, 'average': 1.098e-05, 'safe_price': 1.098e-05, 'cost': 0.0009999999999054, 'filled': 91.07468123, 'ft_order_side': 'buy', 'order_date': ANY, 'order_timestamp': ANY, 'order_filled_date': ANY, 'order_filled_timestamp': ANY, 'order_type': 'limit', 'price': 1.098e-05, - 'is_open': False, 'pair': 'ETH/BTC', + 'is_open': False, 'pair': 'ETH/BTC', 'order_id': ANY, 'remaining': ANY, 'status': ANY}], - 'filled_exit_orders': [] } mocker.patch('freqtrade.exchange.Exchange.get_rate', @@ -184,14 +183,13 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'stoploss_entry_dist_ratio': -0.10448878, 'open_order': None, 'exchange': 'binance', - 'filled_entry_orders': [{ + 'orders': [{ 'amount': 91.07468123, 'average': 1.098e-05, 'safe_price': 1.098e-05, 'cost': 0.0009999999999054, 'filled': 91.07468123, 'ft_order_side': 'buy', 'order_date': ANY, 'order_timestamp': ANY, 'order_filled_date': ANY, 'order_filled_timestamp': ANY, 'order_type': 'limit', 'price': 1.098e-05, - 'is_open': False, 'pair': 'ETH/BTC', + 'is_open': False, 'pair': 'ETH/BTC', 'order_id': ANY, 'remaining': ANY, 'status': ANY}], - 'filled_exit_orders': [] } diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index de7dca47b..84a18440e 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -902,6 +902,8 @@ def test_api_status(botclient, mocker, ticker, fee, markets): 'buy_tag': None, 'timeframe': 5, 'exchange': 'binance', + 'orders': [ANY], + } mocker.patch('freqtrade.exchange.Exchange.get_rate', @@ -1089,6 +1091,7 @@ def test_api_forcebuy(botclient, mocker, fee): 'buy_tag': None, 'timeframe': 5, 'exchange': 'binance', + 'orders': [], } diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 0044601a8..84613dd9b 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -203,7 +203,7 @@ def test_telegram_status(default_conf, update, mocker) -> None: 'stop_loss_ratio': -0.0001, 'open_order': '(limit buy rem=0.00000000)', 'is_open': True, - 'filled_entry_orders': [] + 'orders': [] }]), ) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 34ef8e7ec..520ac725f 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -902,8 +902,7 @@ def test_to_json(default_conf, fee): 'buy_tag': None, 'timeframe': None, 'exchange': 'binance', - 'filled_entry_orders': [], - 'filled_exit_orders': [] + 'orders': [], } # Simulate dry_run entries @@ -971,8 +970,7 @@ def test_to_json(default_conf, fee): 'buy_tag': 'buys_signal_001', 'timeframe': None, 'exchange': 'binance', - 'filled_entry_orders': [], - 'filled_exit_orders': [] + 'orders': [], }