diff --git a/freqtrade/analyze.py b/freqtrade/analyze.py index 401d99479..8bc552d74 100644 --- a/freqtrade/analyze.py +++ b/freqtrade/analyze.py @@ -3,13 +3,15 @@ Functions to analyze ticker data with indicators and produce buy and sell signal """ from datetime import datetime, timedelta from enum import Enum -from typing import Dict, List +from typing import Dict, List, Tuple + import arrow from pandas import DataFrame, to_datetime + from freqtrade.exchange import get_ticker_history from freqtrade.logger import Logger -from freqtrade.strategy.strategy import Strategy from freqtrade.persistence import Trade +from freqtrade.strategy.strategy import Strategy class SignalType(Enum): @@ -96,9 +98,7 @@ class Analyze(object): dataframe = self.populate_sell_trend(dataframe) return dataframe - # FIX: Maybe return False, if an error has occured, - # Otherwise we might mask an error as an non-signal-scenario - def get_signal(self, pair: str, interval: int) -> (bool, bool): + def get_signal(self, pair: str, interval: int) -> Tuple[bool, bool]: """ Calculates current signal based several technical analysis indicators :param pair: pair in format BTC_ANT or BTC-ANT @@ -108,7 +108,7 @@ class Analyze(object): ticker_hist = get_ticker_history(pair, interval) if not ticker_hist: self.logger.warning('Empty ticker history for pair %s', pair) - return (False, False) # return False ? + return False, False try: dataframe = self.analyze_ticker(ticker_hist) @@ -118,18 +118,18 @@ class Analyze(object): pair, str(error) ) - return (False, False) # return False ? + return False, False except Exception as error: self.logger.exception( 'Unexpected error when analyzing ticker for pair %s: %s', pair, str(error) ) - return (False, False) # return False ? + return False, False if dataframe.empty: self.logger.warning('Empty dataframe for pair %s', pair) - return (False, False) # return False ? + return False, False latest = dataframe.iloc[-1] @@ -141,7 +141,7 @@ class Analyze(object): pair, (arrow.utcnow() - signal_date).seconds // 60 ) - return (False, False) # return False ? + return False, False (buy, sell) = latest[SignalType.BUY.value] == 1, latest[SignalType.SELL.value] == 1 self.logger.debug( @@ -151,7 +151,7 @@ class Analyze(object): str(buy), str(sell) ) - return (buy, sell) + return buy, sell def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool, sell: bool) -> bool: """ diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index 30f94fddc..c69135117 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -3,10 +3,10 @@ This module contains the argument manager class """ import argparse +import logging import os import re -import logging -from typing import List +from typing import List, Tuple, Optional from freqtrade import __version__ from freqtrade.constants import Constants @@ -22,11 +22,11 @@ class Arguments(object): self.parsed_arg = None self.parser = argparse.ArgumentParser(description=description) - def _load_args(self): + def _load_args(self) -> None: self.common_args_parser() self._build_subcommands() - def get_parsed_arg(self) -> List[str]: + def get_parsed_arg(self) -> argparse.Namespace: """ Return the list of arguments :return: List[str] List of arguments @@ -37,7 +37,7 @@ class Arguments(object): return self.parsed_arg - def parse_args(self) -> List[str]: + def parse_args(self) -> argparse.Namespace: """ Parses given arguments and returns an argparse Namespace instance. """ @@ -205,7 +205,7 @@ class Arguments(object): self.hyperopt_options(hyperopt_cmd) @staticmethod - def parse_timerange(text: str) -> (List, int, int): + def parse_timerange(text: str) -> Optional[Tuple[List, int, int]]: """ Parse the value of the argument --timerange to determine what is the range desired :param text: value from --timerange @@ -236,10 +236,10 @@ class Arguments(object): stop = rvals[index] if stype[1] != 'date': stop = int(stop) - return (stype, start, stop) + return stype, start, stop raise Exception('Incorrect syntax for timerange "%s"' % text) - def scripts_options(self): + def scripts_options(self) -> None: """ Parses given arguments for plot scripts. """ diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index fd9872eb3..1f6cea4e6 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -3,8 +3,9 @@ This module contains the configuration class """ import json +from argparse import Namespace +from typing import Dict, Any -from typing import Dict, List, Any from jsonschema import Draft4Validator, validate from jsonschema.exceptions import ValidationError, best_match @@ -17,7 +18,7 @@ class Configuration(object): Class to read and init the bot configuration Reuse this class for the bot, backtesting, hyperopt and every script that required configuration """ - def __init__(self, args: List[str], do_not_init=False) -> None: + def __init__(self, args: Namespace) -> None: self.args = args self.logging = Logger(name=__name__) self.logger = self.logging.get_logger() diff --git a/freqtrade/exchange/bittrex.py b/freqtrade/exchange/bittrex.py index 5aa07e460..0cba621af 100644 --- a/freqtrade/exchange/bittrex.py +++ b/freqtrade/exchange/bittrex.py @@ -1,8 +1,8 @@ import logging from typing import Dict, List, Optional -from bittrex.bittrex import Bittrex as _Bittrex from bittrex.bittrex import API_V1_1, API_V2_0 +from bittrex.bittrex import Bittrex as _Bittrex from requests.exceptions import ContentDecodingError from freqtrade import OperationalException diff --git a/freqtrade/fiat_convert.py b/freqtrade/fiat_convert.py index 4874247db..b86b56ec5 100644 --- a/freqtrade/fiat_convert.py +++ b/freqtrade/fiat_convert.py @@ -5,12 +5,13 @@ e.g BTC to USD import logging import time + from coinmarketcap import Market logger = logging.getLogger(__name__) -class CryptoFiat(): +class CryptoFiat(object): """ Object to describe what is the price of Crypto-currency in a FIAT """ diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index b18d6540b..e57f177e9 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -6,11 +6,14 @@ import copy import json import time import traceback -from typing import Dict, List, Optional, Any, Callable from datetime import datetime -import requests +from typing import Dict, List, Optional, Any, Callable + import arrow +import requests from cachetools import cached, TTLCache + +from freqtrade import (DependencyException, OperationalException, exchange, persistence) from freqtrade.analyze import Analyze from freqtrade.constants import Constants from freqtrade.fiat_convert import CryptoToFiatConverter @@ -18,7 +21,6 @@ from freqtrade.logger import Logger from freqtrade.persistence import Trade from freqtrade.rpc.rpc_manager import RPCManager from freqtrade.state import State -from freqtrade import (DependencyException, OperationalException, exchange, persistence) class FreqtradeBot(object): @@ -27,7 +29,7 @@ class FreqtradeBot(object): This is from here the bot start its logic. """ - def __init__(self, config: Dict[str, Any], db_url: Optional[str] = None) -> bool: + def __init__(self, config: Dict[str, Any], db_url: Optional[str] = None): """ Init all variables and object the bot need to work :param config: configuration dict, you can use the Configuration.get_config() diff --git a/freqtrade/indicator_helpers.py b/freqtrade/indicator_helpers.py index c3cc42e6a..14519d7a2 100644 --- a/freqtrade/indicator_helpers.py +++ b/freqtrade/indicator_helpers.py @@ -1,19 +1,19 @@ from math import exp, pi, sqrt, cos -import numpy +import numpy as np import talib as ta from pandas import Series -def went_up(series: Series) -> Series: +def went_up(series: Series) -> bool: return series > series.shift(1) -def went_down(series: Series) -> Series: +def went_down(series: Series) -> bool: return series < series.shift(1) -def ehlers_super_smoother(series: Series, smoothing: float = 6): +def ehlers_super_smoother(series: Series, smoothing: float = 6) -> type(Series): magic = pi * sqrt(2) / smoothing a1 = exp(-magic) coeff2 = 2 * a1 * cos(magic) @@ -29,7 +29,7 @@ def ehlers_super_smoother(series: Series, smoothing: float = 6): return filtered -def fishers_inverse(series: Series, smoothing: float = 0): +def fishers_inverse(series: Series, smoothing: float = 0) -> np.ndarray: """ Does a smoothed fishers inverse transformation. Can be used with any oscillator that goes from 0 to 100 like RSI or MFI """ v1 = 0.1 * (series - 50) @@ -37,4 +37,4 @@ def fishers_inverse(series: Series, smoothing: float = 0): v2 = ta.WMA(v1.values, timeperiod=smoothing) else: v2 = v1 - return (numpy.exp(2 * v2)-1) / (numpy.exp(2 * v2) + 1) + return (np.exp(2 * v2)-1) / (np.exp(2 * v2) + 1) diff --git a/freqtrade/main.py b/freqtrade/main.py index 97515b68b..d2cfc6f9f 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -6,17 +6,18 @@ Read the documentation to know what cli arguments you need. import logging import sys -from typing import Dict -from freqtrade.configuration import Configuration +from typing import List + +from freqtrade import (__version__) from freqtrade.arguments import Arguments +from freqtrade.configuration import Configuration from freqtrade.freqtradebot import FreqtradeBot from freqtrade.logger import Logger -from freqtrade import (__version__) logger = Logger(name='freqtrade').get_logger() -def main(sysargv: Dict) -> None: +def main(sysargv: List[str]) -> None: """ This function will initiate the bot and start the trading loop. :return: None diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 814e0fb6f..f5d045c44 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -2,16 +2,19 @@ Various tool function for Freqtrade and scripts """ -import re import json import logging +import re from datetime import datetime +from typing import Dict + import numpy as np +from pandas import DataFrame logger = logging.getLogger(__name__) -def shorten_date(_date): +def shorten_date(_date: str) -> str: """ Trim the date so it fits on small screens """ @@ -28,7 +31,7 @@ def shorten_date(_date): # Matplotlib doesn't support ::datetime64, # # so we need to convert it into ::datetime # ############################################ -def datesarray_to_datetimearray(dates): +def datesarray_to_datetimearray(dates: np.ndarray) -> np.ndarray: """ Convert an pandas-array of timestamps into An numpy-array of datetimes @@ -42,10 +45,10 @@ def datesarray_to_datetimearray(dates): return np.array(times) -def common_datearray(dfs): +def common_datearray(dfs: Dict[str, DataFrame]) -> np.ndarray: """ Return dates from Dataframe - :param dfs: Dataframe + :param dfs: Dict with format pair: pair_data :return: List of dates """ alldates = {} diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 049b2f94f..a26744691 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -1,20 +1,20 @@ # pragma pylint: disable=missing-docstring +import gzip import json import os -from typing import Optional, List, Dict -import gzip -from freqtrade.exchange import get_ticker_history +from typing import Optional, List, Dict, Tuple from freqtrade import misc +from freqtrade.exchange import get_ticker_history from freqtrade.logger import Logger from user_data.hyperopt_conf import hyperopt_optimize_conf logger = Logger(name=__name__).get_logger() -def trim_tickerlist(tickerlist, timerange): - (stype, start, stop) = timerange +def trim_tickerlist(tickerlist: List[Dict], timerange: Tuple[Tuple, int, int]) -> List[Dict]: + stype, start, stop = timerange if stype == (None, 'line'): return tickerlist[stop:] elif stype == ('line', None): @@ -25,7 +25,10 @@ def trim_tickerlist(tickerlist, timerange): return tickerlist -def load_tickerdata_file(datadir, pair, ticker_interval, timerange=None): +def load_tickerdata_file( + datadir: str, pair: str, + ticker_interval: int, + timerange: Optional[Tuple[Tuple, int, int]] = None) -> Optional[List[Dict]]: """ Load a pair from file, :return dict OR empty if unsuccesful @@ -55,12 +58,12 @@ def load_tickerdata_file(datadir, pair, ticker_interval, timerange=None): return pairdata -def load_data(datadir: str, ticker_interval: int, pairs: Optional[List[str]] = None, - refresh_pairs: Optional[bool] = False, timerange=None) -> Dict[str, List]: +def load_data(datadir: str, ticker_interval: int, + pairs: Optional[List[str]] = None, + refresh_pairs: Optional[bool] = False, + timerange: Optional[Tuple[Tuple, int, int]] = None) -> Dict[str, List]: """ Loads ticker history data for the given parameters - :param ticker_interval: ticker interval in minutes - :param pairs: list of pairs :return: dict """ result = {} diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 8d22d00e3..d8af47326 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -3,18 +3,19 @@ """ This module contains the backtesting logic """ +from argparse import Namespace +from typing import Dict, Tuple, Any, List, Optional -from typing import Dict, Tuple, Any import arrow from pandas import DataFrame, Series from tabulate import tabulate import freqtrade.optimize as optimize -from freqtrade.arguments import Arguments -from freqtrade.exchange import Bittrex -from freqtrade.configuration import Configuration from freqtrade import exchange from freqtrade.analyze import Analyze +from freqtrade.arguments import Arguments +from freqtrade.configuration import Configuration +from freqtrade.exchange import Bittrex from freqtrade.logger import Logger from freqtrade.misc import file_dump_json from freqtrade.persistence import Trade @@ -101,7 +102,10 @@ class Backtesting(object): ]) return tabulate(tabular_data, headers=headers, floatfmt=floatfmt) - def _get_sell_trade_entry(self, pair, buy_row, partial_ticker, trade_count_lock, args): + def _get_sell_trade_entry( + self, pair: str, buy_row: DataFrame, + partial_ticker: List, trade_count_lock: Dict, args: Dict) -> Optional[Tuple]: + stake_amount = args['stake_amount'] max_open_trades = args.get('max_open_trades', 0) trade = Trade( @@ -132,7 +136,7 @@ class Backtesting(object): sell_row.date return None - def backtest(self, args) -> DataFrame: + def backtest(self, args: Dict) -> DataFrame: """ Implements backtesting functionality @@ -273,7 +277,7 @@ class Backtesting(object): ) -def setup_configuration(args) -> Dict[str, Any]: +def setup_configuration(args: Namespace) -> Dict[str, Any]: """ Prepare the configuration for the backtesting :param args: Cli args from Arguments() @@ -289,7 +293,7 @@ def setup_configuration(args) -> Dict[str, Any]: return config -def start(args) -> None: +def start(args: Namespace) -> None: """ Start Backtesting script :param args: Cli args from Arguments() diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 39bade554..7dcd46fd2 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -10,6 +10,7 @@ import os import pickle import signal import sys +from argparse import Namespace from functools import reduce from math import exp from operator import itemgetter @@ -22,11 +23,11 @@ from hyperopt.mongoexp import MongoTrials from pandas import DataFrame import freqtrade.vendor.qtpylib.indicators as qtpylib -from freqtrade.configuration import Configuration -from freqtrade.optimize import load_data from freqtrade.arguments import Arguments -from freqtrade.optimize.backtesting import Backtesting +from freqtrade.configuration import Configuration from freqtrade.logger import Logger +from freqtrade.optimize import load_data +from freqtrade.optimize.backtesting import Backtesting from user_data.hyperopt_conf import hyperopt_optimize_conf @@ -240,7 +241,7 @@ class Hyperopt(Backtesting): return trade_loss + profit_loss + duration_loss @staticmethod - def generate_roi_table(params) -> Dict[int, float]: + def generate_roi_table(params: Dict) -> Dict[int, float]: """ Generate the ROI table thqt will be used by Hyperopt """ @@ -335,7 +336,7 @@ class Hyperopt(Backtesting): ]), } - def has_space(self, space) -> bool: + def has_space(self, space: str) -> bool: """ Tell if a space value is contained in the configuration """ @@ -433,7 +434,7 @@ class Hyperopt(Backtesting): return populate_buy_trend - def generate_optimizer(self, params) -> Dict: + def generate_optimizer(self, params: Dict) -> Dict: if self.has_space('roi'): self.analyze.strategy.minimal_roi = self.generate_roi_table(params) @@ -496,7 +497,7 @@ class Hyperopt(Backtesting): results.duration.mean(), ) - def start(self): + def start(self) -> None: timerange = Arguments.parse_timerange(self.config.get('timerange')) data = load_data( datadir=self.config.get('datadir'), @@ -571,7 +572,7 @@ class Hyperopt(Backtesting): # Store trials result to file to resume next time self.save_trials() - def signal_handler(self, sig, frame): + def signal_handler(self, sig, frame) -> None: """ Hyperopt SIGINT handler """ @@ -585,7 +586,7 @@ class Hyperopt(Backtesting): sys.exit(0) -def start(args) -> None: +def start(args: Namespace) -> None: """ Start Backtesting script :param args: Cli args from Arguments() diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index e0d433a24..56fe336d2 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -207,6 +207,7 @@ class Trade(_DECL_BASE): Calculates the profit in percentage (including fee). :param rate: rate to compare with (optional). If rate is not set self.close_rate will be used + :param fee: fee to use on the close rate (optional). :return: profit in percentage as float """ getcontext().prec = 8 diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index fa9c2022d..b4592f78a 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -2,16 +2,19 @@ This module contains class to define a RPC communications """ -from decimal import Decimal from datetime import datetime, timedelta +from decimal import Decimal +from typing import Tuple, Any + import arrow -from pandas import DataFrame import sqlalchemy as sql +from pandas import DataFrame + +from freqtrade import exchange from freqtrade.logger import Logger +from freqtrade.misc import shorten_date from freqtrade.persistence import Trade from freqtrade.state import State -from freqtrade import exchange -from freqtrade.misc import shorten_date class RPC(object): @@ -30,7 +33,7 @@ class RPC(object): level=self.freqtrade.config.get('loglevel') ).get_logger() - def rpc_trade_status(self) -> (bool, Trade): + def rpc_trade_status(self) -> Tuple[bool, Any]: """ Below follows the RPC backend it is prefixed with rpc_ to raise awareness that it is a remotely exposed function @@ -39,9 +42,9 @@ class RPC(object): # Fetch open trade trades = Trade.query.filter(Trade.is_open.is_(True)).all() if self.freqtrade.get_state() != State.RUNNING: - return (True, '*Status:* `trader is not running`') + return True, '*Status:* `trader is not running`' elif not trades: - return (True, '*Status:* `no active trade`') + return True, '*Status:* `no active trade`' else: result = [] for trade in trades: @@ -80,14 +83,14 @@ class RPC(object): ) if order else None, ) result.append(message) - return (False, result) + return False, result - def rpc_status_table(self) -> (bool, DataFrame): + def rpc_status_table(self) -> Tuple[bool, Any]: trades = Trade.query.filter(Trade.is_open.is_(True)).all() if self.freqtrade.get_state() != State.RUNNING: - return (True, '*Status:* `trader is not running`') + return True, '*Status:* `trader is not running`' elif not trades: - return (True, '*Status:* `no active order`') + return True, '*Status:* `no active order`' else: trades_list = [] for trade in trades: @@ -107,14 +110,16 @@ class RPC(object): # consisting of (error_occured?, result) # Another approach would be to just return the # result, or raise error - return (False, df_statuses) + return False, df_statuses - def rpc_daily_profit(self, timescale, stake_currency, fiat_display_currency): + def rpc_daily_profit( + self, timescale: int, + stake_currency: str, fiat_display_currency: str) -> Tuple[bool, Any]: today = datetime.utcnow().date() profit_days = {} 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 for day in range(0, timescale): @@ -153,9 +158,10 @@ class RPC(object): ] for key, value in profit_days.items() ] - return (False, stats) + return False, stats - def rpc_trade_statistics(self, stake_currency, fiat_display_currency) -> None: + def rpc_trade_statistics( + self, stake_currency: str, fiat_display_currency: str) -> Tuple[bool, Any]: """ :return: cumulative profit statistics. """ @@ -190,15 +196,13 @@ class RPC(object): profit_all_percent.append(profit_percent) best_pair = Trade.session.query( - Trade.pair, - sql.func.sum(Trade.close_profit).label('profit_sum') - )\ - .filter(Trade.is_open.is_(False))\ - .group_by(Trade.pair)\ + Trade.pair, sql.func.sum(Trade.close_profit).label('profit_sum') + ).filter(Trade.is_open.is_(False)) \ + .group_by(Trade.pair) \ .order_by(sql.text('profit_sum DESC')).first() if not best_pair: - return (True, '*Status:* `no closed trade`') + return True, '*Status:* `no closed trade`' bp_pair, bp_rate = best_pair @@ -239,7 +243,7 @@ class RPC(object): } ) - def rpc_balance(self, fiat_display_currency): + def rpc_balance(self, fiat_display_currency: str) -> Tuple[bool, Any]: """ :return: current account balance per crypto """ @@ -248,7 +252,7 @@ class RPC(object): if c['Balance'] or c['Available'] or c['Pending'] ] if not balances: - return (True, '`All balances are zero.`') + return True, '`All balances are zero.`' output = [] total = 0.0 @@ -275,17 +279,17 @@ class RPC(object): fiat = self.freqtrade.fiat_converter symbol = fiat_display_currency value = fiat.convert_amount(total, 'BTC', symbol) - return (False, (output, total, symbol, value)) + return False, (output, total, symbol, value) def rpc_start(self) -> (bool, str): """ Handler for start. """ if self.freqtrade.get_state() == State.RUNNING: - return (True, '*Status:* `already running`') + return True, '*Status:* `already running`' self.freqtrade.update_state(State.RUNNING) - return (False, '`Starting trader ...`') + return False, '`Starting trader ...`' def rpc_stop(self) -> (bool, str): """ @@ -293,18 +297,18 @@ class RPC(object): """ if self.freqtrade.get_state() == State.RUNNING: self.freqtrade.update_state(State.STOPPED) - return (False, '`Stopping trader ...`') + return False, '`Stopping trader ...`' - return (True, '*Status:* `already stopped`') + return True, '*Status:* `already stopped`' # FIX: no test for this!!!! - def rpc_forcesell(self, trade_id) -> None: + def rpc_forcesell(self, trade_id) -> Tuple[bool, Any]: """ Handler for forcesell . Sells the given trade at current price :return: error or None """ - def _exec_forcesell(trade: Trade) -> str: + def _exec_forcesell(trade: Trade) -> None: # Check if there is there is an open order if trade.open_order_id: order = exchange.get_order(trade.open_order_id) @@ -326,13 +330,13 @@ class RPC(object): # ---- EOF def _exec_forcesell ---- if self.freqtrade.get_state() != State.RUNNING: - return (True, '`trader is not running`') + return True, '`trader is not running`' if trade_id == 'all': # Execute sell for all open orders for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): _exec_forcesell(trade) - return (False, '') + return False, '' # Query for trade trade = Trade.query.filter( @@ -343,18 +347,18 @@ class RPC(object): ).first() if not trade: self.logger.warning('forcesell: Invalid argument received') - return (True, 'Invalid argument.') + return True, 'Invalid argument.' _exec_forcesell(trade) - return (False, '') + return False, '' - def rpc_performance(self) -> None: + def rpc_performance(self) -> Tuple[bool, Any]: """ Handler for performance. Shows a performance statistic from finished trades """ 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, sql.func.sum(Trade.close_profit).label('profit_sum'), @@ -367,15 +371,15 @@ class RPC(object): for (pair, rate, count) in pair_rates: 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) -> Tuple[bool, Any]: """ Returns the number of trades running :return: None """ 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() - return (False, trades) + return False, trades diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index cd3a694c2..fb18a8d73 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -28,7 +28,7 @@ class RPCManager(object): self.telegram = None self._init() - def _init(self): + def _init(self) -> None: """ Init RPC modules :return: diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 0d4e4403b..fce7a81f9 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -5,10 +5,12 @@ This module manage Telegram communication """ from typing import Any, Callable + from tabulate import tabulate from telegram import Bot, ParseMode, ReplyKeyboardMarkup, Update from telegram.error import NetworkError, TelegramError from telegram.ext import CommandHandler, Updater + from freqtrade.__init__ import __version__ from freqtrade.rpc.rpc import RPC diff --git a/freqtrade/strategy/default_strategy.py b/freqtrade/strategy/default_strategy.py index 2247ecf27..ea37735b7 100644 --- a/freqtrade/strategy/default_strategy.py +++ b/freqtrade/strategy/default_strategy.py @@ -2,9 +2,10 @@ import talib.abstract as ta from pandas import DataFrame + import freqtrade.vendor.qtpylib.indicators as qtpylib -from freqtrade.strategy.interface import IStrategy from freqtrade.indicator_helpers import fishers_inverse +from freqtrade.strategy.interface import IStrategy class_name = 'DefaultStrategy' diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index dc9f33244..4eb73fb2e 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -4,6 +4,7 @@ This module defines the interface to apply for strategies """ from abc import ABC, abstractmethod + from pandas import DataFrame diff --git a/freqtrade/strategy/strategy.py b/freqtrade/strategy/strategy.py index ed1dee10e..d7a89d1de 100644 --- a/freqtrade/strategy/strategy.py +++ b/freqtrade/strategy/strategy.py @@ -7,11 +7,12 @@ import importlib import os import sys from collections import OrderedDict -from pandas import DataFrame -from freqtrade.logger import Logger -from freqtrade.constants import Constants -from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +from freqtrade.constants import Constants +from freqtrade.logger import Logger +from freqtrade.strategy.interface import IStrategy sys.path.insert(0, r'../../user_data/strategies') @@ -59,7 +60,7 @@ class Strategy(object): # Minimal ROI designed for the strategy self.minimal_roi = OrderedDict(sorted( {int(key): value for (key, value) in self.custom_strategy.minimal_roi.items()}.items(), - key=lambda tuple: tuple[0])) # sort after converting to number + key=lambda t: t[0])) # sort after converting to number # Optimal stoploss designed for the strategy self.stoploss = float(self.custom_strategy.stoploss) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index cb726633b..07dc45a3e 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -1,14 +1,15 @@ # pragma pylint: disable=missing-docstring -from datetime import datetime -from unittest.mock import MagicMock -from functools import reduce -import logging import json +import logging +from datetime import datetime +from functools import reduce +from unittest.mock import MagicMock + import arrow import pytest from jsonschema import validate -from telegram import Chat, Message, Update from sqlalchemy import create_engine +from telegram import Chat, Message, Update from freqtrade.analyze import Analyze from freqtrade.constants import Constants diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 4f6eab083..f2874f2da 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -1,15 +1,16 @@ # pragma pylint: disable=missing-docstring, C0103, bad-continuation, global-statement # pragma pylint: disable=protected-access -from unittest.mock import MagicMock -from random import randint import logging -from requests.exceptions import RequestException -import pytest +from random import randint +from unittest.mock import MagicMock +import pytest +from requests.exceptions import RequestException + +import freqtrade.exchange as exchange from freqtrade import OperationalException from freqtrade.exchange import init, validate_pairs, buy, sell, get_balance, get_balances, \ get_ticker, get_ticker_history, cancel_order, get_name, get_fee -import freqtrade.exchange as exchange from freqtrade.tests.conftest import log_has API_INIT = False diff --git a/freqtrade/tests/exchange/test_exchange_bittrex.py b/freqtrade/tests/exchange/test_exchange_bittrex.py index 058c25de1..2c66215c2 100644 --- a/freqtrade/tests/exchange/test_exchange_bittrex.py +++ b/freqtrade/tests/exchange/test_exchange_bittrex.py @@ -1,10 +1,12 @@ # pragma pylint: disable=missing-docstring, C0103, protected-access, unused-argument from unittest.mock import MagicMock + import pytest from requests.exceptions import ContentDecodingError -from freqtrade.exchange.bittrex import Bittrex + import freqtrade.exchange.bittrex as btx +from freqtrade.exchange.bittrex import Bittrex # Eat this flake8 diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 731b93176..021474d5c 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -1,20 +1,21 @@ # pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument import json -import random import math -from typing import List +import random from copy import deepcopy +from typing import List from unittest.mock import MagicMock -from arrow import Arrow -import pandas as pd -import numpy as np -from freqtrade import optimize -from freqtrade.optimize.backtesting import Backtesting, start, setup_configuration -from freqtrade.arguments import Arguments -from freqtrade.analyze import Analyze -from freqtrade.tests.conftest import default_conf, log_has +import numpy as np +import pandas as pd +from arrow import Arrow + +from freqtrade import optimize +from freqtrade.analyze import Analyze +from freqtrade.arguments import Arguments +from freqtrade.optimize.backtesting import Backtesting, start, setup_configuration +from freqtrade.tests.conftest import default_conf, log_has # Avoid to reinit the same object again and again _BACKTESTING = Backtesting(default_conf()) diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index f29694c2f..6d376471a 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -3,6 +3,7 @@ import json import os from copy import deepcopy from unittest.mock import MagicMock + import pandas as pd from freqtrade.optimize.__init__ import load_tickerdata_file diff --git a/freqtrade/tests/optimize/test_optimize.py b/freqtrade/tests/optimize/test_optimize.py index 43f46291f..e26d30534 100644 --- a/freqtrade/tests/optimize/test_optimize.py +++ b/freqtrade/tests/optimize/test_optimize.py @@ -1,13 +1,14 @@ # pragma pylint: disable=missing-docstring, protected-access, C0103 -import os import json +import os import uuid from shutil import copyfile + from freqtrade import optimize -from freqtrade.optimize.__init__ import make_testdata_path, download_pairs,\ - download_backtesting_testdata, load_tickerdata_file, trim_tickerlist from freqtrade.misc import file_dump_json +from freqtrade.optimize.__init__ import make_testdata_path, download_pairs, \ + download_backtesting_testdata, load_tickerdata_file, trim_tickerlist from freqtrade.tests.conftest import log_has # Change this if modifying BTC_UNITEST testdatafile diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 69ddc5ef5..4796b077e 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -6,23 +6,23 @@ Unit test file for rpc/telegram.py """ import re +from copy import deepcopy from datetime import datetime from random import randint from unittest.mock import MagicMock -from copy import deepcopy from sqlalchemy import create_engine from telegram import Update, Message, Chat from telegram.error import NetworkError from freqtrade import __version__ -from freqtrade.rpc.telegram import authorized_only from freqtrade.freqtradebot import FreqtradeBot -from freqtrade.rpc.telegram import Telegram from freqtrade.persistence import Trade +from freqtrade.rpc.telegram import Telegram +from freqtrade.rpc.telegram import authorized_only from freqtrade.state import State -from freqtrade.tests.test_freqtradebot import patch_get_signal, patch_coinmarketcap from freqtrade.tests.conftest import get_patched_freqtradebot, log_has +from freqtrade.tests.test_freqtradebot import patch_get_signal, patch_coinmarketcap class DummyCls(Telegram): diff --git a/freqtrade/tests/strategy/test_default_strategy.py b/freqtrade/tests/strategy/test_default_strategy.py index 02b5630fc..2b91fbec5 100644 --- a/freqtrade/tests/strategy/test_default_strategy.py +++ b/freqtrade/tests/strategy/test_default_strategy.py @@ -1,8 +1,10 @@ import json + import pytest from pandas import DataFrame -from freqtrade.strategy.default_strategy import DefaultStrategy, class_name + from freqtrade.analyze import Analyze +from freqtrade.strategy.default_strategy import DefaultStrategy, class_name @pytest.fixture diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index 77250659f..7ce9ae0ef 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -1,6 +1,7 @@ # pragma pylint: disable=missing-docstring, protected-access, C0103 import logging + from freqtrade.strategy.strategy import Strategy diff --git a/freqtrade/tests/test_analyze.py b/freqtrade/tests/test_analyze.py index 4d1c31de2..558ea7ee5 100644 --- a/freqtrade/tests/test_analyze.py +++ b/freqtrade/tests/test_analyze.py @@ -5,8 +5,9 @@ Unit test file for analyse.py """ import datetime -from unittest.mock import MagicMock import logging +from unittest.mock import MagicMock + import arrow from pandas import DataFrame @@ -14,7 +15,6 @@ from freqtrade.analyze import Analyze, SignalType from freqtrade.optimize.__init__ import load_tickerdata_file from freqtrade.tests.conftest import log_has - # Avoid to reinit the same object again and again _ANALYZE = Analyze({'strategy': 'default_strategy'}) diff --git a/freqtrade/tests/test_arguments.py b/freqtrade/tests/test_arguments.py index 6ad507e85..3e0639304 100644 --- a/freqtrade/tests/test_arguments.py +++ b/freqtrade/tests/test_arguments.py @@ -6,6 +6,7 @@ Unit test file for arguments.py import argparse import logging + import pytest from freqtrade.arguments import Arguments diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index 4ca92c343..002eac722 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -4,10 +4,10 @@ Unit test file for configuration.py """ import json - from copy import deepcopy -import pytest from unittest.mock import MagicMock + +import pytest from jsonschema import ValidationError from freqtrade.arguments import Arguments diff --git a/freqtrade/tests/test_dataframe.py b/freqtrade/tests/test_dataframe.py index 86944e4f3..1f69a7d32 100644 --- a/freqtrade/tests/test_dataframe.py +++ b/freqtrade/tests/test_dataframe.py @@ -2,8 +2,8 @@ import pandas -from freqtrade.optimize import load_data from freqtrade.analyze import Analyze +from freqtrade.optimize import load_data from freqtrade.strategy.strategy import Strategy _pairs = ['BTC_ETH'] diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 5d9b9aba1..d58b428da 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -5,22 +5,23 @@ Unit test file for freqtradebot.py """ import logging +import re import time -from unittest.mock import MagicMock from copy import deepcopy from typing import Dict, Optional +from unittest.mock import MagicMock + import arrow import pytest import requests -import re from sqlalchemy import create_engine -from freqtrade.tests.conftest import log_has from freqtrade import DependencyException, OperationalException from freqtrade.exchange import Exchanges from freqtrade.freqtradebot import FreqtradeBot -from freqtrade.state import State from freqtrade.persistence import Trade +from freqtrade.state import State +from freqtrade.tests.conftest import log_has # Functions for recurrent object patching diff --git a/freqtrade/tests/test_indicator_helpers.py b/freqtrade/tests/test_indicator_helpers.py index 90330a6ef..87b085a0b 100644 --- a/freqtrade/tests/test_indicator_helpers.py +++ b/freqtrade/tests/test_indicator_helpers.py @@ -1,4 +1,5 @@ import pandas as pd + from freqtrade.indicator_helpers import went_up, went_down diff --git a/freqtrade/tests/test_logger.py b/freqtrade/tests/test_logger.py index d6e7a2e06..8e094b2d1 100644 --- a/freqtrade/tests/test_logger.py +++ b/freqtrade/tests/test_logger.py @@ -3,6 +3,7 @@ Unit test file for logger.py """ import logging + from freqtrade.logger import Logger diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index 83da04495..71b816968 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -4,6 +4,7 @@ Unit test file for main.py import logging from unittest.mock import MagicMock + import pytest from freqtrade.main import main, set_loggers diff --git a/freqtrade/tests/test_misc.py b/freqtrade/tests/test_misc.py index 1d2417332..3560b2db1 100644 --- a/freqtrade/tests/test_misc.py +++ b/freqtrade/tests/test_misc.py @@ -6,10 +6,11 @@ Unit test file for misc.py import datetime from unittest.mock import MagicMock + from freqtrade.analyze import Analyze -from freqtrade.optimize.__init__ import load_tickerdata_file from freqtrade.misc import (shorten_date, datesarray_to_datetimearray, common_datearray, file_dump_json) +from freqtrade.optimize.__init__ import load_tickerdata_file def test_shorten_date() -> None: diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py index f6c5318ce..70199b12a 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -1,7 +1,9 @@ # pragma pylint: disable=missing-docstring, C0103 import os + import pytest from sqlalchemy import create_engine + from freqtrade.exchange import Exchanges from freqtrade.persistence import Trade, init, clean_dry_run_db diff --git a/freqtrade/tests/testdata/download_backtest_data.py b/freqtrade/tests/testdata/download_backtest_data.py index 0cb545b3a..ceb8388a1 100755 --- a/freqtrade/tests/testdata/download_backtest_data.py +++ b/freqtrade/tests/testdata/download_backtest_data.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 """This script generate json data from bittrex""" -import sys import json +import sys from freqtrade import exchange -from freqtrade.exchange import Bittrex from freqtrade import misc +from freqtrade.exchange import Bittrex parser = misc.common_args_parser('download utility') parser.add_argument( diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index e7a52b2bf..285ba6d97 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -13,8 +13,9 @@ Optional Cli parameters """ import sys +from argparse import Namespace -from typing import Dict +from typing import List from plotly import tools from plotly.offline import plot @@ -30,10 +31,9 @@ import freqtrade.optimize as optimize logger = Logger(name="Graph dataframe").get_logger() -def plot_analyzed_dataframe(args) -> None: +def plot_analyzed_dataframe(args: Namespace) -> None: """ Calls analyze() and plots the returned dataframe - :param pair: pair as str :return: None """ pair = args.pair.replace('-', '_') @@ -153,7 +153,7 @@ def plot_analyzed_dataframe(args) -> None: plot(fig, filename='freqtrade-plot.html') -def plot_parse_args(args): +def plot_parse_args(args: List[str]) -> Namespace: """ Parse args passed to the script :param args: Cli arguments @@ -168,7 +168,7 @@ def plot_parse_args(args): return arguments.parse_args() -def main(sysargv: Dict) -> None: +def main(sysargv: List[str]) -> None: """ This function will initiate the bot and start the trading loop. :return: None diff --git a/scripts/plot_profit.py b/scripts/plot_profit.py index 68434f1fb..022bbf33c 100755 --- a/scripts/plot_profit.py +++ b/scripts/plot_profit.py @@ -13,7 +13,8 @@ Optional Cli parameters import sys import json -from typing import Dict +from argparse import Namespace +from typing import List, Optional import numpy as np from plotly import tools @@ -34,8 +35,11 @@ logger = Logger(name="Graph profits").get_logger() # data:: [ pair, profit-%, enter, exit, time, duration] # data:: ["BTC_ETH", 0.0023975, "1515598200", "1515602100", "2018-01-10 07:30:00+00:00", 65] -def make_profit_array(data, px, min_date, interval, filter_pairs=[]): +def make_profit_array( + data: List, px: int, min_date: int, + interval: int, filter_pairs: Optional[List] = None) -> np.ndarray: pg = np.zeros(px) + filter_pairs = filter_pairs or [] # Go through the trades # and make an total profit # array @@ -63,7 +67,7 @@ def make_profit_array(data, px, min_date, interval, filter_pairs=[]): return pg -def plot_profit(args) -> None: +def plot_profit(args: Namespace) -> None: """ Plots the total profit for all pairs. Note, the profit calculation isn't realistic. @@ -183,14 +187,14 @@ def plot_profit(args) -> None: plot(fig, filename='freqtrade-profit-plot.html') -def define_index(min_date, max_date, interval): +def define_index(min_date: int, max_date: int, interval: int) -> int: """ Return the index of a specific date """ return int((max_date - min_date) / (interval * 60)) -def plot_parse_args(args): +def plot_parse_args(args: List[str]) -> Namespace: """ Parse args passed to the script :param args: Cli arguments @@ -205,7 +209,7 @@ def plot_parse_args(args): return arguments.parse_args() -def main(sysargv: Dict) -> None: +def main(sysargv: List[str]) -> None: """ This function will initiate the bot and start the trading loop. :return: None