Merge branch 'develop' into data_handler
This commit is contained in:
commit
2409261cb7
@ -79,12 +79,14 @@ Please also read about the [strategy startup period](strategy-customization.md#s
|
|||||||
|
|
||||||
Sometimes your account has certain fee rebates (fee reductions starting with a certain account size or monthly volume), which are not visible to ccxt.
|
Sometimes your account has certain fee rebates (fee reductions starting with a certain account size or monthly volume), which are not visible to ccxt.
|
||||||
To account for this in backtesting, you can use `--fee 0.001` to supply this value to backtesting.
|
To account for this in backtesting, you can use `--fee 0.001` to supply this value to backtesting.
|
||||||
This fee must be a percentage, and will be applied twice (once for trade entry, and once for trade exit).
|
This fee must be a ratio, and will be applied twice (once for trade entry, and once for trade exit).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
freqtrade backtesting --fee 0.001
|
freqtrade backtesting --fee 0.001
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
Only supply this option (or the corresponding configuration parameter) if you want to experiment with different fee values. By default, Backtesting fetches the default fee from the exchange pair/market info.
|
||||||
|
|
||||||
#### Running backtest with smaller testset by using timerange
|
#### Running backtest with smaller testset by using timerange
|
||||||
|
|
||||||
|
@ -270,3 +270,18 @@ The easiest way is to download install Microsoft Visual Studio Community [here](
|
|||||||
|
|
||||||
Now you have an environment ready, the next step is
|
Now you have an environment ready, the next step is
|
||||||
[Bot Configuration](configuration.md).
|
[Bot Configuration](configuration.md).
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### MacOS installation error
|
||||||
|
|
||||||
|
Newer versions of MacOS may have installation failed with errors like `error: command 'g++' failed with exit status 1`.
|
||||||
|
|
||||||
|
This error will require explicit installation of the SDK Headers, which are not installed by default in this version of MacOS.
|
||||||
|
For MacOS 10.14, this can be accomplished with the below command.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg
|
||||||
|
```
|
||||||
|
|
||||||
|
If this file is inexistant, then you're probably on a different version of MacOS, so you may need to consult the internet for specific resolution details.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from copy import deepcopy
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from jsonschema import Draft4Validator, validators
|
from jsonschema import Draft4Validator, validators
|
||||||
@ -42,15 +43,25 @@ def validate_config_schema(conf: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
:param conf: Config in JSON format
|
:param conf: Config in JSON format
|
||||||
:return: Returns the config if valid, otherwise throw an exception
|
:return: Returns the config if valid, otherwise throw an exception
|
||||||
"""
|
"""
|
||||||
|
conf_schema = deepcopy(constants.CONF_SCHEMA)
|
||||||
|
if conf.get('runmode', RunMode.OTHER) in (RunMode.DRY_RUN, RunMode.LIVE):
|
||||||
|
conf_schema['required'] = constants.SCHEMA_TRADE_REQUIRED
|
||||||
|
else:
|
||||||
|
conf_schema['required'] = constants.SCHEMA_MINIMAL_REQUIRED
|
||||||
|
# Dynamically allow empty stake-currency
|
||||||
|
# Since the minimal config specifies this too.
|
||||||
|
# It's not allowed for Dry-run or live modes
|
||||||
|
conf_schema['properties']['stake_currency']['enum'] += [''] # type: ignore
|
||||||
|
|
||||||
try:
|
try:
|
||||||
FreqtradeValidator(constants.CONF_SCHEMA).validate(conf)
|
FreqtradeValidator(conf_schema).validate(conf)
|
||||||
return conf
|
return conf
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
logger.critical(
|
logger.critical(
|
||||||
f"Invalid configuration. See config.json.example. Reason: {e}"
|
f"Invalid configuration. See config.json.example. Reason: {e}"
|
||||||
)
|
)
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
best_match(Draft4Validator(constants.CONF_SCHEMA).iter_errors(conf)).message
|
best_match(Draft4Validator(conf_schema).iter_errors(conf)).message
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -282,7 +282,9 @@ CONF_SCHEMA = {
|
|||||||
'required': ['process_throttle_secs', 'allowed_risk', 'capital_available_percentage']
|
'required': ['process_throttle_secs', 'allowed_risk', 'capital_available_percentage']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'required': [
|
}
|
||||||
|
|
||||||
|
SCHEMA_TRADE_REQUIRED = [
|
||||||
'exchange',
|
'exchange',
|
||||||
'max_open_trades',
|
'max_open_trades',
|
||||||
'stake_currency',
|
'stake_currency',
|
||||||
@ -297,4 +299,10 @@ CONF_SCHEMA = {
|
|||||||
'dataformat_ohlcv',
|
'dataformat_ohlcv',
|
||||||
'dataformat_trades',
|
'dataformat_trades',
|
||||||
]
|
]
|
||||||
}
|
|
||||||
|
SCHEMA_MINIMAL_REQUIRED = [
|
||||||
|
'exchange',
|
||||||
|
'dry_run',
|
||||||
|
'dataformat_ohlcv',
|
||||||
|
'dataformat_trades',
|
||||||
|
]
|
||||||
|
@ -47,7 +47,7 @@ def load_backtest_data(filename) -> pd.DataFrame:
|
|||||||
utc=True,
|
utc=True,
|
||||||
infer_datetime_format=True
|
infer_datetime_format=True
|
||||||
)
|
)
|
||||||
df['profitabs'] = df['close_rate'] - df['open_rate']
|
df['profit'] = df['close_rate'] - df['open_rate']
|
||||||
df = df.sort_values("open_time").reset_index(drop=True)
|
df = df.sort_values("open_time").reset_index(drop=True)
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ from freqtrade.state import State
|
|||||||
from freqtrade.strategy.interface import IStrategy, SellType
|
from freqtrade.strategy.interface import IStrategy, SellType
|
||||||
from freqtrade.wallets import Wallets
|
from freqtrade.wallets import Wallets
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -132,12 +133,12 @@ class FreqtradeBot:
|
|||||||
self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist),
|
self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist),
|
||||||
self.strategy.informative_pairs())
|
self.strategy.informative_pairs())
|
||||||
|
|
||||||
# First process current opened trades
|
# First process current opened trades (positions)
|
||||||
self.process_maybe_execute_sells(trades)
|
self.exit_positions(trades)
|
||||||
|
|
||||||
# Then looking for buy opportunities
|
# Then looking for buy opportunities
|
||||||
if self.get_free_open_trades():
|
if self.get_free_open_trades():
|
||||||
self.process_maybe_execute_buys()
|
self.enter_positions()
|
||||||
|
|
||||||
# Check and handle any timed out open orders
|
# Check and handle any timed out open orders
|
||||||
self.check_handle_timedout()
|
self.check_handle_timedout()
|
||||||
@ -181,6 +182,43 @@ class FreqtradeBot:
|
|||||||
open_trades = len(Trade.get_open_trades())
|
open_trades = len(Trade.get_open_trades())
|
||||||
return max(0, self.config['max_open_trades'] - open_trades)
|
return max(0, self.config['max_open_trades'] - open_trades)
|
||||||
|
|
||||||
|
#
|
||||||
|
# BUY / enter positions / open trades logic and methods
|
||||||
|
#
|
||||||
|
|
||||||
|
def enter_positions(self) -> int:
|
||||||
|
"""
|
||||||
|
Tries to execute buy orders for new trades (positions)
|
||||||
|
"""
|
||||||
|
trades_created = 0
|
||||||
|
|
||||||
|
whitelist = copy.deepcopy(self.active_pair_whitelist)
|
||||||
|
if not whitelist:
|
||||||
|
logger.info("Active pair whitelist is empty.")
|
||||||
|
else:
|
||||||
|
# Remove pairs for currently opened trades from the whitelist
|
||||||
|
for trade in Trade.get_open_trades():
|
||||||
|
if trade.pair in whitelist:
|
||||||
|
whitelist.remove(trade.pair)
|
||||||
|
logger.debug('Ignoring %s in pair whitelist', trade.pair)
|
||||||
|
|
||||||
|
if not whitelist:
|
||||||
|
logger.info("No currency pair in active pair whitelist, "
|
||||||
|
"but checking to sell open trades.")
|
||||||
|
else:
|
||||||
|
# Create entity and execute trade for each pair from whitelist
|
||||||
|
for pair in whitelist:
|
||||||
|
try:
|
||||||
|
trades_created += self.create_trade(pair)
|
||||||
|
except DependencyException as exception:
|
||||||
|
logger.warning('Unable to create trade for %s: %s', pair, exception)
|
||||||
|
|
||||||
|
if not trades_created:
|
||||||
|
logger.debug("Found no buy signals for whitelisted currencies. "
|
||||||
|
"Trying again...")
|
||||||
|
|
||||||
|
return trades_created
|
||||||
|
|
||||||
def get_target_bid(self, pair: str, tick: Dict = None) -> float:
|
def get_target_bid(self, pair: str, tick: Dict = None) -> float:
|
||||||
"""
|
"""
|
||||||
Calculates bid target between current ask price and last price
|
Calculates bid target between current ask price and last price
|
||||||
@ -294,65 +332,50 @@ class FreqtradeBot:
|
|||||||
# See also #2575 at github.
|
# See also #2575 at github.
|
||||||
return max(min_stake_amounts) / amount_reserve_percent
|
return max(min_stake_amounts) / amount_reserve_percent
|
||||||
|
|
||||||
def create_trades(self) -> bool:
|
def create_trade(self, pair: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks the implemented trading strategy for buy-signals, using the active pair whitelist.
|
Check the implemented trading strategy for buy signals.
|
||||||
If a pair triggers the buy_signal a new trade record gets created.
|
|
||||||
Checks pairs as long as the open trade count is below `max_open_trades`.
|
If the pair triggers the buy signal a new trade record gets created
|
||||||
:return: True if at least one trade has been created.
|
and the buy-order opening the trade gets issued towards the exchange.
|
||||||
|
|
||||||
|
:return: True if a trade has been created.
|
||||||
"""
|
"""
|
||||||
whitelist = copy.deepcopy(self.active_pair_whitelist)
|
logger.debug(f"create_trade for pair {pair}")
|
||||||
|
|
||||||
if not whitelist:
|
|
||||||
logger.info("Active pair whitelist is empty.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Remove currently opened and latest pairs from whitelist
|
|
||||||
for trade in Trade.get_open_trades():
|
|
||||||
if trade.pair in whitelist:
|
|
||||||
whitelist.remove(trade.pair)
|
|
||||||
logger.debug('Ignoring %s in pair whitelist', trade.pair)
|
|
||||||
|
|
||||||
if not whitelist:
|
|
||||||
logger.info("No currency pair in active pair whitelist, "
|
|
||||||
"but checking to sell open trades.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
buycount = 0
|
|
||||||
# running get_signal on historical data fetched
|
|
||||||
for pair in whitelist:
|
|
||||||
if self.strategy.is_pair_locked(pair):
|
if self.strategy.is_pair_locked(pair):
|
||||||
logger.info(f"Pair {pair} is currently locked.")
|
logger.info(f"Pair {pair} is currently locked.")
|
||||||
continue
|
return False
|
||||||
|
|
||||||
|
# running get_signal on historical data fetched
|
||||||
(buy, sell) = self.strategy.get_signal(
|
(buy, sell) = self.strategy.get_signal(
|
||||||
pair, self.strategy.ticker_interval,
|
pair, self.strategy.ticker_interval,
|
||||||
self.dataprovider.ohlcv(pair, self.strategy.ticker_interval))
|
self.dataprovider.ohlcv(pair, self.strategy.ticker_interval))
|
||||||
|
|
||||||
if buy and not sell:
|
if buy and not sell:
|
||||||
if not self.get_free_open_trades():
|
if not self.get_free_open_trades():
|
||||||
logger.debug("Can't open a new trade: max number of trades is reached")
|
logger.debug("Can't open a new trade: max number of trades is reached.")
|
||||||
continue
|
return False
|
||||||
|
|
||||||
stake_amount = self.get_trade_stake_amount(pair)
|
stake_amount = self.get_trade_stake_amount(pair)
|
||||||
if not stake_amount:
|
if not stake_amount:
|
||||||
logger.debug("Stake amount is 0, ignoring possible trade for {pair}.")
|
logger.debug("Stake amount is 0, ignoring possible trade for {pair}.")
|
||||||
continue
|
return False
|
||||||
|
|
||||||
logger.info(f"Buy signal found: about create a new trade with stake_amount: "
|
logger.info(f"Buy signal found: about create a new trade with stake_amount: "
|
||||||
f"{stake_amount} ...")
|
f"{stake_amount} ...")
|
||||||
|
|
||||||
bidstrat_check_depth_of_market = self.config.get('bid_strategy', {}).\
|
bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {})
|
||||||
get('check_depth_of_market', {})
|
if ((bid_check_dom.get('enabled', False)) and
|
||||||
if (bidstrat_check_depth_of_market.get('enabled', False)) and\
|
(bid_check_dom.get('bids_to_ask_delta', 0) > 0)):
|
||||||
(bidstrat_check_depth_of_market.get('bids_to_ask_delta', 0) > 0):
|
if self._check_depth_of_market_buy(pair, bid_check_dom):
|
||||||
if self._check_depth_of_market_buy(pair, bidstrat_check_depth_of_market):
|
return self.execute_buy(pair, stake_amount)
|
||||||
buycount += self.execute_buy(pair, stake_amount)
|
else:
|
||||||
continue
|
return False
|
||||||
|
|
||||||
buycount += self.execute_buy(pair, stake_amount)
|
return self.execute_buy(pair, stake_amount)
|
||||||
|
else:
|
||||||
return buycount > 0
|
return False
|
||||||
|
|
||||||
def _check_depth_of_market_buy(self, pair: str, conf: Dict) -> bool:
|
def _check_depth_of_market_buy(self, pair: str, conf: Dict) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -377,14 +400,12 @@ class FreqtradeBot:
|
|||||||
:param pair: pair for which we want to create a LIMIT_BUY
|
:param pair: pair for which we want to create a LIMIT_BUY
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
stake_currency = self.config['stake_currency']
|
|
||||||
fiat_currency = self.config.get('fiat_display_currency', None)
|
|
||||||
time_in_force = self.strategy.order_time_in_force['buy']
|
time_in_force = self.strategy.order_time_in_force['buy']
|
||||||
|
|
||||||
if price:
|
if price:
|
||||||
buy_limit_requested = price
|
buy_limit_requested = price
|
||||||
else:
|
else:
|
||||||
# Calculate amount
|
# Calculate price
|
||||||
buy_limit_requested = self.get_target_bid(pair)
|
buy_limit_requested = self.get_target_bid(pair)
|
||||||
|
|
||||||
min_stake_amount = self._get_min_pair_stake_amount(pair, buy_limit_requested)
|
min_stake_amount = self._get_min_pair_stake_amount(pair, buy_limit_requested)
|
||||||
@ -435,17 +456,6 @@ class FreqtradeBot:
|
|||||||
amount = order['amount']
|
amount = order['amount']
|
||||||
buy_limit_filled_price = order['price']
|
buy_limit_filled_price = order['price']
|
||||||
|
|
||||||
self.rpc.send_msg({
|
|
||||||
'type': RPCMessageType.BUY_NOTIFICATION,
|
|
||||||
'exchange': self.exchange.name.capitalize(),
|
|
||||||
'pair': pair,
|
|
||||||
'limit': buy_limit_filled_price,
|
|
||||||
'order_type': order_type,
|
|
||||||
'stake_amount': stake_amount,
|
|
||||||
'stake_currency': stake_currency,
|
|
||||||
'fiat_currency': fiat_currency
|
|
||||||
})
|
|
||||||
|
|
||||||
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
|
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
|
||||||
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
|
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
@ -463,6 +473,8 @@ class FreqtradeBot:
|
|||||||
ticker_interval=timeframe_to_minutes(self.config['ticker_interval'])
|
ticker_interval=timeframe_to_minutes(self.config['ticker_interval'])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self._notify_buy(trade, order_type)
|
||||||
|
|
||||||
# Update fees if order is closed
|
# Update fees if order is closed
|
||||||
if order_status == 'closed':
|
if order_status == 'closed':
|
||||||
self.update_trade_state(trade, order)
|
self.update_trade_state(trade, order)
|
||||||
@ -475,121 +487,53 @@ class FreqtradeBot:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def process_maybe_execute_buys(self) -> None:
|
def _notify_buy(self, trade: Trade, order_type: str):
|
||||||
"""
|
"""
|
||||||
Tries to execute buy orders for trades in a safe way
|
Sends rpc notification when a buy occured.
|
||||||
"""
|
"""
|
||||||
try:
|
msg = {
|
||||||
# Create entity and execute trade
|
'type': RPCMessageType.BUY_NOTIFICATION,
|
||||||
if not self.create_trades():
|
'exchange': self.exchange.name.capitalize(),
|
||||||
logger.debug('Found no buy signals for whitelisted currencies. Trying again...')
|
'pair': trade.pair,
|
||||||
except DependencyException as exception:
|
'limit': trade.open_rate,
|
||||||
logger.warning('Unable to create trade: %s', exception)
|
'order_type': order_type,
|
||||||
|
'stake_amount': trade.stake_amount,
|
||||||
|
'stake_currency': self.config['stake_currency'],
|
||||||
|
'fiat_currency': self.config.get('fiat_display_currency', None),
|
||||||
|
}
|
||||||
|
|
||||||
def process_maybe_execute_sells(self, trades: List[Any]) -> None:
|
# Send the message
|
||||||
|
self.rpc.send_msg(msg)
|
||||||
|
|
||||||
|
#
|
||||||
|
# SELL / exit positions / close trades logic and methods
|
||||||
|
#
|
||||||
|
|
||||||
|
def exit_positions(self, trades: List[Any]) -> int:
|
||||||
"""
|
"""
|
||||||
Tries to execute sell orders for trades in a safe way
|
Tries to execute sell orders for open trades (positions)
|
||||||
"""
|
"""
|
||||||
result = False
|
trades_closed = 0
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
try:
|
try:
|
||||||
self.update_trade_state(trade)
|
self.update_trade_state(trade)
|
||||||
|
|
||||||
if (self.strategy.order_types.get('stoploss_on_exchange') and
|
if (self.strategy.order_types.get('stoploss_on_exchange') and
|
||||||
self.handle_stoploss_on_exchange(trade)):
|
self.handle_stoploss_on_exchange(trade)):
|
||||||
result = True
|
trades_closed += 1
|
||||||
continue
|
continue
|
||||||
# Check if we can sell our current pair
|
# Check if we can sell our current pair
|
||||||
if trade.open_order_id is None and self.handle_trade(trade):
|
if trade.open_order_id is None and self.handle_trade(trade):
|
||||||
result = True
|
trades_closed += 1
|
||||||
|
|
||||||
except DependencyException as exception:
|
except DependencyException as exception:
|
||||||
logger.warning('Unable to sell trade: %s', exception)
|
logger.warning('Unable to sell trade: %s', exception)
|
||||||
|
|
||||||
# Updating wallets if any trade occured
|
# Updating wallets if any trade occured
|
||||||
if result:
|
if trades_closed:
|
||||||
self.wallets.update()
|
self.wallets.update()
|
||||||
|
|
||||||
def get_real_amount(self, trade: Trade, order: Dict, order_amount: float = None) -> float:
|
return trades_closed
|
||||||
"""
|
|
||||||
Get real amount for the trade
|
|
||||||
Necessary for exchanges which charge fees in base currency (e.g. binance)
|
|
||||||
"""
|
|
||||||
if order_amount is None:
|
|
||||||
order_amount = order['amount']
|
|
||||||
# Only run for closed orders
|
|
||||||
if trade.fee_open == 0 or order['status'] == 'open':
|
|
||||||
return order_amount
|
|
||||||
|
|
||||||
# use fee from order-dict if possible
|
|
||||||
if ('fee' in order and order['fee'] is not None and
|
|
||||||
(order['fee'].keys() >= {'currency', 'cost'})):
|
|
||||||
if (order['fee']['currency'] is not None and
|
|
||||||
order['fee']['cost'] is not None and
|
|
||||||
trade.pair.startswith(order['fee']['currency'])):
|
|
||||||
new_amount = order_amount - order['fee']['cost']
|
|
||||||
logger.info("Applying fee on amount for %s (from %s to %s) from Order",
|
|
||||||
trade, order['amount'], new_amount)
|
|
||||||
return new_amount
|
|
||||||
|
|
||||||
# Fallback to Trades
|
|
||||||
trades = self.exchange.get_trades_for_order(trade.open_order_id, trade.pair,
|
|
||||||
trade.open_date)
|
|
||||||
|
|
||||||
if len(trades) == 0:
|
|
||||||
logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade)
|
|
||||||
return order_amount
|
|
||||||
amount = 0
|
|
||||||
fee_abs = 0
|
|
||||||
for exectrade in trades:
|
|
||||||
amount += exectrade['amount']
|
|
||||||
if ("fee" in exectrade and exectrade['fee'] is not None and
|
|
||||||
(exectrade['fee'].keys() >= {'currency', 'cost'})):
|
|
||||||
# only applies if fee is in quote currency!
|
|
||||||
if (exectrade['fee']['currency'] is not None and
|
|
||||||
exectrade['fee']['cost'] is not None and
|
|
||||||
trade.pair.startswith(exectrade['fee']['currency'])):
|
|
||||||
fee_abs += exectrade['fee']['cost']
|
|
||||||
|
|
||||||
if not isclose(amount, order_amount, abs_tol=constants.MATH_CLOSE_PREC):
|
|
||||||
logger.warning(f"Amount {amount} does not match amount {trade.amount}")
|
|
||||||
raise DependencyException("Half bought? Amounts don't match")
|
|
||||||
real_amount = amount - fee_abs
|
|
||||||
if fee_abs != 0:
|
|
||||||
logger.info(f"Applying fee on amount for {trade} "
|
|
||||||
f"(from {order_amount} to {real_amount}) from Trades")
|
|
||||||
return real_amount
|
|
||||||
|
|
||||||
def update_trade_state(self, trade, action_order: dict = None):
|
|
||||||
"""
|
|
||||||
Checks trades with open orders and updates the amount if necessary
|
|
||||||
"""
|
|
||||||
# Get order details for actual price per unit
|
|
||||||
if trade.open_order_id:
|
|
||||||
# Update trade with order values
|
|
||||||
logger.info('Found open order for %s', trade)
|
|
||||||
try:
|
|
||||||
order = action_order or self.exchange.get_order(trade.open_order_id, trade.pair)
|
|
||||||
except InvalidOrderException as exception:
|
|
||||||
logger.warning('Unable to fetch order %s: %s', trade.open_order_id, exception)
|
|
||||||
return
|
|
||||||
# Try update amount (binance-fix)
|
|
||||||
try:
|
|
||||||
new_amount = self.get_real_amount(trade, order)
|
|
||||||
if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC):
|
|
||||||
order['amount'] = new_amount
|
|
||||||
# Fee was applied, so set to 0
|
|
||||||
trade.fee_open = 0
|
|
||||||
trade.recalc_open_trade_price()
|
|
||||||
|
|
||||||
except DependencyException as exception:
|
|
||||||
logger.warning("Could not update trade amount: %s", exception)
|
|
||||||
|
|
||||||
trade.update(order)
|
|
||||||
|
|
||||||
# Updating wallets when order is closed
|
|
||||||
if not trade.is_open:
|
|
||||||
self.wallets.update()
|
|
||||||
|
|
||||||
def get_sell_rate(self, pair: str, refresh: bool) -> float:
|
def get_sell_rate(self, pair: str, refresh: bool) -> float:
|
||||||
"""
|
"""
|
||||||
@ -963,16 +907,16 @@ class FreqtradeBot:
|
|||||||
except InvalidOrderException:
|
except InvalidOrderException:
|
||||||
logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}")
|
logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}")
|
||||||
|
|
||||||
ordertype = self.strategy.order_types[sell_type]
|
order_type = self.strategy.order_types[sell_type]
|
||||||
if sell_reason == SellType.EMERGENCY_SELL:
|
if sell_reason == SellType.EMERGENCY_SELL:
|
||||||
# Emergencysells (default to market!)
|
# Emergencysells (default to market!)
|
||||||
ordertype = self.strategy.order_types.get("emergencysell", "market")
|
order_type = self.strategy.order_types.get("emergencysell", "market")
|
||||||
|
|
||||||
amount = self._safe_sell_amount(trade.pair, trade.amount)
|
amount = self._safe_sell_amount(trade.pair, trade.amount)
|
||||||
|
|
||||||
# Execute sell and update trade record
|
# Execute sell and update trade record
|
||||||
order = self.exchange.sell(pair=str(trade.pair),
|
order = self.exchange.sell(pair=str(trade.pair),
|
||||||
ordertype=ordertype,
|
ordertype=order_type,
|
||||||
amount=amount, rate=limit,
|
amount=amount, rate=limit,
|
||||||
time_in_force=self.strategy.order_time_in_force['sell']
|
time_in_force=self.strategy.order_time_in_force['sell']
|
||||||
)
|
)
|
||||||
@ -988,7 +932,7 @@ class FreqtradeBot:
|
|||||||
# Lock pair for one candle to prevent immediate rebuys
|
# Lock pair for one candle to prevent immediate rebuys
|
||||||
self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['ticker_interval']))
|
self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['ticker_interval']))
|
||||||
|
|
||||||
self._notify_sell(trade, ordertype)
|
self._notify_sell(trade, order_type)
|
||||||
|
|
||||||
def _notify_sell(self, trade: Trade, order_type: str):
|
def _notify_sell(self, trade: Trade, order_type: str):
|
||||||
"""
|
"""
|
||||||
@ -1015,17 +959,99 @@ class FreqtradeBot:
|
|||||||
'profit_percent': profit_percent,
|
'profit_percent': profit_percent,
|
||||||
'sell_reason': trade.sell_reason,
|
'sell_reason': trade.sell_reason,
|
||||||
'open_date': trade.open_date,
|
'open_date': trade.open_date,
|
||||||
'close_date': trade.close_date or datetime.utcnow()
|
'close_date': trade.close_date or datetime.utcnow(),
|
||||||
|
'stake_currency': self.config['stake_currency'],
|
||||||
}
|
}
|
||||||
|
|
||||||
# For regular case, when the configuration exists
|
if 'fiat_display_currency' in self.config:
|
||||||
if 'stake_currency' in self.config and 'fiat_display_currency' in self.config:
|
|
||||||
stake_currency = self.config['stake_currency']
|
|
||||||
fiat_currency = self.config['fiat_display_currency']
|
|
||||||
msg.update({
|
msg.update({
|
||||||
'stake_currency': stake_currency,
|
'fiat_currency': self.config['fiat_display_currency'],
|
||||||
'fiat_currency': fiat_currency,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
# Send the message
|
# Send the message
|
||||||
self.rpc.send_msg(msg)
|
self.rpc.send_msg(msg)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Common update trade state methods
|
||||||
|
#
|
||||||
|
|
||||||
|
def update_trade_state(self, trade, action_order: dict = None):
|
||||||
|
"""
|
||||||
|
Checks trades with open orders and updates the amount if necessary
|
||||||
|
"""
|
||||||
|
# Get order details for actual price per unit
|
||||||
|
if trade.open_order_id:
|
||||||
|
# Update trade with order values
|
||||||
|
logger.info('Found open order for %s', trade)
|
||||||
|
try:
|
||||||
|
order = action_order or self.exchange.get_order(trade.open_order_id, trade.pair)
|
||||||
|
except InvalidOrderException as exception:
|
||||||
|
logger.warning('Unable to fetch order %s: %s', trade.open_order_id, exception)
|
||||||
|
return
|
||||||
|
# Try update amount (binance-fix)
|
||||||
|
try:
|
||||||
|
new_amount = self.get_real_amount(trade, order)
|
||||||
|
if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC):
|
||||||
|
order['amount'] = new_amount
|
||||||
|
# Fee was applied, so set to 0
|
||||||
|
trade.fee_open = 0
|
||||||
|
trade.recalc_open_trade_price()
|
||||||
|
|
||||||
|
except DependencyException as exception:
|
||||||
|
logger.warning("Could not update trade amount: %s", exception)
|
||||||
|
|
||||||
|
trade.update(order)
|
||||||
|
|
||||||
|
# Updating wallets when order is closed
|
||||||
|
if not trade.is_open:
|
||||||
|
self.wallets.update()
|
||||||
|
|
||||||
|
def get_real_amount(self, trade: Trade, order: Dict, order_amount: float = None) -> float:
|
||||||
|
"""
|
||||||
|
Get real amount for the trade
|
||||||
|
Necessary for exchanges which charge fees in base currency (e.g. binance)
|
||||||
|
"""
|
||||||
|
if order_amount is None:
|
||||||
|
order_amount = order['amount']
|
||||||
|
# Only run for closed orders
|
||||||
|
if trade.fee_open == 0 or order['status'] == 'open':
|
||||||
|
return order_amount
|
||||||
|
|
||||||
|
# use fee from order-dict if possible
|
||||||
|
if ('fee' in order and order['fee'] is not None and
|
||||||
|
(order['fee'].keys() >= {'currency', 'cost'})):
|
||||||
|
if (order['fee']['currency'] is not None and
|
||||||
|
order['fee']['cost'] is not None and
|
||||||
|
trade.pair.startswith(order['fee']['currency'])):
|
||||||
|
new_amount = order_amount - order['fee']['cost']
|
||||||
|
logger.info("Applying fee on amount for %s (from %s to %s) from Order",
|
||||||
|
trade, order['amount'], new_amount)
|
||||||
|
return new_amount
|
||||||
|
|
||||||
|
# Fallback to Trades
|
||||||
|
trades = self.exchange.get_trades_for_order(trade.open_order_id, trade.pair,
|
||||||
|
trade.open_date)
|
||||||
|
|
||||||
|
if len(trades) == 0:
|
||||||
|
logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade)
|
||||||
|
return order_amount
|
||||||
|
amount = 0
|
||||||
|
fee_abs = 0
|
||||||
|
for exectrade in trades:
|
||||||
|
amount += exectrade['amount']
|
||||||
|
if ("fee" in exectrade and exectrade['fee'] is not None and
|
||||||
|
(exectrade['fee'].keys() >= {'currency', 'cost'})):
|
||||||
|
# only applies if fee is in quote currency!
|
||||||
|
if (exectrade['fee']['currency'] is not None and
|
||||||
|
exectrade['fee']['cost'] is not None and
|
||||||
|
trade.pair.startswith(exectrade['fee']['currency'])):
|
||||||
|
fee_abs += exectrade['fee']['cost']
|
||||||
|
|
||||||
|
if not isclose(amount, order_amount, abs_tol=constants.MATH_CLOSE_PREC):
|
||||||
|
logger.warning(f"Amount {amount} does not match amount {trade.amount}")
|
||||||
|
raise DependencyException("Half bought? Amounts don't match")
|
||||||
|
real_amount = amount - fee_abs
|
||||||
|
if fee_abs != 0:
|
||||||
|
logger.info(f"Applying fee on amount for {trade} "
|
||||||
|
f"(from {order_amount} to {real_amount}) from Trades")
|
||||||
|
return real_amount
|
||||||
|
@ -122,8 +122,8 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
# Create description for sell summarizing the trade
|
# Create description for sell summarizing the trade
|
||||||
desc = trades.apply(lambda row: f"{round(row['profitperc'], 3)}%, {row['sell_reason']}, "
|
desc = trades.apply(lambda row: f"{round(row['profitperc'] * 100, 1)}%, "
|
||||||
f"{row['duration']} min",
|
f"{row['sell_reason']}, {row['duration']} min",
|
||||||
axis=1)
|
axis=1)
|
||||||
trade_sells = go.Scatter(
|
trade_sells = go.Scatter(
|
||||||
x=trades["close_time"],
|
x=trades["close_time"],
|
||||||
|
@ -12,7 +12,8 @@ from colorama import init as colorama_init
|
|||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
from freqtrade.configuration import (Configuration, TimeRange,
|
from freqtrade.configuration import (Configuration, TimeRange,
|
||||||
remove_credentials)
|
remove_credentials,
|
||||||
|
validate_config_consistency)
|
||||||
from freqtrade.configuration.directory_operations import (copy_sample_files,
|
from freqtrade.configuration.directory_operations import (copy_sample_files,
|
||||||
create_userdata_dir)
|
create_userdata_dir)
|
||||||
from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGY
|
from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGY
|
||||||
@ -42,6 +43,7 @@ def setup_utils_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str
|
|||||||
|
|
||||||
# Ensure we do not use Exchange credentials
|
# Ensure we do not use Exchange credentials
|
||||||
remove_credentials(config)
|
remove_credentials(config)
|
||||||
|
validate_config_consistency(config)
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
2
setup.sh
2
setup.sh
@ -263,7 +263,7 @@ function install() {
|
|||||||
echo "-------------------------"
|
echo "-------------------------"
|
||||||
echo "Run the bot !"
|
echo "Run the bot !"
|
||||||
echo "-------------------------"
|
echo "-------------------------"
|
||||||
echo "You can now use the bot by executing 'source .env/bin/activate; freqtrade'."
|
echo "You can now use the bot by executing 'source .env/bin/activate; freqtrade trade'."
|
||||||
}
|
}
|
||||||
|
|
||||||
function plot() {
|
function plot() {
|
||||||
|
@ -20,7 +20,7 @@ def test_load_backtest_data(testdatadir):
|
|||||||
filename = testdatadir / "backtest-result_test.json"
|
filename = testdatadir / "backtest-result_test.json"
|
||||||
bt_data = load_backtest_data(filename)
|
bt_data = load_backtest_data(filename)
|
||||||
assert isinstance(bt_data, DataFrame)
|
assert isinstance(bt_data, DataFrame)
|
||||||
assert list(bt_data.columns) == BT_DATA_COLUMNS + ["profitabs"]
|
assert list(bt_data.columns) == BT_DATA_COLUMNS + ["profit"]
|
||||||
assert len(bt_data) == 179
|
assert len(bt_data) == 179
|
||||||
|
|
||||||
# Test loading from string (must yield same result)
|
# Test loading from string (must yield same result)
|
||||||
|
@ -41,7 +41,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
with pytest.raises(RPCException, match=r'.*no active trade*'):
|
with pytest.raises(RPCException, match=r'.*no active trade*'):
|
||||||
rpc._rpc_trade_status()
|
rpc._rpc_trade_status()
|
||||||
|
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
results = rpc._rpc_trade_status()
|
results = rpc._rpc_trade_status()
|
||||||
assert {
|
assert {
|
||||||
'trade_id': 1,
|
'trade_id': 1,
|
||||||
@ -116,7 +116,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
|||||||
with pytest.raises(RPCException, match=r'.*no active trade*'):
|
with pytest.raises(RPCException, match=r'.*no active trade*'):
|
||||||
rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
|
rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
|
||||||
|
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
|
|
||||||
result, headers = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
|
result, headers = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
|
||||||
assert "Since" in headers
|
assert "Since" in headers
|
||||||
@ -162,7 +162,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
|||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
rpc._fiat_converter = CryptoToFiatConverter()
|
rpc._fiat_converter = CryptoToFiatConverter()
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
|||||||
rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
|
rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
# Simulate fulfilled LIMIT_BUY order for trade
|
# Simulate fulfilled LIMIT_BUY order for trade
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -231,7 +231,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
|||||||
trade.close_date = datetime.utcnow()
|
trade.close_date = datetime.utcnow()
|
||||||
trade.is_open = False
|
trade.is_open = False
|
||||||
|
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
# Simulate fulfilled LIMIT_BUY order for trade
|
# Simulate fulfilled LIMIT_BUY order for trade
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -299,7 +299,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee,
|
|||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
# Simulate fulfilled LIMIT_BUY order for trade
|
# Simulate fulfilled LIMIT_BUY order for trade
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -529,7 +529,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
|||||||
msg = rpc._rpc_forcesell('all')
|
msg = rpc._rpc_forcesell('all')
|
||||||
assert msg == {'result': 'Created sell orders for all open trades.'}
|
assert msg == {'result': 'Created sell orders for all open trades.'}
|
||||||
|
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
msg = rpc._rpc_forcesell('all')
|
msg = rpc._rpc_forcesell('all')
|
||||||
assert msg == {'result': 'Created sell orders for all open trades.'}
|
assert msg == {'result': 'Created sell orders for all open trades.'}
|
||||||
|
|
||||||
@ -563,7 +563,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
|||||||
assert cancel_order_mock.call_count == 1
|
assert cancel_order_mock.call_count == 1
|
||||||
assert trade.amount == filled_amount
|
assert trade.amount == filled_amount
|
||||||
|
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
trade = Trade.query.filter(Trade.id == '2').first()
|
trade = Trade.query.filter(Trade.id == '2').first()
|
||||||
amount = trade.amount
|
amount = trade.amount
|
||||||
# make an limit-buy open trade, if there is no 'filled', don't sell it
|
# make an limit-buy open trade, if there is no 'filled', don't sell it
|
||||||
@ -582,7 +582,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
|||||||
assert cancel_order_mock.call_count == 2
|
assert cancel_order_mock.call_count == 2
|
||||||
assert trade.amount == amount
|
assert trade.amount == amount
|
||||||
|
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
# make an limit-sell open trade
|
# make an limit-sell open trade
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.exchange.Exchange.get_order',
|
'freqtrade.exchange.Exchange.get_order',
|
||||||
@ -613,7 +613,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
|||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -649,7 +649,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None:
|
|||||||
assert counts["current"] == 0
|
assert counts["current"] == 0
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
counts = rpc._rpc_count()
|
counts = rpc._rpc_count()
|
||||||
assert counts["current"] == 1
|
assert counts["current"] == 1
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ def test_api_count(botclient, mocker, ticker, fee, markets):
|
|||||||
assert rc.json["max"] == 1.0
|
assert rc.json["max"] == 1.0
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
ftbot.create_trades()
|
ftbot.enter_positions()
|
||||||
rc = client_get(client, f"{BASE_URI}/count")
|
rc = client_get(client, f"{BASE_URI}/count")
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
assert rc.json["current"] == 1.0
|
assert rc.json["current"] == 1.0
|
||||||
@ -333,7 +333,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, limit_buy_order, li
|
|||||||
assert len(rc.json) == 1
|
assert len(rc.json) == 1
|
||||||
assert rc.json == {"error": "Error querying _profit: no closed trade"}
|
assert rc.json == {"error": "Error querying _profit: no closed trade"}
|
||||||
|
|
||||||
ftbot.create_trades()
|
ftbot.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
|
|
||||||
# Simulate fulfilled LIMIT_BUY order for trade
|
# Simulate fulfilled LIMIT_BUY order for trade
|
||||||
@ -422,7 +422,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
|
|||||||
assert_response(rc, 200)
|
assert_response(rc, 200)
|
||||||
assert rc.json == []
|
assert rc.json == []
|
||||||
|
|
||||||
ftbot.create_trades()
|
ftbot.enter_positions()
|
||||||
rc = client_get(client, f"{BASE_URI}/status")
|
rc = client_get(client, f"{BASE_URI}/status")
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
assert len(rc.json) == 1
|
assert len(rc.json) == 1
|
||||||
@ -552,7 +552,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets):
|
|||||||
assert_response(rc, 502)
|
assert_response(rc, 502)
|
||||||
assert rc.json == {"error": "Error querying _forcesell: invalid argument"}
|
assert rc.json == {"error": "Error querying _forcesell: invalid argument"}
|
||||||
|
|
||||||
ftbot.create_trades()
|
ftbot.enter_positions()
|
||||||
|
|
||||||
rc = client_post(client, f"{BASE_URI}/forcesell",
|
rc = client_post(client, f"{BASE_URI}/forcesell",
|
||||||
data='{"tradeid": "1"}')
|
data='{"tradeid": "1"}')
|
||||||
|
@ -148,11 +148,6 @@ def test_status(default_conf, update, mocker, fee, ticker,) -> None:
|
|||||||
default_conf['telegram']['enabled'] = False
|
default_conf['telegram']['enabled'] = False
|
||||||
default_conf['telegram']['chat_id'] = "123"
|
default_conf['telegram']['chat_id'] = "123"
|
||||||
|
|
||||||
mocker.patch.multiple(
|
|
||||||
'freqtrade.exchange.Exchange',
|
|
||||||
fetch_ticker=ticker,
|
|
||||||
get_fee=fee,
|
|
||||||
)
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
status_table = MagicMock()
|
status_table = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -184,13 +179,8 @@ def test_status(default_conf, update, mocker, fee, ticker,) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
|
||||||
for _ in range(3):
|
|
||||||
freqtradebot.create_trades()
|
|
||||||
|
|
||||||
telegram._status(update=update, context=MagicMock())
|
telegram._status(update=update, context=MagicMock())
|
||||||
assert msg_mock.call_count == 1
|
assert msg_mock.call_count == 1
|
||||||
|
|
||||||
@ -236,7 +226,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None:
|
|||||||
msg_mock.reset_mock()
|
msg_mock.reset_mock()
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
# Trigger status while we have a fulfilled order for the open trade
|
# Trigger status while we have a fulfilled order for the open trade
|
||||||
telegram._status(update=update, context=MagicMock())
|
telegram._status(update=update, context=MagicMock())
|
||||||
|
|
||||||
@ -285,7 +275,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None:
|
|||||||
msg_mock.reset_mock()
|
msg_mock.reset_mock()
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
|
|
||||||
telegram._status_table(update=update, context=MagicMock())
|
telegram._status_table(update=update, context=MagicMock())
|
||||||
|
|
||||||
@ -322,7 +312,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
|||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -352,7 +342,8 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
|||||||
msg_mock.reset_mock()
|
msg_mock.reset_mock()
|
||||||
freqtradebot.config['max_open_trades'] = 2
|
freqtradebot.config['max_open_trades'] = 2
|
||||||
# Add two other trades
|
# Add two other trades
|
||||||
freqtradebot.create_trades()
|
n = freqtradebot.enter_positions()
|
||||||
|
assert n == 2
|
||||||
|
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
@ -431,7 +422,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
|||||||
msg_mock.reset_mock()
|
msg_mock.reset_mock()
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
|
|
||||||
# Simulate fulfilled LIMIT_BUY order for trade
|
# Simulate fulfilled LIMIT_BUY order for trade
|
||||||
@ -709,7 +700,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
|
|||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
@ -764,7 +755,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
|||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
|
|
||||||
# Decrease the price and sell it
|
# Decrease the price and sell it
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -821,7 +812,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
|
|||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
rpc_mock.reset_mock()
|
rpc_mock.reset_mock()
|
||||||
|
|
||||||
# /forcesell all
|
# /forcesell all
|
||||||
@ -971,7 +962,7 @@ def test_performance_handle(default_conf, update, ticker, fee,
|
|||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -1014,7 +1005,7 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None:
|
|||||||
freqtradebot.state = State.RUNNING
|
freqtradebot.state = State.RUNNING
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.create_trades()
|
freqtradebot.enter_positions()
|
||||||
msg_mock.reset_mock()
|
msg_mock.reset_mock()
|
||||||
telegram._count(update=update, context=MagicMock())
|
telegram._count(update=update, context=MagicMock())
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ def test_load_config_missing_attributes(default_conf) -> None:
|
|||||||
|
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf.pop('stake_currency')
|
conf.pop('stake_currency')
|
||||||
|
conf['runmode'] = RunMode.DRY_RUN
|
||||||
with pytest.raises(ValidationError, match=r".*'stake_currency' is a required property.*"):
|
with pytest.raises(ValidationError, match=r".*'stake_currency' is a required property.*"):
|
||||||
validate_config_schema(conf)
|
validate_config_schema(conf)
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, edge_conf
|
|||||||
freqtrade.active_pair_whitelist = ['NEO/BTC']
|
freqtrade.active_pair_whitelist = ['NEO/BTC']
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
#############################################
|
#############################################
|
||||||
@ -287,7 +287,7 @@ def test_edge_should_ignore_strategy_stoploss(limit_buy_order, fee,
|
|||||||
freqtrade.active_pair_whitelist = ['NEO/BTC']
|
freqtrade.active_pair_whitelist = ['NEO/BTC']
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
#############################################
|
#############################################
|
||||||
@ -310,7 +310,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker,
|
|||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
|
|
||||||
assert trade is not None
|
assert trade is not None
|
||||||
@ -318,7 +318,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker,
|
|||||||
assert trade.is_open
|
assert trade.is_open
|
||||||
assert trade.open_date is not None
|
assert trade.open_date is not None
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.order_by(Trade.id.desc()).first()
|
trade = Trade.query.order_by(Trade.id.desc()).first()
|
||||||
|
|
||||||
assert trade is not None
|
assert trade is not None
|
||||||
@ -462,7 +462,7 @@ def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None:
|
|||||||
assert round(result, 8) == round(max(0.0001, 0.001 * 0.020405) / 0.9, 8)
|
assert round(result, 8) == round(max(0.0001, 0.001 * 0.020405) / 0.9, 8)
|
||||||
|
|
||||||
|
|
||||||
def test_create_trades(default_conf, ticker, limit_buy_order, fee, mocker) -> None:
|
def test_create_trade(default_conf, ticker, limit_buy_order, fee, mocker) -> None:
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -476,7 +476,7 @@ def test_create_trades(default_conf, ticker, limit_buy_order, fee, mocker) -> No
|
|||||||
whitelist = deepcopy(default_conf['exchange']['pair_whitelist'])
|
whitelist = deepcopy(default_conf['exchange']['pair_whitelist'])
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.create_trades()
|
freqtrade.create_trade('ETH/BTC')
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade is not None
|
assert trade is not None
|
||||||
@ -494,7 +494,7 @@ def test_create_trades(default_conf, ticker, limit_buy_order, fee, mocker) -> No
|
|||||||
assert whitelist == default_conf['exchange']['pair_whitelist']
|
assert whitelist == default_conf['exchange']['pair_whitelist']
|
||||||
|
|
||||||
|
|
||||||
def test_create_trades_no_stake_amount(default_conf, ticker, limit_buy_order,
|
def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order,
|
||||||
fee, mocker) -> None:
|
fee, mocker) -> None:
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -509,10 +509,10 @@ def test_create_trades_no_stake_amount(default_conf, ticker, limit_buy_order,
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
|
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
|
||||||
freqtrade.create_trades()
|
freqtrade.create_trade('ETH/BTC')
|
||||||
|
|
||||||
|
|
||||||
def test_create_trades_minimal_amount(default_conf, ticker, limit_buy_order,
|
def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order,
|
||||||
fee, mocker) -> None:
|
fee, mocker) -> None:
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -527,12 +527,12 @@ def test_create_trades_minimal_amount(default_conf, ticker, limit_buy_order,
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.create_trade('ETH/BTC')
|
||||||
rate, amount = buy_mock.call_args[1]['rate'], buy_mock.call_args[1]['amount']
|
rate, amount = buy_mock.call_args[1]['rate'], buy_mock.call_args[1]['amount']
|
||||||
assert rate * amount >= default_conf['stake_amount']
|
assert rate * amount >= default_conf['stake_amount']
|
||||||
|
|
||||||
|
|
||||||
def test_create_trades_too_small_stake_amount(default_conf, ticker, limit_buy_order,
|
def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_order,
|
||||||
fee, mocker) -> None:
|
fee, mocker) -> None:
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -549,10 +549,10 @@ def test_create_trades_too_small_stake_amount(default_conf, ticker, limit_buy_or
|
|||||||
|
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
assert not freqtrade.create_trades()
|
assert not freqtrade.create_trade('ETH/BTC')
|
||||||
|
|
||||||
|
|
||||||
def test_create_trades_limit_reached(default_conf, ticker, limit_buy_order,
|
def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order,
|
||||||
fee, markets, mocker) -> None:
|
fee, markets, mocker) -> None:
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -569,11 +569,11 @@ def test_create_trades_limit_reached(default_conf, ticker, limit_buy_order,
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
assert not freqtrade.create_trades()
|
assert not freqtrade.create_trade('ETH/BTC')
|
||||||
assert freqtrade.get_trade_stake_amount('ETH/BTC') is None
|
assert freqtrade.get_trade_stake_amount('ETH/BTC') is None
|
||||||
|
|
||||||
|
|
||||||
def test_create_trades_no_pairs_let(default_conf, ticker, limit_buy_order, fee,
|
def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order, fee,
|
||||||
mocker, caplog) -> None:
|
mocker, caplog) -> None:
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -588,13 +588,15 @@ def test_create_trades_no_pairs_let(default_conf, ticker, limit_buy_order, fee,
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
assert freqtrade.create_trades()
|
n = freqtrade.enter_positions()
|
||||||
assert not freqtrade.create_trades()
|
assert n == 1
|
||||||
assert log_has("No currency pair in active pair whitelist, "
|
assert not log_has_re(r"No currency pair in active pair whitelist.*", caplog)
|
||||||
"but checking to sell open trades.", caplog)
|
n = freqtrade.enter_positions()
|
||||||
|
assert n == 0
|
||||||
|
assert log_has_re(r"No currency pair in active pair whitelist.*", caplog)
|
||||||
|
|
||||||
|
|
||||||
def test_create_trades_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee,
|
def test_enter_positions_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee,
|
||||||
mocker, caplog) -> None:
|
mocker, caplog) -> None:
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -608,11 +610,12 @@ def test_create_trades_no_pairs_in_whitelist(default_conf, ticker, limit_buy_ord
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
assert not freqtrade.create_trades()
|
n = freqtrade.enter_positions()
|
||||||
|
assert n == 0
|
||||||
assert log_has("Active pair whitelist is empty.", caplog)
|
assert log_has("Active pair whitelist is empty.", caplog)
|
||||||
|
|
||||||
|
|
||||||
def test_create_trades_no_signal(default_conf, fee, mocker) -> None:
|
def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
|
||||||
default_conf['dry_run'] = True
|
default_conf['dry_run'] = True
|
||||||
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
@ -628,7 +631,7 @@ def test_create_trades_no_signal(default_conf, fee, mocker) -> None:
|
|||||||
|
|
||||||
Trade.query = MagicMock()
|
Trade.query = MagicMock()
|
||||||
Trade.query.filter = MagicMock()
|
Trade.query.filter = MagicMock()
|
||||||
assert not freqtrade.create_trades()
|
assert not freqtrade.create_trade('ETH/BTC')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("max_open", range(0, 5))
|
@pytest.mark.parametrize("max_open", range(0, 5))
|
||||||
@ -646,7 +649,8 @@ def test_create_trades_multiple_trades(default_conf, ticker,
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
n = freqtrade.enter_positions()
|
||||||
|
assert n == max_open
|
||||||
|
|
||||||
trades = Trade.get_open_trades()
|
trades = Trade.get_open_trades()
|
||||||
assert len(trades) == max_open
|
assert len(trades) == max_open
|
||||||
@ -672,7 +676,8 @@ def test_create_trades_preopen(default_conf, ticker, fee, mocker) -> None:
|
|||||||
assert len(Trade.get_open_trades()) == 2
|
assert len(Trade.get_open_trades()) == 2
|
||||||
|
|
||||||
# Create 2 new trades using create_trades
|
# Create 2 new trades using create_trades
|
||||||
assert freqtrade.create_trades()
|
assert freqtrade.create_trade('ETH/BTC')
|
||||||
|
assert freqtrade.create_trade('NEO/BTC')
|
||||||
|
|
||||||
trades = Trade.get_open_trades()
|
trades = Trade.get_open_trades()
|
||||||
assert len(trades) == 4
|
assert len(trades) == 4
|
||||||
@ -987,7 +992,7 @@ def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None
|
|||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trades = [trade]
|
trades = [trade]
|
||||||
|
|
||||||
freqtrade.process_maybe_execute_sells(trades)
|
freqtrade.exit_positions(trades)
|
||||||
assert trade.stoploss_order_id == '13434334'
|
assert trade.stoploss_order_id == '13434334'
|
||||||
assert stoploss_limit.call_count == 1
|
assert stoploss_limit.call_count == 1
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
@ -1056,7 +1061,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
|
|||||||
# should unset stoploss_order_id and return true
|
# should unset stoploss_order_id and return true
|
||||||
# as a trade actually happened
|
# as a trade actually happened
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
@ -1114,7 +1119,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog,
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trade.open_order_id = '12345'
|
trade.open_order_id = '12345'
|
||||||
@ -1149,7 +1154,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee,
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
freqtrade.create_stoploss_order(trade, 200, 199)
|
freqtrade.create_stoploss_order(trade, 200, 199)
|
||||||
@ -1207,7 +1212,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
|||||||
|
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
@ -1295,7 +1300,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c
|
|||||||
# setting stoploss_on_exchange_interval to 60 seconds
|
# setting stoploss_on_exchange_interval to 60 seconds
|
||||||
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
|
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
@ -1376,7 +1381,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
|
|||||||
|
|
||||||
freqtrade.active_pair_whitelist = freqtrade.edge.adjust(freqtrade.active_pair_whitelist)
|
freqtrade.active_pair_whitelist = freqtrade.edge.adjust(freqtrade.active_pair_whitelist)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
@ -1440,27 +1445,33 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
|
|||||||
stop_price=0.00002344 * 0.99)
|
stop_price=0.00002344 * 0.99)
|
||||||
|
|
||||||
|
|
||||||
def test_process_maybe_execute_buys(mocker, default_conf, caplog) -> None:
|
def test_enter_positions(mocker, default_conf, caplog) -> None:
|
||||||
caplog.set_level(logging.DEBUG)
|
caplog.set_level(logging.DEBUG)
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trades', MagicMock(return_value=False))
|
mock_ct = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade',
|
||||||
freqtrade.process_maybe_execute_buys()
|
MagicMock(return_value=False))
|
||||||
|
n = freqtrade.enter_positions()
|
||||||
|
assert n == 0
|
||||||
assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog)
|
assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog)
|
||||||
|
# create_trade should be called once for every pair in the whitelist.
|
||||||
|
assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist'])
|
||||||
|
|
||||||
|
|
||||||
def test_process_maybe_execute_buys_exception(mocker, default_conf, caplog) -> None:
|
def test_enter_positions_exception(mocker, default_conf, caplog) -> None:
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
mocker.patch(
|
mock_ct = mocker.patch(
|
||||||
'freqtrade.freqtradebot.FreqtradeBot.create_trades',
|
'freqtrade.freqtradebot.FreqtradeBot.create_trade',
|
||||||
MagicMock(side_effect=DependencyException)
|
MagicMock(side_effect=DependencyException)
|
||||||
)
|
)
|
||||||
freqtrade.process_maybe_execute_buys()
|
n = freqtrade.enter_positions()
|
||||||
assert log_has('Unable to create trade: ', caplog)
|
assert n == 0
|
||||||
|
assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist'])
|
||||||
|
assert log_has('Unable to create trade for ETH/BTC: ', caplog)
|
||||||
|
|
||||||
|
|
||||||
def test_process_maybe_execute_sells(mocker, default_conf, limit_buy_order, caplog) -> None:
|
def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None:
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
|
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
|
||||||
@ -1473,7 +1484,8 @@ def test_process_maybe_execute_sells(mocker, default_conf, limit_buy_order, capl
|
|||||||
trade.open_order_id = '123'
|
trade.open_order_id = '123'
|
||||||
trade.open_fee = 0.001
|
trade.open_fee = 0.001
|
||||||
trades = [trade]
|
trades = [trade]
|
||||||
assert not freqtrade.process_maybe_execute_sells(trades)
|
n = freqtrade.exit_positions(trades)
|
||||||
|
assert n == 0
|
||||||
# Test amount not modified by fee-logic
|
# Test amount not modified by fee-logic
|
||||||
assert not log_has(
|
assert not log_has(
|
||||||
'Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format(trade), caplog
|
'Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format(trade), caplog
|
||||||
@ -1481,11 +1493,11 @@ def test_process_maybe_execute_sells(mocker, default_conf, limit_buy_order, capl
|
|||||||
|
|
||||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
|
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
|
||||||
# test amount modified by fee-logic
|
# test amount modified by fee-logic
|
||||||
assert not freqtrade.process_maybe_execute_sells(trades)
|
n = freqtrade.exit_positions(trades)
|
||||||
|
assert n == 0
|
||||||
|
|
||||||
|
|
||||||
def test_process_maybe_execute_sells_exception(mocker, default_conf,
|
def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) -> None:
|
||||||
limit_buy_order, caplog) -> None:
|
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
|
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
|
||||||
|
|
||||||
@ -1499,7 +1511,8 @@ def test_process_maybe_execute_sells_exception(mocker, default_conf,
|
|||||||
'freqtrade.freqtradebot.FreqtradeBot.update_trade_state',
|
'freqtrade.freqtradebot.FreqtradeBot.update_trade_state',
|
||||||
side_effect=DependencyException()
|
side_effect=DependencyException()
|
||||||
)
|
)
|
||||||
freqtrade.process_maybe_execute_sells(trades)
|
n = freqtrade.exit_positions(trades)
|
||||||
|
assert n == 0
|
||||||
assert log_has('Unable to sell trade: ', caplog)
|
assert log_has('Unable to sell trade: ', caplog)
|
||||||
|
|
||||||
|
|
||||||
@ -1674,7 +1687,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mock
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
@ -1697,7 +1710,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mock
|
|||||||
assert trade.close_date is not None
|
assert trade.close_date is not None
|
||||||
|
|
||||||
|
|
||||||
def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee, mocker) -> None:
|
def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order, fee, mocker) -> None:
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -1711,7 +1724,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee,
|
|||||||
patch_get_signal(freqtrade, value=(True, True))
|
patch_get_signal(freqtrade, value=(True, True))
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
# Buy and Sell triggering, so doing nothing ...
|
# Buy and Sell triggering, so doing nothing ...
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
@ -1720,7 +1733,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee,
|
|||||||
|
|
||||||
# Buy is triggering, so buying ...
|
# Buy is triggering, so buying ...
|
||||||
patch_get_signal(freqtrade, value=(True, False))
|
patch_get_signal(freqtrade, value=(True, False))
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 1
|
assert nb_trades == 1
|
||||||
@ -1764,7 +1777,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
|
|||||||
patch_get_signal(freqtrade, value=(True, False))
|
patch_get_signal(freqtrade, value=(True, False))
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
@ -1795,7 +1808,7 @@ def test_handle_trade_use_sell_signal(
|
|||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
@ -1823,7 +1836,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order,
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create trade and sell it
|
# Create trade and sell it
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
@ -2183,7 +2196,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
@ -2231,7 +2244,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker)
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
@ -2281,7 +2294,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
@ -2339,7 +2352,7 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, c
|
|||||||
|
|
||||||
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
Trade.session = MagicMock()
|
Trade.session = MagicMock()
|
||||||
@ -2382,13 +2395,13 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
trades = [trade]
|
trades = [trade]
|
||||||
|
|
||||||
freqtrade.process_maybe_execute_sells(trades)
|
freqtrade.exit_positions(trades)
|
||||||
|
|
||||||
# Increase the price and sell it
|
# Increase the price and sell it
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -2432,10 +2445,10 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trades = [trade]
|
trades = [trade]
|
||||||
freqtrade.process_maybe_execute_sells(trades)
|
freqtrade.exit_positions(trades)
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.stoploss_order_id == '123'
|
assert trade.stoploss_order_id == '123'
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
@ -2463,7 +2476,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f
|
|||||||
})
|
})
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_limit_executed)
|
mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_limit_executed)
|
||||||
|
|
||||||
freqtrade.process_maybe_execute_sells(trades)
|
freqtrade.exit_positions(trades)
|
||||||
assert trade.stoploss_order_id is None
|
assert trade.stoploss_order_id is None
|
||||||
assert trade.is_open is False
|
assert trade.is_open is False
|
||||||
assert trade.sell_reason == SellType.STOPLOSS_ON_EXCHANGE.value
|
assert trade.sell_reason == SellType.STOPLOSS_ON_EXCHANGE.value
|
||||||
@ -2484,7 +2497,7 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
@ -2546,7 +2559,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -2577,7 +2590,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order,
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -2608,7 +2621,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, mocker
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple(
|
freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple(
|
||||||
sell_flag=False, sell_type=SellType.NONE))
|
sell_flag=False, sell_type=SellType.NONE))
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -2638,7 +2651,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocke
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -2667,7 +2680,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order,
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
amnt = trade.amount
|
amnt = trade.amount
|
||||||
@ -2735,7 +2748,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
@ -2754,7 +2767,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo
|
|||||||
|
|
||||||
# reinit - should buy other pair.
|
# reinit - should buy other pair.
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
assert log_has(f"Pair {trade.pair} is currently locked.", caplog)
|
assert log_has(f"Pair {trade.pair} is currently locked.", caplog)
|
||||||
|
|
||||||
@ -2779,7 +2792,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, mocker) ->
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -2812,7 +2825,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, caplog, mocker)
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert freqtrade.handle_trade(trade) is False
|
assert freqtrade.handle_trade(trade) is False
|
||||||
|
|
||||||
@ -2867,7 +2880,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee,
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -2924,7 +2937,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -2987,7 +3000,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee,
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -3046,7 +3059,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order,
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
@ -3377,7 +3390,7 @@ def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order, fee,
|
|||||||
whitelist = deepcopy(default_conf['exchange']['pair_whitelist'])
|
whitelist = deepcopy(default_conf['exchange']['pair_whitelist'])
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade is not None
|
assert trade is not None
|
||||||
@ -3412,7 +3425,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o
|
|||||||
# Save state of current whitelist
|
# Save state of current whitelist
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade is None
|
assert trade is None
|
||||||
@ -3514,7 +3527,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
assert trade
|
assert trade
|
||||||
@ -3604,7 +3617,7 @@ def test_process_i_am_alive(default_conf, mocker, caplog):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order):
|
def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order, caplog):
|
||||||
default_conf['dry_run'] = True
|
default_conf['dry_run'] = True
|
||||||
# Initialize to 2 times stake amount
|
# Initialize to 2 times stake amount
|
||||||
default_conf['dry_run_wallet'] = 0.002
|
default_conf['dry_run_wallet'] = 0.002
|
||||||
@ -3621,12 +3634,14 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order)
|
|||||||
patch_get_signal(bot)
|
patch_get_signal(bot)
|
||||||
assert bot.wallets.get_free('BTC') == 0.002
|
assert bot.wallets.get_free('BTC') == 0.002
|
||||||
|
|
||||||
bot.create_trades()
|
n = bot.enter_positions()
|
||||||
|
assert n == 2
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
assert len(trades) == 2
|
assert len(trades) == 2
|
||||||
|
|
||||||
bot.config['max_open_trades'] = 3
|
bot.config['max_open_trades'] = 3
|
||||||
with pytest.raises(
|
n = bot.enter_positions()
|
||||||
DependencyException,
|
assert n == 0
|
||||||
match=r"Available balance \(0 BTC\) is lower than stake amount \(0.001 BTC\)"):
|
assert log_has_re(r"Unable to create trade for XRP/BTC: "
|
||||||
bot.create_trades()
|
r"Available balance \(0 BTC\) is lower than stake amount \(0.001 BTC\)",
|
||||||
|
caplog)
|
||||||
|
@ -80,7 +80,7 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trades()
|
freqtrade.enter_positions()
|
||||||
wallets_mock.reset_mock()
|
wallets_mock.reset_mock()
|
||||||
Trade.session = MagicMock()
|
Trade.session = MagicMock()
|
||||||
|
|
||||||
@ -90,7 +90,8 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
|||||||
trade.stoploss_order_id = 3
|
trade.stoploss_order_id = 3
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
|
|
||||||
freqtrade.process_maybe_execute_sells(trades)
|
n = freqtrade.exit_positions(trades)
|
||||||
|
assert n == 2
|
||||||
assert should_sell_mock.call_count == 2
|
assert should_sell_mock.call_count == 2
|
||||||
|
|
||||||
# Only order for 3rd trade needs to be cancelled
|
# Only order for 3rd trade needs to be cancelled
|
||||||
@ -153,7 +154,8 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, moc
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create 4 trades
|
# Create 4 trades
|
||||||
freqtrade.create_trades()
|
n = freqtrade.enter_positions()
|
||||||
|
assert n == 4
|
||||||
|
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
assert len(trades) == 4
|
assert len(trades) == 4
|
||||||
@ -170,7 +172,8 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, moc
|
|||||||
assert len(trades) == 5
|
assert len(trades) == 5
|
||||||
bals = freqtrade.wallets.get_all_balances()
|
bals = freqtrade.wallets.get_all_balances()
|
||||||
|
|
||||||
freqtrade.process_maybe_execute_sells(trades)
|
n = freqtrade.exit_positions(trades)
|
||||||
|
assert n == 1
|
||||||
trades = Trade.get_open_trades()
|
trades = Trade.get_open_trades()
|
||||||
# One trade sold
|
# One trade sold
|
||||||
assert len(trades) == 4
|
assert len(trades) == 4
|
||||||
|
@ -119,6 +119,7 @@ def test_plot_trades(testdatadir, caplog):
|
|||||||
assert trade_sell.yaxis == 'y'
|
assert trade_sell.yaxis == 'y'
|
||||||
assert len(trades) == len(trade_sell.x)
|
assert len(trades) == len(trade_sell.x)
|
||||||
assert trade_sell.marker.color == 'red'
|
assert trade_sell.marker.color == 'red'
|
||||||
|
assert trade_sell.text[0] == "4.0%, roi, 15 min"
|
||||||
|
|
||||||
|
|
||||||
def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog):
|
def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog):
|
||||||
|
Loading…
Reference in New Issue
Block a user