Merge remote-tracking branch 'origin/wohlgemuth' into aws

# Conflicts:
#	requirements.txt
#	scripts/plot_profit.py
This commit is contained in:
Gert
2018-05-23 18:56:27 -07:00
46 changed files with 893 additions and 175 deletions

View File

@@ -2,6 +2,7 @@
import json
import logging
from datetime import datetime
from typing import Dict, Optional
from functools import reduce
from unittest.mock import MagicMock
@@ -38,7 +39,8 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
:param config: Config to pass to the bot
:return: None
"""
mocker.patch('freqtrade.fiat_convert.Market', {'price_usd': 12345.0})
# mocker.patch('freqtrade.fiat_convert.Market', {'price_usd': 12345.0})
patch_coinmarketcap(mocker, {'price_usd': 12345.0})
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
@@ -50,6 +52,27 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
return FreqtradeBot(config, create_engine('sqlite://'))
def patch_coinmarketcap(mocker, value: Optional[Dict[str, float]] = None) -> None:
"""
Mocker to coinmarketcap to speed up tests
:param mocker: mocker to patch coinmarketcap class
:return: None
"""
tickermock = MagicMock(return_value={'price_usd': 12345.0})
listmock = MagicMock(return_value={'data': [{'id': 1, 'name': 'Bitcoin', 'symbol': 'BTC',
'website_slug': 'bitcoin'},
{'id': 1027, 'name': 'Ethereum', 'symbol': 'ETH',
'website_slug': 'ethereum'}
]})
mocker.patch.multiple(
'freqtrade.fiat_convert.Market',
ticker=tickermock,
listings=listmock,
)
@pytest.fixture(scope="function")
def default_conf():
""" Returns validated configuration suitable for most tests """

View File

@@ -123,7 +123,7 @@ def test_loss_calculation_has_limited_profit(init_hyperopt) -> None:
assert under > correct
def test_log_results_if_loss_improves(capsys) -> None:
def test_log_results_if_loss_improves(init_hyperopt, capsys) -> None:
hyperopt = _HYPEROPT
hyperopt.current_best_loss = 2
hyperopt.log_results(

View File

@@ -67,6 +67,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'*Close Rate:* `None`\n'
'*Current Rate:* `0.00001098`\n'
'*Close Profit:* `None`\n'
'*Stake Value:* `0.00099909`\n'
'*Current Profit:* `-0.59%`\n'
'*Open Order:* `(limit buy rem=0.00000000)`'
]
@@ -106,6 +107,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
assert 'just now' in result['Since'].all()
assert 'ETH/BTC' in result['Pair'].all()
assert '-0.59%' in result['Profit'].all()
assert 'Value' in result
def test_rpc_daily_profit(default_conf, update, ticker, fee,
@@ -288,22 +290,18 @@ def test_rpc_balance_handle(default_conf, mocker):
"""
Test rpc_balance() method
"""
mock_balance = [
{
'Currency': 'BTC',
'Balance': 10.0,
'Available': 12.0,
'Pending': 0.0,
'CryptoAddress': 'XXXX',
mock_balance = {
'BTC': {
'free': 10.0,
'total': 12.0,
'used': 2.0,
},
{
'Currency': 'ETH',
'Balance': 0.0,
'Available': 0.0,
'Pending': 0.0,
'CryptoAddress': 'XXXX',
'ETH': {
'free': 0.0,
'total': 0.0,
'used': 0.0,
}
]
}
patch_get_signal(mocker, (True, False))
mocker.patch.multiple(
@@ -324,15 +322,15 @@ def test_rpc_balance_handle(default_conf, mocker):
(error, res) = rpc.rpc_balance(default_conf['fiat_display_currency'])
assert not error
(trade, x, y, z) = res
assert prec_satoshi(x, 10)
assert prec_satoshi(z, 150000)
assert prec_satoshi(x, 12)
assert prec_satoshi(z, 180000)
assert 'USD' in y
assert len(trade) == 1
assert 'BTC' in trade[0]['currency']
assert prec_satoshi(trade[0]['available'], 12)
assert prec_satoshi(trade[0]['balance'], 10)
assert prec_satoshi(trade[0]['pending'], 0)
assert prec_satoshi(trade[0]['est_btc'], 10)
assert prec_satoshi(trade[0]['available'], 10)
assert prec_satoshi(trade[0]['balance'], 12)
assert prec_satoshi(trade[0]['pending'], 2)
assert prec_satoshi(trade[0]['est_btc'], 12)
def test_rpc_start(mocker, default_conf) -> None:

View File

@@ -554,36 +554,29 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None:
"""
Test _balance() method
"""
mock_balance = [
{
'Currency': 'BTC',
'Balance': 10.0,
'Available': 12.0,
'Pending': 0.0,
'CryptoAddress': 'XXXX',
mock_balance = {
'BTC': {
'total': 12.0,
'free': 12.0,
'used': 0.0
},
{
'Currency': 'ETH',
'Balance': 0.0,
'Available': 0.0,
'Pending': 0.0,
'CryptoAddress': 'XXXX',
'ETH': {
'total': 0.0,
'free': 0.0,
'used': 0.0
},
{
'Currency': 'USDT',
'Balance': 10000.0,
'Available': 0.0,
'Pending': 0.0,
'CryptoAddress': 'XXXX',
'USDT': {
'total': 10000.0,
'free': 10000.0,
'used': 0.0
},
{
'Currency': 'LTC',
'Balance': 10.0,
'Available': 10.0,
'Pending': 0.0,
'CryptoAddress': 'XXXX',
'LTC': {
'total': 10.0,
'free': 10.0,
'used': 0.0
}
]
}
def mock_ticker(symbol, refresh):
"""
@@ -621,12 +614,12 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None:
telegram._balance(bot=MagicMock(), update=update)
result = msg_mock.call_args_list[0][0][0]
assert msg_mock.call_count == 1
assert '*Currency*: BTC' in result
assert '*Currency*: ETH' not in result
assert '*Currency*: USDT' in result
assert 'Balance' in result
assert 'Est. BTC' in result
assert '*BTC*: 12.00000000' in result
assert '*BTC:*' in result
assert '*ETH:*' not in result
assert '*USDT:*' in result
assert 'Balance:' in result
assert 'Est. BTC:' in result
assert 'BTC: 14.00000000' in result
def test_zero_balance_handle(default_conf, update, mocker) -> None:
@@ -636,7 +629,7 @@ def test_zero_balance_handle(default_conf, update, mocker) -> None:
patch_get_signal(mocker, (True, False))
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
mocker.patch('freqtrade.freqtradebot.exchange.get_balances', return_value=[])
mocker.patch('freqtrade.freqtradebot.exchange.get_balances', return_value={})
msg_mock = MagicMock()
mocker.patch.multiple(

View File

@@ -38,10 +38,17 @@ def test_load_strategy_from_url(result):
def test_load_strategy_custom_directory(result):
resolver = StrategyResolver()
extra_dir = os.path.join('some', 'path')
with pytest.raises(
FileNotFoundError,
match=r".*No such file or directory: '{}'".format(extra_dir)):
resolver._load_strategy('TestStrategy', extra_dir)
if os.name == 'nt':
with pytest.raises(
FileNotFoundError,
match="FileNotFoundError: [WinError 3] The system cannot find the path specified: '{}'".format(extra_dir)):
resolver._load_strategy('TestStrategy', extra_dir)
else:
with pytest.raises(
FileNotFoundError,
match=r".*No such file or directory: '{}'".format(extra_dir)):
resolver._load_strategy('TestStrategy', extra_dir)
assert hasattr(resolver.strategy, 'populate_indicators')
assert 'adx' in resolver.strategy.populate_indicators(result)

View File

@@ -7,6 +7,7 @@ from unittest.mock import MagicMock
import pytest
from freqtrade.fiat_convert import CryptoFiat, CryptoToFiatConverter
from freqtrade.tests.conftest import patch_coinmarketcap
def test_pair_convertion_object():
@@ -123,12 +124,23 @@ def test_fiat_convert_get_price(mocker):
assert fiat_convert._pairs[0]._expiration is not expiration
def test_loadcryptomap(mocker):
patch_coinmarketcap(mocker)
fiat_convert = CryptoToFiatConverter()
assert len(fiat_convert._cryptomap) == 2
assert fiat_convert._cryptomap["BTC"] == "1"
def test_fiat_convert_without_network():
# Because CryptoToFiatConverter is a Singleton we reset the value of _coinmarketcap
fiat_convert = CryptoToFiatConverter()
cmc_temp = CryptoToFiatConverter._coinmarketcap
CryptoToFiatConverter._coinmarketcap = None
assert fiat_convert._coinmarketcap is None
assert fiat_convert._find_price(crypto_symbol='BTC', fiat_symbol='USD') == 0.0
CryptoToFiatConverter._coinmarketcap = cmc_temp

View File

@@ -8,7 +8,6 @@ import logging
import re
import time
from copy import deepcopy
from typing import Dict, Optional
from unittest.mock import MagicMock
import arrow
@@ -20,7 +19,7 @@ from freqtrade import DependencyException, OperationalException, TemporaryError
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Trade
from freqtrade.state import State
from freqtrade.tests.conftest import log_has
from freqtrade.tests.conftest import log_has, patch_coinmarketcap
# Functions for recurrent object patching
@@ -64,20 +63,6 @@ def patch_RPCManager(mocker) -> MagicMock:
return rpc_mock
def patch_coinmarketcap(mocker, value: Optional[Dict[str, float]] = None) -> None:
"""
Mocker to coinmarketcap to speed up tests
:param mocker: mocker to patch coinmarketcap class
:return: None
"""
mock = MagicMock()
if value:
mock.ticker = {'price_usd': 12345.0}
mocker.patch('freqtrade.fiat_convert.Market', mock)
# Unit tests
def test_freqtradebot_object() -> None:
"""
@@ -1458,7 +1443,7 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order)
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=[trades_for_order])
amount = float(sum(x['amount'] for x in trades_for_order))
trade = Trade(
pair='LTC/ETH',
@@ -1473,3 +1458,53 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996) from Order',
caplog.record_tuples)
def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, mocker):
"""
Test get_real_amount with split trades (multiple trades for this order)
"""
limit_buy_order = deepcopy(buy_order_fee)
limit_buy_order['fee'] = {'cost': 0.004}
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=[])
amount = float(sum(x['amount'] for x in trades_for_order))
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount does not change
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount
def test_get_real_amount_invalid(default_conf, trades_for_order, buy_order_fee, mocker):
"""
Test get_real_amount - fees in Stake currency
"""
# Remove "Currency" from fee dict
trades_for_order[0]['fee'] = {'cost': 0.008}
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order)
amount = sum(x['amount'] for x in trades_for_order)
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount does not change
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount

View File

@@ -375,3 +375,105 @@ def test_clean_dry_run_db(default_conf, fee):
# We have now only the prod
assert len(Trade.query.filter(Trade.open_order_id.isnot(None)).all()) == 1
def test_migrate_old(default_conf, fee):
"""
Test Database migration(starting with old pairformat)
"""
amount = 103.223
create_table_old = """CREATE TABLE IF NOT EXISTS "trades" (
id INTEGER NOT NULL,
exchange VARCHAR NOT NULL,
pair VARCHAR NOT NULL,
is_open BOOLEAN NOT NULL,
fee FLOAT NOT NULL,
open_rate FLOAT,
close_rate FLOAT,
close_profit FLOAT,
stake_amount FLOAT NOT NULL,
amount FLOAT,
open_date DATETIME NOT NULL,
close_date DATETIME,
open_order_id VARCHAR,
PRIMARY KEY (id),
CHECK (is_open IN (0, 1))
);"""
insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee,
open_rate, stake_amount, amount, open_date)
VALUES ('BITTREX', 'BTC_ETC', 1, {fee},
0.00258580, {stake}, {amount},
'2017-11-28 12:44:24.000000')
""".format(fee=fee.return_value,
stake=default_conf.get("stake_amount"),
amount=amount
)
engine = create_engine('sqlite://')
# Create table using the old format
engine.execute(create_table_old)
engine.execute(insert_table_old)
# Run init to test migration
init(default_conf, engine)
assert len(Trade.query.filter(Trade.id == 1).all()) == 1
trade = Trade.query.filter(Trade.id == 1).first()
assert trade.fee_open == fee.return_value
assert trade.fee_close == fee.return_value
assert trade.open_rate_requested is None
assert trade.close_rate_requested is None
assert trade.is_open == 1
assert trade.amount == amount
assert trade.stake_amount == default_conf.get("stake_amount")
assert trade.pair == "ETC/BTC"
assert trade.exchange == "bittrex"
def test_migrate_new(default_conf, fee):
"""
Test Database migration (starting with new pairformat)
"""
amount = 103.223
create_table_old = """CREATE TABLE IF NOT EXISTS "trades" (
id INTEGER NOT NULL,
exchange VARCHAR NOT NULL,
pair VARCHAR NOT NULL,
is_open BOOLEAN NOT NULL,
fee FLOAT NOT NULL,
open_rate FLOAT,
close_rate FLOAT,
close_profit FLOAT,
stake_amount FLOAT NOT NULL,
amount FLOAT,
open_date DATETIME NOT NULL,
close_date DATETIME,
open_order_id VARCHAR,
PRIMARY KEY (id),
CHECK (is_open IN (0, 1))
);"""
insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee,
open_rate, stake_amount, amount, open_date)
VALUES ('binance', 'ETC/BTC', 1, {fee},
0.00258580, {stake}, {amount},
'2019-11-28 12:44:24.000000')
""".format(fee=fee.return_value,
stake=default_conf.get("stake_amount"),
amount=amount
)
engine = create_engine('sqlite://')
# Create table using the old format
engine.execute(create_table_old)
engine.execute(insert_table_old)
# Run init to test migration
init(default_conf, engine)
assert len(Trade.query.filter(Trade.id == 1).all()) == 1
trade = Trade.query.filter(Trade.id == 1).first()
assert trade.fee_open == fee.return_value
assert trade.fee_close == fee.return_value
assert trade.open_rate_requested is None
assert trade.close_rate_requested is None
assert trade.is_open == 1
assert trade.amount == amount
assert trade.stake_amount == default_conf.get("stake_amount")
assert trade.pair == "ETC/BTC"
assert trade.exchange == "binance"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long