diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 14ac65e61..340aad331 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -167,12 +167,23 @@ class Order(_DECL_BASE): def to_json(self) -> Dict[str, Any]: return { - 'cost': self.cost if self.cost else 0, 'amount': self.amount, - 'price': self.price, 'average': round(self.average, 8) if self.average else 0, + 'cost': self.cost if self.cost else 0, + 'filled': self.filled, + 'ft_order_side': self.ft_order_side, + 'order_date': self.order_date.strftime(DATETIME_PRINT_FORMAT) + if self.order_date else None, + 'order_timestamp': int(self.order_date.replace( + tzinfo=timezone.utc).timestamp() * 1000) if self.order_date else None, 'order_filled_date': self.order_filled_date.strftime(DATETIME_PRINT_FORMAT) - if self.order_filled_date else None + if self.order_filled_date else None, + '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, + 'price': self.price, + 'remaining': self.remaining, + 'status': self.status, } @staticmethod @@ -292,11 +303,15 @@ class LocalTrade(): return self.close_date.replace(tzinfo=timezone.utc) def to_json(self) -> Dict[str, Any]: - fill_buy = self.select_filled_orders('buy') - buys_json = dict() - if len(fill_buy) > 0: - for x in range(len(fill_buy)): - buys_json[str(x)] = fill_buy[x].to_json() + filled_orders = self.select_filled_orders() + filled_buys = [] + filled_sells = [] + if len(filled_orders) > 0: + for x in range(len(filled_orders)): + if filled_orders[x].ft_order_side == 'buy': + filled_buys.append(filled_orders[x].to_json()) + elif filled_orders[x].ft_order_side == 'sell': + filled_sells.append(filled_orders[x].to_json()) return { 'trade_id': self.id, @@ -361,7 +376,8 @@ class LocalTrade(): 'max_rate': self.max_rate, 'open_order_id': self.open_order_id, - 'filled_buys': buys_json, + 'filled_buys': filled_buys, + 'filled_sells': filled_sells, } @staticmethod @@ -631,14 +647,14 @@ class LocalTrade(): else: return None - def select_filled_orders(self, order_side: str) -> List['Order']: + def select_filled_orders(self, order_side: Optional[str] = None) -> List['Order']: """ Finds filled orders for this orderside. - :param order_side: Side of the order (either 'buy' or 'sell') + :param order_side: Side of the order (either 'buy', 'sell', or None) :return: array of Order objects """ - return [o for o in self.orders if o.ft_order_side == order_side and - o.ft_is_open is False and + return [o for o in self.orders if ((o.ft_order_side == order_side) or (order_side is None)) + and o.ft_is_open is False and (o.filled or 0) > 0 and o.status in NON_OPEN_EXCHANGE_STATES] diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 03eb836fa..7f8c3fb1a 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -375,37 +375,37 @@ class Telegram(RPCHandler): """ lines = [] for x in range(len(filled_trades)): - cur_buy_date = arrow.get(filled_trades[str(x)]["order_filled_date"]) - cur_buy_amount = filled_trades[str(x)]["amount"] - cur_buy_average = filled_trades[str(x)]["average"] + current_buy_datetime = arrow.get(filled_trades[x]["order_filled_date"]) + cur_buy_amount = filled_trades[x]["amount"] + cur_buy_average = filled_trades[x]["average"] lines.append(" ") if x == 0: lines.append("*Buy #{}:*".format(x+1)) lines.append("*Buy Amount:* {} ({:.8f} {})" - .format(cur_buy_amount, filled_trades[str(x)]["cost"], base_currency)) + .format(cur_buy_amount, filled_trades[x]["cost"], base_currency)) lines.append("*Average Buy Price:* {}".format(cur_buy_average)) else: sumA = 0 sumB = 0 for y in range(x): - sumA += (filled_trades[str(y)]["amount"] * filled_trades[str(y)]["average"]) - sumB += filled_trades[str(y)]["amount"] + sumA += (filled_trades[y]["amount"] * filled_trades[y]["average"]) + sumB += filled_trades[y]["amount"] prev_avg_price = sumA/sumB - price_to_1st_buy = (cur_buy_average - filled_trades["0"]["average"]) \ - / filled_trades["0"]["average"] + price_to_1st_buy = (cur_buy_average - filled_trades[0]["average"]) \ + / filled_trades[0]["average"] minus_on_buy = (cur_buy_average - prev_avg_price)/prev_avg_price - dur_buys = cur_buy_date - arrow.get(filled_trades[str(x-1)]["order_filled_date"]) + dur_buys = current_buy_datetime - arrow.get(filled_trades[x-1]["order_filled_date"]) days = dur_buys.days hours, remainder = divmod(dur_buys.seconds, 3600) minutes, seconds = divmod(remainder, 60) lines.append("*Buy #{}:* at {:.2%} avg profit".format(x+1, minus_on_buy)) - lines.append("({})".format(cur_buy_date + lines.append("({})".format(current_buy_datetime .humanize(granularity=["day", "hour", "minute"]))) lines.append("*Buy Amount:* {} ({:.8f} {})" - .format(cur_buy_amount, filled_trades[str(x)]["cost"], base_currency)) + .format(cur_buy_amount, filled_trades[x]["cost"], base_currency)) lines.append("*Average Buy Price:* {} ({:.2%} from 1st buy rate)" .format(cur_buy_average, price_to_1st_buy)) - lines.append("*Filled at:* {}".format(filled_trades[str(x)]["order_filled_date"])) + lines.append("*Order filled at:* {}".format(filled_trades[x]["order_filled_date"])) lines.append("({}d {}h {}m {}s from previous buy)" .format(days, hours, minutes, seconds)) return lines @@ -437,7 +437,6 @@ class Telegram(RPCHandler): messages = [] for r in results: r['open_date_hum'] = arrow.get(r['open_date']).humanize() - r['filled_buys'] = r.get('filled_buys', []) r['num_buys'] = len(r['filled_buys']) r['sell_reason'] = r.get('sell_reason', "") r['position_adjustment_enable'] = r.get('position_adjustment_enable', False) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index a91340e39..2a1964732 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -109,8 +109,12 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'open_order': None, 'exchange': 'binance', 'position_adjustment_enable': False, - 'filled_buys': {'0': {'amount': 91.07468123, 'average': 1.098e-05, - 'cost': 0.0009999999999054, 'order_filled_date': ANY, 'price': 1.098e-05}}, + 'filled_buys': [{'amount': 91.07468123, 'average': 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, + 'remaining': ANY, 'status': ANY}], + 'filled_sells': [] } mocker.patch('freqtrade.exchange.Exchange.get_rate', @@ -179,8 +183,12 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'open_order': None, 'exchange': 'binance', 'position_adjustment_enable': False, - 'filled_buys': {'0': {'amount': 91.07468123, 'average': 1.098e-05, - 'cost': 0.0009999999999054, 'order_filled_date': ANY, 'price': 1.098e-05}}, + 'filled_buys': [{'amount': 91.07468123, 'average': 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, + 'remaining': ANY, 'status': ANY}], + 'filled_sells': [] } diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 1d638eed1..600568580 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -201,7 +201,8 @@ def test_telegram_status(default_conf, update, mocker) -> None: 'stoploss_current_dist_ratio': -0.0002, 'stop_loss_ratio': -0.0001, 'open_order': '(limit buy rem=0.00000000)', - 'is_open': True + 'is_open': True, + 'filled_buys': [] }]), ) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index d6b12ea8c..1691af820 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -903,7 +903,8 @@ def test_to_json(default_conf, fee): 'buy_tag': None, 'timeframe': None, 'exchange': 'binance', - 'filled_buys': {} + 'filled_buys': [], + 'filled_sells': [] } # Simulate dry_run entries @@ -971,7 +972,8 @@ def test_to_json(default_conf, fee): 'buy_tag': 'buys_signal_001', 'timeframe': None, 'exchange': 'binance', - 'filled_buys': {} + 'filled_buys': [], + 'filled_sells': [] }