mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-11-29 08:33:07 +00:00
343 lines
11 KiB
Python
343 lines
11 KiB
Python
import json
|
|
from datetime import datetime, timezone
|
|
|
|
import pytest
|
|
|
|
from freqtrade.persistence.trade_model import LocalTrade, Trade
|
|
from tests.conftest import create_mock_trades_usdt
|
|
|
|
|
|
@pytest.mark.usefixtures("init_persistence")
|
|
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_last_update": "2022-10-18 09:13:42",
|
|
"stoploss_last_update_timestamp": 1666077222000,
|
|
"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,
|
|
"amount_precision": 1.0,
|
|
"price_precision": 3.0,
|
|
"precision_mode": 2,
|
|
"contract_size": 1.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,
|
|
"funding_fee": -0.055
|
|
}
|
|
]
|
|
}"""
|
|
trade = Trade.from_json(trade_string)
|
|
Trade.session.add(trade)
|
|
Trade.commit()
|
|
|
|
assert trade.id == 25
|
|
assert trade.pair == "ETH/USDT"
|
|
assert trade.open_date_utc == datetime(2022, 10, 18, 9, 12, 42, tzinfo=timezone.utc)
|
|
assert isinstance(trade.open_date, datetime)
|
|
assert trade.exit_reason == "no longer good"
|
|
assert trade.realized_profit == 2.76315361
|
|
assert trade.precision_mode == 2
|
|
assert trade.amount_precision == 1.0
|
|
assert trade.contract_size == 1.0
|
|
|
|
assert len(trade.orders) == 5
|
|
last_o = trade.orders[-1]
|
|
assert last_o.order_filled_utc == datetime(2022, 10, 18, 9, 45, 22, tzinfo=timezone.utc)
|
|
assert isinstance(last_o.order_date, datetime)
|
|
assert last_o.funding_fee == -0.055
|
|
|
|
|
|
@pytest.mark.usefixtures("init_persistence")
|
|
def test_trade_serialize_load_back(fee):
|
|
create_mock_trades_usdt(fee, None)
|
|
|
|
t = Trade.get_trades([Trade.id == 1]).first()
|
|
assert t.id == 1
|
|
t.funding_fees = 0.025
|
|
t.orders[0].funding_fee = 0.0125
|
|
assert len(t.orders) == 2
|
|
Trade.commit()
|
|
|
|
tjson = t.to_json(False)
|
|
assert isinstance(tjson, dict)
|
|
trade_string = json.dumps(tjson)
|
|
trade = Trade.from_json(trade_string)
|
|
|
|
assert trade.id == t.id
|
|
assert trade.funding_fees == t.funding_fees
|
|
assert len(trade.orders) == len(t.orders)
|
|
assert trade.orders[0].funding_fee == t.orders[0].funding_fee
|
|
excluded = [
|
|
"trade_id",
|
|
"quote_currency",
|
|
"open_timestamp",
|
|
"close_timestamp",
|
|
"realized_profit_ratio",
|
|
"close_profit_pct",
|
|
"trade_duration_s",
|
|
"trade_duration",
|
|
"profit_ratio",
|
|
"profit_pct",
|
|
"profit_abs",
|
|
"stop_loss_abs",
|
|
"initial_stop_loss_abs",
|
|
"open_fill_date",
|
|
"open_fill_timestamp",
|
|
"orders",
|
|
]
|
|
failed = []
|
|
# Ensure all attributes written can be read.
|
|
for obj, value in tjson.items():
|
|
if obj in excluded:
|
|
continue
|
|
tattr = getattr(trade, obj, None)
|
|
if isinstance(tattr, datetime):
|
|
tattr = tattr.strftime("%Y-%m-%d %H:%M:%S")
|
|
if tattr != value:
|
|
failed.append((obj, tattr, value))
|
|
|
|
assert tjson.get("trade_id") == trade.id
|
|
assert tjson.get("quote_currency") == trade.stake_currency
|
|
assert tjson.get("stop_loss_abs") == trade.stop_loss
|
|
assert tjson.get("initial_stop_loss_abs") == trade.initial_stop_loss
|
|
|
|
excluded_o = [
|
|
"order_filled_timestamp",
|
|
"ft_is_entry",
|
|
"pair",
|
|
"is_open",
|
|
"order_timestamp",
|
|
]
|
|
order_obj = trade.orders[0]
|
|
for obj, value in tjson["orders"][0].items():
|
|
if obj in excluded_o:
|
|
continue
|
|
tattr = getattr(order_obj, obj, None)
|
|
if isinstance(tattr, datetime):
|
|
tattr = tattr.strftime("%Y-%m-%d %H:%M:%S")
|
|
if tattr != value:
|
|
failed.append((obj, tattr, value))
|
|
|
|
assert tjson["orders"][0]["pair"] == order_obj.ft_pair
|
|
assert not failed
|
|
|
|
trade2 = LocalTrade.from_json(trade_string)
|
|
assert len(trade2.orders) == len(t.orders)
|
|
|
|
trade3 = LocalTrade.from_json(trade_string)
|
|
assert len(trade3.orders) == len(t.orders)
|
|
|
|
|
|
@pytest.mark.usefixtures("init_persistence")
|
|
def test_trade_fromjson_backtesting():
|
|
"""
|
|
trade.from_json should be able to load a trade output via backtesting.
|
|
"""
|
|
trade_string = """
|
|
{
|
|
"pair":"XRP/USDT:USDT",
|
|
"stake_amount":3015.294001,
|
|
"max_stake_amount":60305.88002,
|
|
"amount":94612.3,
|
|
"open_date":"2024-03-02 10:40:00+00:00",
|
|
"close_date":"2024-03-02 11:10:00+00:00",
|
|
"open_rate":0.6374,
|
|
"close_rate":0.6399,
|
|
"fee_open":0.0005,
|
|
"fee_close":0.0005,
|
|
"trade_duration":30,
|
|
"profit_ratio":-0.09853216535933962,
|
|
"profit_abs":-296.95489539,
|
|
"exit_reason":"trailing_stop_loss",
|
|
"initial_stop_loss_abs":0.6689,
|
|
"initial_stop_loss_ratio":-0.99,
|
|
"stop_loss_abs":0.6399,
|
|
"stop_loss_ratio":-0.3368287257705749,
|
|
"min_rate":0.6294,
|
|
"max_rate":0.6421,
|
|
"is_open":false,
|
|
"enter_tag":"[0.6373, 0.5993, 0.64]",
|
|
"leverage":20,
|
|
"is_short":true,
|
|
"open_timestamp":1709376000000,
|
|
"close_timestamp":1709377800000,
|
|
"orders":[
|
|
{
|
|
"amount":94612.3,
|
|
"safe_price":0.6374,
|
|
"ft_order_side":"sell",
|
|
"order_filled_timestamp":1709376000000,
|
|
"ft_is_entry":true,
|
|
"ft_order_tag":"[0.6373, 0.5993, 0.64]",
|
|
"cost":60336.032960009994
|
|
},
|
|
{
|
|
"amount":94612.3,
|
|
"safe_price":0.6399,
|
|
"ft_order_side":"buy",
|
|
"order_filled_timestamp":1709377800000,
|
|
"ft_is_entry":false,
|
|
"ft_order_tag":"trailing_stop_loss",
|
|
"cost":60572.681975385
|
|
}
|
|
]
|
|
}
|
|
"""
|
|
|
|
trade = Trade.from_json(trade_string)
|
|
Trade.session.add(trade)
|
|
Trade.commit()
|
|
|
|
# Trade-id not given - use first available
|
|
assert trade.id == 1
|