Make Pylint Happy chapter 1

This commit is contained in:
Gerald Lonlas 2018-03-02 23:22:00 +08:00
parent d274f13480
commit 390501bac0
13 changed files with 161 additions and 147 deletions

View File

@ -1,11 +1,11 @@
""" """
Functions to analyze ticker data with indicators and produce buy and sell signals Functions to analyze ticker data with indicators and produce buy and sell signals
""" """
import arrow
from datetime import datetime, timedelta from datetime import datetime, timedelta
from enum import Enum from enum import Enum
from pandas import DataFrame, to_datetime
from typing import Dict, List from typing import Dict, List
import arrow
from pandas import DataFrame, to_datetime
from freqtrade.exchange import get_ticker_history from freqtrade.exchange import get_ticker_history
from freqtrade.logger import Logger from freqtrade.logger import Logger
from freqtrade.strategy.strategy import Strategy from freqtrade.strategy.strategy import Strategy
@ -188,10 +188,9 @@ class Analyze(object):
) )
return False return False
def tickerdata_to_dataframe(self, tickerdata: Dict[str, List]) -> Dict[str, DataFrame]: def tickerdata_to_dataframe(self, tickerdata: Dict[str, List]) -> Dict[str, DataFrame]:
""" """
Creates a dataframe and populates indicators for given ticker data Creates a dataframe and populates indicators for given ticker data
""" """
return {pair: self.populate_indicators(self.parse_ticker_dataframe(pair_data)) return {pair: self.populate_indicators(self.parse_ticker_dataframe(pair_data))
for pair, pair_data in tickerdata.items()} for pair, pair_data in tickerdata.items()}

View File

@ -2,16 +2,15 @@
Freqtrade is the main module of this bot. It contains the class Freqtrade() Freqtrade is the main module of this bot. It contains the class Freqtrade()
""" """
import logging
import arrow
import copy import copy
import json import json
import requests
import time import time
import traceback import traceback
from cachetools import cached, TTLCache
from datetime import datetime
from typing import Dict, List, Optional, Any, Callable from typing import Dict, List, Optional, Any, Callable
from datetime import datetime
import requests
import arrow
from cachetools import cached, TTLCache
from freqtrade.analyze import Analyze from freqtrade.analyze import Analyze
from freqtrade.constants import Constants from freqtrade.constants import Constants
from freqtrade.fiat_convert import CryptoToFiatConverter from freqtrade.fiat_convert import CryptoToFiatConverter
@ -507,14 +506,14 @@ class FreqtradeBot(object):
"*Current Rate:* `{current_rate:.8f}`\n" \ "*Current Rate:* `{current_rate:.8f}`\n" \
"*Profit:* `{profit:.2f}%`" \ "*Profit:* `{profit:.2f}%`" \
"".format( "".format(
exchange=trade.exchange, exchange=trade.exchange,
pair=trade.pair, pair=trade.pair,
pair_url=exchange.get_pair_detail_url(trade.pair), pair_url=exchange.get_pair_detail_url(trade.pair),
limit=limit, limit=limit,
open_rate=trade.open_rate, open_rate=trade.open_rate,
current_rate=current_rate, current_rate=current_rate,
amount=round(trade.amount, 8), amount=round(trade.amount, 8),
profit=round(profit * 100, 2), profit=round(profit * 100, 2),
) )
# For regular case, when the configuration exists # For regular case, when the configuration exists
@ -528,12 +527,12 @@ class FreqtradeBot(object):
message += '` ({gain}: {profit_percent:.2f}%, {profit_coin:.8f} {coin}`' \ message += '` ({gain}: {profit_percent:.2f}%, {profit_coin:.8f} {coin}`' \
'` / {profit_fiat:.3f} {fiat})`' \ '` / {profit_fiat:.3f} {fiat})`' \
''.format( ''.format(
gain="profit" if fmt_exp_profit > 0 else "loss", gain="profit" if fmt_exp_profit > 0 else "loss",
profit_percent=fmt_exp_profit, profit_percent=fmt_exp_profit,
profit_coin=profit_trade, profit_coin=profit_trade,
coin=self.config['stake_currency'], coin=self.config['stake_currency'],
profit_fiat=profit_fiat, profit_fiat=profit_fiat,
fiat=self.config['fiat_display_currency'], fiat=self.config['fiat_display_currency'],
) )
# Because telegram._forcesell does not have the configuration # Because telegram._forcesell does not have the configuration
# Ignore the FIAT value and does not show the stake_currency as well # Ignore the FIAT value and does not show the stake_currency as well

View File

@ -97,10 +97,11 @@ def download_pairs(datadir, pairs: List[str], ticker_interval: int) -> bool:
try: try:
download_backtesting_testdata(datadir, pair=pair, interval=ticker_interval) download_backtesting_testdata(datadir, pair=pair, interval=ticker_interval)
except BaseException: except BaseException:
logger.info('Failed to download the pair: "{pair}", Interval: {interval} min'.format( logger.info(
pair=pair, 'Failed to download the pair: "%s", Interval: %s min',
interval=ticker_interval, pair,
)) ticker_interval
)
return False return False
return True return True
@ -115,10 +116,11 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) ->
""" """
path = make_testdata_path(datadir) path = make_testdata_path(datadir)
logger.info('Download the pair: "{pair}", Interval: {interval} min'.format( logger.info(
pair=pair, 'Download the pair: "%s", Interval: %s min',
interval=interval, pair,
)) interval
)
filepair = pair.replace("-", "_") filepair = pair.replace("-", "_")
filename = os.path.join(path, '{pair}-{interval}.json'.format( filename = os.path.join(path, '{pair}-{interval}.json'.format(
@ -129,8 +131,8 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) ->
if os.path.isfile(filename): if os.path.isfile(filename):
with open(filename, "rt") as file: with open(filename, "rt") as file:
data = json.load(file) data = json.load(file)
logger.debug("Current Start: {}".format(data[1]['T'])) logger.debug("Current Start: %s", data[1]['T'])
logger.debug("Current End: {}".format(data[-1:][0]['T'])) logger.debug("Current End: %s", data[-1:][0]['T'])
else: else:
data = [] data = []
logger.debug("Current Start: None") logger.debug("Current Start: None")
@ -140,8 +142,8 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) ->
for row in new_data: for row in new_data:
if row not in data: if row not in data:
data.append(row) data.append(row)
logger.debug("New Start: {}".format(data[1]['T'])) logger.debug("New Start: %s", data[1]['T'])
logger.debug("New End: {}".format(data[-1:][0]['T'])) logger.debug("New End: %s", data[-1:][0]['T'])
data = sorted(data, key=lambda data: data['T']) data = sorted(data, key=lambda data: data['T'])
misc.file_dump_json(filename, data) misc.file_dump_json(filename, data)

View File

@ -25,7 +25,7 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.configuration import Configuration from freqtrade.configuration import Configuration
from freqtrade.optimize import load_data from freqtrade.optimize import load_data
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.optimize.backtesting import Backtesting, setup_configuration from freqtrade.optimize.backtesting import Backtesting
from freqtrade.logger import Logger from freqtrade.logger import Logger
from user_data.hyperopt_conf import hyperopt_optimize_conf from user_data.hyperopt_conf import hyperopt_optimize_conf
@ -46,7 +46,6 @@ class Hyperopt(Backtesting):
self.logging = Logger(name=__name__, level=config['loglevel']) self.logging = Logger(name=__name__, level=config['loglevel'])
self.logger = self.logging.get_logger() self.logger = self.logging.get_logger()
# set TARGET_TRADES to suit your number concurrent trades so its realistic # set TARGET_TRADES to suit your number concurrent trades so its realistic
# to the number of days # to the number of days
self.target_trades = 600 self.target_trades = 600
@ -353,6 +352,9 @@ class Hyperopt(Backtesting):
Define the buy strategy parameters to be used by hyperopt Define the buy strategy parameters to be used by hyperopt
""" """
def populate_buy_trend(dataframe: DataFrame) -> DataFrame: def populate_buy_trend(dataframe: DataFrame) -> DataFrame:
"""
Buy strategy Hyperopt will build and use
"""
conditions = [] conditions = []
# GUARDS AND TRENDS # GUARDS AND TRENDS
if 'uptrend_long_ema' in params and params['uptrend_long_ema']['enabled']: if 'uptrend_long_ema' in params and params['uptrend_long_ema']['enabled']:
@ -513,8 +515,9 @@ class Hyperopt(Backtesting):
self.current_tries = len(self.trials.results) self.current_tries = len(self.trials.results)
self.total_tries += self.current_tries self.total_tries += self.current_tries
self.logger.info( self.logger.info(
'Continuing with trials. Current: {}, Total: {}' 'Continuing with trials. Current: %d, Total: %d',
.format(self.current_tries, self.total_tries) self.current_tries,
self.total_tries
) )
try: try:
@ -557,7 +560,10 @@ class Hyperopt(Backtesting):
""" """
Hyperopt SIGINT handler Hyperopt SIGINT handler
""" """
self.logger.info('Hyperopt received {}'.format(signal.Signals(sig).name)) self.logger.info(
'Hyperopt received %s',
signal.Signals(sig).name
)
self.save_trials() self.save_trials()
self.log_trials_result() self.log_trials_result()
@ -580,9 +586,7 @@ def start(args) -> None:
logger.info('Starting freqtrade in Hyperopt mode') logger.info('Starting freqtrade in Hyperopt mode')
# Initialize configuration # Initialize configuration
#config = setup_configuration(args) # Monkey patch the configuration with hyperopt_conf.py
# Monkey patch of the configuration with hyperopt_conf.py
configuration = Configuration(args) configuration = Configuration(args)
optimize_config = hyperopt_optimize_conf() optimize_config = hyperopt_optimize_conf()
config = configuration._load_backtesting_config(optimize_config) config = configuration._load_backtesting_config(optimize_config)

View File

@ -1,3 +1,7 @@
"""
This module contains the class to persist trades into SQLite
"""
import logging import logging
from datetime import datetime from datetime import datetime
from decimal import Decimal, getcontext from decimal import Decimal, getcontext
@ -72,6 +76,9 @@ def clean_dry_run_db() -> None:
class Trade(_DECL_BASE): class Trade(_DECL_BASE):
"""
Class used to define a trade structure
"""
__tablename__ = 'trades' __tablename__ = 'trades'
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)

View File

@ -2,9 +2,9 @@
This module contains class to define a RPC communications This module contains class to define a RPC communications
""" """
import arrow
from decimal import Decimal from decimal import Decimal
from datetime import datetime, timedelta from datetime import datetime, timedelta
import arrow
from pandas import DataFrame from pandas import DataFrame
import sqlalchemy as sql import sqlalchemy as sql
from freqtrade.logger import Logger from freqtrade.logger import Logger
@ -18,7 +18,6 @@ class RPC(object):
""" """
RPC class can be used to have extra feature, like bot data, and access to DB data RPC class can be used to have extra feature, like bot data, and access to DB data
""" """
def __init__(self, freqtrade) -> None: def __init__(self, freqtrade) -> None:
""" """
Initializes all enabled rpc modules Initializes all enabled rpc modules
@ -65,21 +64,21 @@ class RPC(object):
"*Close Profit:* `{close_profit}`\n" \ "*Close Profit:* `{close_profit}`\n" \
"*Current Profit:* `{current_profit:.2f}%`\n" \ "*Current Profit:* `{current_profit:.2f}%`\n" \
"*Open Order:* `{open_order}`"\ "*Open Order:* `{open_order}`"\
.format( .format(
trade_id=trade.id, trade_id=trade.id,
pair=trade.pair, pair=trade.pair,
market_url=exchange.get_pair_detail_url(trade.pair), market_url=exchange.get_pair_detail_url(trade.pair),
date=arrow.get(trade.open_date).humanize(), date=arrow.get(trade.open_date).humanize(),
open_rate=trade.open_rate, open_rate=trade.open_rate,
close_rate=trade.close_rate, close_rate=trade.close_rate,
current_rate=current_rate, current_rate=current_rate,
amount=round(trade.amount, 8), amount=round(trade.amount, 8),
close_profit=fmt_close_profit, close_profit=fmt_close_profit,
current_profit=round(current_profit * 100, 2), current_profit=round(current_profit * 100, 2),
open_order='({} rem={:.8f})'.format( open_order='({} rem={:.8f})'.format(
order['type'], order['remaining'] order['type'], order['remaining']
) if order else None, ) if order else None,
) )
result.append(message) result.append(message)
return (False, result) return (False, result)
@ -100,7 +99,7 @@ class RPC(object):
shorten_date(arrow.get(trade.open_date).humanize(only_distance=True)), shorten_date(arrow.get(trade.open_date).humanize(only_distance=True)),
'{:.2f}%'.format(100 * trade.calc_profit_percent(current_rate)) '{:.2f}%'.format(100 * trade.calc_profit_percent(current_rate))
]) ])
columns = ['ID', 'Pair', 'Since', 'Profit'] columns = ['ID', 'Pair', 'Since', 'Profit']
df_statuses = DataFrame.from_records(trades_list, columns=columns) df_statuses = DataFrame.from_records(trades_list, columns=columns)
df_statuses = df_statuses.set_index(columns[0]) df_statuses = df_statuses.set_index(columns[0])
@ -113,10 +112,10 @@ class RPC(object):
def rpc_daily_profit(self, timescale, stake_currency, fiat_display_currency): def rpc_daily_profit(self, timescale, stake_currency, fiat_display_currency):
today = datetime.utcnow().date() today = datetime.utcnow().date()
profit_days = {} profit_days = {}
if not (isinstance(timescale, int) and timescale > 0): if not (isinstance(timescale, int) and timescale > 0):
return (True, '*Daily [n]:* `must be an integer greater than 0`') return (True, '*Daily [n]:* `must be an integer greater than 0`')
fiat = self.freqtrade.fiat_converter fiat = self.freqtrade.fiat_converter
for day in range(0, timescale): for day in range(0, timescale):
profitday = today - timedelta(days=day) profitday = today - timedelta(days=day)
@ -131,7 +130,7 @@ class RPC(object):
'amount': format(curdayprofit, '.8f'), 'amount': format(curdayprofit, '.8f'),
'trades': len(trades) 'trades': len(trades)
} }
stats = [ stats = [
[ [
key, key,
@ -147,7 +146,10 @@ class RPC(object):
), ),
symbol=fiat_display_currency symbol=fiat_display_currency
), ),
'{value} trade{s}'.format(value=value['trades'], s='' if value['trades'] < 2 else 's'), '{value} trade{s}'.format(
value=value['trades'],
s='' if value['trades'] < 2 else 's'
),
] ]
for key, value in profit_days.items() for key, value in profit_days.items()
] ]
@ -158,21 +160,21 @@ class RPC(object):
:return: cumulative profit statistics. :return: cumulative profit statistics.
""" """
trades = Trade.query.order_by(Trade.id).all() trades = Trade.query.order_by(Trade.id).all()
profit_all_coin = [] profit_all_coin = []
profit_all_percent = [] profit_all_percent = []
profit_closed_coin = [] profit_closed_coin = []
profit_closed_percent = [] profit_closed_percent = []
durations = [] durations = []
for trade in trades: for trade in trades:
current_rate = None current_rate = None
if not trade.open_rate: if not trade.open_rate:
continue continue
if trade.close_date: if trade.close_date:
durations.append((trade.close_date - trade.open_date).total_seconds()) durations.append((trade.close_date - trade.open_date).total_seconds())
if not trade.is_open: if not trade.is_open:
profit_percent = trade.calc_profit_percent() profit_percent = trade.calc_profit_percent()
profit_closed_coin.append(trade.calc_profit()) profit_closed_coin.append(trade.calc_profit())
@ -181,22 +183,25 @@ class RPC(object):
# Get current rate # Get current rate
current_rate = exchange.get_ticker(trade.pair, False)['bid'] current_rate = exchange.get_ticker(trade.pair, False)['bid']
profit_percent = trade.calc_profit_percent(rate=current_rate) profit_percent = trade.calc_profit_percent(rate=current_rate)
profit_all_coin.append(trade.calc_profit(rate=Decimal(trade.close_rate or current_rate))) profit_all_coin.append(
trade.calc_profit(rate=Decimal(trade.close_rate or current_rate))
)
profit_all_percent.append(profit_percent) profit_all_percent.append(profit_percent)
best_pair = Trade.session.query(Trade.pair, best_pair = Trade.session.query(
sql.func.sum(Trade.close_profit).label('profit_sum')) \ Trade.pair,
.filter(Trade.is_open.is_(False)) \ sql.func.sum(Trade.close_profit).label('profit_sum')
.group_by(Trade.pair) \ )\
.order_by(sql.text('profit_sum DESC')) \ .filter(Trade.is_open.is_(False))\
.first() .group_by(Trade.pair)\
.order_by(sql.text('profit_sum DESC')).first()
if not best_pair: if not best_pair:
return (True, '*Status:* `no closed trade`') return (True, '*Status:* `no closed trade`')
bp_pair, bp_rate = best_pair bp_pair, bp_rate = best_pair
# FIX: we want to keep fiatconverter in a state/environment, # FIX: we want to keep fiatconverter in a state/environment,
# doing this will utilize its caching functionallity, instead we reinitialize it here # doing this will utilize its caching functionallity, instead we reinitialize it here
fiat = self.freqtrade.fiat_converter fiat = self.freqtrade.fiat_converter
@ -244,7 +249,7 @@ class RPC(object):
] ]
if not balances: if not balances:
return (True, '`All balances are zero.`') return (True, '`All balances are zero.`')
output = [] output = []
total = 0.0 total = 0.0
for currency in balances: for currency in balances:
@ -258,12 +263,15 @@ class RPC(object):
currency["Rate"] = exchange.get_ticker('BTC_' + coin, False)['bid'] currency["Rate"] = exchange.get_ticker('BTC_' + coin, False)['bid']
currency['BTC'] = currency["Rate"] * currency["Balance"] currency['BTC'] = currency["Rate"] * currency["Balance"]
total = total + currency['BTC'] total = total + currency['BTC']
output.append({'currency': currency['Currency'], output.append(
'available': currency['Available'], {
'balance': currency['Balance'], 'currency': currency['Currency'],
'pending': currency['Pending'], 'available': currency['Available'],
'est_btc': currency['BTC'] 'balance': currency['Balance'],
}) 'pending': currency['Pending'],
'est_btc': currency['BTC']
}
)
fiat = self.freqtrade.fiat_converter fiat = self.freqtrade.fiat_converter
symbol = fiat_display_currency symbol = fiat_display_currency
value = fiat.convert_amount(total, 'BTC', symbol) value = fiat.convert_amount(total, 'BTC', symbol)
@ -275,9 +283,9 @@ class RPC(object):
""" """
if self.freqtrade.get_state() == State.RUNNING: if self.freqtrade.get_state() == State.RUNNING:
return (True, '*Status:* `already running`') return (True, '*Status:* `already running`')
else:
self.freqtrade.update_state(State.RUNNING) self.freqtrade.update_state(State.RUNNING)
return (False, '`Starting trader ...`') return (False, '`Starting trader ...`')
def rpc_stop(self) -> (bool, str): def rpc_stop(self) -> (bool, str):
""" """
@ -286,8 +294,8 @@ class RPC(object):
if self.freqtrade.get_state() == State.RUNNING: if self.freqtrade.get_state() == State.RUNNING:
self.freqtrade.update_state(State.STOPPED) self.freqtrade.update_state(State.STOPPED)
return (False, '`Stopping trader ...`') return (False, '`Stopping trader ...`')
else:
return (True, '*Status:* `already stopped`') return (True, '*Status:* `already stopped`')
# FIX: no test for this!!!! # FIX: no test for this!!!!
def rpc_forcesell(self, trade_id) -> None: def rpc_forcesell(self, trade_id) -> None:
@ -300,18 +308,18 @@ class RPC(object):
# Check if there is there is an open order # Check if there is there is an open order
if trade.open_order_id: if trade.open_order_id:
order = exchange.get_order(trade.open_order_id) order = exchange.get_order(trade.open_order_id)
# Cancel open LIMIT_BUY orders and close trade # Cancel open LIMIT_BUY orders and close trade
if order and not order['closed'] and order['type'] == 'LIMIT_BUY': if order and not order['closed'] and order['type'] == 'LIMIT_BUY':
exchange.cancel_order(trade.open_order_id) exchange.cancel_order(trade.open_order_id)
trade.close(order.get('rate') or trade.open_rate) trade.close(order.get('rate') or trade.open_rate)
# TODO: sell amount which has been bought already # TODO: sell amount which has been bought already
return return
# Ignore trades with an attached LIMIT_SELL order # Ignore trades with an attached LIMIT_SELL order
if order and not order['closed'] and order['type'] == 'LIMIT_SELL': if order and not order['closed'] and order['type'] == 'LIMIT_SELL':
return return
# Get current rate and execute sell # Get current rate and execute sell
current_rate = exchange.get_ticker(trade.pair, False)['bid'] current_rate = exchange.get_ticker(trade.pair, False)['bid']
self.freqtrade.execute_sell(trade, current_rate) self.freqtrade.execute_sell(trade, current_rate)
@ -319,13 +327,13 @@ class RPC(object):
if self.freqtrade.get_state() != State.RUNNING: if self.freqtrade.get_state() != State.RUNNING:
return (True, '`trader is not running`') return (True, '`trader is not running`')
if trade_id == 'all': if trade_id == 'all':
# Execute sell for all open orders # Execute sell for all open orders
for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
_exec_forcesell(trade) _exec_forcesell(trade)
return (False, '') return (False, '')
# Query for trade # Query for trade
trade = Trade.query.filter( trade = Trade.query.filter(
sql.and_( sql.and_(
@ -336,7 +344,7 @@ class RPC(object):
if not trade: if not trade:
self.logger.warning('forcesell: Invalid argument received') self.logger.warning('forcesell: Invalid argument received')
return (True, 'Invalid argument.') return (True, 'Invalid argument.')
_exec_forcesell(trade) _exec_forcesell(trade)
return (False, '') return (False, '')
@ -347,7 +355,7 @@ class RPC(object):
""" """
if self.freqtrade.get_state() != State.RUNNING: if self.freqtrade.get_state() != State.RUNNING:
return (True, '`trader is not running`') return (True, '`trader is not running`')
pair_rates = Trade.session.query(Trade.pair, pair_rates = Trade.session.query(Trade.pair,
sql.func.sum(Trade.close_profit).label('profit_sum'), sql.func.sum(Trade.close_profit).label('profit_sum'),
sql.func.count(Trade.pair).label('count')) \ sql.func.count(Trade.pair).label('count')) \
@ -358,9 +366,9 @@ class RPC(object):
trades = [] trades = []
for (pair, rate, count) in pair_rates: for (pair, rate, count) in pair_rates:
trades.append({'pair': pair, 'profit': round(rate * 100, 2), 'count': count}) trades.append({'pair': pair, 'profit': round(rate * 100, 2), 'count': count})
return (False, trades) return (False, trades)
def rpc_count(self) -> None: def rpc_count(self) -> None:
""" """
Returns the number of trades running Returns the number of trades running
@ -368,6 +376,6 @@ class RPC(object):
""" """
if self.freqtrade.get_state() != State.RUNNING: if self.freqtrade.get_state() != State.RUNNING:
return (True, '`trader is not running`') return (True, '`trader is not running`')
trades = Trade.query.filter(Trade.is_open.is_(True)).all() trades = Trade.query.filter(Trade.is_open.is_(True)).all()
return (False, trades) return (False, trades)

View File

@ -10,7 +10,6 @@ class RPCManager(object):
""" """
Class to manage RPC objects (Telegram, Slack, ...) Class to manage RPC objects (Telegram, Slack, ...)
""" """
def __init__(self, freqtrade) -> None: def __init__(self, freqtrade) -> None:
""" """
Initializes all enabled rpc modules Initializes all enabled rpc modules

View File

@ -1,14 +1,16 @@
# pragma pylint: disable=unused-argument, unused-variable, protected-access, invalid-name
""" """
This module manage Telegram communication This module manage Telegram communication
""" """
from typing import Any, Callable from typing import Any, Callable
from freqtrade.rpc.rpc import RPC
from tabulate import tabulate from tabulate import tabulate
from telegram import Bot, ParseMode, ReplyKeyboardMarkup, Update from telegram import Bot, ParseMode, ReplyKeyboardMarkup, Update
from telegram.error import NetworkError, TelegramError from telegram.error import NetworkError, TelegramError
from telegram.ext import CommandHandler, Updater from telegram.ext import CommandHandler, Updater
from freqtrade.__init__ import __version__ from freqtrade.__init__ import __version__
from freqtrade.rpc.rpc import RPC
def authorized_only(command_handler: Callable[[Bot, Update], None]) -> Callable[..., Any]: def authorized_only(command_handler: Callable[[Bot, Update], None]) -> Callable[..., Any]:
@ -17,10 +19,10 @@ def authorized_only(command_handler: Callable[[Bot, Update], None]) -> Callable[
:param command_handler: Telegram CommandHandler :param command_handler: Telegram CommandHandler
:return: decorated function :return: decorated function
""" """
#def wrapper(self, bot: Bot, update: Update):
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
"""
Decorator logic
"""
update = kwargs.get('update') or args[1] update = kwargs.get('update') or args[1]
# Reject unauthorized messages # Reject unauthorized messages
@ -45,6 +47,7 @@ def authorized_only(command_handler: Callable[[Bot, Update], None]) -> Callable[
return wrapper return wrapper
class Telegram(RPC): class Telegram(RPC):
""" """
Telegram, this class send messages to Telegram Telegram, this class send messages to Telegram
@ -57,7 +60,7 @@ class Telegram(RPC):
""" """
super().__init__(freqtrade) super().__init__(freqtrade)
self._updater = Updater = None self._updater = None
self._config = freqtrade.config self._config = freqtrade.config
self._init() self._init()
@ -190,10 +193,10 @@ class Telegram(RPC):
], ],
tablefmt='simple') tablefmt='simple')
message = '<b>Daily Profit over the last {} days</b>:\n<pre>{}</pre>'\ message = '<b>Daily Profit over the last {} days</b>:\n<pre>{}</pre>'\
.format( .format(
timescale, timescale,
stats stats
) )
self.send_msg(message, bot=bot, parse_mode=ParseMode.HTML) self.send_msg(message, bot=bot, parse_mode=ParseMode.HTML)
@authorized_only @authorized_only
@ -225,22 +228,22 @@ class Telegram(RPC):
"*Latest Trade opened:* `{latest_trade_date}`\n" \ "*Latest Trade opened:* `{latest_trade_date}`\n" \
"*Avg. Duration:* `{avg_duration}`\n" \ "*Avg. Duration:* `{avg_duration}`\n" \
"*Best Performing:* `{best_pair}: {best_rate:.2f}%`"\ "*Best Performing:* `{best_pair}: {best_rate:.2f}%`"\
.format( .format(
coin=self._config['stake_currency'], coin=self._config['stake_currency'],
fiat=self._config['fiat_display_currency'], fiat=self._config['fiat_display_currency'],
profit_closed_coin=stats['profit_closed_coin'], profit_closed_coin=stats['profit_closed_coin'],
profit_closed_percent=stats['profit_closed_percent'], profit_closed_percent=stats['profit_closed_percent'],
profit_closed_fiat=stats['profit_closed_fiat'], profit_closed_fiat=stats['profit_closed_fiat'],
profit_all_coin=stats['profit_all_coin'], profit_all_coin=stats['profit_all_coin'],
profit_all_percent=stats['profit_all_percent'], profit_all_percent=stats['profit_all_percent'],
profit_all_fiat=stats['profit_all_fiat'], profit_all_fiat=stats['profit_all_fiat'],
trade_count=stats['trade_count'], trade_count=stats['trade_count'],
first_trade_date=stats['first_trade_date'], first_trade_date=stats['first_trade_date'],
latest_trade_date=stats['latest_trade_date'], latest_trade_date=stats['latest_trade_date'],
avg_duration=stats['avg_duration'], avg_duration=stats['avg_duration'],
best_pair=stats['best_pair'], best_pair=stats['best_pair'],
best_rate=stats['best_rate'] best_rate=stats['best_rate']
) )
self.send_msg(markdown_msg, bot=bot) self.send_msg(markdown_msg, bot=bot)
@authorized_only @authorized_only
@ -294,7 +297,6 @@ class Telegram(RPC):
(error, msg) = self.rpc_stop() (error, msg) = self.rpc_stop()
self.send_msg(msg, bot=bot) self.send_msg(msg, bot=bot)
# FIX: no test for this!!!!
@authorized_only @authorized_only
def _forcesell(self, bot: Bot, update: Update) -> None: def _forcesell(self, bot: Bot, update: Update) -> None:
""" """
@ -370,10 +372,12 @@ class Telegram(RPC):
"*/status [table]:* `Lists all open trades`\n" \ "*/status [table]:* `Lists all open trades`\n" \
" *table :* `will display trades in a table`\n" \ " *table :* `will display trades in a table`\n" \
"*/profit:* `Lists cumulative profit from all finished trades`\n" \ "*/profit:* `Lists cumulative profit from all finished trades`\n" \
"*/forcesell <trade_id>|all:* `Instantly sells the given trade or all trades, regardless of profit`\n" \ "*/forcesell <trade_id>|all:* `Instantly sells the given trade or all trades, " \
"regardless of profit`\n" \
"*/performance:* `Show performance of each finished trade grouped by pair`\n" \ "*/performance:* `Show performance of each finished trade grouped by pair`\n" \
"*/daily <n>:* `Shows profit or loss per day, over the last n days`\n" \ "*/daily <n>:* `Shows profit or loss per day, over the last n days`\n" \
"*/count:* `Show number of trades running compared to allowed number of trades`\n" \ "*/count:* `Show number of trades running compared to allowed number of trades`" \
"\n" \
"*/balance:* `Show account balance per currency`\n" \ "*/balance:* `Show account balance per currency`\n" \
"*/help:* `This help message`\n" \ "*/help:* `This help message`\n" \
"*/version:* `Show version`" "*/version:* `Show version`"
@ -391,7 +395,8 @@ class Telegram(RPC):
""" """
self.send_msg('*Version:* `{}`'.format(__version__), bot=bot) self.send_msg('*Version:* `{}`'.format(__version__), bot=bot)
def send_msg(self, msg: str, bot: Bot = None, parse_mode: ParseMode = ParseMode.MARKDOWN) -> None: def send_msg(self, msg: str, bot: Bot = None,
parse_mode: ParseMode = ParseMode.MARKDOWN) -> None:
""" """
Send given markdown message Send given markdown message
:param msg: message :param msg: message

View File

@ -19,7 +19,7 @@ class Strategy(object):
""" """
This class contains all the logic to load custom strategy class This class contains all the logic to load custom strategy class
""" """
def __init__(self, config: dict={}) -> None: def __init__(self, config: dict = {}) -> None:
""" """
Load the custom class from config parameter Load the custom class from config parameter
:param config: :param config:

View File

@ -1,14 +1,8 @@
# pragma pylint: disable=missing-docstring,W0212,C0103 # pragma pylint: disable=missing-docstring,W0212,C0103
import logging
import os import os
import pytest
from copy import deepcopy from copy import deepcopy
#from freqtrade.optimize.hyperopt import EXPECTED_MAX_PROFIT, start, \
# log_results, save_trials, read_trials, generate_roi_table
from unittest.mock import MagicMock from unittest.mock import MagicMock
from freqtrade.optimize.hyperopt import Hyperopt
from freqtrade.optimize.hyperopt import Hyperopt, start
import freqtrade.tests.conftest as tt # test tools import freqtrade.tests.conftest as tt # test tools
@ -24,7 +18,7 @@ def create_trials(mocker) -> None:
- we might have a pickle'd file so make sure that we return - we might have a pickle'd file so make sure that we return
false when looking for it false when looking for it
""" """
_HYPEROPT.trials_file = os.path.join('freqtrade', 'tests', 'optimize','ut_trials.pickle') _HYPEROPT.trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle')
mocker.patch('freqtrade.optimize.hyperopt.os.path.exists', return_value=False) mocker.patch('freqtrade.optimize.hyperopt.os.path.exists', return_value=False)
mocker.patch('freqtrade.optimize.hyperopt.os.remove', return_value=True) mocker.patch('freqtrade.optimize.hyperopt.os.remove', return_value=True)

View File

@ -1,4 +1,5 @@
# pragma pylint: disable=protected-access, invalid-name # pragma pylint: disable=protected-access, invalid-name
""" """
Unit test file for configuration.py Unit test file for configuration.py
""" """
@ -270,4 +271,3 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
'Parameter --export detected: {} ...'.format(config['export']), 'Parameter --export detected: {} ...'.format(config['export']),
caplog.record_tuples caplog.record_tuples
) )

View File

@ -8,7 +8,6 @@ from freqtrade.constants import Constants
def test_constant_object() -> None: def test_constant_object() -> None:
""" """
Test the Constants object has the mandatory Constants Test the Constants object has the mandatory Constants
:return: None
""" """
assert hasattr(Constants, 'CONF_SCHEMA') assert hasattr(Constants, 'CONF_SCHEMA')
assert hasattr(Constants, 'DYNAMIC_WHITELIST') assert hasattr(Constants, 'DYNAMIC_WHITELIST')
@ -19,11 +18,9 @@ def test_constant_object() -> None:
assert hasattr(Constants, 'DEFAULT_STRATEGY') assert hasattr(Constants, 'DEFAULT_STRATEGY')
def test_conf_schema() -> None: def test_conf_schema() -> None:
""" """
Test the CONF_SCHEMA is from the right type Test the CONF_SCHEMA is from the right type
:return:
""" """
constant = Constants() constant = Constants()
assert isinstance(constant.CONF_SCHEMA, dict) assert isinstance(constant.CONF_SCHEMA, dict)

View File

@ -2,7 +2,7 @@
import pandas import pandas
from freqtrade.optimize import load_data from freqtrade.optimize import load_data
from freqtrade.analyze import Analyze, SignalType from freqtrade.analyze import Analyze
_pairs = ['BTC_ETH'] _pairs = ['BTC_ETH']