Merge pull request #6886 from freqtrade/fix/typing

Fix/typing
This commit is contained in:
Matthias 2022-05-24 19:41:59 +02:00 committed by GitHub
commit d6773bc32c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 100 additions and 62 deletions

View File

@ -667,7 +667,7 @@ class DigDeeperStrategy(IStrategy):
# This is called when placing the initial order (opening trade) # This is called when placing the initial order (opening trade)
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float, proposed_stake: float, min_stake: Optional[float], max_stake: float,
entry_tag: Optional[str], side: str, **kwargs) -> float: entry_tag: Optional[str], side: str, **kwargs) -> float:
# We need to leave most of the funds for possible further DCA orders # We need to leave most of the funds for possible further DCA orders
@ -675,7 +675,7 @@ class DigDeeperStrategy(IStrategy):
return proposed_stake / self.max_dca_multiplier return proposed_stake / self.max_dca_multiplier
def adjust_trade_position(self, trade: Trade, current_time: datetime, def adjust_trade_position(self, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, min_stake: float, current_rate: float, current_profit: float, min_stake: Optional[float],
max_stake: float, **kwargs): max_stake: float, **kwargs):
""" """
Custom trade adjustment logic, returning the stake amount that a trade should be increased. Custom trade adjustment logic, returning the stake amount that a trade should be increased.

View File

@ -199,7 +199,7 @@ New string argument `side` - which can be either `"long"` or `"short"`.
``` python hl_lines="4" ``` python hl_lines="4"
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float, proposed_stake: float, min_stake: Optional[float], max_stake: float,
entry_tag: Optional[str], **kwargs) -> float: entry_tag: Optional[str], **kwargs) -> float:
# ... # ...
return proposed_stake return proposed_stake
@ -208,7 +208,7 @@ class AwesomeStrategy(IStrategy):
``` python hl_lines="4" ``` python hl_lines="4"
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float, proposed_stake: float, min_stake: Optional[float], max_stake: float,
entry_tag: Optional[str], side: str, **kwargs) -> float: entry_tag: Optional[str], side: str, **kwargs) -> float:
# ... # ...
return proposed_stake return proposed_stake

View File

@ -27,7 +27,7 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool:
return True return True
logger.info("Checking exchange...") logger.info("Checking exchange...")
exchange = config.get('exchange', {}).get('name').lower() exchange = config.get('exchange', {}).get('name', '').lower()
if not exchange: if not exchange:
raise OperationalException( raise OperationalException(
f'This command requires a configured exchange. You should either use ' f'This command requires a configured exchange. You should either use '

View File

@ -282,6 +282,7 @@ def refresh_backtest_ohlcv_data(exchange: Exchange, pairs: List[str], timeframes
pairs_not_available = [] pairs_not_available = []
data_handler = get_datahandler(datadir, data_format) data_handler = get_datahandler(datadir, data_format)
candle_type = CandleType.get_default(trading_mode) candle_type = CandleType.get_default(trading_mode)
process = ''
for idx, pair in enumerate(pairs, start=1): for idx, pair in enumerate(pairs, start=1):
if pair not in exchange.markets: if pair not in exchange.markets:
pairs_not_available.append(pair) pairs_not_available.append(pair)

View File

@ -2,6 +2,7 @@ import asyncio
import logging import logging
import time import time
from functools import wraps from functools import wraps
from typing import Any, Callable, Optional, TypeVar, cast, overload
from freqtrade.exceptions import DDosProtection, RetryableOrderError, TemporaryError from freqtrade.exceptions import DDosProtection, RetryableOrderError, TemporaryError
from freqtrade.mixins import LoggingMixin from freqtrade.mixins import LoggingMixin
@ -133,8 +134,22 @@ def retrier_async(f):
return wrapper return wrapper
def retrier(_func=None, retries=API_RETRY_COUNT): F = TypeVar('F', bound=Callable[..., Any])
def decorator(f):
# Type shenanigans
@overload
def retrier(_func: F) -> F:
...
@overload
def retrier(*, retries=API_RETRY_COUNT) -> Callable[[F], F]:
...
def retrier(_func: Optional[F] = None, *, retries=API_RETRY_COUNT):
def decorator(f: F) -> F:
@wraps(f) @wraps(f)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
count = kwargs.pop('count', retries) count = kwargs.pop('count', retries)
@ -155,7 +170,7 @@ def retrier(_func=None, retries=API_RETRY_COUNT):
else: else:
logger.warning(msg + 'Giving up.') logger.warning(msg + 'Giving up.')
raise ex raise ex
return wrapper return cast(F, wrapper)
# Support both @retrier and @retrier(retries=2) syntax # Support both @retrier and @retrier(retries=2) syntax
if _func is None: if _func is None:
return decorator return decorator

View File

@ -92,8 +92,8 @@ class Exchange:
it does basic validation whether the specified exchange and pairs are valid. it does basic validation whether the specified exchange and pairs are valid.
:return: None :return: None
""" """
self._api: ccxt.Exchange = None self._api: ccxt.Exchange
self._api_async: ccxt_async.Exchange = None self._api_async: ccxt_async.Exchange
self._markets: Dict = {} self._markets: Dict = {}
self._trading_fees: Dict[str, Any] = {} self._trading_fees: Dict[str, Any] = {}
self._leverage_tiers: Dict[str, List[Dict]] = {} self._leverage_tiers: Dict[str, List[Dict]] = {}
@ -291,7 +291,7 @@ class Exchange:
return self._markets return self._markets
@property @property
def precisionMode(self) -> str: def precisionMode(self) -> int:
"""exchange ccxt precisionMode""" """exchange ccxt precisionMode"""
return self._api.precisionMode return self._api.precisionMode
@ -322,7 +322,7 @@ class Exchange:
return int(self._ft_has.get('ohlcv_candle_limit_per_timeframe', {}).get( return int(self._ft_has.get('ohlcv_candle_limit_per_timeframe', {}).get(
timeframe, self._ft_has.get('ohlcv_candle_limit'))) timeframe, self._ft_has.get('ohlcv_candle_limit')))
def get_markets(self, base_currencies: List[str] = None, quote_currencies: List[str] = None, def get_markets(self, base_currencies: List[str] = [], quote_currencies: List[str] = [],
spot_only: bool = False, margin_only: bool = False, futures_only: bool = False, spot_only: bool = False, margin_only: bool = False, futures_only: bool = False,
tradable_only: bool = True, tradable_only: bool = True,
active_only: bool = False) -> Dict[str, Any]: active_only: bool = False) -> Dict[str, Any]:
@ -1164,7 +1164,7 @@ class Exchange:
raise OperationalException(e) from e raise OperationalException(e) from e
@retrier(retries=API_FETCH_ORDER_RETRY_COUNT) @retrier(retries=API_FETCH_ORDER_RETRY_COUNT)
def fetch_order(self, order_id: str, pair: str, params={}) -> Dict: def fetch_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
if self._config['dry_run']: if self._config['dry_run']:
return self.fetch_dry_run_order(order_id) return self.fetch_dry_run_order(order_id)
try: try:
@ -1186,8 +1186,8 @@ class Exchange:
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) from e raise OperationalException(e) from e
# Assign method to fetch_stoploss_order to allow easy overriding in other classes def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
fetch_stoploss_order = fetch_order return self.fetch_order(order_id, pair, params)
def fetch_order_or_stoploss_order(self, order_id: str, pair: str, def fetch_order_or_stoploss_order(self, order_id: str, pair: str,
stoploss_order: bool = False) -> Dict: stoploss_order: bool = False) -> Dict:
@ -1212,7 +1212,7 @@ class Exchange:
and order.get('filled') == 0.0) and order.get('filled') == 0.0)
@retrier @retrier
def cancel_order(self, order_id: str, pair: str, params={}) -> Dict: def cancel_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
if self._config['dry_run']: if self._config['dry_run']:
try: try:
order = self.fetch_dry_run_order(order_id) order = self.fetch_dry_run_order(order_id)
@ -1238,8 +1238,8 @@ class Exchange:
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) from e raise OperationalException(e) from e
# Assign method to cancel_stoploss_order to allow easy overriding in other classes def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
cancel_stoploss_order = cancel_order return self.cancel_order(order_id, pair, params)
def is_cancel_order_result_suitable(self, corder) -> bool: def is_cancel_order_result_suitable(self, corder) -> bool:
if not isinstance(corder, dict): if not isinstance(corder, dict):
@ -1718,7 +1718,7 @@ class Exchange:
async def _async_get_historic_ohlcv(self, pair: str, timeframe: str, async def _async_get_historic_ohlcv(self, pair: str, timeframe: str,
since_ms: int, candle_type: CandleType, since_ms: int, candle_type: CandleType,
is_new_pair: bool = False, raise_: bool = False, is_new_pair: bool = False, raise_: bool = False,
until_ms: int = None until_ms: Optional[int] = None
) -> Tuple[str, str, str, List]: ) -> Tuple[str, str, str, List]:
""" """
Download historic ohlcv Download historic ohlcv
@ -1779,7 +1779,7 @@ class Exchange:
def refresh_latest_ohlcv(self, pair_list: ListPairsWithTimeframes, *, def refresh_latest_ohlcv(self, pair_list: ListPairsWithTimeframes, *,
since_ms: Optional[int] = None, cache: bool = True, since_ms: Optional[int] = None, cache: bool = True,
drop_incomplete: bool = None drop_incomplete: Optional[bool] = None
) -> Dict[PairWithTimeframe, DataFrame]: ) -> Dict[PairWithTimeframe, DataFrame]:
""" """
Refresh in-memory OHLCV asynchronously and set `_klines` with the result Refresh in-memory OHLCV asynchronously and set `_klines` with the result

View File

@ -104,7 +104,7 @@ class Ftx(Exchange):
raise OperationalException(e) from e raise OperationalException(e) from e
@retrier(retries=API_FETCH_ORDER_RETRY_COUNT) @retrier(retries=API_FETCH_ORDER_RETRY_COUNT)
def fetch_stoploss_order(self, order_id: str, pair: str) -> Dict: def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
if self._config['dry_run']: if self._config['dry_run']:
return self.fetch_dry_run_order(order_id) return self.fetch_dry_run_order(order_id)
@ -145,7 +145,7 @@ class Ftx(Exchange):
raise OperationalException(e) from e raise OperationalException(e) from e
@retrier @retrier
def cancel_stoploss_order(self, order_id: str, pair: str) -> Dict: def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
if self._config['dry_run']: if self._config['dry_run']:
return {} return {}
try: try:

View File

@ -71,14 +71,14 @@ class Gateio(Exchange):
} }
return trades return trades
def fetch_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
return self.fetch_order( return self.fetch_order(
order_id=order_id, order_id=order_id,
pair=pair, pair=pair,
params={'stop': True} params={'stop': True}
) )
def cancel_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
return self.cancel_order( return self.cancel_order(
order_id=order_id, order_id=order_id,
pair=pair, pair=pair,

View File

@ -500,7 +500,8 @@ class Backtesting:
stake_available = self.wallets.get_available_stake_amount() stake_available = self.wallets.get_available_stake_amount()
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position, stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
default_retval=None)( default_retval=None)(
trade=trade, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX], trade=trade, # type: ignore[arg-type]
current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX],
current_profit=current_profit, min_stake=min_stake, current_profit=current_profit, min_stake=min_stake,
max_stake=min(max_stake, stake_available)) max_stake=min(max_stake, stake_available))
@ -574,7 +575,8 @@ class Backtesting:
if order_type == 'limit': if order_type == 'limit':
close_rate = strategy_safe_wrapper(self.strategy.custom_exit_price, close_rate = strategy_safe_wrapper(self.strategy.custom_exit_price,
default_retval=close_rate)( default_retval=close_rate)(
pair=trade.pair, trade=trade, pair=trade.pair,
trade=trade, # type: ignore[arg-type]
current_time=exit_candle_time, current_time=exit_candle_time,
proposed_rate=close_rate, current_profit=current_profit, proposed_rate=close_rate, current_profit=current_profit,
exit_tag=exit_reason) exit_tag=exit_reason)
@ -588,7 +590,10 @@ class Backtesting:
time_in_force = self.strategy.order_time_in_force['exit'] time_in_force = self.strategy.order_time_in_force['exit']
if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)(
pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount, pair=trade.pair,
trade=trade, # type: ignore[arg-type]
order_type='limit',
amount=trade.amount,
rate=close_rate, rate=close_rate,
time_in_force=time_in_force, time_in_force=time_in_force,
sell_reason=exit_reason, # deprecated sell_reason=exit_reason, # deprecated
@ -664,7 +669,7 @@ class Backtesting:
return self._get_exit_trade_entry_for_candle(trade, row) return self._get_exit_trade_entry_for_candle(trade, row)
def get_valid_price_and_stake( def get_valid_price_and_stake(
self, pair: str, row: Tuple, propose_rate: float, stake_amount: Optional[float], self, pair: str, row: Tuple, propose_rate: float, stake_amount: float,
direction: LongShort, current_time: datetime, entry_tag: Optional[str], direction: LongShort, current_time: datetime, entry_tag: Optional[str],
trade: Optional[LocalTrade], order_type: str trade: Optional[LocalTrade], order_type: str
) -> Tuple[float, float, float, float]: ) -> Tuple[float, float, float, float]:
@ -738,8 +743,9 @@ class Backtesting:
order_type = self.strategy.order_types['entry'] order_type = self.strategy.order_types['entry']
pos_adjust = trade is not None and requested_rate is None pos_adjust = trade is not None and requested_rate is None
stake_amount_ = stake_amount or (trade.stake_amount if trade else 0.0)
propose_rate, stake_amount, leverage, min_stake_amount = self.get_valid_price_and_stake( propose_rate, stake_amount, leverage, min_stake_amount = self.get_valid_price_and_stake(
pair, row, row[OPEN_IDX], stake_amount, direction, current_time, entry_tag, trade, pair, row, row[OPEN_IDX], stake_amount_, direction, current_time, entry_tag, trade,
order_type order_type
) )
@ -909,7 +915,9 @@ class Backtesting:
Check if current analyzed order has to be canceled. Check if current analyzed order has to be canceled.
Returns True if the trade should be Deleted (initial order was canceled). Returns True if the trade should be Deleted (initial order was canceled).
""" """
timedout = self.strategy.ft_check_timed_out(trade, order, current_time) timedout = self.strategy.ft_check_timed_out(
trade, # type: ignore[arg-type]
order, current_time)
if timedout: if timedout:
if order.side == trade.entry_side: if order.side == trade.entry_side:
self.timedout_entry_orders += 1 self.timedout_entry_orders += 1
@ -938,7 +946,8 @@ class Backtesting:
if order.side == trade.entry_side and current_time > order.order_date_utc: if order.side == trade.entry_side and current_time > order.order_date_utc:
requested_rate = strategy_safe_wrapper(self.strategy.adjust_entry_price, requested_rate = strategy_safe_wrapper(self.strategy.adjust_entry_price,
default_retval=order.price)( default_retval=order.price)(
trade=trade, order=order, pair=trade.pair, current_time=current_time, trade=trade, # type: ignore[arg-type]
order=order, pair=trade.pair, current_time=current_time,
proposed_rate=row[OPEN_IDX], current_order_rate=order.price, proposed_rate=row[OPEN_IDX], current_order_rate=order.price,
entry_tag=trade.enter_tag, side=trade.trade_direction entry_tag=trade.enter_tag, side=trade.trade_direction
) # default value is current order price ) # default value is current order price

View File

@ -16,7 +16,7 @@ from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, SignalDirecti
SignalType, TradingMode) SignalType, TradingMode)
from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exceptions import OperationalException, StrategyError
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date, timeframe_to_seconds from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date, timeframe_to_seconds
from freqtrade.persistence import LocalTrade, Order, PairLocks, Trade from freqtrade.persistence import Order, PairLocks, Trade
from freqtrade.strategy.hyper import HyperStrategyMixin from freqtrade.strategy.hyper import HyperStrategyMixin
from freqtrade.strategy.informative_decorator import (InformativeData, PopulateIndicators, from freqtrade.strategy.informative_decorator import (InformativeData, PopulateIndicators,
_create_and_merge_informative_pair, _create_and_merge_informative_pair,
@ -429,7 +429,7 @@ class IStrategy(ABC, HyperStrategyMixin):
return self.custom_sell(pair, trade, current_time, current_rate, current_profit, **kwargs) return self.custom_sell(pair, trade, current_time, current_rate, current_profit, **kwargs)
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float, proposed_stake: float, min_stake: Optional[float], max_stake: float,
entry_tag: Optional[str], side: str, **kwargs) -> float: entry_tag: Optional[str], side: str, **kwargs) -> float:
""" """
Customize stake size for each new trade. Customize stake size for each new trade.
@ -447,8 +447,9 @@ class IStrategy(ABC, HyperStrategyMixin):
return proposed_stake return proposed_stake
def adjust_trade_position(self, trade: Trade, current_time: datetime, def adjust_trade_position(self, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, min_stake: float, current_rate: float, current_profit: float,
max_stake: float, **kwargs) -> Optional[float]: min_stake: Optional[float], max_stake: float,
**kwargs) -> Optional[float]:
""" """
Custom trade adjustment logic, returning the stake amount that a trade should be increased. Custom trade adjustment logic, returning the stake amount that a trade should be increased.
This means extra buy orders with additional fees. This means extra buy orders with additional fees.
@ -917,19 +918,20 @@ class IStrategy(ABC, HyperStrategyMixin):
if exit_ and not enter: if exit_ and not enter:
exit_signal = ExitType.EXIT_SIGNAL exit_signal = ExitType.EXIT_SIGNAL
else: else:
custom_reason = strategy_safe_wrapper(self.custom_exit, default_retval=False)( reason_cust = strategy_safe_wrapper(self.custom_exit, default_retval=False)(
pair=trade.pair, trade=trade, current_time=current_time, pair=trade.pair, trade=trade, current_time=current_time,
current_rate=current_rate, current_profit=current_profit) current_rate=current_rate, current_profit=current_profit)
if custom_reason: if reason_cust:
exit_signal = ExitType.CUSTOM_EXIT exit_signal = ExitType.CUSTOM_EXIT
if isinstance(custom_reason, str): if isinstance(reason_cust, str):
if len(custom_reason) > CUSTOM_EXIT_MAX_LENGTH: custom_reason = reason_cust
if len(reason_cust) > CUSTOM_EXIT_MAX_LENGTH:
logger.warning(f'Custom exit reason returned from ' logger.warning(f'Custom exit reason returned from '
f'custom_exit is too long and was trimmed' f'custom_exit is too long and was trimmed'
f'to {CUSTOM_EXIT_MAX_LENGTH} characters.') f'to {CUSTOM_EXIT_MAX_LENGTH} characters.')
custom_reason = custom_reason[:CUSTOM_EXIT_MAX_LENGTH] custom_reason = reason_cust[:CUSTOM_EXIT_MAX_LENGTH]
else: else:
custom_reason = None custom_reason = ''
if ( if (
exit_signal == ExitType.CUSTOM_EXIT exit_signal == ExitType.CUSTOM_EXIT
or (exit_signal == ExitType.EXIT_SIGNAL or (exit_signal == ExitType.EXIT_SIGNAL
@ -1075,7 +1077,7 @@ class IStrategy(ABC, HyperStrategyMixin):
else: else:
return current_profit > roi return current_profit > roi
def ft_check_timed_out(self, trade: LocalTrade, order: Order, def ft_check_timed_out(self, trade: Trade, order: Order,
current_time: datetime) -> bool: current_time: datetime) -> bool:
""" """
FT Internal method. FT Internal method.

View File

@ -1,5 +1,7 @@
import logging import logging
from copy import deepcopy from copy import deepcopy
from functools import wraps
from typing import Any, Callable, TypeVar, cast
from freqtrade.exceptions import StrategyError from freqtrade.exceptions import StrategyError
@ -7,12 +9,16 @@ from freqtrade.exceptions import StrategyError
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def strategy_safe_wrapper(f, message: str = "", default_retval=None, supress_error=False): F = TypeVar('F', bound=Callable[..., Any])
def strategy_safe_wrapper(f: F, message: str = "", default_retval=None, supress_error=False) -> F:
""" """
Wrapper around user-provided methods and functions. Wrapper around user-provided methods and functions.
Caches all exceptions and returns either the default_retval (if it's not None) or raises Caches all exceptions and returns either the default_retval (if it's not None) or raises
a StrategyError exception, which then needs to be handled by the calling method. a StrategyError exception, which then needs to be handled by the calling method.
""" """
@wraps(f)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
try: try:
if 'trade' in kwargs: if 'trade' in kwargs:
@ -37,4 +43,4 @@ def strategy_safe_wrapper(f, message: str = "", default_retval=None, supress_err
raise StrategyError(str(error)) from error raise StrategyError(str(error)) from error
return default_retval return default_retval
return wrapper return cast(F, wrapper)

View File

@ -6,7 +6,7 @@ import numpy as np # noqa
import pandas as pd # noqa import pandas as pd # noqa
from pandas import DataFrame # noqa from pandas import DataFrame # noqa
from datetime import datetime # noqa from datetime import datetime # noqa
from typing import Optional # noqa from typing import Optional, Union # noqa
from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter,
IStrategy, IntParameter) IStrategy, IntParameter)

View File

@ -13,7 +13,7 @@ def bot_loop_start(self, **kwargs) -> None:
pass pass
def custom_entry_price(self, pair: str, current_time: 'datetime', proposed_rate: float, def custom_entry_price(self, pair: str, current_time: 'datetime', proposed_rate: float,
entry_tag: Optional[str], **kwargs) -> float: entry_tag: 'Optional[str]', side: str, **kwargs) -> float:
""" """
Custom entry price logic, returning the new entry price. Custom entry price logic, returning the new entry price.
@ -80,8 +80,8 @@ def custom_exit_price(self, pair: str, trade: 'Trade',
return proposed_rate return proposed_rate
def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: float, def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float, proposed_stake: float, min_stake: Optional[float], max_stake: float,
side: str, entry_tag: Optional[str], **kwargs) -> float: entry_tag: 'Optional[str]', side: str, **kwargs) -> float:
""" """
Customize stake size for each new trade. Customize stake size for each new trade.
@ -244,8 +244,8 @@ def check_exit_timeout(self, pair: str, trade: 'Trade', order: 'Order',
return False return False
def adjust_trade_position(self, trade: 'Trade', current_time: 'datetime', def adjust_trade_position(self, trade: 'Trade', current_time: 'datetime',
current_rate: float, current_profit: float, min_stake: float, current_rate: float, current_profit: float, min_stake: Optional[float],
max_stake: float, **kwargs) -> Optional[float]: max_stake: float, **kwargs) -> 'Optional[float]':
""" """
Custom trade adjustment logic, returning the stake amount that a trade should be increased. Custom trade adjustment logic, returning the stake amount that a trade should be increased.
This means extra buy orders with additional fees. This means extra buy orders with additional fees.

View File

@ -28,6 +28,17 @@ skip_glob = ["**/.env*", "**/env/*", "**/.venv/*", "**/docs/*", "**/user_data/*"
[tool.pytest.ini_options] [tool.pytest.ini_options]
asyncio_mode = "auto" asyncio_mode = "auto"
[tool.mypy]
ignore_missing_imports = true
warn_unused_ignores = true
exclude = [
'^build_helpers\.py$'
]
[[tool.mypy.overrides]]
module = "tests.*"
ignore_errors = true
[build-system] [build-system]
requires = ["setuptools >= 46.4.0", "wheel"] requires = ["setuptools >= 46.4.0", "wheel"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"

View File

@ -50,13 +50,3 @@ exclude =
.eggs, .eggs,
user_data, user_data,
[mypy]
ignore_missing_imports = True
warn_unused_ignores = True
exclude = (?x)(
^build_helpers\.py$
)
[mypy-tests.*]
ignore_errors = True

View File

@ -1,6 +1,7 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
from datetime import datetime from datetime import datetime
from typing import Optional
import talib.abstract as ta import talib.abstract as ta
from pandas import DataFrame from pandas import DataFrame
@ -151,7 +152,8 @@ class StrategyTestV2(IStrategy):
return dataframe return dataframe
def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float,
current_profit: float, min_stake: float, max_stake: float, **kwargs): current_profit: float,
min_stake: Optional[float], max_stake: float, **kwargs):
if current_profit < -0.0075: if current_profit < -0.0075:
orders = trade.select_filled_orders('buy') orders = trade.select_filled_orders('buy')

View File

@ -1,6 +1,7 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
from datetime import datetime from datetime import datetime
from typing import Optional
import talib.abstract as ta import talib.abstract as ta
from pandas import DataFrame from pandas import DataFrame
@ -185,7 +186,8 @@ class StrategyTestV3(IStrategy):
return 3.0 return 3.0
def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float,
current_profit: float, min_stake: float, max_stake: float, **kwargs): current_profit: float,
min_stake: Optional[float], max_stake: float, **kwargs):
if current_profit < -0.0075: if current_profit < -0.0075:
orders = trade.select_filled_orders(trade.entry_side) orders = trade.select_filled_orders(trade.entry_side)