Make Pylint Happy chapter 1
This commit is contained in:
parent
d274f13480
commit
390501bac0
@ -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()}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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']
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user