Merge remote-tracking branch 'upstream/feature/flask-rest' into feature/flask-rest

This commit is contained in:
Matthias 2018-11-25 14:10:08 +01:00
commit ebd26fea43
15 changed files with 210 additions and 200 deletions

View File

@ -1,5 +1,5 @@
""" FreqTrade bot """
__version__ = '0.17.0'
__version__ = '0.17.1'
class DependencyException(BaseException):

View File

@ -334,3 +334,10 @@ class Arguments(object):
nargs='+',
dest='timeframes',
)
self.parser.add_argument(
'--erase',
help='Clean all existing data for the selected exchange/pairs/timeframes',
dest='erase',
action='store_true'
)

View File

@ -93,7 +93,9 @@ class FreqtradeBot(object):
# Log state transition
state = self.state
if state != old_state:
self.rpc.send_msg(f'*Status:* `{state.name.lower()}`')
self.rpc.send_msg({
'status': f'{state.name.lower()}'
})
logger.info('Changing state to: %s', state.name)
if state == State.STOPPED:
@ -169,9 +171,9 @@ class FreqtradeBot(object):
except OperationalException:
tb = traceback.format_exc()
hint = 'Issue `/start` if you think it is safe to restart.'
self.rpc.send_msg(
f'*Status:* OperationalException:\n```\n{tb}```{hint}'
)
self.rpc.send_msg({
'status': f'OperationalException:\n```\n{tb}```{hint}'
})
logger.exception('OperationalException. Stopping trader ...')
self.state = State.STOPPED
return state_changed
@ -356,11 +358,12 @@ class FreqtradeBot(object):
)
# Create trade entity and return
self.rpc.send_msg(
self.rpc.send_msg({
'status':
f"""*{exc_name}:* Buying [{pair_s}]({pair_url}) \
with limit `{buy_limit:.8f} ({stake_amount:.6f} \
{stake_currency}, {stake_amount_fiat:.3f} {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(
@ -540,7 +543,9 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
Trade.session.delete(trade)
Trade.session.flush()
logger.info('Buy order timeout for %s.', trade)
self.rpc.send_msg(f'*Timeout:* Unfilled buy order for {pair_s} cancelled')
self.rpc.send_msg({
'status': f'Unfilled buy order for {pair_s} cancelled due to timeout'
})
return True
# if trade is partially complete, edit the stake details for the trade
@ -549,7 +554,9 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
trade.stake_amount = trade.amount * trade.open_rate
trade.open_order_id = None
logger.info('Partial buy order timeout for %s.', trade)
self.rpc.send_msg(f'*Timeout:* Remaining buy order for {pair_s} cancelled')
self.rpc.send_msg({
'status': f'Remaining buy order for {pair_s} cancelled due to timeout'
})
return False
# FIX: 20180110, should cancel_order() be cond. or unconditionally called?
@ -567,7 +574,9 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
trade.close_date = None
trade.is_open = True
trade.open_order_id = None
self.rpc.send_msg(f'*Timeout:* Unfilled sell order for {pair_s} cancelled')
self.rpc.send_msg({
'status': f'Unfilled sell order for {pair_s} cancelled due to timeout'
})
logger.info('Sell order timeout for %s.', trade)
return True
@ -627,5 +636,5 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
)
# Send the message
self.rpc.send_msg(message)
self.rpc.send_msg({'status': message})
Trade.session.flush()

View File

@ -59,7 +59,9 @@ def main(sysargv: List[str]) -> None:
logger.exception('Fatal exception!')
finally:
if freqtrade:
freqtrade.rpc.send_msg('*Status:* `Process died ...`')
freqtrade.rpc.send_msg({
'status': 'process died'
})
freqtrade.cleanup()
sys.exit(return_code)
@ -73,11 +75,9 @@ def reconfigure(freqtrade: FreqtradeBot, args: Namespace) -> FreqtradeBot:
# Create new instance
freqtrade = FreqtradeBot(Configuration(args).get_config())
freqtrade.rpc.send_msg(
'*Status:* `Config reloaded ...`'.format(
freqtrade.state.name.lower()
)
)
freqtrade.rpc.send_msg({
'status': 'config reloaded'
})
return freqtrade

View File

@ -16,11 +16,11 @@ app = Flask(__name__)
class ApiServer(RPC):
"""
This class is for REST calls across api server
This class runs api server and provides rpc.rpc functionality to it
This class starts a none blocking thread the api server runs within\
This class starts a none blocking thread the api server runs within
"""
def __init__(self, freqtrade) -> None:
"""
Init the api server, and init the super class RPC

View File

@ -1,8 +1,8 @@
"""
This module contains class to manage RPC communications (Telegram, Slack, ....)
This module contains class to manage RPC communications (Telegram, Slack, Rest ....)
"""
import logging
from typing import List
from typing import List, Dict
from freqtrade.rpc.rpc import RPC
@ -18,11 +18,17 @@ class RPCManager(object):
self.registered_modules: List[RPC] = []
# Enable telegram
if freqtrade.config['telegram'].get('enabled', False):
if freqtrade.config.get('telegram', {}).get('enabled', False):
logger.info('Enabling rpc.telegram ...')
from freqtrade.rpc.telegram import Telegram
self.registered_modules.append(Telegram(freqtrade))
# Enable local rest api server for cmd line control
if freqtrade.config.get('api_server', {}).get('enabled', False):
logger.info('Enabling rpc.api_server')
from freqtrade.rpc.api_server import ApiServer
self.registered_modules.append(ApiServer(freqtrade))
def cleanup(self) -> None:
""" Stops all enabled rpc modules """
logger.info('Cleaning up rpc modules ...')
@ -32,11 +38,14 @@ class RPCManager(object):
mod.cleanup()
del mod
def send_msg(self, msg: str) -> None:
def send_msg(self, msg: Dict[str, str]) -> None:
"""
Send given markdown message to all registered rpc modules
:param msg: message
:return: None
Send given message to all registered rpc modules.
A message consists of one or more key value pairs of strings.
e.g.:
{
'status': 'stopping bot'
}
"""
logger.info('Sending rpc message: %s', msg)
for mod in self.registered_modules:

View File

@ -4,7 +4,7 @@
This module manage Telegram communication
"""
import logging
from typing import Any, Callable
from typing import Any, Callable, Dict
import arrow
from pandas import DataFrame
@ -58,10 +58,6 @@ def authorized_only(command_handler: Callable[[Any, Bot, Update], None]) -> Call
class Telegram(RPC):
""" This class handles all telegram communication """
@property
def name(self) -> str:
return "telegram"
def __init__(self, freqtrade) -> None:
"""
Init the Telegram call, and init the super class RPC
@ -117,9 +113,9 @@ class Telegram(RPC):
"""
self._updater.stop()
def send_msg(self, msg: str) -> None:
def send_msg(self, msg: Dict[str, str]) -> None:
""" Send a message to telegram channel """
self._send_msg(msg)
self._send_msg('*Status:* `{status}`'.format(**msg))
@authorized_only
def _status(self, bot: Bot, update: Update) -> None:

View File

@ -83,6 +83,15 @@ def patch_coinmarketcap(mocker, value: Optional[Dict[str, float]] = None) -> Non
)
def patch_apiserver(mocker) -> None:
mocker.patch.multiple(
'freqtrade.rpc.api_server.ApiServer',
run=MagicMock(),
register_rest_other=MagicMock(),
register_rest_rpc_urls=MagicMock(),
)
@pytest.fixture(scope="function")
def default_conf():
""" Returns validated configuration suitable for most tests """

View File

@ -7,6 +7,7 @@ Unit test file for rpc/rpc.py
from datetime import datetime
from unittest.mock import MagicMock
import arrow
import pytest
from freqtrade.freqtradebot import FreqtradeBot
@ -40,6 +41,10 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
get_markets=markets
)
now = arrow.utcnow()
now_mock = mocker.patch('freqtrade.freqtradebot.datetime', MagicMock())
now_mock.utcnow = lambda: now.datetime
freqtradebot = FreqtradeBot(default_conf)
rpc = RPC(freqtradebot)
@ -58,7 +63,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
'trade_id': 1,
'pair': 'ETH/BTC',
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
'date': 'just now',
'open_date': now,
'open_rate': 1.099e-05,
'close_rate': None,
'current_rate': 1.098e-05,
@ -69,39 +74,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
} == results[0]
def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None:
"""
Test rpc_status_table() method
"""
patch_get_signal(mocker, (True, False))
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
get_fee=fee,
get_markets=markets
)
freqtradebot = FreqtradeBot(default_conf)
rpc = RPC(freqtradebot)
freqtradebot.state = State.STOPPED
with pytest.raises(RPCException, match=r'.*trader is not running*'):
rpc._rpc_status_table()
freqtradebot.state = State.RUNNING
with pytest.raises(RPCException, match=r'.*no active order*'):
rpc._rpc_status_table()
freqtradebot.create_trade()
result = rpc._rpc_status_table()
assert 'just now' in result['Since'].all()
assert 'ETH/BTC' in result['Pair'].all()
assert '-0.59%' in result['Profit'].all()
def test_rpc_daily_profit(default_conf, update, ticker, fee,
limit_buy_order, limit_sell_order, markets, mocker) -> None:
"""

View File

@ -0,0 +1,52 @@
"""
Unit test file for rpc/api_server.py
"""
from unittest.mock import MagicMock
from freqtrade.rpc.api_server import ApiServer
from freqtrade.state import State
from freqtrade.tests.conftest import get_patched_freqtradebot, patch_apiserver
def test__init__(default_conf, mocker):
"""
Test __init__() method
"""
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
mocker.patch('freqtrade.rpc.api_server.ApiServer.run', MagicMock())
apiserver = ApiServer(get_patched_freqtradebot(mocker, default_conf))
assert apiserver._config == default_conf
def test_start_endpoint(default_conf, mocker):
"""Test /start endpoint"""
patch_apiserver(mocker)
bot = get_patched_freqtradebot(mocker, default_conf)
apiserver = ApiServer(bot)
bot.state = State.STOPPED
assert bot.state == State.STOPPED
result = apiserver.start()
assert result == '{"status": "starting trader ..."}'
assert bot.state == State.RUNNING
result = apiserver.start()
assert result == '{"status": "already running"}'
def test_stop_endpoint(default_conf, mocker):
"""Test /stop endpoint"""
patch_apiserver(mocker)
bot = get_patched_freqtradebot(mocker, default_conf)
apiserver = ApiServer(bot)
bot.state = State.RUNNING
assert bot.state == State.RUNNING
result = apiserver.stop()
assert result == '{"status": "stopping trader ..."}'
assert bot.state == State.STOPPED
result = apiserver.stop()
assert result == '{"status": "already stopped"}'

View File

@ -102,9 +102,9 @@ def test_send_msg_telegram_disabled(mocker, default_conf, caplog) -> None:
freqtradebot = get_patched_freqtradebot(mocker, conf)
rpc_manager = RPCManager(freqtradebot)
rpc_manager.send_msg('test')
rpc_manager.send_msg({'status': 'test'})
assert log_has('Sending rpc message: test', caplog.record_tuples)
assert log_has("Sending rpc message: {'status': 'test'}", caplog.record_tuples)
assert telegram_mock.call_count == 0
@ -117,7 +117,7 @@ def test_send_msg_telegram_enabled(mocker, default_conf, caplog) -> None:
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc_manager = RPCManager(freqtradebot)
rpc_manager.send_msg('test')
rpc_manager.send_msg({'status': 'test'})
assert log_has('Sending rpc message: test', caplog.record_tuples)
assert log_has("Sending rpc message: {'status': 'test'}", caplog.record_tuples)
assert telegram_mock.call_count == 1

View File

@ -185,65 +185,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None:
)
def test_status(default_conf, update, mocker, fee, ticker, markets) -> None:
"""
Test _status() method
"""
update.message.chat.id = 123
conf = deepcopy(default_conf)
conf['telegram']['enabled'] = False
conf['telegram']['chat_id'] = 123
patch_get_signal(mocker, (True, False))
patch_coinmarketcap(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
get_pair_detail_url=MagicMock(),
get_fee=fee,
get_markets=markets
)
msg_mock = MagicMock()
status_table = MagicMock()
mocker.patch.multiple(
'freqtrade.rpc.telegram.Telegram',
_init=MagicMock(),
_rpc_trade_status=MagicMock(return_value=[{
'trade_id': 1,
'pair': 'ETH/BTC',
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
'date': 'just now',
'open_rate': 1.099e-05,
'close_rate': None,
'current_rate': 1.098e-05,
'amount': 90.99181074,
'close_profit': None,
'current_profit': -0.59,
'open_order': '(limit buy rem=0.00000000)'
}]),
_status_table=status_table,
_send_msg=msg_mock
)
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
freqtradebot = FreqtradeBot(conf)
telegram = Telegram(freqtradebot)
# Create some test data
for _ in range(3):
freqtradebot.create_trade()
telegram._status(bot=MagicMock(), update=update)
assert msg_mock.call_count == 1
update.message.text = MagicMock()
update.message.text.replace = MagicMock(return_value='table 2 3')
telegram._status(bot=MagicMock(), update=update)
assert status_table.call_count == 1
def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> None:
def test_status_handle(default_conf, markets, update, ticker, fee, mocker) -> None:
"""
Test _status() method
"""
@ -290,7 +232,8 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No
assert '[ETH/BTC]' in msg_mock.call_args_list[0][0][0]
def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) -> None:
def test_status_table_handle(
default_conf, markets, limit_buy_order, update, ticker, fee, mocker) -> None:
"""
Test _status_table() method
"""
@ -300,7 +243,8 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker)
'freqtrade.exchange.Exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value={'id': 'mocked_order_id'}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_order=MagicMock(return_value=limit_buy_order),
get_fee=fee,
get_markets=markets
)
@ -326,7 +270,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker)
freqtradebot.state = State.RUNNING
telegram._status_table(bot=MagicMock(), update=update)
assert msg_mock.call_count == 1
assert 'no active order' in msg_mock.call_args_list[0][0][0]
assert 'no active trade' in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
# Create some test data
@ -756,12 +700,13 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
telegram._forcesell(bot=MagicMock(), update=update)
assert rpc_mock.call_count == 2
assert 'Selling' 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]
assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0]
last_call = rpc_mock.call_args_list[-1][0][0]['status']
assert 'Selling' in last_call
assert '[ETH/BTC]' in last_call
assert 'Amount' in last_call
assert '0.00001172' in last_call
assert 'profit: 6.11%, 0.00006126' in last_call
assert '0.919 USD' in last_call
def test_forcesell_down_handle(default_conf, update, ticker, fee,
@ -801,13 +746,14 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
update.message.text = '/forcesell 1'
telegram._forcesell(bot=MagicMock(), update=update)
last_call = rpc_mock.call_args_list[-1][0][0]['status']
assert rpc_mock.call_count == 2
assert 'Selling' 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]
assert '-0.824 USD' in rpc_mock.call_args_list[-1][0][0]
assert 'Selling' in last_call
assert '[ETH/BTC]' in last_call
assert 'Amount' in last_call
assert '0.00001044' in last_call
assert 'loss: -5.48%, -0.00005492' in last_call
assert '-0.824 USD' in last_call
def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker) -> None:
@ -841,9 +787,9 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker
assert rpc_mock.call_count == 4
for args in rpc_mock.call_args_list:
assert '0.00001098' in args[0][0]
assert 'loss: -0.59%, -0.00000591 BTC' in args[0][0]
assert '-0.089 USD' in args[0][0]
assert '0.00001098' in args[0][0]['status']
assert 'loss: -0.59%, -0.00000591 BTC' in args[0][0]['status']
assert '-0.089 USD' in args[0][0]['status']
def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:

View File

@ -725,7 +725,7 @@ def test_process_operational_exception(default_conf, ticker, markets, mocker) ->
result = freqtrade._process()
assert result is False
assert freqtrade.state == State.STOPPED
assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]
assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]['status']
def test_process_trade_handling(
@ -1345,13 +1345,14 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'])
assert rpc_mock.call_count == 2
assert 'Selling' 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]
assert 'profit: 6.11%, 0.00006126' in rpc_mock.call_args_list[-1][0][0]
assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0]
last_call = rpc_mock.call_args_list[-1][0][0]['status']
assert 'Selling' in last_call
assert '[ETH/BTC]' in last_call
assert 'Amount' in last_call
assert 'Profit' in last_call
assert '0.00001172' in last_call
assert 'profit: 6.11%, 0.00006126' in last_call
assert '0.919 USD' in last_call
def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets, mocker) -> None:
@ -1387,12 +1388,13 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets,
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'])
assert rpc_mock.call_count == 2
assert 'Selling' 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]
assert '-0.824 USD' in rpc_mock.call_args_list[-1][0][0]
last_call = rpc_mock.call_args_list[-1][0][0]['status']
assert 'Selling' in last_call
assert '[ETH/BTC]' in last_call
assert 'Amount' in last_call
assert '0.00001044' in last_call
assert 'loss: -5.48%, -0.00005492' in last_call
assert '-0.824 USD' in last_call
def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
@ -1429,12 +1431,13 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'])
assert rpc_mock.call_count == 2
assert 'Selling' 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]
assert 'USD' not in rpc_mock.call_args_list[-1][0][0]
last_call = rpc_mock.call_args_list[-1][0][0]['status']
assert 'Selling' in last_call
assert '[ETH/BTC]' in last_call
assert 'Amount' in last_call
assert '0.00001172' in last_call
assert '(profit: 6.11%, 0.00006126)' in last_call
assert 'USD' not in last_call
def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
@ -1471,10 +1474,11 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'])
assert rpc_mock.call_count == 2
assert 'Selling' 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]
last_call = rpc_mock.call_args_list[-1][0][0]['status']
assert 'Selling' in last_call
assert '[ETH/BTC]' in last_call
assert '0.00001044' in last_call
assert 'loss: -5.48%, -0.00005492' in last_call
def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,

View File

@ -1,4 +1,4 @@
ccxt==1.14.256
ccxt==1.14.257
SQLAlchemy==1.2.8
python-telegram-bot==10.1.0
arrow==0.12.1
@ -23,3 +23,7 @@ coinmarketcap==5.0.3
# Required for plotting data
#plotly==2.7.0
#Added for local rest client
Flask==1.0.2
flask-restful==0.3.6

View File

@ -3,11 +3,14 @@
"""This script generate json data from bittrex"""
import json
import sys
import os
from pathlib import Path
import arrow
from freqtrade import (arguments, misc)
from freqtrade import arguments
from freqtrade.arguments import TimeRange
from freqtrade.exchange import Exchange
from freqtrade.optimize import download_backtesting_testdata
DEFAULT_DL_PATH = 'user_data/data'
@ -17,25 +20,27 @@ args = arguments.parse_args()
timeframes = args.timeframes
dl_path = os.path.join(DEFAULT_DL_PATH, args.exchange)
dl_path = Path(DEFAULT_DL_PATH).joinpath(args.exchange)
if args.export:
dl_path = args.export
dl_path = Path(args.export)
if not os.path.isdir(dl_path):
if not dl_path.is_dir():
sys.exit(f'Directory {dl_path} does not exist.')
pairs_file = args.pairs_file if args.pairs_file else os.path.join(dl_path, 'pairs.json')
if not os.path.isfile(pairs_file):
pairs_file = Path(args.pairs_file) if args.pairs_file else dl_path.joinpath('pairs.json')
if not pairs_file.exists():
sys.exit(f'No pairs file found with path {pairs_file}.')
with open(pairs_file) as file:
with pairs_file.open() as file:
PAIRS = list(set(json.load(file)))
PAIRS.sort()
since_time = None
timerange = TimeRange()
if args.days:
since_time = arrow.utcnow().shift(days=-args.days).timestamp * 1000
time_since = arrow.utcnow().shift(days=-args.days).strftime("%Y%m%d")
timerange = arguments.parse_timerange(f'{time_since}-')
print(f'About to download pairs: {PAIRS} to {dl_path}')
@ -59,21 +64,18 @@ for pair in PAIRS:
print(f"skipping pair {pair}")
continue
for tick_interval in timeframes:
print(f'downloading pair {pair}, interval {tick_interval}')
data = exchange.get_ticker_history(pair, tick_interval, since_ms=since_time)
if not data:
print('\tNo data was downloaded')
break
print('\tData was downloaded for period %s - %s' % (
arrow.get(data[0][0] / 1000).format(),
arrow.get(data[-1][0] / 1000).format()))
# save data
pair_print = pair.replace('/', '_')
filename = f'{pair_print}-{tick_interval}.json'
misc.file_dump_json(os.path.join(dl_path, filename), data)
dl_file = dl_path.joinpath(filename)
if args.erase and dl_file.exists():
print(f'Deleting existing data for pair {pair}, interval {tick_interval}')
dl_file.unlink()
print(f'downloading pair {pair}, interval {tick_interval}')
download_backtesting_testdata(str(dl_path), exchange=exchange,
pair=pair,
tick_interval=tick_interval,
timerange=timerange)
if pairs_not_available: