Merge branch 'develop' into data_handler

This commit is contained in:
Matthias 2020-01-04 11:36:27 +01:00
commit 2409261cb7
18 changed files with 400 additions and 325 deletions

View File

@ -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

View File

@ -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.

View File

@ -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
) )

View File

@ -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',
]

View File

@ -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

View File

@ -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

View File

@ -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"],

View File

@ -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

View File

@ -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() {

View File

@ -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)

View File

@ -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

View File

@ -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"}')

View File

@ -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())

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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):