From 85af68d8075ea4df19ec6ea4e653da13d81228ef Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 24 Mar 2018 19:45:23 +0100 Subject: [PATCH 01/14] ccxt - make backtesting work --- freqtrade/optimize/__init__.py | 4 ++-- freqtrade/optimize/backtesting.py | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index a26744691..61694d884 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -35,7 +35,7 @@ def load_tickerdata_file( """ path = make_testdata_path(datadir) file = os.path.join(path, '{pair}-{ticker_interval}.json'.format( - pair=pair, + pair=pair.replace('/', '_'), ticker_interval=ticker_interval, )) gzipfile = file + '.gz' @@ -126,7 +126,7 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) -> interval ) - filepair = pair.replace("-", "_") + filepair = pair.replace("/", "_") filename = os.path.join(path, '{pair}-{interval}.json'.format( pair=filepair, interval=interval, diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 6202edead..2b583da3c 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -53,7 +53,10 @@ class Backtesting(object): self.tickerdata_to_dataframe = self.analyze.tickerdata_to_dataframe self.populate_buy_trend = self.analyze.populate_buy_trend self.populate_sell_trend = self.analyze.populate_sell_trend - exchange.init({'key': '', 'secret': ''}) + # Reest keys for backtesting + self.config['exchange']['key'] = '' + self.config['exchange']['secret'] = '' + exchange.init(self.config) @staticmethod def get_timeframe(data: Dict[str, DataFrame]) -> Tuple[arrow.Arrow, arrow.Arrow]: From ab6e32f6bbbbf681a24d4a9ee44d5e4242999943 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 24 Mar 2018 19:51:40 +0100 Subject: [PATCH 02/14] have backtest and dry-mode working partially revert d20e3f79be2ed7f3a8f4df270b165fe8b6c2e3b1 - Changing the OHLVC format should not be done at this time --- freqtrade/__init__.py | 8 ++++++++ freqtrade/analyze.py | 12 ++++++------ freqtrade/exchange/__init__.py | 36 ++++++++++++++++++++++++++++------ 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index cd4515a3b..16f8b3b4a 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -14,3 +14,11 @@ class OperationalException(BaseException): Requires manual intervention. This happens when an exchange returns an unexpected error during runtime. """ + + +class NetworkException(BaseException): + """ + Network related error. + This could happen when an exchange is congested, unavailable, or the user + has networking problems. Usually resolves itself after a time. + """ diff --git a/freqtrade/analyze.py b/freqtrade/analyze.py index decb73628..f3e44b57b 100644 --- a/freqtrade/analyze.py +++ b/freqtrade/analyze.py @@ -44,13 +44,13 @@ class Analyze(object): :param ticker: See exchange.get_ticker_history :return: DataFrame """ - cols = ['date', 'open', 'high', 'low', 'close', 'volume'] - frame = DataFrame(ticker, columns=cols) + columns = {'C': 'close', 'V': 'volume', 'O': 'open', 'H': 'high', 'L': 'low', 'T': 'date'} + frame = DataFrame(ticker) \ + .rename(columns=columns) + if 'BV' in frame: + frame.drop('BV', 1, inplace=True) + frame['date'] = to_datetime(frame['date'], utc=True, infer_datetime_format=True) - frame['date'] = to_datetime(frame['date'], - unit='ms', - utc=True, - infer_datetime_format=True) frame.sort_values('date', inplace=True) return frame diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 1b1d2f7aa..127bc7b69 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -6,11 +6,12 @@ import ccxt from random import randint from typing import List, Dict, Any, Optional from cachetools import cached, TTLCache +from datetime import datetime import arrow import requests -from freqtrade import OperationalException +from freqtrade import OperationalException, NetworkException logger = logging.getLogger(__name__) @@ -183,11 +184,34 @@ def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict: def get_ticker_history(pair: str, tick_interval) -> List[Dict]: # TODO: tickers need to be in format 1m,5m # fetch_ohlcv returns an [[datetime,o,h,l,c,v]] - if not _API.markets: - _API.load_markets() - ohlcv = _API.fetch_ohlcv(pair, str(tick_interval)+'m') + if 'fetchOHLCV' not in _API.has or not _API.has['fetchOHLCV']: + raise OperationalException( + 'Exhange {} does not support fetching historical candlestick data.'.format( + _API.name) + ) - return ohlcv + try: + history = _API.fetch_ohlcv(pair, timeframe=str(tick_interval)+"m") + history_json = [] + for candlestick in history: + history_json.append({ + 'T': datetime.fromtimestamp(candlestick[0]/1000.0).strftime('%Y-%m-%dT%H:%M:%S.%f'), + 'O': candlestick[1], + 'H': candlestick[2], + 'L': candlestick[3], + 'C': candlestick[4], + 'V': candlestick[5], + }) + return history_json + except IndexError as e: + logger.warning('Empty ticker history. Msg %s', str(e)) + return [] + except ccxt.NetworkError as e: + raise NetworkException( + 'Could not load ticker history due to networking error. Message: {}'.format(e) + ) + except ccxt.BaseError as e: + raise OperationalException('Could not fetch ticker data. Msg: {}'.format(e)) def cancel_order(order_id: str) -> None: @@ -235,7 +259,7 @@ def get_fee_taker() -> float: def get_fee() -> float: - return _API.fees['trading'] + return get_fee_taker() def get_wallet_health() -> List[Dict]: From 609c1eee5514a4c210cd789d7bd571eeed3ef64a Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 24 Mar 2018 20:02:13 +0100 Subject: [PATCH 03/14] fix persistance tests --- freqtrade/tests/conftest.py | 16 +++++----- freqtrade/tests/test_persistence.py | 48 ++++++++++++++--------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 07dc45a3e..5e290a27c 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -73,11 +73,11 @@ def default_conf(): "key": "key", "secret": "secret", "pair_whitelist": [ - "BTC_ETH", - "BTC_TKN", - "BTC_TRST", - "BTC_SWT", - "BTC_BCC" + "ETH/BTC", + "TKN/BTC", + "TRST/BTC", + "SWT/BTC", + "BCC/BTC" ] }, "telegram": { @@ -175,7 +175,7 @@ def limit_buy_order_old(): return { 'id': 'mocked_limit_buy_old', 'type': 'LIMIT_BUY', - 'pair': 'BTC_ETH', + 'pair': 'ETH/BTC', 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), 'rate': 0.00001099, 'amount': 90.99181073, @@ -188,7 +188,7 @@ def limit_sell_order_old(): return { 'id': 'mocked_limit_sell_old', 'type': 'LIMIT_SELL', - 'pair': 'BTC_ETH', + 'pair': 'ETH/BTC', 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), 'rate': 0.00001099, 'amount': 90.99181073, @@ -201,7 +201,7 @@ def limit_buy_order_old_partial(): return { 'id': 'mocked_limit_buy_old_partial', 'type': 'LIMIT_BUY', - 'pair': 'BTC_ETH', + 'pair': 'ETH/BTC', 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), 'rate': 0.00001099, 'amount': 90.99181073, diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py index 70199b12a..7d3ae3efc 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -4,7 +4,7 @@ import os import pytest from sqlalchemy import create_engine -from freqtrade.exchange import Exchanges +from freqtrade import exchange from freqtrade.persistence import Trade, init, clean_dry_run_db @@ -122,7 +122,7 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order): pair='BTC_ETH', stake_amount=0.001, fee=0.0025, - exchange=Exchanges.BITTREX, + exchange='binance', ) assert trade.open_order_id is None assert trade.open_rate is None @@ -146,10 +146,10 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order): def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order): trade = Trade( - pair='BTC_ETH', + pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange=Exchanges.BITTREX, + exchange='binance', ) trade.open_order_id = 'something' @@ -168,10 +168,10 @@ def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order): def test_calc_close_trade_price_exception(limit_buy_order): trade = Trade( - pair='BTC_ETH', + pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange=Exchanges.BITTREX, + exchange='binance', ) trade.open_order_id = 'something' @@ -181,10 +181,10 @@ def test_calc_close_trade_price_exception(limit_buy_order): def test_update_open_order(limit_buy_order): trade = Trade( - pair='BTC_ETH', + pair='ETH/BTC', stake_amount=1.00, fee=0.1, - exchange=Exchanges.BITTREX, + exchange='binance', ) assert trade.open_order_id is None @@ -203,10 +203,10 @@ def test_update_open_order(limit_buy_order): def test_update_invalid_order(limit_buy_order): trade = Trade( - pair='BTC_ETH', + pair='ETH/BTC', stake_amount=1.00, fee=0.1, - exchange=Exchanges.BITTREX, + exchange='binance', ) limit_buy_order['type'] = 'invalid' with pytest.raises(ValueError, match=r'Unknown order type'): @@ -215,10 +215,10 @@ def test_update_invalid_order(limit_buy_order): def test_calc_open_trade_price(limit_buy_order): trade = Trade( - pair='BTC_ETH', + pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange=Exchanges.BITTREX, + exchange='binance', ) trade.open_order_id = 'open_trade' trade.update(limit_buy_order) # Buy @ 0.00001099 @@ -232,10 +232,10 @@ def test_calc_open_trade_price(limit_buy_order): def test_calc_close_trade_price(limit_buy_order, limit_sell_order): trade = Trade( - pair='BTC_ETH', + pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange=Exchanges.BITTREX, + exchange='binance', ) trade.open_order_id = 'close_trade' trade.update(limit_buy_order) # Buy @ 0.00001099 @@ -253,10 +253,10 @@ def test_calc_close_trade_price(limit_buy_order, limit_sell_order): def test_calc_profit(limit_buy_order, limit_sell_order): trade = Trade( - pair='BTC_ETH', + pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange=Exchanges.BITTREX, + exchange='binance', ) trade.open_order_id = 'profit_percent' trade.update(limit_buy_order) # Buy @ 0.00001099 @@ -283,10 +283,10 @@ def test_calc_profit(limit_buy_order, limit_sell_order): def test_calc_profit_percent(limit_buy_order, limit_sell_order): trade = Trade( - pair='BTC_ETH', + pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange=Exchanges.BITTREX, + exchange='binance', ) trade.open_order_id = 'profit_percent' trade.update(limit_buy_order) # Buy @ 0.00001099 @@ -310,35 +310,35 @@ def test_clean_dry_run_db(default_conf): # Simulate dry_run entries trade = Trade( - pair='BTC_ETH', + pair='ETH/BTC', stake_amount=0.001, amount=123.0, fee=0.0025, open_rate=0.123, - exchange='BITTREX', + exchange='binance', open_order_id='dry_run_buy_12345' ) Trade.session.add(trade) trade = Trade( - pair='BTC_ETC', + pair='ETC/BTC', stake_amount=0.001, amount=123.0, fee=0.0025, open_rate=0.123, - exchange='BITTREX', + exchange='binance', open_order_id='dry_run_sell_12345' ) Trade.session.add(trade) # Simulate prod entry trade = Trade( - pair='BTC_ETC', + pair='ETC/BTC', stake_amount=0.001, amount=123.0, fee=0.0025, open_rate=0.123, - exchange='BITTREX', + exchange='binance', open_order_id='prod_buy_12345' ) Trade.session.add(trade) From 4dc1d7538e2bdb20cabb9c5ea0f2a65fe1694a56 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 24 Mar 2018 20:07:04 +0100 Subject: [PATCH 04/14] switch currencies to new format --- config.json.example | 22 +++++++++++----------- config_full.json.example | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/config.json.example b/config.json.example index 2c464a925..6a4f20cd6 100644 --- a/config.json.example +++ b/config.json.example @@ -13,19 +13,19 @@ "key": "your_exchange_key", "secret": "your_exchange_secret", "pair_whitelist": [ - "BTC_ETH", - "BTC_LTC", - "BTC_ETC", - "BTC_DASH", - "BTC_ZEC", - "BTC_XLM", - "BTC_NXT", - "BTC_POWR", - "BTC_ADA", - "BTC_XMR" + "ETH/BTC", + "LTC/BTC", + "ETC/BTC", + "DASH/BTC", + "ZEC/BTC", + "XLM/BTC", + "NXT/BTC", + "POWR/BTC", + "ADA/BTC", + "XMR/BTC" ], "pair_blacklist": [ - "BTC_DOGE" + "DOGE/BTC" ] }, "experimental": { diff --git a/config_full.json.example b/config_full.json.example index c74b59660..d0f9d1260 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -21,19 +21,19 @@ "key": "your_exchange_key", "secret": "your_exchange_secret", "pair_whitelist": [ - "BTC_ETH", - "BTC_LTC", - "BTC_ETC", - "BTC_DASH", - "BTC_ZEC", - "BTC_XLM", - "BTC_NXT", - "BTC_POWR", - "BTC_ADA", - "BTC_XMR" + "ETH/BTC", + "LTC/BTC", + "ETC/BTC", + "DASH/BTC", + "ZEC/BTC", + "XLM/BTC", + "NXT/BTC", + "POWR/BTC", + "ADA/BTC", + "XMR/BTC" ], "pair_blacklist": [ - "BTC_DOGE" + "DOGE/BTC" ] }, "experimental": { From a6587b209f217386f2b1ee8a815e71d9010ba5b6 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 24 Mar 2018 20:11:42 +0100 Subject: [PATCH 05/14] freqtradebot_tests - change currency to new format --- freqtrade/tests/test_freqtradebot.py | 29 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index d58b428da..4c99a4d6a 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -17,7 +17,6 @@ import requests from sqlalchemy import create_engine from freqtrade import DependencyException, OperationalException -from freqtrade.exchange import Exchanges from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import Trade from freqtrade.state import State @@ -263,7 +262,7 @@ def test_create_trade(default_conf, ticker, limit_buy_order, mocker) -> None: assert trade.stake_amount == 0.001 assert trade.is_open assert trade.open_date is not None - assert trade.exchange == Exchanges.BITTREX.name + assert trade.exchange == 'BITTREX' # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) @@ -333,8 +332,8 @@ def test_create_trade_no_pairs(default_conf, ticker, mocker) -> None: ) conf = deepcopy(default_conf) - conf['exchange']['pair_whitelist'] = ["BTC_ETH"] - conf['exchange']['pair_blacklist'] = ["BTC_ETH"] + conf['exchange']['pair_whitelist'] = ["ETH/BTC"] + conf['exchange']['pair_blacklist'] = ["ETH/BTC"] freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) freqtrade.create_trade() @@ -358,8 +357,8 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, mocker) -> ) conf = deepcopy(default_conf) - conf['exchange']['pair_whitelist'] = ["BTC_ETH"] - conf['exchange']['pair_blacklist'] = ["BTC_ETH"] + conf['exchange']['pair_whitelist'] = ["ETH/BTC"] + conf['exchange']['pair_blacklist'] = ["ETH/BTC"] freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) freqtrade.create_trade() @@ -793,7 +792,7 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, mo freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) trade_buy = Trade( - pair='BTC_ETH', + pair='ETH/BTC', open_rate=0.00001099, exchange='BITTREX', open_order_id='123456789', @@ -832,7 +831,7 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) trade_sell = Trade( - pair='BTC_ETH', + pair='ETH/BTC', open_rate=0.00001099, exchange='BITTREX', open_order_id='123456789', @@ -871,7 +870,7 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) trade_buy = Trade( - pair='BTC_ETH', + pair='ETH/BTC', open_rate=0.00001099, exchange='BITTREX', open_order_id='123456789', @@ -918,7 +917,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) - freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) trade_buy = Trade( - pair='BTC_ETH', + pair='ETH/BTC', open_rate=0.00001099, exchange='BITTREX', open_order_id='123456789', @@ -931,7 +930,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) - Trade.session.add(trade_buy) regexp = re.compile( - 'Cannot query order for Trade(id=1, pair=BTC_ETH, amount=90.99181073, ' + 'Cannot query order for Trade(id=1, pair=ETH/BTC, amount=90.99181073, ' 'open_rate=0.00001099, open_since=10 hours ago) due to Traceback (most ' 'recent call last):\n.*' ) @@ -1024,7 +1023,7 @@ def test_execute_sell_up(default_conf, ticker, ticker_sell_up, mocker) -> None: assert rpc_mock.call_count == 2 assert 'Selling' in rpc_mock.call_args_list[-1][0][0] - assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0] + assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0] assert 'Amount' in rpc_mock.call_args_list[-1][0][0] assert 'Profit' in rpc_mock.call_args_list[-1][0][0] assert '0.00001172' in rpc_mock.call_args_list[-1][0][0] @@ -1064,7 +1063,7 @@ def test_execute_sell_down(default_conf, ticker, ticker_sell_down, mocker) -> No assert rpc_mock.call_count == 2 assert 'Selling' in rpc_mock.call_args_list[-1][0][0] - assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0] + assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0] assert 'Amount' in rpc_mock.call_args_list[-1][0][0] assert '0.00001044' in rpc_mock.call_args_list[-1][0][0] assert 'loss: -5.48%, -0.00005492' in rpc_mock.call_args_list[-1][0][0] @@ -1103,7 +1102,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, ticker_sell_up, assert rpc_mock.call_count == 2 assert 'Selling' in rpc_mock.call_args_list[-1][0][0] - assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0] + assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0] assert 'Amount' in rpc_mock.call_args_list[-1][0][0] assert '0.00001172' in rpc_mock.call_args_list[-1][0][0] assert '(profit: 6.11%, 0.00006126)' in rpc_mock.call_args_list[-1][0][0] @@ -1143,7 +1142,7 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, assert rpc_mock.call_count == 2 assert 'Selling' in rpc_mock.call_args_list[-1][0][0] - assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0] + assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0] assert '0.00001044' in rpc_mock.call_args_list[-1][0][0] assert 'loss: -5.48%, -0.00005492' in rpc_mock.call_args_list[-1][0][0] From 22ef8603122f9b0eea44c71e390817db5b23c919 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 24 Mar 2018 20:32:15 +0100 Subject: [PATCH 06/14] Change freqbottest currencies --- freqtrade/tests/test_freqtradebot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 4c99a4d6a..02edf3322 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -215,15 +215,15 @@ def test_gen_pair_whitelist(mocker, default_conf, get_market_summaries_data) -> # Test to retrieved BTC sorted on BaseVolume whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC') - assert whitelist == ['BTC_ZCL', 'BTC_ZEC', 'BTC_XZC', 'BTC_XWC'] + assert whitelist == ['ZCL/BTC', 'ZEC/BTC', 'XZC/BTC', 'XWC/BTC'] # Test to retrieved BTC sorted on OpenBuyOrders whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC', key='OpenBuyOrders') - assert whitelist == ['BTC_XWC', 'BTC_ZCL', 'BTC_ZEC', 'BTC_XZC'] + assert whitelist == ['XWC/BTC', 'ZCL/BTC', 'ZEC/BTC', 'XZC/BTC'] # Test with USDT sorted on BaseVolume whitelist = freqtrade._gen_pair_whitelist(base_currency='USDT') - assert whitelist == ['USDT_XRP', 'USDT_XVG', 'USDT_XMR', 'USDT_ZEC'] + assert whitelist == ['XRP/USDT', 'XVG/USDT', 'XMR/USDT', 'ZEC/USDT'] # Test with ETH (our fixture does not have ETH, but Bittrex returns them) whitelist = freqtrade._gen_pair_whitelist(base_currency='ETH') @@ -424,7 +424,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order, assert trade.stake_amount == default_conf['stake_amount'] assert trade.is_open assert trade.open_date is not None - assert trade.exchange == Exchanges.BITTREX.name + assert trade.exchange == "BITTREX" assert trade.open_rate == 0.00001099 assert trade.amount == 90.99181073703367 From 82a21442964995645c5b50f30cd1f7159e0d7480 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 24 Mar 2018 20:32:42 +0100 Subject: [PATCH 07/14] change format of health fixture and get_market_summaries fixture --- freqtrade/tests/conftest.py | 303 +++++++++++++++++++----------------- 1 file changed, 163 insertions(+), 140 deletions(-) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 5e290a27c..d3d2b4d9a 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -128,32 +128,31 @@ def ticker_sell_down(): @pytest.fixture def health(): - return MagicMock(return_value=[{ - 'Currency': 'BTC', - 'IsActive': True, - 'LastChecked': '2017-11-13T20:15:00.00', - 'Notice': None - }, { - 'Currency': 'ETH', - 'IsActive': True, - 'LastChecked': '2017-11-13T20:15:00.00', - 'Notice': None - }, { - 'Currency': 'TRST', - 'IsActive': True, - 'LastChecked': '2017-11-13T20:15:00.00', - 'Notice': None - }, { - 'Currency': 'SWT', - 'IsActive': True, - 'LastChecked': '2017-11-13T20:15:00.00', - 'Notice': None - }, { - 'Currency': 'BCC', - 'IsActive': False, - 'LastChecked': '2017-11-13T20:15:00.00', - 'Notice': None - }]) + return MagicMock(return_value={ + "ETH/BTC": { + 'base': 'ETH', + 'active': True, + 'LastChecked': '2017-11-13T20:15:00.00', + 'Notice': None + }, + "TRST/BTC": { + 'base': 'TRST', + 'active': True, + 'LastChecked': '2017-11-13T20:15:00.00', + 'Notice': None + }, + "SWT/BTC": { + 'base': 'SWT', + 'active': True, + 'LastChecked': '2017-11-13T20:15:00.00', + 'Notice': None + }, + "BCC/BTC": { + 'base': 'BCC', + 'active': False, + 'LastChecked': '2017-11-13T20:15:00.00', + 'Notice': None + }}) @pytest.fixture @@ -307,125 +306,149 @@ def get_market_summaries_data(): 8 entries. 4 BTC, 4 USTD :return: JSON market summaries """ - return [ - { - 'Ask': 1.316e-05, - 'BaseVolume': 5.72599471, - 'Bid': 1.3e-05, - 'Created': '2014-04-14T00:00:00', - 'High': 1.414e-05, - 'Last': 1.298e-05, - 'Low': 1.282e-05, - 'MarketName': 'BTC-XWC', - 'OpenBuyOrders': 2000, - 'OpenSellOrders': 1484, - 'PrevDay': 1.376e-05, - 'TimeStamp': '2018-02-05T01:32:40.493', - 'Volume': 424041.21418375 + return { + 'XWC/BTC': { + 'symbol': 'XWC/BTC', + 'info': { + 'Ask': 1.316e-05, + 'BaseVolume': 5.72599471, + 'Bid': 1.3e-05, + 'Created': '2014-04-14T00:00:00', + 'High': 1.414e-05, + 'Last': 1.298e-05, + 'Low': 1.282e-05, + 'MarketName': 'BTC-XWC', + 'OpenBuyOrders': 2000, + 'OpenSellOrders': 1484, + 'PrevDay': 1.376e-05, + 'TimeStamp': '2018-02-05T01:32:40.493', + 'Volume': 424041.21418375 + } }, - { - 'Ask': 0.00627051, - 'BaseVolume': 93.23302388, - 'Bid': 0.00618192, - 'Created': '2016-10-20T04:48:30.387', - 'High': 0.00669897, - 'Last': 0.00618192, - 'Low': 0.006, - 'MarketName': 'BTC-XZC', - 'OpenBuyOrders': 343, - 'OpenSellOrders': 2037, - 'PrevDay': 0.00668229, - 'TimeStamp': '2018-02-05T01:32:43.383', - 'Volume': 14863.60730702 + 'XZC/BTC': { + 'symbol': 'XZC/BTC', + 'info': { + 'Ask': 0.00627051, + 'BaseVolume': 93.23302388, + 'Bid': 0.00618192, + 'Created': '2016-10-20T04:48:30.387', + 'High': 0.00669897, + 'Last': 0.00618192, + 'Low': 0.006, + 'MarketName': 'BTC-XZC', + 'OpenBuyOrders': 343, + 'OpenSellOrders': 2037, + 'PrevDay': 0.00668229, + 'TimeStamp': '2018-02-05T01:32:43.383', + 'Volume': 14863.60730702 + } }, - { - 'Ask': 0.01137247, - 'BaseVolume': 383.55922657, - 'Bid': 0.01136006, - 'Created': '2016-11-15T20:29:59.73', - 'High': 0.012, - 'Last': 0.01137247, - 'Low': 0.01119883, - 'MarketName': 'BTC-ZCL', - 'OpenBuyOrders': 1332, - 'OpenSellOrders': 5317, - 'PrevDay': 0.01179603, - 'TimeStamp': '2018-02-05T01:32:42.773', - 'Volume': 33308.07358285 + 'ZCL/BTC': { + 'symbol': 'ZCL/BTC', + 'info': { + 'Ask': 0.01137247, + 'BaseVolume': 383.55922657, + 'Bid': 0.01136006, + 'Created': '2016-11-15T20:29:59.73', + 'High': 0.012, + 'Last': 0.01137247, + 'Low': 0.01119883, + 'MarketName': 'BTC-ZCL', + 'OpenBuyOrders': 1332, + 'OpenSellOrders': 5317, + 'PrevDay': 0.01179603, + 'TimeStamp': '2018-02-05T01:32:42.773', + 'Volume': 33308.07358285 + } }, - { - 'Ask': 0.04155821, - 'BaseVolume': 274.75369074, - 'Bid': 0.04130002, - 'Created': '2016-10-28T17:13:10.833', - 'High': 0.04354429, - 'Last': 0.041585, - 'Low': 0.0413, - 'MarketName': 'BTC-ZEC', - 'OpenBuyOrders': 863, - 'OpenSellOrders': 5579, - 'PrevDay': 0.0429, - 'TimeStamp': '2018-02-05T01:32:43.21', - 'Volume': 6479.84033259 + 'ZEC/BTC': { + 'symbol': 'ZEC/BTC', + 'info': { + 'Ask': 0.04155821, + 'BaseVolume': 274.75369074, + 'Bid': 0.04130002, + 'Created': '2016-10-28T17:13:10.833', + 'High': 0.04354429, + 'Last': 0.041585, + 'Low': 0.0413, + 'MarketName': 'BTC-ZEC', + 'OpenBuyOrders': 863, + 'OpenSellOrders': 5579, + 'PrevDay': 0.0429, + 'TimeStamp': '2018-02-05T01:32:43.21', + 'Volume': 6479.84033259 + } }, - { - 'Ask': 210.99999999, - 'BaseVolume': 615132.70989532, - 'Bid': 210.05503736, - 'Created': '2017-07-21T01:08:49.397', - 'High': 257.396, - 'Last': 211.0, - 'Low': 209.05333589, - 'MarketName': 'USDT-XMR', - 'OpenBuyOrders': 180, - 'OpenSellOrders': 1203, - 'PrevDay': 247.93528899, - 'TimeStamp': '2018-02-05T01:32:43.117', - 'Volume': 2688.17410793 + 'XMR/USDT': { + 'symbol': 'XMR/USDT', + 'info': { + 'Ask': 210.99999999, + 'BaseVolume': 615132.70989532, + 'Bid': 210.05503736, + 'Created': '2017-07-21T01:08:49.397', + 'High': 257.396, + 'Last': 211.0, + 'Low': 209.05333589, + 'MarketName': 'USDT-XMR', + 'OpenBuyOrders': 180, + 'OpenSellOrders': 1203, + 'PrevDay': 247.93528899, + 'TimeStamp': '2018-02-05T01:32:43.117', + 'Volume': 2688.17410793 + } }, - { - 'Ask': 0.79589979, - 'BaseVolume': 9349557.01853031, - 'Bid': 0.789226, - 'Created': '2017-07-14T17:10:10.737', - 'High': 0.977, - 'Last': 0.79589979, - 'Low': 0.781, - 'MarketName': 'USDT-XRP', - 'OpenBuyOrders': 1075, - 'OpenSellOrders': 6508, - 'PrevDay': 0.93300218, - 'TimeStamp': '2018-02-05T01:32:42.383', - 'Volume': 10801663.00788851 + 'XRP/USDT': { + 'symbol': 'XRP/USDT', + 'info': { + 'Ask': 0.79589979, + 'BaseVolume': 9349557.01853031, + 'Bid': 0.789226, + 'Created': '2017-07-14T17:10:10.737', + 'High': 0.977, + 'Last': 0.79589979, + 'Low': 0.781, + 'MarketName': 'USDT-XRP', + 'OpenBuyOrders': 1075, + 'OpenSellOrders': 6508, + 'PrevDay': 0.93300218, + 'TimeStamp': '2018-02-05T01:32:42.383', + 'Volume': 10801663.00788851 + } }, - { - 'Ask': 0.05154982, - 'BaseVolume': 2311087.71232136, - 'Bid': 0.05040107, - 'Created': '2017-12-29T19:29:18.357', - 'High': 0.06668561, - 'Last': 0.0508, - 'Low': 0.05006731, - 'MarketName': 'USDT-XVG', - 'OpenBuyOrders': 655, - 'OpenSellOrders': 5544, - 'PrevDay': 0.0627, - 'TimeStamp': '2018-02-05T01:32:41.507', - 'Volume': 40031424.2152716 + 'XVG/USDT': { + 'symbol': 'XVG/USDT', + 'info': { + 'Ask': 0.05154982, + 'BaseVolume': 2311087.71232136, + 'Bid': 0.05040107, + 'Created': '2017-12-29T19:29:18.357', + 'High': 0.06668561, + 'Last': 0.0508, + 'Low': 0.05006731, + 'MarketName': 'USDT-XVG', + 'OpenBuyOrders': 655, + 'OpenSellOrders': 5544, + 'PrevDay': 0.0627, + 'TimeStamp': '2018-02-05T01:32:41.507', + 'Volume': 40031424.2152716 + } }, - { - 'Ask': 332.65500022, - 'BaseVolume': 562911.87455665, - 'Bid': 330.00000001, - 'Created': '2017-07-14T17:10:10.673', - 'High': 401.59999999, - 'Last': 332.65500019, - 'Low': 330.0, - 'MarketName': 'USDT-ZEC', - 'OpenBuyOrders': 161, - 'OpenSellOrders': 1731, - 'PrevDay': 391.42, - 'TimeStamp': '2018-02-05T01:32:42.947', - 'Volume': 1571.09647946 + 'ZEC/USDT': { + 'symbol': 'ZEC/USDT', + 'info': { + 'Ask': 332.65500022, + 'BaseVolume': 562911.87455665, + 'Bid': 330.00000001, + 'Created': '2017-07-14T17:10:10.673', + 'High': 401.59999999, + 'Last': 332.65500019, + 'Low': 330.0, + 'MarketName': 'USDT-ZEC', + 'OpenBuyOrders': 161, + 'OpenSellOrders': 1731, + 'PrevDay': 391.42, + 'TimeStamp': '2018-02-05T01:32:42.947', + 'Volume': 1571.09647946 + } } - ] + } From 32222ae6ef6fa2f7d6d0d5bf39a0e29358991889 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 24 Mar 2018 20:42:51 +0100 Subject: [PATCH 08/14] Fix tests in acl_pair --- freqtrade/tests/test_acl_pair.py | 130 +++++++++++++++++-------------- 1 file changed, 70 insertions(+), 60 deletions(-) diff --git a/freqtrade/tests/test_acl_pair.py b/freqtrade/tests/test_acl_pair.py index b5f52774d..174f1fde9 100644 --- a/freqtrade/tests/test_acl_pair.py +++ b/freqtrade/tests/test_acl_pair.py @@ -12,77 +12,87 @@ def whitelist_conf(): config['stake_currency'] = 'BTC' config['exchange']['pair_whitelist'] = [ - 'BTC_ETH', - 'BTC_TKN', - 'BTC_TRST', - 'BTC_SWT', - 'BTC_BCC' + 'ETH/BTC', + 'TKN/BTC', + 'TRST/BTC', + 'SWT/BTC', + 'BCC/BTC' ] config['exchange']['pair_blacklist'] = [ - 'BTC_BLK' + 'BLK/BTC' ] return config def get_market_summaries(): - return [{ - 'MarketName': 'BTC-TKN', - 'High': 0.00000919, - 'Low': 0.00000820, - 'Volume': 74339.61396015, - 'Last': 0.00000820, - 'BaseVolume': 1664, - 'TimeStamp': '2014-07-09T07:19:30.15', - 'Bid': 0.00000820, - 'Ask': 0.00000831, - 'OpenBuyOrders': 15, - 'OpenSellOrders': 15, - 'PrevDay': 0.00000821, - 'Created': '2014-03-20T06:00:00', - 'DisplayMarketName': '' - }, { - 'MarketName': 'BTC-ETH', - 'High': 0.00000072, - 'Low': 0.00000001, - 'Volume': 166340678.42280999, - 'Last': 0.00000005, - 'BaseVolume': 42, - 'TimeStamp': '2014-07-09T07:21:40.51', - 'Bid': 0.00000004, - 'Ask': 0.00000005, - 'OpenBuyOrders': 18, - 'OpenSellOrders': 18, - 'PrevDay': 0.00000002, - 'Created': '2014-05-30T07:57:49.637', - 'DisplayMarketName': '' - }, { - 'MarketName': 'BTC-BLK', - 'High': 0.00000072, - 'Low': 0.00000001, - 'Volume': 166340678.42280999, - 'Last': 0.00000005, - 'BaseVolume': 3, - 'TimeStamp': '2014-07-09T07:21:40.51', - 'Bid': 0.00000004, - 'Ask': 0.00000005, - 'OpenBuyOrders': 18, - 'OpenSellOrders': 18, - 'PrevDay': 0.00000002, - 'Created': '2014-05-30T07:57:49.637', - 'DisplayMarketName': '' - }] + return { + 'TKN/BTC': { + 'symbol': 'TKN/BTC', + 'info': { + 'High': 0.00000919, + 'Low': 0.00000820, + 'Volume': 74339.61396015, + 'Last': 0.00000820, + 'BaseVolume': 1664, + 'TimeStamp': '2014-07-09T07:19:30.15', + 'Bid': 0.00000820, + 'Ask': 0.00000831, + 'OpenBuyOrders': 15, + 'OpenSellOrders': 15, + 'PrevDay': 0.00000821, + 'Created': '2014-03-20T06:00:00', + 'DisplayMarketName': '' + } + }, + 'ETH/BTC': { + 'symbol': 'ETH/BTC', + 'info': { + 'High': 0.00000072, + 'Low': 0.00000001, + 'Volume': 166340678.42280999, + 'Last': 0.00000005, + 'BaseVolume': 42, + 'TimeStamp': '2014-07-09T07:21:40.51', + 'Bid': 0.00000004, + 'Ask': 0.00000005, + 'OpenBuyOrders': 18, + 'OpenSellOrders': 18, + 'PrevDay': 0.00000002, + 'Created': '2014-05-30T07:57:49.637', + 'DisplayMarketName': '' + } + }, + 'BLK/BTC': { + 'symbol': 'BLK/BTC', + 'info': { + 'High': 0.00000072, + 'Low': 0.00000001, + 'Volume': 166340678.42280999, + 'Last': 0.00000005, + 'BaseVolume': 3, + 'TimeStamp': '2014-07-09T07:21:40.51', + 'Bid': 0.00000004, + 'Ask': 0.00000005, + 'OpenBuyOrders': 18, + 'OpenSellOrders': 18, + 'PrevDay': 0.00000002, + 'Created': '2014-05-30T07:57:49.637', + 'DisplayMarketName': '' + }} + } def get_health(): - return [{'Currency': 'ETH', 'IsActive': True}, - {'Currency': 'TKN', 'IsActive': True}, - {'Currency': 'BLK', 'IsActive': True}] + return { + 'ETH/BTC': {'base': 'ETH', 'active': True}, + 'TKN/BTC': {'base': 'TKN', 'active': True}, + 'BLK/BTC': {'base': 'BLK', 'active': True}} def get_health_empty(): - return [] + return {} def test_refresh_market_pair_not_in_whitelist(mocker): @@ -92,10 +102,10 @@ def test_refresh_market_pair_not_in_whitelist(mocker): mocker.patch('freqtrade.freqtradebot.exchange.get_wallet_health', get_health) refreshedwhitelist = freqtradebot._refresh_whitelist( - conf['exchange']['pair_whitelist'] + ['BTC_XXX'] + conf['exchange']['pair_whitelist'] + ['XXX/BTC'] ) # List ordered by BaseVolume - whitelist = ['BTC_ETH', 'BTC_TKN'] + whitelist = ['ETH/BTC', 'TKN/BTC'] # Ensure all except those in whitelist are removed assert whitelist == refreshedwhitelist @@ -108,7 +118,7 @@ def test_refresh_whitelist(mocker): refreshedwhitelist = freqtradebot._refresh_whitelist(conf['exchange']['pair_whitelist']) # List ordered by BaseVolume - whitelist = ['BTC_ETH', 'BTC_TKN'] + whitelist = ['ETH/BTC', 'TKN/BTC'] # Ensure all except those in whitelist are removed assert whitelist == refreshedwhitelist @@ -123,7 +133,7 @@ def test_refresh_whitelist_dynamic(mocker): ) # argument: use the whitelist dynamically by exchange-volume - whitelist = ['BTC_TKN', 'BTC_ETH'] + whitelist = ['TKN/BTC', 'ETH/BTC'] refreshedwhitelist = freqtradebot._refresh_whitelist( freqtradebot._gen_pair_whitelist(conf['stake_currency']) From 0a068db28548f7a9abc39516bba272622cd8839a Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 24 Mar 2018 20:59:09 +0100 Subject: [PATCH 09/14] Switch rpc_test to new currency style --- freqtrade/tests/rpc/test_rpc.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index 50943b1bc..191916488 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -59,7 +59,7 @@ def test_rpc_trade_status(default_conf, ticker, mocker) -> None: result_message = [ '*Trade ID:* `1`\n' '*Current Pair:* ' - '[BTC_ETH](https://www.bittrex.com/Market/Index?MarketName=BTC-ETH)\n' + '[ETH/BTC](https://bittrex.com/Market/Index?MarketName=BTC-ETH)\n' '*Open Since:* `just now`\n' '*Amount:* `90.99181074`\n' '*Open Rate:* `0.00001099`\n' @@ -70,7 +70,7 @@ def test_rpc_trade_status(default_conf, ticker, mocker) -> None: '*Open Order:* `(LIMIT_BUY rem=0.00000000)`' ] assert result == result_message - assert trade.find('[BTC_ETH]') >= 0 + assert trade.find('[ETH/BTC]') >= 0 def test_rpc_status_table(default_conf, ticker, mocker) -> None: @@ -102,7 +102,7 @@ def test_rpc_status_table(default_conf, ticker, mocker) -> None: freqtradebot.create_trade() (error, result) = rpc.rpc_status_table() assert 'just now' in result['Since'].all() - assert 'BTC_ETH' in result['Pair'].all() + assert 'ETH/BTC' in result['Pair'].all() assert '-0.59%' in result['Profit'].all() @@ -214,7 +214,7 @@ def test_rpc_trade_statistics( assert stats['first_trade_date'] == 'just now' assert stats['latest_trade_date'] == 'just now' assert stats['avg_duration'] == '0:00:00' - assert stats['best_pair'] == 'BTC_ETH' + assert stats['best_pair'] == 'ETH/BTC' assert prec_satoshi(stats['best_rate'], 6.2) @@ -274,7 +274,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, ticker_sell_u assert stats['first_trade_date'] == 'just now' assert stats['latest_trade_date'] == 'just now' assert stats['avg_duration'] == '0:00:00' - assert stats['best_pair'] == 'BTC_ETH' + assert stats['best_pair'] == 'ETH/BTC' assert prec_satoshi(stats['best_rate'], 6.2) @@ -509,7 +509,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, (error, res) = rpc.rpc_performance() assert not error assert len(res) == 1 - assert res[0]['pair'] == 'BTC_ETH' + assert res[0]['pair'] == 'ETH/BTC' assert res[0]['count'] == 1 assert prec_satoshi(res[0]['profit'], 6.2) From ae803474f97e6eff20379dc5ac50ad33b0874ce4 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 24 Mar 2018 20:59:25 +0100 Subject: [PATCH 10/14] switch rpc_telgram to new style and make it pass --- freqtrade/tests/rpc/test_rpc_telegram.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 4796b077e..2df43f0fc 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -248,7 +248,8 @@ def test_status(default_conf, update, mocker, ticker) -> None: mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_pair_detail_url=MagicMock() ) msg_mock = MagicMock() status_table = MagicMock() @@ -319,7 +320,7 @@ def test_status_handle(default_conf, update, ticker, mocker) -> None: telegram._status(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 - assert '[BTC_ETH]' in msg_mock.call_args_list[0][0][0] + assert '[ETH/BTC]' in msg_mock.call_args_list[0][0][0] def test_status_table_handle(default_conf, update, ticker, mocker) -> None: @@ -369,7 +370,7 @@ def test_status_table_handle(default_conf, update, ticker, mocker) -> None: fields = re.sub('[ ]+', ' ', line[2].strip()).split(' ') assert int(fields[0]) == 1 - assert fields[1] == 'BTC_ETH' + assert fields[1] == 'ETH/BTC' assert msg_mock.call_count == 1 @@ -387,7 +388,8 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_pair_detail_url=MagicMock() ) msg_mock = MagicMock() mocker.patch.multiple( @@ -541,7 +543,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, assert '∙ `0.00006217 BTC (6.20%)`' in msg_mock.call_args_list[-1][0][0] assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0] - assert '*Best Performing:* `BTC_ETH: 6.20%`' in msg_mock.call_args_list[-1][0][0] + assert '*Best Performing:* `ETH/BTC: 6.20%`' in msg_mock.call_args_list[-1][0][0] def test_telegram_balance_handle(default_conf, update, mocker) -> None: @@ -779,7 +781,7 @@ def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker) assert rpc_mock.call_count == 2 assert 'Selling' in rpc_mock.call_args_list[-1][0][0] - assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0] + assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0] assert 'Amount' in rpc_mock.call_args_list[-1][0][0] assert '0.00001172' in rpc_mock.call_args_list[-1][0][0] assert 'profit: 6.11%, 0.00006126' in rpc_mock.call_args_list[-1][0][0] @@ -822,7 +824,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, m assert rpc_mock.call_count == 2 assert 'Selling' in rpc_mock.call_args_list[-1][0][0] - assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0] + assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0] assert 'Amount' in rpc_mock.call_args_list[-1][0][0] assert '0.00001044' in rpc_mock.call_args_list[-1][0][0] assert 'loss: -5.48%, -0.00005492' in rpc_mock.call_args_list[-1][0][0] @@ -838,6 +840,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, mocker) -> None: mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0) rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock()) mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) + mocker.patch('freqtrade.exchange.get_pair_detail_url', MagicMock()) mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), @@ -942,7 +945,7 @@ def test_performance_handle(default_conf, update, ticker, limit_buy_order, telegram._performance(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 assert 'Performance' in msg_mock.call_args_list[0][0][0] - assert 'BTC_ETH\t6.20% (1)' in msg_mock.call_args_list[0][0][0] + assert 'ETH/BTC\t6.20% (1)' in msg_mock.call_args_list[0][0][0] def test_performance_handle_invalid(default_conf, update, mocker) -> None: From b07ee26e0869c18d471f8d833cdcc36423cff75d Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 25 Mar 2018 12:57:59 +0200 Subject: [PATCH 11/14] Revert testing exchange to bittrex --- freqtrade/tests/test_persistence.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py index 7d3ae3efc..c09774a37 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -122,7 +122,7 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order): pair='BTC_ETH', stake_amount=0.001, fee=0.0025, - exchange='binance', + exchange='bittrex', ) assert trade.open_order_id is None assert trade.open_rate is None @@ -149,7 +149,7 @@ def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order): pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange='binance', + exchange='bittrex', ) trade.open_order_id = 'something' @@ -171,7 +171,7 @@ def test_calc_close_trade_price_exception(limit_buy_order): pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange='binance', + exchange='bittrex', ) trade.open_order_id = 'something' @@ -184,7 +184,7 @@ def test_update_open_order(limit_buy_order): pair='ETH/BTC', stake_amount=1.00, fee=0.1, - exchange='binance', + exchange='bittrex', ) assert trade.open_order_id is None @@ -206,7 +206,7 @@ def test_update_invalid_order(limit_buy_order): pair='ETH/BTC', stake_amount=1.00, fee=0.1, - exchange='binance', + exchange='bittrex', ) limit_buy_order['type'] = 'invalid' with pytest.raises(ValueError, match=r'Unknown order type'): @@ -218,7 +218,7 @@ def test_calc_open_trade_price(limit_buy_order): pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange='binance', + exchange='bittrex', ) trade.open_order_id = 'open_trade' trade.update(limit_buy_order) # Buy @ 0.00001099 @@ -235,7 +235,7 @@ def test_calc_close_trade_price(limit_buy_order, limit_sell_order): pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange='binance', + exchange='bittrex', ) trade.open_order_id = 'close_trade' trade.update(limit_buy_order) # Buy @ 0.00001099 @@ -256,7 +256,7 @@ def test_calc_profit(limit_buy_order, limit_sell_order): pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange='binance', + exchange='bittrex', ) trade.open_order_id = 'profit_percent' trade.update(limit_buy_order) # Buy @ 0.00001099 @@ -286,7 +286,7 @@ def test_calc_profit_percent(limit_buy_order, limit_sell_order): pair='ETH/BTC', stake_amount=0.001, fee=0.0025, - exchange='binance', + exchange='bittrex', ) trade.open_order_id = 'profit_percent' trade.update(limit_buy_order) # Buy @ 0.00001099 @@ -315,7 +315,7 @@ def test_clean_dry_run_db(default_conf): amount=123.0, fee=0.0025, open_rate=0.123, - exchange='binance', + exchange='bittrex', open_order_id='dry_run_buy_12345' ) Trade.session.add(trade) @@ -326,7 +326,7 @@ def test_clean_dry_run_db(default_conf): amount=123.0, fee=0.0025, open_rate=0.123, - exchange='binance', + exchange='bittrex', open_order_id='dry_run_sell_12345' ) Trade.session.add(trade) @@ -338,7 +338,7 @@ def test_clean_dry_run_db(default_conf): amount=123.0, fee=0.0025, open_rate=0.123, - exchange='binance', + exchange='bittrex', open_order_id='prod_buy_12345' ) Trade.session.add(trade) From dbb0a6261f6e8dfa748a0883945896b72ef53657 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 25 Mar 2018 13:01:33 +0200 Subject: [PATCH 12/14] don't raise exceptions from get_ticker_history --- freqtrade/exchange/__init__.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 127bc7b69..2a9222141 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -185,10 +185,8 @@ def get_ticker_history(pair: str, tick_interval) -> List[Dict]: # TODO: tickers need to be in format 1m,5m # fetch_ohlcv returns an [[datetime,o,h,l,c,v]] if 'fetchOHLCV' not in _API.has or not _API.has['fetchOHLCV']: - raise OperationalException( - 'Exhange {} does not support fetching historical candlestick data.'.format( - _API.name) - ) + logger.warning('Exhange %s does not support fetching historical candlestick data.', _API.name) + return [] try: history = _API.fetch_ohlcv(pair, timeframe=str(tick_interval)+"m") @@ -205,13 +203,11 @@ def get_ticker_history(pair: str, tick_interval) -> List[Dict]: return history_json except IndexError as e: logger.warning('Empty ticker history. Msg %s', str(e)) - return [] except ccxt.NetworkError as e: - raise NetworkException( - 'Could not load ticker history due to networking error. Message: {}'.format(e) - ) + logger.warning('Could not load ticker history due to networking error. Message: %s', str(e)) except ccxt.BaseError as e: - raise OperationalException('Could not fetch ticker data. Msg: {}'.format(e)) + logger.warning('Could not fetch ticker data. Msg: %s', str(e)) + return [] def cancel_order(order_id: str) -> None: From 016232a8e97cc13e3cf292096d57d95609397c5e Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 25 Mar 2018 13:32:46 +0200 Subject: [PATCH 13/14] Revert OHLVC dataformat to ccxt format * Also fixes backtesting - but data must be refreshed for now as no conversation is happening yet --- freqtrade/analyze.py | 13 +++++++------ freqtrade/exchange/__init__.py | 20 ++++++-------------- freqtrade/optimize/__init__.py | 15 ++++++++++----- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/freqtrade/analyze.py b/freqtrade/analyze.py index f3e44b57b..e6e8023f0 100644 --- a/freqtrade/analyze.py +++ b/freqtrade/analyze.py @@ -44,12 +44,13 @@ class Analyze(object): :param ticker: See exchange.get_ticker_history :return: DataFrame """ - columns = {'C': 'close', 'V': 'volume', 'O': 'open', 'H': 'high', 'L': 'low', 'T': 'date'} - frame = DataFrame(ticker) \ - .rename(columns=columns) - if 'BV' in frame: - frame.drop('BV', 1, inplace=True) - frame['date'] = to_datetime(frame['date'], utc=True, infer_datetime_format=True) + cols = ['date', 'open', 'high', 'low', 'close', 'volume'] + frame = DataFrame(ticker, columns=cols) + + frame['date'] = to_datetime(frame['date'], + unit='ms', + utc=True, + infer_datetime_format=True) frame.sort_values('date', inplace=True) return frame diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 2a9222141..e2e22a358 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -181,26 +181,18 @@ def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict: # @cached(TTLCache(maxsize=100, ttl=30)) @retrier -def get_ticker_history(pair: str, tick_interval) -> List[Dict]: +def get_ticker_history(pair: str, tick_interval) -> List[List]: # TODO: tickers need to be in format 1m,5m # fetch_ohlcv returns an [[datetime,o,h,l,c,v]] if 'fetchOHLCV' not in _API.has or not _API.has['fetchOHLCV']: - logger.warning('Exhange %s does not support fetching historical candlestick data.', _API.name) + logger.warning('Exhange %s does not support fetching historical candlestick data.', + _API.name) return [] try: - history = _API.fetch_ohlcv(pair, timeframe=str(tick_interval)+"m") - history_json = [] - for candlestick in history: - history_json.append({ - 'T': datetime.fromtimestamp(candlestick[0]/1000.0).strftime('%Y-%m-%dT%H:%M:%S.%f'), - 'O': candlestick[1], - 'H': candlestick[2], - 'L': candlestick[3], - 'C': candlestick[4], - 'V': candlestick[5], - }) - return history_json + ohlcv = _API.fetch_ohlcv(pair, timeframe=str(tick_interval)+"m") + + return ohlcv except IndexError as e: logger.warning('Empty ticker history. Msg %s', str(e)) except ccxt.NetworkError as e: diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 61694d884..be2300de7 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -4,6 +4,7 @@ import gzip import json import os from typing import Optional, List, Dict, Tuple +from datetime import datetime from freqtrade import misc from freqtrade.exchange import get_ticker_history @@ -135,8 +136,8 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) -> if os.path.isfile(filename): with open(filename, "rt") as file: data = json.load(file) - logger.debug("Current Start: %s", data[1]['T']) - logger.debug("Current End: %s", data[-1:][0]['T']) + logger.debug("Current Start: %s", format_ms_time(data[1][0])) + logger.debug("Current End: %s", format_ms_time(data[-1:][0][0])) else: data = [] logger.debug("Current Start: None") @@ -146,10 +147,14 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) -> for row in new_data: if row not in data: data.append(row) - logger.debug("New Start: %s", data[1]['T']) - logger.debug("New End: %s", data[-1:][0]['T']) - data = sorted(data, key=lambda data: data['T']) + logger.debug("New Start: %s", format_ms_time(data[0][0])) + logger.debug("New End: %s", format_ms_time(data[-1:][0][0])) + data = sorted(data, key=lambda data: data[0]) misc.file_dump_json(filename, data) return True + + +def format_ms_time(date: str) -> str: + return datetime.fromtimestamp(date/1000.0).strftime('%Y-%m-%dT%H:%M:%S') From f51ef1a7915611459657d44234e2258548621338 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 25 Mar 2018 13:38:17 +0200 Subject: [PATCH 14/14] refactor format_ms_time to misc.py --- freqtrade/misc.py | 8 ++++++++ freqtrade/optimize/__init__.py | 13 ++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/freqtrade/misc.py b/freqtrade/misc.py index f5d045c44..bc04d6b88 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -72,3 +72,11 @@ def file_dump_json(filename, data) -> None: """ with open(filename, 'w') as fp: json.dump(data, fp, default=str) + + +def format_ms_time(date: str) -> str: + """ + convert MS date to readable format. + : epoch-string in ms + """ + return datetime.fromtimestamp(date/1000.0).strftime('%Y-%m-%dT%H:%M:%S') diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index be2300de7..30be5dc33 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -4,7 +4,6 @@ import gzip import json import os from typing import Optional, List, Dict, Tuple -from datetime import datetime from freqtrade import misc from freqtrade.exchange import get_ticker_history @@ -136,8 +135,8 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) -> if os.path.isfile(filename): with open(filename, "rt") as file: data = json.load(file) - logger.debug("Current Start: %s", format_ms_time(data[1][0])) - logger.debug("Current End: %s", format_ms_time(data[-1:][0][0])) + logger.debug("Current Start: %s", misc.format_ms_time(data[1][0])) + logger.debug("Current End: %s", misc.format_ms_time(data[-1:][0][0])) else: data = [] logger.debug("Current Start: None") @@ -147,14 +146,10 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) -> for row in new_data: if row not in data: data.append(row) - logger.debug("New Start: %s", format_ms_time(data[0][0])) - logger.debug("New End: %s", format_ms_time(data[-1:][0][0])) + logger.debug("New Start: %s", misc.format_ms_time(data[0][0])) + logger.debug("New End: %s", misc.format_ms_time(data[-1:][0][0])) data = sorted(data, key=lambda data: data[0]) misc.file_dump_json(filename, data) return True - - -def format_ms_time(date: str) -> str: - return datetime.fromtimestamp(date/1000.0).strftime('%Y-%m-%dT%H:%M:%S')