Extract open_trade generation from freqtradebot
This commit is contained in:
parent
a39d51d7d0
commit
c735d35265
@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional, Tuple
|
|||||||
import arrow
|
import arrow
|
||||||
from requests.exceptions import RequestException
|
from requests.exceptions import RequestException
|
||||||
|
|
||||||
from freqtrade import (DependencyException, OperationalException, InvalidOrderException,
|
from freqtrade import (DependencyException, InvalidOrderException,
|
||||||
__version__, constants, persistence)
|
__version__, constants, persistence)
|
||||||
from freqtrade.data.converter import order_book_to_dataframe
|
from freqtrade.data.converter import order_book_to_dataframe
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
@ -508,7 +508,7 @@ class FreqtradeBot:
|
|||||||
|
|
||||||
if not isclose(amount, order_amount, abs_tol=constants.MATH_CLOSE_PREC):
|
if not isclose(amount, order_amount, abs_tol=constants.MATH_CLOSE_PREC):
|
||||||
logger.warning(f"Amount {amount} does not match amount {trade.amount}")
|
logger.warning(f"Amount {amount} does not match amount {trade.amount}")
|
||||||
raise OperationalException("Half bought? Amounts don't match")
|
raise DependencyException("Half bought? Amounts don't match")
|
||||||
real_amount = amount - fee_abs
|
real_amount = amount - fee_abs
|
||||||
if fee_abs != 0:
|
if fee_abs != 0:
|
||||||
logger.info(f"Applying fee on amount for {trade} "
|
logger.info(f"Applying fee on amount for {trade} "
|
||||||
@ -536,7 +536,7 @@ class FreqtradeBot:
|
|||||||
# Fee was applied, so set to 0
|
# Fee was applied, so set to 0
|
||||||
trade.fee_open = 0
|
trade.fee_open = 0
|
||||||
|
|
||||||
except OperationalException as exception:
|
except DependencyException as exception:
|
||||||
logger.warning("Could not update trade amount: %s", exception)
|
logger.warning("Could not update trade amount: %s", exception)
|
||||||
|
|
||||||
trade.update(order)
|
trade.update(order)
|
||||||
|
@ -9,8 +9,8 @@ from pathlib import Path
|
|||||||
from unittest.mock import MagicMock, PropertyMock
|
from unittest.mock import MagicMock, PropertyMock
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
import pytest
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import pytest
|
||||||
from telegram import Chat, Message, Update
|
from telegram import Chat, Message, Update
|
||||||
|
|
||||||
from freqtrade import constants, persistence
|
from freqtrade import constants, persistence
|
||||||
@ -19,10 +19,10 @@ from freqtrade.data.converter import parse_ticker_dataframe
|
|||||||
from freqtrade.edge import Edge, PairInfo
|
from freqtrade.edge import Edge, PairInfo
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.resolvers import ExchangeResolver
|
from freqtrade.resolvers import ExchangeResolver
|
||||||
from freqtrade.worker import Worker
|
from freqtrade.worker import Worker
|
||||||
|
|
||||||
|
|
||||||
logging.getLogger('').setLevel(logging.INFO)
|
logging.getLogger('').setLevel(logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
@ -1069,3 +1069,19 @@ def import_fails() -> None:
|
|||||||
|
|
||||||
# restore previous importfunction
|
# restore previous importfunction
|
||||||
builtins.__import__ = realimport
|
builtins.__import__ = realimport
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="function")
|
||||||
|
def open_trade():
|
||||||
|
return Trade(
|
||||||
|
pair='ETH/BTC',
|
||||||
|
open_rate=0.00001099,
|
||||||
|
exchange='bittrex',
|
||||||
|
open_order_id='123456789',
|
||||||
|
amount=90.99181073,
|
||||||
|
fee_open=0.0,
|
||||||
|
fee_close=0.0,
|
||||||
|
stake_amount=1,
|
||||||
|
open_date=arrow.utcnow().shift(minutes=-601).datetime,
|
||||||
|
is_open=True
|
||||||
|
)
|
||||||
|
@ -1916,7 +1916,8 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order,
|
|||||||
freqtrade.handle_trade(trade)
|
freqtrade.handle_trade(trade)
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, fee, mocker) -> None:
|
def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, open_trade,
|
||||||
|
fee, mocker) -> None:
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -1929,31 +1930,18 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, fe
|
|||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
trade_buy = Trade(
|
Trade.session.add(open_trade)
|
||||||
pair='ETH/BTC',
|
|
||||||
open_rate=0.00001099,
|
|
||||||
exchange='bittrex',
|
|
||||||
open_order_id='123456789',
|
|
||||||
amount=90.99181073,
|
|
||||||
fee_open=0.0,
|
|
||||||
fee_close=0.0,
|
|
||||||
stake_amount=1,
|
|
||||||
open_date=arrow.utcnow().shift(minutes=-601).datetime,
|
|
||||||
is_open=True
|
|
||||||
)
|
|
||||||
|
|
||||||
Trade.session.add(trade_buy)
|
|
||||||
|
|
||||||
# check it does cancel buy orders over the time limit
|
# check it does cancel buy orders over the time limit
|
||||||
freqtrade.check_handle_timedout()
|
freqtrade.check_handle_timedout()
|
||||||
assert cancel_order_mock.call_count == 1
|
assert cancel_order_mock.call_count == 1
|
||||||
assert rpc_mock.call_count == 1
|
assert rpc_mock.call_count == 1
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all()
|
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 0
|
assert nb_trades == 0
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old,
|
def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, open_trade,
|
||||||
fee, mocker, caplog) -> None:
|
fee, mocker, caplog) -> None:
|
||||||
""" Handle Buy order cancelled on exchange"""
|
""" Handle Buy order cancelled on exchange"""
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
@ -1969,32 +1957,19 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old,
|
|||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
trade_buy = Trade(
|
Trade.session.add(open_trade)
|
||||||
pair='ETH/BTC',
|
|
||||||
open_rate=0.00001099,
|
|
||||||
exchange='bittrex',
|
|
||||||
open_order_id='123456789',
|
|
||||||
amount=90.99181073,
|
|
||||||
fee_open=0.0,
|
|
||||||
fee_close=0.0,
|
|
||||||
stake_amount=1,
|
|
||||||
open_date=arrow.utcnow().shift(minutes=-601).datetime,
|
|
||||||
is_open=True
|
|
||||||
)
|
|
||||||
|
|
||||||
Trade.session.add(trade_buy)
|
|
||||||
|
|
||||||
# check it does cancel buy orders over the time limit
|
# check it does cancel buy orders over the time limit
|
||||||
freqtrade.check_handle_timedout()
|
freqtrade.check_handle_timedout()
|
||||||
assert cancel_order_mock.call_count == 0
|
assert cancel_order_mock.call_count == 0
|
||||||
assert rpc_mock.call_count == 1
|
assert rpc_mock.call_count == 1
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all()
|
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 0
|
assert nb_trades == 0
|
||||||
assert log_has_re("Buy order canceled on Exchange for Trade.*", caplog)
|
assert log_has_re("Buy order canceled on Exchange for Trade.*", caplog)
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old,
|
def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, open_trade,
|
||||||
fee, mocker) -> None:
|
fee, mocker) -> None:
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
@ -2009,31 +1984,19 @@ def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_ord
|
|||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
trade_buy = Trade(
|
Trade.session.add(open_trade)
|
||||||
pair='ETH/BTC',
|
|
||||||
open_rate=0.00001099,
|
|
||||||
exchange='bittrex',
|
|
||||||
open_order_id='123456789',
|
|
||||||
amount=90.99181073,
|
|
||||||
fee_open=0.0,
|
|
||||||
fee_close=0.0,
|
|
||||||
stake_amount=1,
|
|
||||||
open_date=arrow.utcnow().shift(minutes=-601).datetime,
|
|
||||||
is_open=True
|
|
||||||
)
|
|
||||||
|
|
||||||
Trade.session.add(trade_buy)
|
|
||||||
|
|
||||||
# check it does cancel buy orders over the time limit
|
# check it does cancel buy orders over the time limit
|
||||||
freqtrade.check_handle_timedout()
|
freqtrade.check_handle_timedout()
|
||||||
assert cancel_order_mock.call_count == 0
|
assert cancel_order_mock.call_count == 0
|
||||||
assert rpc_mock.call_count == 0
|
assert rpc_mock.call_count == 0
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all()
|
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 1
|
assert nb_trades == 1
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, mocker) -> None:
|
def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, mocker,
|
||||||
|
open_trade) -> None:
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -2045,30 +2008,20 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old,
|
|||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
trade_sell = Trade(
|
open_trade.open_date = arrow.utcnow().shift(hours=-5).datetime
|
||||||
pair='ETH/BTC',
|
open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime
|
||||||
open_rate=0.00001099,
|
open_trade.is_open = False
|
||||||
exchange='bittrex',
|
|
||||||
open_order_id='123456789',
|
|
||||||
amount=90.99181073,
|
|
||||||
fee_open=0.0,
|
|
||||||
fee_close=0.0,
|
|
||||||
stake_amount=1,
|
|
||||||
open_date=arrow.utcnow().shift(hours=-5).datetime,
|
|
||||||
close_date=arrow.utcnow().shift(minutes=-601).datetime,
|
|
||||||
is_open=False
|
|
||||||
)
|
|
||||||
|
|
||||||
Trade.session.add(trade_sell)
|
Trade.session.add(open_trade)
|
||||||
|
|
||||||
# check it does cancel sell orders over the time limit
|
# check it does cancel sell orders over the time limit
|
||||||
freqtrade.check_handle_timedout()
|
freqtrade.check_handle_timedout()
|
||||||
assert cancel_order_mock.call_count == 1
|
assert cancel_order_mock.call_count == 1
|
||||||
assert rpc_mock.call_count == 1
|
assert rpc_mock.call_count == 1
|
||||||
assert trade_sell.is_open is True
|
assert open_trade.is_open is True
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old,
|
def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, open_trade,
|
||||||
mocker, caplog) -> None:
|
mocker, caplog) -> None:
|
||||||
""" Handle sell order cancelled on exchange"""
|
""" Handle sell order cancelled on exchange"""
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
@ -2083,32 +2036,22 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old,
|
|||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
trade_sell = Trade(
|
open_trade.open_date = arrow.utcnow().shift(hours=-5).datetime
|
||||||
pair='ETH/BTC',
|
open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime
|
||||||
open_rate=0.00001099,
|
open_trade.is_open = False
|
||||||
exchange='bittrex',
|
|
||||||
open_order_id='123456789',
|
|
||||||
amount=90.99181073,
|
|
||||||
fee_open=0.0,
|
|
||||||
fee_close=0.0,
|
|
||||||
stake_amount=1,
|
|
||||||
open_date=arrow.utcnow().shift(hours=-5).datetime,
|
|
||||||
close_date=arrow.utcnow().shift(minutes=-601).datetime,
|
|
||||||
is_open=False
|
|
||||||
)
|
|
||||||
|
|
||||||
Trade.session.add(trade_sell)
|
Trade.session.add(open_trade)
|
||||||
|
|
||||||
# check it does cancel sell orders over the time limit
|
# check it does cancel sell orders over the time limit
|
||||||
freqtrade.check_handle_timedout()
|
freqtrade.check_handle_timedout()
|
||||||
assert cancel_order_mock.call_count == 0
|
assert cancel_order_mock.call_count == 0
|
||||||
assert rpc_mock.call_count == 1
|
assert rpc_mock.call_count == 1
|
||||||
assert trade_sell.is_open is True
|
assert open_trade.is_open is True
|
||||||
assert log_has_re("Sell order canceled on exchange for Trade.*", caplog)
|
assert log_has_re("Sell order canceled on exchange for Trade.*", caplog)
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial,
|
def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial,
|
||||||
mocker) -> None:
|
open_trade, mocker) -> None:
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -2120,33 +2063,20 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old
|
|||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
trade_buy = Trade(
|
Trade.session.add(open_trade)
|
||||||
pair='ETH/BTC',
|
|
||||||
open_rate=0.00001099,
|
|
||||||
exchange='bittrex',
|
|
||||||
open_order_id='123456789',
|
|
||||||
amount=90.99181073,
|
|
||||||
fee_open=0.0,
|
|
||||||
fee_close=0.0,
|
|
||||||
stake_amount=1,
|
|
||||||
open_date=arrow.utcnow().shift(minutes=-601).datetime,
|
|
||||||
is_open=True
|
|
||||||
)
|
|
||||||
|
|
||||||
Trade.session.add(trade_buy)
|
|
||||||
|
|
||||||
# check it does cancel buy orders over the time limit
|
# check it does cancel buy orders over the time limit
|
||||||
# note this is for a partially-complete buy order
|
# note this is for a partially-complete buy order
|
||||||
freqtrade.check_handle_timedout()
|
freqtrade.check_handle_timedout()
|
||||||
assert cancel_order_mock.call_count == 1
|
assert cancel_order_mock.call_count == 1
|
||||||
assert rpc_mock.call_count == 1
|
assert rpc_mock.call_count == 1
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all()
|
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
||||||
assert len(trades) == 1
|
assert len(trades) == 1
|
||||||
assert trades[0].amount == 23.0
|
assert trades[0].amount == 23.0
|
||||||
assert trades[0].stake_amount == trade_buy.open_rate * trades[0].amount
|
assert trades[0].stake_amount == open_trade.open_rate * trades[0].amount
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -> None:
|
def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocker, caplog) -> None:
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
@ -2164,26 +2094,12 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
|
|||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
open_date = arrow.utcnow().shift(minutes=-601)
|
Trade.session.add(open_trade)
|
||||||
trade_buy = Trade(
|
|
||||||
pair='ETH/BTC',
|
|
||||||
open_rate=0.00001099,
|
|
||||||
exchange='bittrex',
|
|
||||||
open_order_id='123456789',
|
|
||||||
amount=90.99181073,
|
|
||||||
fee_open=0.0,
|
|
||||||
fee_close=0.0,
|
|
||||||
stake_amount=1,
|
|
||||||
open_date=open_date.datetime,
|
|
||||||
is_open=True
|
|
||||||
)
|
|
||||||
|
|
||||||
Trade.session.add(trade_buy)
|
|
||||||
|
|
||||||
freqtrade.check_handle_timedout()
|
freqtrade.check_handle_timedout()
|
||||||
assert log_has_re(r"Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, "
|
assert log_has_re(r"Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, "
|
||||||
r"open_rate=0.00001099, open_since="
|
r"open_rate=0.00001099, open_since="
|
||||||
f"{open_date.strftime('%Y-%m-%d %H:%M:%S')}"
|
f"{open_trade.open_date.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
r"\) due to Traceback \(most recent call last\):\n*",
|
r"\) due to Traceback \(most recent call last\):\n*",
|
||||||
caplog)
|
caplog)
|
||||||
|
|
||||||
@ -2204,9 +2120,11 @@ def test_handle_timedout_limit_buy(mocker, default_conf, limit_buy_order) -> Non
|
|||||||
limit_buy_order['remaining'] = limit_buy_order['amount']
|
limit_buy_order['remaining'] = limit_buy_order['amount']
|
||||||
assert freqtrade.handle_timedout_limit_buy(trade, limit_buy_order)
|
assert freqtrade.handle_timedout_limit_buy(trade, limit_buy_order)
|
||||||
assert cancel_order_mock.call_count == 1
|
assert cancel_order_mock.call_count == 1
|
||||||
|
|
||||||
|
cancel_order_mock.reset_mock()
|
||||||
limit_buy_order['amount'] = 2
|
limit_buy_order['amount'] = 2
|
||||||
assert not freqtrade.handle_timedout_limit_buy(trade, limit_buy_order)
|
assert not freqtrade.handle_timedout_limit_buy(trade, limit_buy_order)
|
||||||
assert cancel_order_mock.call_count == 2
|
assert cancel_order_mock.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_handle_timedout_limit_sell(mocker, default_conf) -> None:
|
def test_handle_timedout_limit_sell(mocker, default_conf) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user