mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
Merge branch 'feat/short' into funding-fee-dry-run
This commit is contained in:
commit
e7fad04eb9
|
@ -23,4 +23,6 @@ class Bibox(Exchange):
|
||||||
@property
|
@property
|
||||||
def _ccxt_config(self) -> Dict:
|
def _ccxt_config(self) -> Dict:
|
||||||
# Parameters to add directly to ccxt sync/async initialization.
|
# Parameters to add directly to ccxt sync/async initialization.
|
||||||
return {"has": {"fetchCurrencies": False}}
|
config = {"has": {"fetchCurrencies": False}}
|
||||||
|
config.update(super()._ccxt_config)
|
||||||
|
return config
|
||||||
|
|
|
@ -28,33 +28,17 @@ class Binance(Exchange):
|
||||||
"trades_pagination": "id",
|
"trades_pagination": "id",
|
||||||
"trades_pagination_arg": "fromId",
|
"trades_pagination_arg": "fromId",
|
||||||
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
|
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
|
||||||
|
"ccxt_futures_name": "future"
|
||||||
}
|
}
|
||||||
|
|
||||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||||
# TradingMode.SPOT always supported and not required in this list
|
# TradingMode.SPOT always supported and not required in this list
|
||||||
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
# TODO-lev: Uncomment once supported
|
||||||
# (TradingMode.FUTURES, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
# (TradingMode.MARGIN, Collateral.CROSS),
|
||||||
# (TradingMode.FUTURES, Collateral.ISOLATED) # TODO-lev: Uncomment once supported
|
# (TradingMode.FUTURES, Collateral.CROSS),
|
||||||
|
# (TradingMode.FUTURES, Collateral.ISOLATED)
|
||||||
]
|
]
|
||||||
|
|
||||||
@property
|
|
||||||
def _ccxt_config(self) -> Dict:
|
|
||||||
# Parameters to add directly to ccxt sync/async initialization.
|
|
||||||
if self.trading_mode == TradingMode.MARGIN:
|
|
||||||
return {
|
|
||||||
"options": {
|
|
||||||
"defaultType": "margin"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elif self.trading_mode == TradingMode.FUTURES:
|
|
||||||
return {
|
|
||||||
"options": {
|
|
||||||
"defaultType": "future"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def stoploss_adjust(self, stop_loss: float, order: Dict, side: str) -> bool:
|
def stoploss_adjust(self, stop_loss: float, order: Dict, side: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Verify stop_loss against stoploss-order value (limit or price)
|
Verify stop_loss against stoploss-order value (limit or price)
|
||||||
|
|
|
@ -21,10 +21,12 @@ class Bybit(Exchange):
|
||||||
|
|
||||||
_ft_has: Dict = {
|
_ft_has: Dict = {
|
||||||
"ohlcv_candle_limit": 200,
|
"ohlcv_candle_limit": 200,
|
||||||
|
"ccxt_futures_name": "linear"
|
||||||
}
|
}
|
||||||
|
|
||||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||||
# TradingMode.SPOT always supported and not required in this list
|
# TradingMode.SPOT always supported and not required in this list
|
||||||
# (TradingMode.FUTURES, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
# TODO-lev: Uncomment once supported
|
||||||
# (TradingMode.FUTURES, Collateral.ISOLATED) # TODO-lev: Uncomment once supported
|
# (TradingMode.FUTURES, Collateral.CROSS),
|
||||||
|
# (TradingMode.FUTURES, Collateral.ISOLATED)
|
||||||
]
|
]
|
||||||
|
|
|
@ -69,7 +69,8 @@ class Exchange:
|
||||||
"trades_pagination_arg": "since",
|
"trades_pagination_arg": "since",
|
||||||
"l2_limit_range": None,
|
"l2_limit_range": None,
|
||||||
"l2_limit_range_required": True, # Allow Empty L2 limit (kucoin)
|
"l2_limit_range_required": True, # Allow Empty L2 limit (kucoin)
|
||||||
"mark_ohlcv_price": "mark"
|
"mark_ohlcv_price": "mark",
|
||||||
|
"ccxt_futures_name": "swap"
|
||||||
}
|
}
|
||||||
_ft_has: Dict = {}
|
_ft_has: Dict = {}
|
||||||
|
|
||||||
|
@ -231,7 +232,20 @@ class Exchange:
|
||||||
@property
|
@property
|
||||||
def _ccxt_config(self) -> Dict:
|
def _ccxt_config(self) -> Dict:
|
||||||
# Parameters to add directly to ccxt sync/async initialization.
|
# Parameters to add directly to ccxt sync/async initialization.
|
||||||
return {}
|
if self.trading_mode == TradingMode.MARGIN:
|
||||||
|
return {
|
||||||
|
"options": {
|
||||||
|
"defaultType": "margin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elif self.trading_mode == TradingMode.FUTURES:
|
||||||
|
return {
|
||||||
|
"options": {
|
||||||
|
"defaultType": self._ft_has["ccxt_futures_name"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
|
|
@ -25,8 +25,9 @@ class Ftx(Exchange):
|
||||||
|
|
||||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||||
# TradingMode.SPOT always supported and not required in this list
|
# TradingMode.SPOT always supported and not required in this list
|
||||||
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
# TODO-lev: Uncomment once supported
|
||||||
# (TradingMode.FUTURES, Collateral.CROSS) # TODO-lev: Uncomment once supported
|
# (TradingMode.MARGIN, Collateral.CROSS),
|
||||||
|
# (TradingMode.FUTURES, Collateral.CROSS)
|
||||||
]
|
]
|
||||||
|
|
||||||
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
|
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
|
||||||
|
|
|
@ -28,29 +28,12 @@ class Gateio(Exchange):
|
||||||
|
|
||||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||||
# TradingMode.SPOT always supported and not required in this list
|
# TradingMode.SPOT always supported and not required in this list
|
||||||
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
# TODO-lev: Uncomment once supported
|
||||||
# (TradingMode.FUTURES, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
# (TradingMode.MARGIN, Collateral.CROSS),
|
||||||
# (TradingMode.FUTURES, Collateral.ISOLATED) # TODO-lev: Uncomment once supported
|
# (TradingMode.FUTURES, Collateral.CROSS),
|
||||||
|
# (TradingMode.FUTURES, Collateral.ISOLATED)
|
||||||
]
|
]
|
||||||
|
|
||||||
@property
|
|
||||||
def _ccxt_config(self) -> Dict:
|
|
||||||
# Parameters to add directly to ccxt sync/async initialization.
|
|
||||||
if self.trading_mode == TradingMode.MARGIN:
|
|
||||||
return {
|
|
||||||
"options": {
|
|
||||||
"defaultType": "margin"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elif self.trading_mode == TradingMode.FUTURES:
|
|
||||||
return {
|
|
||||||
"options": {
|
|
||||||
"defaultType": "swap"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def validate_ordertypes(self, order_types: Dict) -> None:
|
def validate_ordertypes(self, order_types: Dict) -> None:
|
||||||
super().validate_ordertypes(order_types)
|
super().validate_ordertypes(order_types)
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,9 @@ class Kraken(Exchange):
|
||||||
|
|
||||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||||
# TradingMode.SPOT always supported and not required in this list
|
# TradingMode.SPOT always supported and not required in this list
|
||||||
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
# TODO-lev: Uncomment once supported
|
||||||
# (TradingMode.FUTURES, Collateral.CROSS) # TODO-lev: No CCXT support
|
# (TradingMode.MARGIN, Collateral.CROSS),
|
||||||
|
# (TradingMode.FUTURES, Collateral.CROSS)
|
||||||
]
|
]
|
||||||
|
|
||||||
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
|
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
|
||||||
|
|
|
@ -20,25 +20,8 @@ class Okex(Exchange):
|
||||||
|
|
||||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||||
# TradingMode.SPOT always supported and not required in this list
|
# TradingMode.SPOT always supported and not required in this list
|
||||||
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
# TODO-lev: Uncomment once supported
|
||||||
# (TradingMode.FUTURES, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
# (TradingMode.MARGIN, Collateral.CROSS),
|
||||||
# (TradingMode.FUTURES, Collateral.ISOLATED) # TODO-lev: Uncomment once supported
|
# (TradingMode.FUTURES, Collateral.CROSS),
|
||||||
|
# (TradingMode.FUTURES, Collateral.ISOLATED)
|
||||||
]
|
]
|
||||||
|
|
||||||
@property
|
|
||||||
def _ccxt_config(self) -> Dict:
|
|
||||||
# Parameters to add directly to ccxt sync/async initialization.
|
|
||||||
if self.trading_mode == TradingMode.MARGIN:
|
|
||||||
return {
|
|
||||||
"options": {
|
|
||||||
"defaultType": "margin"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elif self.trading_mode == TradingMode.FUTURES:
|
|
||||||
return {
|
|
||||||
"options": {
|
|
||||||
"defaultType": "swap"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
|
|
|
@ -923,8 +923,7 @@ class FreqtradeBot(LoggingMixin):
|
||||||
Check if trade is fulfilled in which case the stoploss
|
Check if trade is fulfilled in which case the stoploss
|
||||||
on exchange should be added immediately if stoploss on exchange
|
on exchange should be added immediately if stoploss on exchange
|
||||||
is enabled.
|
is enabled.
|
||||||
# TODO-lev: liquidation price will always be on exchange, even though
|
# TODO-lev: liquidation price always on exchange, even without stoploss_on_exchange
|
||||||
# TODO-lev: stoploss_on_exchange might not be enabled
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug('Handling stoploss on exchange %s ...', trade)
|
logger.debug('Handling stoploss on exchange %s ...', trade)
|
||||||
|
@ -1523,7 +1522,7 @@ class FreqtradeBot(LoggingMixin):
|
||||||
self.wallets.update()
|
self.wallets.update()
|
||||||
if fee_abs != 0 and self.wallets.get_free(trade_base_currency) >= amount:
|
if fee_abs != 0 and self.wallets.get_free(trade_base_currency) >= amount:
|
||||||
# Eat into dust if we own more than base currency
|
# Eat into dust if we own more than base currency
|
||||||
# TODO-lev: won't be in base currency for shorts
|
# TODO-lev: settle currency for futures
|
||||||
logger.info(f"Fee amount for {trade} was in base currency - "
|
logger.info(f"Fee amount for {trade} was in base currency - "
|
||||||
f"Eating Fee {fee_abs} into dust.")
|
f"Eating Fee {fee_abs} into dust.")
|
||||||
elif fee_abs != 0:
|
elif fee_abs != 0:
|
||||||
|
|
|
@ -916,8 +916,8 @@ class Trade(_DECL_BASE, LocalTrade):
|
||||||
max_rate = Column(Float, nullable=True, default=0.0)
|
max_rate = Column(Float, nullable=True, default=0.0)
|
||||||
# Lowest price reached
|
# Lowest price reached
|
||||||
min_rate = Column(Float, nullable=True)
|
min_rate = Column(Float, nullable=True)
|
||||||
sell_reason = Column(String(100), nullable=True) # TODO-lev: Change to close_reason
|
sell_reason = Column(String(100), nullable=True)
|
||||||
sell_order_status = Column(String(100), nullable=True) # TODO-lev: Change to close_order_status
|
sell_order_status = Column(String(100), nullable=True)
|
||||||
strategy = Column(String(100), nullable=True)
|
strategy = Column(String(100), nullable=True)
|
||||||
buy_tag = Column(String(100), nullable=True)
|
buy_tag = Column(String(100), nullable=True)
|
||||||
timeframe = Column(Integer, nullable=True)
|
timeframe = Column(Integer, nullable=True)
|
||||||
|
|
|
@ -32,7 +32,7 @@ class StoplossGuard(IProtection):
|
||||||
def _reason(self) -> str:
|
def _reason(self) -> str:
|
||||||
"""
|
"""
|
||||||
LockReason to use
|
LockReason to use
|
||||||
#TODO-lev: check if min is the right word for shorts
|
# TODO-lev: check if min is the right word for shorts
|
||||||
"""
|
"""
|
||||||
return (f'{self._trade_limit} stoplosses in {self._lookback_period} min, '
|
return (f'{self._trade_limit} stoplosses in {self._lookback_period} min, '
|
||||||
f'locking for {self._stop_duration} min.')
|
f'locking for {self._stop_duration} min.')
|
||||||
|
|
|
@ -488,7 +488,7 @@ def leverage_trade(fee):
|
||||||
open_order_id='dry_run_leverage_buy_12368',
|
open_order_id='dry_run_leverage_buy_12368',
|
||||||
strategy='DefaultStrategy',
|
strategy='DefaultStrategy',
|
||||||
timeframe=5,
|
timeframe=5,
|
||||||
sell_reason='sell_signal', # TODO-lev: Update to exit/close reason
|
sell_reason='sell_signal',
|
||||||
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=300),
|
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=300),
|
||||||
close_date=datetime.now(tz=timezone.utc),
|
close_date=datetime.now(tz=timezone.utc),
|
||||||
interest_rate=0.0005
|
interest_rate=0.0005
|
||||||
|
|
|
@ -3253,16 +3253,16 @@ def test_validate_trading_mode_and_collateral(
|
||||||
("binance", "spot", {}),
|
("binance", "spot", {}),
|
||||||
("binance", "margin", {"options": {"defaultType": "margin"}}),
|
("binance", "margin", {"options": {"defaultType": "margin"}}),
|
||||||
("binance", "futures", {"options": {"defaultType": "future"}}),
|
("binance", "futures", {"options": {"defaultType": "future"}}),
|
||||||
("kraken", "spot", {}),
|
("bibox", "spot", {"has": {"fetchCurrencies": False}}),
|
||||||
("kraken", "margin", {}),
|
("bibox", "margin", {"has": {"fetchCurrencies": False}, "options": {"defaultType": "margin"}}),
|
||||||
("kraken", "futures", {}),
|
("bibox", "futures", {"has": {"fetchCurrencies": False}, "options": {"defaultType": "swap"}}),
|
||||||
("ftx", "spot", {}),
|
("bybit", "futures", {"options": {"defaultType": "linear"}}),
|
||||||
("ftx", "margin", {}),
|
("ftx", "futures", {"options": {"defaultType": "swap"}}),
|
||||||
("ftx", "futures", {}),
|
|
||||||
("bittrex", "spot", {}),
|
|
||||||
("gateio", "spot", {}),
|
|
||||||
("gateio", "margin", {"options": {"defaultType": "margin"}}),
|
|
||||||
("gateio", "futures", {"options": {"defaultType": "swap"}}),
|
("gateio", "futures", {"options": {"defaultType": "swap"}}),
|
||||||
|
("hitbtc", "futures", {"options": {"defaultType": "swap"}}),
|
||||||
|
("kraken", "futures", {"options": {"defaultType": "swap"}}),
|
||||||
|
("kucoin", "futures", {"options": {"defaultType": "swap"}}),
|
||||||
|
("okex", "futures", {"options": {"defaultType": "swap"}}),
|
||||||
])
|
])
|
||||||
def test__ccxt_config(
|
def test__ccxt_config(
|
||||||
default_conf,
|
default_conf,
|
||||||
|
|
|
@ -586,10 +586,10 @@ def test_api_trades(botclient, mocker, fee, markets, is_short):
|
||||||
assert rc.json()['total_trades'] == 2
|
assert rc.json()['total_trades'] == 2
|
||||||
|
|
||||||
|
|
||||||
# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
|
@pytest.mark.parametrize('is_short', [True, False])
|
||||||
def test_api_trade_single(botclient, mocker, fee, ticker, markets):
|
def test_api_trade_single(botclient, mocker, fee, ticker, markets, is_short):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot)
|
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
markets=PropertyMock(return_value=markets),
|
markets=PropertyMock(return_value=markets),
|
||||||
|
@ -599,7 +599,7 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets):
|
||||||
assert_response(rc, 404)
|
assert_response(rc, 404)
|
||||||
assert rc.json()['detail'] == 'Trade not found.'
|
assert rc.json()['detail'] == 'Trade not found.'
|
||||||
|
|
||||||
create_mock_trades(fee, False)
|
create_mock_trades(fee, is_short=is_short)
|
||||||
Trade.query.session.flush()
|
Trade.query.session.flush()
|
||||||
|
|
||||||
rc = client_get(client, f"{BASE_URI}/trade/3")
|
rc = client_get(client, f"{BASE_URI}/trade/3")
|
||||||
|
@ -607,10 +607,10 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets):
|
||||||
assert rc.json()['trade_id'] == 3
|
assert rc.json()['trade_id'] == 3
|
||||||
|
|
||||||
|
|
||||||
# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
|
@pytest.mark.parametrize('is_short', [True, False])
|
||||||
def test_api_delete_trade(botclient, mocker, fee, markets):
|
def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot)
|
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
|
||||||
stoploss_mock = MagicMock()
|
stoploss_mock = MagicMock()
|
||||||
cancel_mock = MagicMock()
|
cancel_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
|
@ -749,10 +749,10 @@ def test_api_profit(botclient, mocker, ticker, fee, markets):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
|
@pytest.mark.parametrize('is_short', [True, False])
|
||||||
def test_api_stats(botclient, mocker, ticker, fee, markets,):
|
def test_api_stats(botclient, mocker, ticker, fee, markets, is_short):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot)
|
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
get_balances=MagicMock(return_value=ticker),
|
get_balances=MagicMock(return_value=ticker),
|
||||||
|
@ -766,7 +766,7 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,):
|
||||||
assert 'durations' in rc.json()
|
assert 'durations' in rc.json()
|
||||||
assert 'sell_reasons' in rc.json()
|
assert 'sell_reasons' in rc.json()
|
||||||
|
|
||||||
create_mock_trades(fee, False)
|
create_mock_trades(fee, is_short=is_short)
|
||||||
|
|
||||||
rc = client_get(client, f"{BASE_URI}/stats")
|
rc = client_get(client, f"{BASE_URI}/stats")
|
||||||
assert_response(rc, 200)
|
assert_response(rc, 200)
|
||||||
|
|
|
@ -1294,8 +1294,8 @@ def test_telegram_trades(mocker, update, default_conf, fee):
|
||||||
msg_mock.call_args_list[0][0][0]))
|
msg_mock.call_args_list[0][0][0]))
|
||||||
|
|
||||||
|
|
||||||
# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
|
@pytest.mark.parametrize('is_short', [True, False])
|
||||||
def test_telegram_delete_trade(mocker, update, default_conf, fee):
|
def test_telegram_delete_trade(mocker, update, default_conf, fee, is_short):
|
||||||
|
|
||||||
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
context = MagicMock()
|
context = MagicMock()
|
||||||
|
@ -1305,7 +1305,7 @@ def test_telegram_delete_trade(mocker, update, default_conf, fee):
|
||||||
assert "Trade-id not set." in msg_mock.call_args_list[0][0][0]
|
assert "Trade-id not set." in msg_mock.call_args_list[0][0][0]
|
||||||
|
|
||||||
msg_mock.reset_mock()
|
msg_mock.reset_mock()
|
||||||
create_mock_trades(fee, False)
|
create_mock_trades(fee, is_short)
|
||||||
|
|
||||||
context = MagicMock()
|
context = MagicMock()
|
||||||
context.args = [1]
|
context.args = [1]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.persistence.models import Trade
|
from freqtrade.persistence.models import Trade
|
||||||
|
@ -7,7 +8,7 @@ from freqtrade.persistence.models import Trade
|
||||||
from .strats.strategy_test_v3 import StrategyTestV3
|
from .strats.strategy_test_v3 import StrategyTestV3
|
||||||
|
|
||||||
|
|
||||||
def test_strategy_test_v2_structure():
|
def test_strategy_test_v3_structure():
|
||||||
assert hasattr(StrategyTestV3, 'minimal_roi')
|
assert hasattr(StrategyTestV3, 'minimal_roi')
|
||||||
assert hasattr(StrategyTestV3, 'stoploss')
|
assert hasattr(StrategyTestV3, 'stoploss')
|
||||||
assert hasattr(StrategyTestV3, 'timeframe')
|
assert hasattr(StrategyTestV3, 'timeframe')
|
||||||
|
@ -16,7 +17,11 @@ def test_strategy_test_v2_structure():
|
||||||
assert hasattr(StrategyTestV3, 'populate_sell_trend')
|
assert hasattr(StrategyTestV3, 'populate_sell_trend')
|
||||||
|
|
||||||
|
|
||||||
def test_strategy_test_v2(result, fee):
|
@pytest.mark.parametrize('is_short,side', [
|
||||||
|
(True, 'short'),
|
||||||
|
(False, 'long'),
|
||||||
|
])
|
||||||
|
def test_strategy_test_v3(result, fee, is_short, side):
|
||||||
strategy = StrategyTestV3({})
|
strategy = StrategyTestV3({})
|
||||||
|
|
||||||
metadata = {'pair': 'ETH/BTC'}
|
metadata = {'pair': 'ETH/BTC'}
|
||||||
|
@ -32,16 +37,18 @@ def test_strategy_test_v2(result, fee):
|
||||||
open_rate=19_000,
|
open_rate=19_000,
|
||||||
amount=0.1,
|
amount=0.1,
|
||||||
pair='ETH/BTC',
|
pair='ETH/BTC',
|
||||||
fee_open=fee.return_value
|
fee_open=fee.return_value,
|
||||||
|
is_short=is_short
|
||||||
)
|
)
|
||||||
|
|
||||||
assert strategy.confirm_trade_entry(pair='ETH/BTC', order_type='limit', amount=0.1,
|
assert strategy.confirm_trade_entry(pair='ETH/BTC', order_type='limit', amount=0.1,
|
||||||
rate=20000, time_in_force='gtc',
|
rate=20000, time_in_force='gtc',
|
||||||
current_time=datetime.utcnow(), side='long') is True
|
current_time=datetime.utcnow(),
|
||||||
|
side=side) is True
|
||||||
assert strategy.confirm_trade_exit(pair='ETH/BTC', trade=trade, order_type='limit', amount=0.1,
|
assert strategy.confirm_trade_exit(pair='ETH/BTC', trade=trade, order_type='limit', amount=0.1,
|
||||||
rate=20000, time_in_force='gtc', sell_reason='roi',
|
rate=20000, time_in_force='gtc', sell_reason='roi',
|
||||||
current_time=datetime.utcnow()) is True
|
current_time=datetime.utcnow(),
|
||||||
|
side=side) is True
|
||||||
|
|
||||||
# TODO-lev: Test for shorts?
|
|
||||||
assert strategy.custom_stoploss(pair='ETH/BTC', trade=trade, current_time=datetime.now(),
|
assert strategy.custom_stoploss(pair='ETH/BTC', trade=trade, current_time=datetime.now(),
|
||||||
current_rate=20_000, current_profit=0.05) == strategy.stoploss
|
current_rate=20_000, current_profit=0.05) == strategy.stoploss
|
||||||
|
|
|
@ -1907,12 +1907,12 @@ def test_get_total_closed_profit(fee, use_db):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
|
@pytest.mark.parametrize('is_short', [True, False])
|
||||||
@pytest.mark.parametrize('use_db', [True, False])
|
@pytest.mark.parametrize('use_db', [True, False])
|
||||||
def test_get_trades_proxy(fee, use_db):
|
def test_get_trades_proxy(fee, use_db, is_short):
|
||||||
Trade.use_db = use_db
|
Trade.use_db = use_db
|
||||||
Trade.reset_trades()
|
Trade.reset_trades()
|
||||||
create_mock_trades(fee, False, use_db)
|
create_mock_trades(fee, is_short, use_db)
|
||||||
trades = Trade.get_trades_proxy()
|
trades = Trade.get_trades_proxy()
|
||||||
assert len(trades) == 6
|
assert len(trades) == 6
|
||||||
|
|
||||||
|
@ -2042,48 +2042,48 @@ def test_update_order_from_ccxt(caplog):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
|
@pytest.mark.parametrize('is_short', [True, False])
|
||||||
def test_select_order(fee):
|
def test_select_order(fee, is_short):
|
||||||
create_mock_trades(fee, False)
|
create_mock_trades(fee, is_short)
|
||||||
|
|
||||||
trades = Trade.get_trades().all()
|
trades = Trade.get_trades().all()
|
||||||
|
|
||||||
# Open buy order, no sell order
|
# Open buy order, no sell order
|
||||||
order = trades[0].select_order('buy', True)
|
order = trades[0].select_order(trades[0].enter_side, True)
|
||||||
assert order is None
|
assert order is None
|
||||||
order = trades[0].select_order('buy', False)
|
order = trades[0].select_order(trades[0].enter_side, False)
|
||||||
assert order is not None
|
assert order is not None
|
||||||
order = trades[0].select_order('sell', None)
|
order = trades[0].select_order(trades[0].exit_side, None)
|
||||||
assert order is None
|
assert order is None
|
||||||
|
|
||||||
# closed buy order, and open sell order
|
# closed buy order, and open sell order
|
||||||
order = trades[1].select_order('buy', True)
|
order = trades[1].select_order(trades[1].enter_side, True)
|
||||||
assert order is None
|
assert order is None
|
||||||
order = trades[1].select_order('buy', False)
|
order = trades[1].select_order(trades[1].enter_side, False)
|
||||||
assert order is not None
|
assert order is not None
|
||||||
order = trades[1].select_order('buy', None)
|
order = trades[1].select_order(trades[1].enter_side, None)
|
||||||
assert order is not None
|
assert order is not None
|
||||||
order = trades[1].select_order('sell', True)
|
order = trades[1].select_order(trades[1].exit_side, True)
|
||||||
assert order is None
|
assert order is None
|
||||||
order = trades[1].select_order('sell', False)
|
order = trades[1].select_order(trades[1].exit_side, False)
|
||||||
assert order is not None
|
assert order is not None
|
||||||
|
|
||||||
# Has open buy order
|
# Has open buy order
|
||||||
order = trades[3].select_order('buy', True)
|
order = trades[3].select_order(trades[3].enter_side, True)
|
||||||
assert order is not None
|
assert order is not None
|
||||||
order = trades[3].select_order('buy', False)
|
order = trades[3].select_order(trades[3].enter_side, False)
|
||||||
assert order is None
|
assert order is None
|
||||||
|
|
||||||
# Open sell order
|
# Open sell order
|
||||||
order = trades[4].select_order('buy', True)
|
order = trades[4].select_order(trades[4].enter_side, True)
|
||||||
assert order is None
|
assert order is None
|
||||||
order = trades[4].select_order('buy', False)
|
order = trades[4].select_order(trades[4].enter_side, False)
|
||||||
assert order is not None
|
assert order is not None
|
||||||
|
|
||||||
order = trades[4].select_order('sell', True)
|
order = trades[4].select_order(trades[4].exit_side, True)
|
||||||
assert order is not None
|
assert order is not None
|
||||||
assert order.ft_order_side == 'stoploss'
|
assert order.ft_order_side == 'stoploss'
|
||||||
order = trades[4].select_order('sell', False)
|
order = trades[4].select_order(trades[4].exit_side, False)
|
||||||
assert order is None
|
assert order is None
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user