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

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
[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
from copy import deepcopy
from typing import Any, Dict
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
: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:
FreqtradeValidator(constants.CONF_SCHEMA).validate(conf)
FreqtradeValidator(conf_schema).validate(conf)
return conf
except ValidationError as e:
logger.critical(
f"Invalid configuration. See config.json.example. Reason: {e}"
)
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': [
}
SCHEMA_TRADE_REQUIRED = [
'exchange',
'max_open_trades',
'stake_currency',
@ -297,4 +299,10 @@ CONF_SCHEMA = {
'dataformat_ohlcv',
'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,
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)
return df

View File

@ -27,6 +27,7 @@ from freqtrade.state import State
from freqtrade.strategy.interface import IStrategy, SellType
from freqtrade.wallets import Wallets
logger = logging.getLogger(__name__)
@ -132,12 +133,12 @@ class FreqtradeBot:
self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist),
self.strategy.informative_pairs())
# First process current opened trades
self.process_maybe_execute_sells(trades)
# First process current opened trades (positions)
self.exit_positions(trades)
# Then looking for buy opportunities
if self.get_free_open_trades():
self.process_maybe_execute_buys()
self.enter_positions()
# Check and handle any timed out open orders
self.check_handle_timedout()
@ -181,6 +182,43 @@ class FreqtradeBot:
open_trades = len(Trade.get_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:
"""
Calculates bid target between current ask price and last price
@ -294,65 +332,50 @@ class FreqtradeBot:
# See also #2575 at github.
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.
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`.
:return: True if at least one trade has been created.
Check the implemented trading strategy for buy signals.
If the pair triggers the buy signal a new trade record gets 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):
logger.info(f"Pair {pair} is currently locked.")
continue
return False
# running get_signal on historical data fetched
(buy, sell) = self.strategy.get_signal(
pair, self.strategy.ticker_interval,
self.dataprovider.ohlcv(pair, self.strategy.ticker_interval))
if buy and not sell:
if not self.get_free_open_trades():
logger.debug("Can't open a new trade: max number of trades is reached")
continue
logger.debug("Can't open a new trade: max number of trades is reached.")
return False
stake_amount = self.get_trade_stake_amount(pair)
if not stake_amount:
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: "
f"{stake_amount} ...")
bidstrat_check_depth_of_market = self.config.get('bid_strategy', {}).\
get('check_depth_of_market', {})
if (bidstrat_check_depth_of_market.get('enabled', False)) and\
(bidstrat_check_depth_of_market.get('bids_to_ask_delta', 0) > 0):
if self._check_depth_of_market_buy(pair, bidstrat_check_depth_of_market):
buycount += self.execute_buy(pair, stake_amount)
continue
bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {})
if ((bid_check_dom.get('enabled', False)) and
(bid_check_dom.get('bids_to_ask_delta', 0) > 0)):
if self._check_depth_of_market_buy(pair, bid_check_dom):
return self.execute_buy(pair, stake_amount)
else:
return False
buycount += self.execute_buy(pair, stake_amount)
return buycount > 0
return self.execute_buy(pair, stake_amount)
else:
return False
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
: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']
if price:
buy_limit_requested = price
else:
# Calculate amount
# Calculate price
buy_limit_requested = self.get_target_bid(pair)
min_stake_amount = self._get_min_pair_stake_amount(pair, buy_limit_requested)
@ -435,17 +456,6 @@ class FreqtradeBot:
amount = order['amount']
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 = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
trade = Trade(
@ -463,6 +473,8 @@ class FreqtradeBot:
ticker_interval=timeframe_to_minutes(self.config['ticker_interval'])
)
self._notify_buy(trade, order_type)
# Update fees if order is closed
if order_status == 'closed':
self.update_trade_state(trade, order)
@ -475,121 +487,53 @@ class FreqtradeBot:
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:
# Create entity and execute trade
if not self.create_trades():
logger.debug('Found no buy signals for whitelisted currencies. Trying again...')
except DependencyException as exception:
logger.warning('Unable to create trade: %s', exception)
msg = {
'type': RPCMessageType.BUY_NOTIFICATION,
'exchange': self.exchange.name.capitalize(),
'pair': trade.pair,
'limit': trade.open_rate,
'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:
try:
self.update_trade_state(trade)
if (self.strategy.order_types.get('stoploss_on_exchange') and
self.handle_stoploss_on_exchange(trade)):
result = True
trades_closed += 1
continue
# Check if we can sell our current pair
if trade.open_order_id is None and self.handle_trade(trade):
result = True
trades_closed += 1
except DependencyException as exception:
logger.warning('Unable to sell trade: %s', exception)
# Updating wallets if any trade occured
if result:
if trades_closed:
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
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()
return trades_closed
def get_sell_rate(self, pair: str, refresh: bool) -> float:
"""
@ -963,16 +907,16 @@ class FreqtradeBot:
except InvalidOrderException:
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:
# 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)
# Execute sell and update trade record
order = self.exchange.sell(pair=str(trade.pair),
ordertype=ordertype,
ordertype=order_type,
amount=amount, rate=limit,
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
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):
"""
@ -1015,17 +959,99 @@ class FreqtradeBot:
'profit_percent': profit_percent,
'sell_reason': trade.sell_reason,
'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 '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']
if 'fiat_display_currency' in self.config:
msg.update({
'stake_currency': stake_currency,
'fiat_currency': fiat_currency,
'fiat_currency': self.config['fiat_display_currency'],
})
# Send the message
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
desc = trades.apply(lambda row: f"{round(row['profitperc'], 3)}%, {row['sell_reason']}, "
f"{row['duration']} min",
desc = trades.apply(lambda row: f"{round(row['profitperc'] * 100, 1)}%, "
f"{row['sell_reason']}, {row['duration']} min",
axis=1)
trade_sells = go.Scatter(
x=trades["close_time"],

View File

@ -12,7 +12,8 @@ from colorama import init as colorama_init
from tabulate import tabulate
from freqtrade.configuration import (Configuration, TimeRange,
remove_credentials)
remove_credentials,
validate_config_consistency)
from freqtrade.configuration.directory_operations import (copy_sample_files,
create_userdata_dir)
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
remove_credentials(config)
validate_config_consistency(config)
return config

View File

@ -263,7 +263,7 @@ function install() {
echo "-------------------------"
echo "Run the bot !"
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() {

View File

@ -20,7 +20,7 @@ def test_load_backtest_data(testdatadir):
filename = testdatadir / "backtest-result_test.json"
bt_data = load_backtest_data(filename)
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
# 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*'):
rpc._rpc_trade_status()
freqtradebot.create_trades()
freqtradebot.enter_positions()
results = rpc._rpc_trade_status()
assert {
'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*'):
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')
assert "Since" in headers
@ -162,7 +162,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter()
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
trade = Trade.query.first()
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)
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade
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.is_open = False
freqtradebot.create_trades()
freqtradebot.enter_positions()
trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order)
@ -299,7 +299,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee,
rpc = RPC(freqtradebot)
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order)
@ -529,7 +529,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
msg = rpc._rpc_forcesell('all')
assert msg == {'result': 'Created sell orders for all open trades.'}
freqtradebot.create_trades()
freqtradebot.enter_positions()
msg = rpc._rpc_forcesell('all')
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 trade.amount == filled_amount
freqtradebot.create_trades()
freqtradebot.enter_positions()
trade = Trade.query.filter(Trade.id == '2').first()
amount = trade.amount
# 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 trade.amount == amount
freqtradebot.create_trades()
freqtradebot.enter_positions()
# make an limit-sell open trade
mocker.patch(
'freqtrade.exchange.Exchange.get_order',
@ -613,7 +613,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
rpc = RPC(freqtradebot)
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
trade = Trade.query.first()
assert trade
@ -649,7 +649,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None:
assert counts["current"] == 0
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
counts = rpc._rpc_count()
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
# Create some test data
ftbot.create_trades()
ftbot.enter_positions()
rc = client_get(client, f"{BASE_URI}/count")
assert_response(rc)
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 rc.json == {"error": "Error querying _profit: no closed trade"}
ftbot.create_trades()
ftbot.enter_positions()
trade = Trade.query.first()
# 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 rc.json == []
ftbot.create_trades()
ftbot.enter_positions()
rc = client_get(client, f"{BASE_URI}/status")
assert_response(rc)
assert len(rc.json) == 1
@ -552,7 +552,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets):
assert_response(rc, 502)
assert rc.json == {"error": "Error querying _forcesell: invalid argument"}
ftbot.create_trades()
ftbot.enter_positions()
rc = client_post(client, f"{BASE_URI}/forcesell",
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']['chat_id'] = "123"
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_fee=fee,
)
msg_mock = MagicMock()
status_table = MagicMock()
mocker.patch.multiple(
@ -184,13 +179,8 @@ def test_status(default_conf, update, mocker, fee, ticker,) -> None:
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot, (True, False))
telegram = Telegram(freqtradebot)
# Create some test data
for _ in range(3):
freqtradebot.create_trades()
telegram._status(update=update, context=MagicMock())
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()
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
# Trigger status while we have a fulfilled order for the open trade
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()
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
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)
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
trade = Trade.query.first()
assert trade
@ -352,7 +342,8 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
msg_mock.reset_mock()
freqtradebot.config['max_open_trades'] = 2
# Add two other trades
freqtradebot.create_trades()
n = freqtradebot.enter_positions()
assert n == 2
trades = Trade.query.all()
for trade in trades:
@ -431,7 +422,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
msg_mock.reset_mock()
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade
@ -709,7 +700,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
telegram = Telegram(freqtradebot)
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
trade = Trade.query.first()
assert trade
@ -764,7 +755,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
telegram = Telegram(freqtradebot)
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
# Decrease the price and sell it
mocker.patch.multiple(
@ -821,7 +812,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
telegram = Telegram(freqtradebot)
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
rpc_mock.reset_mock()
# /forcesell all
@ -971,7 +962,7 @@ def test_performance_handle(default_conf, update, ticker, fee,
telegram = Telegram(freqtradebot)
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
trade = Trade.query.first()
assert trade
@ -1014,7 +1005,7 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None:
freqtradebot.state = State.RUNNING
# Create some test data
freqtradebot.create_trades()
freqtradebot.enter_positions()
msg_mock.reset_mock()
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.pop('stake_currency')
conf['runmode'] = RunMode.DRY_RUN
with pytest.raises(ValidationError, match=r".*'stake_currency' is a required property.*"):
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']
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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']
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
trade.update(limit_buy_order)
#############################################
@ -310,7 +310,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker,
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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.open_date is not None
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.order_by(Trade.id.desc()).first()
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)
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_exchange(mocker)
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'])
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trades()
freqtrade.create_trade('ETH/BTC')
trade = Trade.query.first()
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']
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:
patch_RPCManager(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)
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:
patch_RPCManager(mocker)
patch_exchange(mocker)
@ -527,12 +527,12 @@ def test_create_trades_minimal_amount(default_conf, ticker, limit_buy_order,
freqtrade = FreqtradeBot(default_conf)
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']
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:
patch_RPCManager(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)
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:
patch_RPCManager(mocker)
patch_exchange(mocker)
@ -569,11 +569,11 @@ def test_create_trades_limit_reached(default_conf, ticker, limit_buy_order,
freqtrade = FreqtradeBot(default_conf)
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
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:
patch_RPCManager(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)
patch_get_signal(freqtrade)
assert freqtrade.create_trades()
assert not freqtrade.create_trades()
assert log_has("No currency pair in active pair whitelist, "
"but checking to sell open trades.", caplog)
n = freqtrade.enter_positions()
assert n == 1
assert not log_has_re(r"No currency pair in active pair whitelist.*", 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:
patch_RPCManager(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)
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)
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
patch_RPCManager(mocker)
@ -628,7 +631,7 @@ def test_create_trades_no_signal(default_conf, fee, mocker) -> None:
Trade.query = 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))
@ -646,7 +649,8 @@ def test_create_trades_multiple_trades(default_conf, ticker,
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trades()
n = freqtrade.enter_positions()
assert n == max_open
trades = Trade.get_open_trades()
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
# 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()
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
trades = [trade]
freqtrade.process_maybe_execute_sells(trades)
freqtrade.exit_positions(trades)
assert trade.stoploss_order_id == '13434334'
assert stoploss_limit.call_count == 1
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
# as a trade actually happened
caplog.clear()
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
trade.is_open = True
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)
patch_get_signal(freqtrade)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
trade.is_open = True
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)
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
caplog.clear()
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)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
trade.is_open = True
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
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
patch_get_signal(freqtrade)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
trade.is_open = True
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.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
trade.is_open = True
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)
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)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trades', MagicMock(return_value=False))
freqtrade.process_maybe_execute_buys()
mock_ct = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade',
MagicMock(return_value=False))
n = freqtrade.enter_positions()
assert n == 0
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)
mocker.patch(
'freqtrade.freqtradebot.FreqtradeBot.create_trades',
mock_ct = mocker.patch(
'freqtrade.freqtradebot.FreqtradeBot.create_trade',
MagicMock(side_effect=DependencyException)
)
freqtrade.process_maybe_execute_buys()
assert log_has('Unable to create trade: ', caplog)
n = freqtrade.enter_positions()
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)
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_fee = 0.001
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
assert not log_has(
'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)
# 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,
limit_buy_order, caplog) -> None:
def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
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',
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)
@ -1674,7 +1687,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mock
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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
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_exchange(mocker)
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))
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
# Buy and Sell triggering, so doing nothing ...
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 ...
patch_get_signal(freqtrade, value=(True, False))
freqtrade.create_trades()
freqtrade.enter_positions()
trades = Trade.query.all()
nb_trades = len(trades)
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))
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
trade.is_open = True
@ -1795,7 +1808,7 @@ def test_handle_trade_use_sell_signal(
freqtrade = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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)
# Create trade and sell it
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
assert trade
@ -2183,7 +2196,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N
patch_get_signal(freqtrade)
# Create some test data
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
assert trade
@ -2231,7 +2244,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker)
patch_get_signal(freqtrade)
# Create some test data
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
assert trade
@ -2281,7 +2294,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
patch_get_signal(freqtrade)
# Create some test data
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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
patch_get_signal(freqtrade)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
Trade.session = MagicMock()
@ -2382,13 +2395,13 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke
patch_get_signal(freqtrade)
# Create some test data
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
assert trade
trades = [trade]
freqtrade.process_maybe_execute_sells(trades)
freqtrade.exit_positions(trades)
# Increase the price and sell it
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)
# Create some test data
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
trades = [trade]
freqtrade.process_maybe_execute_sells(trades)
freqtrade.exit_positions(trades)
assert trade
assert trade.stoploss_order_id == '123'
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)
freqtrade.process_maybe_execute_sells(trades)
freqtrade.exit_positions(trades)
assert trade.stoploss_order_id is None
assert trade.is_open is False
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)
# Create some test data
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
assert trade
@ -2546,7 +2559,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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)
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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)
freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple(
sell_flag=False, sell_type=SellType.NONE))
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
trade.update(limit_buy_order)
@ -2667,7 +2680,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order,
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
amnt = trade.amount
@ -2735,7 +2748,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo
patch_get_signal(freqtrade)
# Create some test data
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
assert trade
@ -2754,7 +2767,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo
# reinit - should buy other pair.
caplog.clear()
freqtrade.create_trades()
freqtrade.enter_positions()
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)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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)
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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)
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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)
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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'])
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
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)
patch_get_signal(freqtrade)
freqtrade.create_trades()
freqtrade.enter_positions()
trade = Trade.query.first()
assert trade
@ -3604,7 +3617,7 @@ def test_process_i_am_alive(default_conf, mocker, caplog):
@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
# Initialize to 2 times stake amount
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)
assert bot.wallets.get_free('BTC') == 0.002
bot.create_trades()
n = bot.enter_positions()
assert n == 2
trades = Trade.query.all()
assert len(trades) == 2
bot.config['max_open_trades'] = 3
with pytest.raises(
DependencyException,
match=r"Available balance \(0 BTC\) is lower than stake amount \(0.001 BTC\)"):
bot.create_trades()
n = bot.enter_positions()
assert n == 0
assert log_has_re(r"Unable to create trade for XRP/BTC: "
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)
# Create some test data
freqtrade.create_trades()
freqtrade.enter_positions()
wallets_mock.reset_mock()
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.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
# 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)
# Create 4 trades
freqtrade.create_trades()
n = freqtrade.enter_positions()
assert n == 4
trades = Trade.query.all()
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
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()
# One trade sold
assert len(trades) == 4

View File

@ -119,6 +119,7 @@ def test_plot_trades(testdatadir, caplog):
assert trade_sell.yaxis == 'y'
assert len(trades) == len(trade_sell.x)
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):