telegram refactor 3/

move out rpc_daily_profit
This commit is contained in:
kryofly 2018-01-17 12:51:45 +01:00
parent a70da513e8
commit 112913531c
3 changed files with 113 additions and 47 deletions

View File

@ -1,11 +1,13 @@
import logging import logging
import re import re
import arrow import arrow
from datetime import datetime, timedelta
from pandas import DataFrame from pandas import DataFrame
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.misc import State, get_state from freqtrade.misc import State, get_state
from freqtrade import exchange from freqtrade import exchange
from freqtrade.fiat_convert import CryptoToFiatConverter
from . import telegram from . import telegram
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -142,3 +144,41 @@ def rpc_status_table():
# Another approach would be to just return the # Another approach would be to just return the
# result, or raise error # result, or raise error
return (False, df_statuses) return (False, df_statuses)
def rpc_daily_profit(timescale, stake_currency, fiat_display_currency):
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`')
# FIX: we might not want to call CryptoToFiatConverter, for every call
fiat = CryptoToFiatConverter()
for day in range(0, timescale):
profitday = today - timedelta(days=day)
trades = Trade.query \
.filter(Trade.is_open.is_(False)) \
.filter(Trade.close_date >= profitday)\
.filter(Trade.close_date < (profitday + timedelta(days=1)))\
.order_by(Trade.close_date)\
.all()
curdayprofit = sum(trade.calc_profit() for trade in trades)
profit_days[profitday] = format(curdayprofit, '.8f')
stats = [
[
key,
'{value:.8f} {symbol}'.format(value=float(value), symbol=stake_currency),
'{value:.3f} {symbol}'.format(
value=fiat.convert_amount(
value,
stake_currency,
fiat_display_currency
),
symbol=fiat_display_currency
)
]
for key, value in profit_days.items()
]
return (False, stats)

View File

@ -1,5 +1,5 @@
import logging import logging
from datetime import datetime, timedelta from datetime import timedelta
from decimal import Decimal from decimal import Decimal
from typing import Any, Callable from typing import Any, Callable
@ -10,7 +10,7 @@ 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.rpc.__init__ import rpc_status_table, rpc_trade_status from freqtrade.rpc.__init__ import rpc_status_table, rpc_trade_status, rpc_daily_profit
from freqtrade import __version__, exchange from freqtrade import __version__, exchange
from freqtrade.fiat_convert import CryptoToFiatConverter from freqtrade.fiat_convert import CryptoToFiatConverter
from freqtrade.misc import State, get_state, update_state from freqtrade.misc import State, get_state, update_state
@ -166,44 +166,16 @@ def _daily(bot: Bot, update: Update) -> None:
:param update: message update :param update: message update
:return: None :return: None
""" """
today = datetime.utcnow().date()
profit_days = {}
try: try:
timescale = int(update.message.text.replace('/daily', '').strip()) timescale = int(update.message.text.replace('/daily', '').strip())
except (TypeError, ValueError): except (TypeError, ValueError):
timescale = 7 timescale = 7
(error, stats) = rpc_daily_profit(timescale,
if not (isinstance(timescale, int) and timescale > 0):
send_msg('*Daily [n]:* `must be an integer greater than 0`', bot=bot)
return
for day in range(0, timescale):
profitday = today - timedelta(days=day)
trades = Trade.query \
.filter(Trade.is_open.is_(False)) \
.filter(Trade.close_date >= profitday)\
.filter(Trade.close_date < (profitday + timedelta(days=1)))\
.order_by(Trade.close_date)\
.all()
curdayprofit = sum(trade.calc_profit() for trade in trades)
profit_days[profitday] = format(curdayprofit, '.8f')
stats = [
[
key,
'{value:.8f} {symbol}'.format(value=float(value), symbol=_CONF['stake_currency']),
'{value:.3f} {symbol}'.format(
value=_FIAT_CONVERT.convert_amount(
value,
_CONF['stake_currency'], _CONF['stake_currency'],
_CONF['fiat_display_currency'] _CONF['fiat_display_currency'])
), if error:
symbol=_CONF['fiat_display_currency'] send_msg(stats, bot=bot)
) else:
]
for key, value in profit_days.items()
]
stats = tabulate(stats, stats = tabulate(stats,
headers=[ headers=[
'Day', 'Day',
@ -211,8 +183,8 @@ def _daily(bot: Bot, update: Update) -> None:
'Profit {}'.format(_CONF['fiat_display_currency']) 'Profit {}'.format(_CONF['fiat_display_currency'])
], ],
tablefmt='simple') tablefmt='simple')
message = '<b>Daily Profit over the last {} days</b>:\n<pre>{}</pre>'.format(
message = '<b>Daily Profit over the last {} days</b>:\n<pre>{}</pre>'.format(timescale, stats) timescale, stats)
send_msg(message, bot=bot, parse_mode=ParseMode.HTML) send_msg(message, bot=bot, parse_mode=ParseMode.HTML)

View File

@ -1,9 +1,11 @@
# pragma pylint: disable=missing-docstring, too-many-arguments, too-many-ancestors, C0103 # pragma pylint: disable=missing-docstring, too-many-arguments, too-many-ancestors, C0103
from datetime import datetime
from copy import deepcopy from copy import deepcopy
from unittest.mock import MagicMock from unittest.mock import MagicMock
from sqlalchemy import create_engine
from freqtrade.rpc import init, cleanup, send_msg from freqtrade.rpc import init, cleanup, send_msg
from sqlalchemy import create_engine from freqtrade.persistence import Trade
import freqtrade.main as main import freqtrade.main as main
import freqtrade.misc as misc import freqtrade.misc as misc
import freqtrade.rpc as rpc import freqtrade.rpc as rpc
@ -88,3 +90,55 @@ def test_rpc_trade_status(default_conf, update, ticker, mocker):
assert not error assert not error
trade = result[0] trade = result[0]
assert trade.find('[BTC_ETH]') >= 0 assert trade.find('[BTC_ETH]') >= 0
def test_rpc_daily_profit(default_conf, update, ticker, limit_buy_order, limit_sell_order, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf,
init=MagicMock())
mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker)
mocker.patch.multiple('freqtrade.fiat_convert.Pymarketcap',
ticker=MagicMock(return_value={'price_usd': 15000.0}),
_cache_symbols=MagicMock(return_value={'BTC': 1}))
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
main.init(default_conf, create_engine('sqlite://'))
stake_currency = default_conf['stake_currency']
fiat_display_currency = default_conf['fiat_display_currency']
# Create some test data
main.create_trade(0.001)
trade = Trade.query.first()
assert trade
# Simulate buy & sell
trade.update(limit_buy_order)
trade.update(limit_sell_order)
trade.close_date = datetime.utcnow()
trade.is_open = False
# Try valid data
update.message.text = '/daily 2'
(error, days) = rpc.rpc_daily_profit(7, stake_currency,
fiat_display_currency)
assert not error
assert len(days) == 7
for day in days:
# [datetime.date(2018, 1, 11), '0.00000000 BTC', '0.000 USD']
assert (day[1] == '0.00000000 BTC' or
day[1] == '0.00006217 BTC')
assert (day[2] == '0.000 USD' or
day[2] == '0.933 USD')
# ensure first day is current date
assert str(days[0][0]) == str(datetime.utcnow().date())
# Try invalid data
(error, days) = rpc.rpc_daily_profit(0, stake_currency,
fiat_display_currency)
assert error
assert days.find('must be an integer greater than 0') >= 0