diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 73e067480..ec47b670b 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -1520,3 +1520,87 @@ class Trade(_DECL_BASE, LocalTrade): Order.status == 'closed' ).scalar() return trading_volume + + @staticmethod + def from_json(json_str: str) -> 'Trade': + """ + Create a Trade instance from a json string. + + Used for debugging purposes - please keep. + :param json_str: json string to parse + :return: Trade instance + """ + import rapidjson + data = rapidjson.loads(json_str) + trade = Trade( + id=data["trade_id"], + pair=data["pair"], + base_currency=data["base_currency"], + stake_currency=data["quote_currency"], + is_open=data["is_open"], + exchange=data["exchange"], + amount=data["amount"], + amount_requested=data["amount_requested"], + stake_amount=data["stake_amount"], + strategy=data["strategy"], + enter_tag=data["enter_tag"], + timeframe=data["timeframe"], + fee_open=data["fee_open"], + fee_open_cost=data["fee_open_cost"], + fee_open_currency=data["fee_open_currency"], + fee_close=data["fee_close"], + fee_close_cost=data["fee_close_cost"], + fee_close_currency=data["fee_close_currency"], + open_date=datetime.fromtimestamp(data["open_timestamp"] // 1000, tz=timezone.utc), + open_rate=data["open_rate"], + open_rate_requested=data["open_rate_requested"], + open_trade_value=data["open_trade_value"], + close_date=(datetime.fromtimestamp(data["close_timestamp"] // 1000, tz=timezone.utc) + if data["close_timestamp"] else None), + realized_profit=data["realized_profit"], + close_rate=data["close_rate"], + close_rate_requested=data["close_rate_requested"], + close_profit=data["close_profit"], + close_profit_abs=data["close_profit_abs"], + exit_reason=data["exit_reason"], + exit_order_status=data["exit_order_status"], + stop_loss=data["stop_loss_abs"], + stop_loss_pct=data["stop_loss_ratio"], + stoploss_order_id=data["stoploss_order_id"], + stoploss_last_update=(datetime.fromtimestamp(data["stoploss_last_update"] // 1000, + tz=timezone.utc) if data["stoploss_last_update"] else None), + initial_stop_loss=data["initial_stop_loss_abs"], + initial_stop_loss_pct=data["initial_stop_loss_ratio"], + min_rate=data["min_rate"], + max_rate=data["max_rate"], + leverage=data["leverage"], + interest_rate=data["interest_rate"], + liquidation_price=data["liquidation_price"], + is_short=data["is_short"], + trading_mode=data["trading_mode"], + funding_fees=data["funding_fees"], + open_order_id=data["open_order_id"], + ) + for order in data["orders"]: + + order_obj = Order( + amount=order["amount"], + ft_order_side=order["ft_order_side"], + ft_pair=order["pair"], + ft_is_open=order["is_open"], + order_id=order["order_id"], + status=order["status"], + average=order["average"], + cost=order["cost"], + filled=order["filled"], + order_date=order["order_date"], + order_filled_date=(datetime.fromtimestamp( + order["order_filled_timestamp"] // 1000, tz=timezone.utc) + if order["order_filled_timestamp"] else None), + order_type=order["order_type"], + price=order["price"], + remaining=order["remaining"], + ) + trade.orders.append(order_obj) + + return trade diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py index ae2672830..3323dd7c6 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/persistence/test_persistence.py @@ -2404,7 +2404,7 @@ def test_Trade_object_idem(): 'get_enter_tag_performance', 'get_mix_tag_performance', 'get_trading_volume', - + 'from_json', ) EXCLUDES2 = ('trades', 'trades_open', 'bt_trades_open_pp', 'bt_open_open_trade_count', 'total_profit') diff --git a/tests/persistence/test_trade_fromjson.py b/tests/persistence/test_trade_fromjson.py new file mode 100644 index 000000000..f097d5e56 --- /dev/null +++ b/tests/persistence/test_trade_fromjson.py @@ -0,0 +1,180 @@ +from datetime import datetime, timezone + +from freqtrade.persistence.trade_model import Trade + + +def test_trade_fromjson(): + """Test the Trade.from_json() method.""" + trade_string = """{ + "trade_id": 25, + "pair": "ETH/USDT", + "base_currency": "ETH", + "quote_currency": "USDT", + "is_open": false, + "exchange": "binance", + "amount": 407.0, + "amount_requested": 102.92547026, + "stake_amount": 102.7494348, + "strategy": "SampleStrategy55", + "buy_tag": "Strategy2", + "enter_tag": "Strategy2", + "timeframe": 5, + "fee_open": 0.001, + "fee_open_cost": 0.1027494, + "fee_open_currency": "ETH", + "fee_close": 0.001, + "fee_close_cost": 0.1054944, + "fee_close_currency": "USDT", + "open_date": "2022-10-18 09:12:42", + "open_timestamp": 1666084362912, + "open_rate": 0.2518998249562391, + "open_rate_requested": 0.2516, + "open_trade_value": 102.62575199, + "close_date": "2022-10-18 09:45:22", + "close_timestamp": 1666086322208, + "realized_profit": 2.76315361, + "close_rate": 0.2592, + "close_rate_requested": 0.2592, + "close_profit": 0.026865, + "close_profit_pct": 2.69, + "close_profit_abs": 2.76315361, + "trade_duration_s": 1959, + "trade_duration": 32, + "profit_ratio": 0.02686, + "profit_pct": 2.69, + "profit_abs": 2.76315361, + "sell_reason": "no longer good", + "exit_reason": "no longer good", + "exit_order_status": "closed", + "stop_loss_abs": 0.1981, + "stop_loss_ratio": -0.216, + "stop_loss_pct": -21.6, + "stoploss_order_id": null, + "stoploss_last_update": null, + "stoploss_last_update_timestamp": null, + "initial_stop_loss_abs": 0.1981, + "initial_stop_loss_ratio": -0.216, + "initial_stop_loss_pct": -21.6, + "min_rate": 0.2495, + "max_rate": 0.2592, + "leverage": 1.0, + "interest_rate": 0.0, + "liquidation_price": null, + "is_short": false, + "trading_mode": "spot", + "funding_fees": 0.0, + "open_order_id": null, + "orders": [ + { + "amount": 102.0, + "safe_price": 0.2526, + "ft_order_side": "buy", + "order_filled_timestamp": 1666084370887, + "ft_is_entry": true, + "pair": "ETH/USDT", + "order_id": "78404228", + "status": "closed", + "average": 0.2526, + "cost": 25.7652, + "filled": 102.0, + "is_open": false, + "order_date": "2022-10-18 09:12:42", + "order_timestamp": 1666084362684, + "order_filled_date": "2022-10-18 09:12:50", + "order_type": "limit", + "price": 0.2526, + "remaining": 0.0 + }, + { + "amount": 102.0, + "safe_price": 0.2517, + "ft_order_side": "buy", + "order_filled_timestamp": 1666084379056, + "ft_is_entry": true, + "pair": "ETH/USDT", + "order_id": "78405139", + "status": "closed", + "average": 0.2517, + "cost": 25.6734, + "filled": 102.0, + "is_open": false, + "order_date": "2022-10-18 09:12:57", + "order_timestamp": 1666084377681, + "order_filled_date": "2022-10-18 09:12:59", + "order_type": "limit", + "price": 0.2517, + "remaining": 0.0 + }, + { + "amount": 102.0, + "safe_price": 0.2517, + "ft_order_side": "buy", + "order_filled_timestamp": 1666084389644, + "ft_is_entry": true, + "pair": "ETH/USDT", + "order_id": "78405265", + "status": "closed", + "average": 0.2517, + "cost": 25.6734, + "filled": 102.0, + "is_open": false, + "order_date": "2022-10-18 09:13:03", + "order_timestamp": 1666084383295, + "order_filled_date": "2022-10-18 09:13:09", + "order_type": "limit", + "price": 0.2517, + "remaining": 0.0 + }, + { + "amount": 102.0, + "safe_price": 0.2516, + "ft_order_side": "buy", + "order_filled_timestamp": 1666084723521, + "ft_is_entry": true, + "pair": "ETH/USDT", + "order_id": "78405395", + "status": "closed", + "average": 0.2516, + "cost": 25.6632, + "filled": 102.0, + "is_open": false, + "order_date": "2022-10-18 09:13:13", + "order_timestamp": 1666084393920, + "order_filled_date": "2022-10-18 09:18:43", + "order_type": "limit", + "price": 0.2516, + "remaining": 0.0 + }, + { + "amount": 407.0, + "safe_price": 0.2592, + "ft_order_side": "sell", + "order_filled_timestamp": 1666086322198, + "ft_is_entry": false, + "pair": "ETH/USDT", + "order_id": "78432649", + "status": "closed", + "average": 0.2592, + "cost": 105.4944, + "filled": 407.0, + "is_open": false, + "order_date": "2022-10-18 09:45:21", + "order_timestamp": 1666086321435, + "order_filled_date": "2022-10-18 09:45:22", + "order_type": "market", + "price": 0.2592, + "remaining": 0.0 + } + ] + }""" + trade = Trade.from_json(trade_string) + + assert trade.id == 25 + assert trade.pair == 'ETH/USDT' + assert trade.open_date == datetime(2022, 10, 18, 9, 12, 42, tzinfo=timezone.utc) + assert isinstance(trade.open_date, datetime) + assert trade.exit_reason == 'no longer good' + + assert len(trade.orders) == 5 + last_o = trade.orders[-1] + assert last_o.order_filled_date == datetime(2022, 10, 18, 9, 45, 22, tzinfo=timezone.utc)