Display profits in fiat
This commit is contained in:
parent
433bf409f4
commit
ff6b0fc1c9
@ -55,6 +55,12 @@ use the `last` price and values between those interpolate between ask and last
|
|||||||
price. Using `ask` price will guarantee quick success in bid, but bot will also
|
price. Using `ask` price will guarantee quick success in bid, but bot will also
|
||||||
end up paying more then would probably have been necessary.
|
end up paying more then would probably have been necessary.
|
||||||
|
|
||||||
|
`fiat_currency` set the fiat to use for the conversion form coin to
|
||||||
|
fiat in Telegram. The valid value are: "AUD", "BRL", "CAD", "CHF",
|
||||||
|
"CLP", "CNY", "CZK", "DKK", "EUR", "GBP", "HKD", "HUF", "IDR", "ILS",
|
||||||
|
"INR", "JPY", "KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN",
|
||||||
|
"RUB", "SEK", "SGD", "THB", "TRY", "TWD", "ZAR", "USD".
|
||||||
|
|
||||||
The other values should be self-explanatory,
|
The other values should be self-explanatory,
|
||||||
if not feel free to raise a github issue.
|
if not feel free to raise a github issue.
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"max_open_trades": 3,
|
"max_open_trades": 3,
|
||||||
"stake_currency": "BTC",
|
"stake_currency": "BTC",
|
||||||
"stake_amount": 0.05,
|
"stake_amount": 0.05,
|
||||||
|
"fiat_display_currency": "USD",
|
||||||
"dry_run": false,
|
"dry_run": false,
|
||||||
"minimal_roi": {
|
"minimal_roi": {
|
||||||
"40": 0.0,
|
"40": 0.0,
|
||||||
|
156
freqtrade/fiat_convert.py
Normal file
156
freqtrade/fiat_convert.py
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import logging
|
||||||
|
import time
|
||||||
|
from pymarketcap import Pymarketcap
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CryptoFiat():
|
||||||
|
# Constants
|
||||||
|
CACHE_DURATION = 6 * 60 * 60 # 6 hours
|
||||||
|
|
||||||
|
def __init__(self, crypto_symbol: str, fiat_symbol: str, price: float) -> None:
|
||||||
|
"""
|
||||||
|
Create an object that will contains the price for a crypto-currency in fiat
|
||||||
|
:param crypto_symbol: Crypto-currency you want to convert (e.g BTC)
|
||||||
|
:param fiat_symbol: FIAT currency you want to convert to (e.g USD)
|
||||||
|
:param price: Price in FIAT
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Public attributes
|
||||||
|
self.crypto_symbol = None
|
||||||
|
self.fiat_symbol = None
|
||||||
|
self.price = 0.0
|
||||||
|
|
||||||
|
# Private attributes
|
||||||
|
self._expiration = 0
|
||||||
|
|
||||||
|
self.crypto_symbol = crypto_symbol.upper()
|
||||||
|
self.fiat_symbol = fiat_symbol.upper()
|
||||||
|
self.set_price(price=price)
|
||||||
|
|
||||||
|
def set_price(self, price: float) -> None:
|
||||||
|
"""
|
||||||
|
Set the price of the Crypto-currency in FIAT and set the expiration time
|
||||||
|
:param price: Price of the current Crypto currency in the fiat
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.price = price
|
||||||
|
self._expiration = time.time() + self.CACHE_DURATION
|
||||||
|
|
||||||
|
def is_expired(self) -> bool:
|
||||||
|
"""
|
||||||
|
Return if the current price is still valid or needs to be refreshed
|
||||||
|
:return: bool, true the price is expired and needs to be refreshed, false the price is
|
||||||
|
still valid
|
||||||
|
"""
|
||||||
|
return self._expiration - time.time() <= 0
|
||||||
|
|
||||||
|
|
||||||
|
class CryptoToFiatConverter():
|
||||||
|
# Constants
|
||||||
|
SUPPORTED_FIAT = [
|
||||||
|
"AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK",
|
||||||
|
"EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY",
|
||||||
|
"KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN",
|
||||||
|
"RUB", "SEK", "SGD", "THB", "TRY", "TWD", "ZAR", "USD"
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._coinmarketcap = Pymarketcap()
|
||||||
|
self._pairs = []
|
||||||
|
|
||||||
|
def convert_amount(self, crypto_amount: float, crypto_symbol: str, fiat_symbol: str) -> float:
|
||||||
|
"""
|
||||||
|
Convert an amount of crypto-currency to fiat
|
||||||
|
:param crypto_amount: amount of crypto-currency to convert
|
||||||
|
:param crypto_symbol: crypto-currency used
|
||||||
|
:param fiat_symbol: fiat to convert to
|
||||||
|
:return: float, value in fiat of the crypto-currency amount
|
||||||
|
"""
|
||||||
|
price = self.get_price(crypto_symbol=crypto_symbol, fiat_symbol=fiat_symbol)
|
||||||
|
return float(crypto_amount) * float(price)
|
||||||
|
|
||||||
|
def get_price(self, crypto_symbol: str, fiat_symbol: str) -> float:
|
||||||
|
"""
|
||||||
|
Return the price of the Crypto-currency in Fiat
|
||||||
|
:param crypto_symbol: Crypto-currency you want to convert (e.g BTC)
|
||||||
|
:param fiat_symbol: FIAT currency you want to convert to (e.g USD)
|
||||||
|
:return: Price in FIAT
|
||||||
|
"""
|
||||||
|
crypto_symbol = crypto_symbol.upper()
|
||||||
|
fiat_symbol = fiat_symbol.upper()
|
||||||
|
|
||||||
|
# Check if the fiat convertion you want is supported
|
||||||
|
if not self._is_supported_fiat(fiat=fiat_symbol):
|
||||||
|
raise ValueError('The fiat {} is not supported.'.format(fiat_symbol))
|
||||||
|
|
||||||
|
# Get the pair that interest us and return the price in fiat
|
||||||
|
for pair in self._pairs:
|
||||||
|
if pair.crypto_symbol == crypto_symbol and pair.fiat_symbol == fiat_symbol:
|
||||||
|
# If the price is expired we refresh it, avoid to call the API all the time
|
||||||
|
if pair.is_expired():
|
||||||
|
pair.set_price(
|
||||||
|
price=self._find_price(
|
||||||
|
crypto_symbol=pair.crypto_symbol,
|
||||||
|
fiat_symbol=pair.fiat_symbol
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# return the last price we have for this pair
|
||||||
|
return pair.price
|
||||||
|
|
||||||
|
# The pair does not exist, so we create it and return the price
|
||||||
|
return self._add_pair(
|
||||||
|
crypto_symbol=crypto_symbol,
|
||||||
|
fiat_symbol=fiat_symbol,
|
||||||
|
price=self._find_price(
|
||||||
|
crypto_symbol=crypto_symbol,
|
||||||
|
fiat_symbol=fiat_symbol
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _add_pair(self, crypto_symbol: str, fiat_symbol: str, price: float) -> float:
|
||||||
|
"""
|
||||||
|
:param crypto_symbol: Crypto-currency you want to convert (e.g BTC)
|
||||||
|
:param fiat_symbol: FIAT currency you want to convert to (e.g USD)
|
||||||
|
:return: price in FIAT
|
||||||
|
"""
|
||||||
|
self._pairs.append(
|
||||||
|
CryptoFiat(
|
||||||
|
crypto_symbol=crypto_symbol,
|
||||||
|
fiat_symbol=fiat_symbol,
|
||||||
|
price=price
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return price
|
||||||
|
|
||||||
|
def _is_supported_fiat(self, fiat: str) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the FIAT your want to convert to is supported
|
||||||
|
:param fiat: FIAT to check (e.g USD)
|
||||||
|
:return: bool, True supported, False not supported
|
||||||
|
"""
|
||||||
|
|
||||||
|
fiat = fiat.upper()
|
||||||
|
|
||||||
|
return fiat in self.SUPPORTED_FIAT
|
||||||
|
|
||||||
|
def _find_price(self, crypto_symbol: str, fiat_symbol: str) -> float:
|
||||||
|
"""
|
||||||
|
Call CoinMarketCap API to retrieve the price in the FIAT
|
||||||
|
:param crypto_symbol: Crypto-currency you want to convert (e.g BTC)
|
||||||
|
:param fiat_symbol: FIAT currency you want to convert to (e.g USD)
|
||||||
|
:return: float, price of the crypto-currency in Fiat
|
||||||
|
"""
|
||||||
|
# Check if the fiat convertion you want is supported
|
||||||
|
if not self._is_supported_fiat(fiat=fiat_symbol):
|
||||||
|
raise ValueError('The fiat {} is not supported.'.format(fiat_symbol))
|
||||||
|
|
||||||
|
return float(
|
||||||
|
self._coinmarketcap.ticker(
|
||||||
|
currency=crypto_symbol,
|
||||||
|
convert=fiat_symbol
|
||||||
|
)['price_' + fiat_symbol.lower()]
|
||||||
|
)
|
@ -17,6 +17,7 @@ from freqtrade.analyze import get_signal, SignalType
|
|||||||
from freqtrade.misc import State, get_state, update_state, parse_args, throttle, \
|
from freqtrade.misc import State, get_state, update_state, parse_args, throttle, \
|
||||||
load_config
|
load_config
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
|
from freqtrade.fiat_convert import CryptoToFiatConverter
|
||||||
|
|
||||||
logger = logging.getLogger('freqtrade')
|
logger = logging.getLogger('freqtrade')
|
||||||
|
|
||||||
@ -119,14 +120,28 @@ def execute_sell(trade: Trade, limit: float) -> None:
|
|||||||
trade.open_order_id = order_id
|
trade.open_order_id = order_id
|
||||||
|
|
||||||
fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2)
|
fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2)
|
||||||
rpc.send_msg('*{}:* Selling [{}]({}) with limit `{:.8f} (profit: ~{:.2f}%, {:.8f})`'.format(
|
profit_trade = trade.calc_profit(rate=limit)
|
||||||
trade.exchange,
|
|
||||||
trade.pair.replace('_', '/'),
|
fiat_converter = CryptoToFiatConverter()
|
||||||
exchange.get_pair_detail_url(trade.pair),
|
profit_fiat = fiat_converter.convert_amount(
|
||||||
limit,
|
profit_trade,
|
||||||
fmt_exp_profit,
|
_CONF['stake_currency'],
|
||||||
trade.calc_profit(rate=limit),
|
_CONF['fiat_display_currency']
|
||||||
))
|
)
|
||||||
|
|
||||||
|
rpc.send_msg('*{exchange}:* Selling [{pair}]({pair_url}) with limit `{limit:.8f}`'
|
||||||
|
'` (profit: ~{profit_percent:.2f}%, {profit_coin:.8f} {coin}`'
|
||||||
|
'` / {profit_fiat:.3f} {fiat})`'.format(
|
||||||
|
exchange=trade.exchange,
|
||||||
|
pair=trade.pair.replace('_', '/'),
|
||||||
|
pair_url=exchange.get_pair_detail_url(trade.pair),
|
||||||
|
limit=limit,
|
||||||
|
profit_percent=fmt_exp_profit,
|
||||||
|
profit_coin=profit_trade,
|
||||||
|
coin=_CONF['stake_currency'],
|
||||||
|
profit_fiat=profit_fiat,
|
||||||
|
fiat=_CONF['fiat_display_currency'],
|
||||||
|
))
|
||||||
Trade.session.flush()
|
Trade.session.flush()
|
||||||
|
|
||||||
|
|
||||||
|
@ -208,6 +208,7 @@ CONF_SCHEMA = {
|
|||||||
'max_open_trades': {'type': 'integer', 'minimum': 1},
|
'max_open_trades': {'type': 'integer', 'minimum': 1},
|
||||||
'stake_currency': {'type': 'string', 'enum': ['BTC', 'ETH', 'USDT']},
|
'stake_currency': {'type': 'string', 'enum': ['BTC', 'ETH', 'USDT']},
|
||||||
'stake_amount': {'type': 'number', 'minimum': 0.0005},
|
'stake_amount': {'type': 'number', 'minimum': 0.0005},
|
||||||
|
'fiat_display_currency': {'type': 'string', 'enum': ['USD', 'EUR', 'CAD', 'SGD']},
|
||||||
'dry_run': {'type': 'boolean'},
|
'dry_run': {'type': 'boolean'},
|
||||||
'minimal_roi': {
|
'minimal_roi': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
|
@ -15,6 +15,7 @@ from telegram.ext import CommandHandler, Updater
|
|||||||
from freqtrade import exchange, __version__
|
from freqtrade import exchange, __version__
|
||||||
from freqtrade.misc import get_state, State, update_state
|
from freqtrade.misc import get_state, State, update_state
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
|
from freqtrade.fiat_convert import CryptoToFiatConverter
|
||||||
|
|
||||||
# Remove noisy log messages
|
# Remove noisy log messages
|
||||||
logging.getLogger('requests.packages.urllib3').setLevel(logging.INFO)
|
logging.getLogger('requests.packages.urllib3').setLevel(logging.INFO)
|
||||||
@ -23,6 +24,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
_UPDATER: Updater = None
|
_UPDATER: Updater = None
|
||||||
_CONF = {}
|
_CONF = {}
|
||||||
|
_FIAT_CONVERT = CryptoToFiatConverter()
|
||||||
|
|
||||||
|
|
||||||
def init(config: dict) -> None:
|
def init(config: dict) -> None:
|
||||||
@ -242,8 +244,28 @@ def _daily(bot: Bot, update: Update) -> None:
|
|||||||
curdayprofit = sum(trade.calc_profit() for trade in trades)
|
curdayprofit = sum(trade.calc_profit() for trade in trades)
|
||||||
profit_days[date.fromordinal(today - day)] = format(curdayprofit, '.8f')
|
profit_days[date.fromordinal(today - day)] = format(curdayprofit, '.8f')
|
||||||
|
|
||||||
stats = [[key, str(value) + ' BTC'] for key, value in profit_days.items()]
|
stats = [
|
||||||
stats = tabulate(stats, headers=['Day', 'Profit'], tablefmt='simple')
|
[
|
||||||
|
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['fiat_display_currency']
|
||||||
|
),
|
||||||
|
symbol=_CONF['fiat_display_currency']
|
||||||
|
)
|
||||||
|
]
|
||||||
|
for key, value in profit_days.items()
|
||||||
|
]
|
||||||
|
stats = tabulate(stats,
|
||||||
|
headers=[
|
||||||
|
'Day',
|
||||||
|
'Profit {}'.format(_CONF['stake_currency']),
|
||||||
|
'Profit {}'.format(_CONF['fiat_display_currency'])
|
||||||
|
],
|
||||||
|
tablefmt='simple')
|
||||||
|
|
||||||
message = '<b>Daily Profit over the last {} days</b>:\n<pre>{}</pre>'.format(timescale, stats)
|
message = '<b>Daily Profit over the last {} days</b>:\n<pre>{}</pre>'.format(timescale, stats)
|
||||||
send_msg(message, bot=bot, parse_mode=ParseMode.HTML)
|
send_msg(message, bot=bot, parse_mode=ParseMode.HTML)
|
||||||
@ -260,9 +282,9 @@ def _profit(bot: Bot, update: Update) -> None:
|
|||||||
"""
|
"""
|
||||||
trades = Trade.query.order_by(Trade.id).all()
|
trades = Trade.query.order_by(Trade.id).all()
|
||||||
|
|
||||||
profit_all_btc = []
|
profit_all_coin = []
|
||||||
profit_all_percent = []
|
profit_all_percent = []
|
||||||
profit_btc_closed = []
|
profit_closed_coin = []
|
||||||
profit_closed_percent = []
|
profit_closed_percent = []
|
||||||
durations = []
|
durations = []
|
||||||
|
|
||||||
@ -276,14 +298,14 @@ def _profit(bot: Bot, update: Update) -> None:
|
|||||||
|
|
||||||
if not trade.is_open:
|
if not trade.is_open:
|
||||||
profit_percent = trade.calc_profit_percent()
|
profit_percent = trade.calc_profit_percent()
|
||||||
profit_btc_closed.append(trade.calc_profit())
|
profit_closed_coin.append(trade.calc_profit())
|
||||||
profit_closed_percent.append(profit_percent)
|
profit_closed_percent.append(profit_percent)
|
||||||
else:
|
else:
|
||||||
# Get current rate
|
# Get current rate
|
||||||
current_rate = exchange.get_ticker(trade.pair)['bid']
|
current_rate = exchange.get_ticker(trade.pair)['bid']
|
||||||
profit_percent = trade.calc_profit_percent(rate=current_rate)
|
profit_percent = trade.calc_profit_percent(rate=current_rate)
|
||||||
|
|
||||||
profit_all_btc.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, func.sum(Trade.close_profit).label('profit_sum')) \
|
best_pair = Trade.session.query(Trade.pair, func.sum(Trade.close_profit).label('profit_sum')) \
|
||||||
@ -297,19 +319,46 @@ def _profit(bot: Bot, update: Update) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
bp_pair, bp_rate = best_pair
|
bp_pair, bp_rate = best_pair
|
||||||
|
|
||||||
|
# Prepare data to display
|
||||||
|
profit_closed_coin = round(sum(profit_closed_coin), 8)
|
||||||
|
profit_closed_percent = round(sum(profit_closed_percent) * 100, 2)
|
||||||
|
profit_closed_fiat = _FIAT_CONVERT.convert_amount(
|
||||||
|
profit_closed_coin,
|
||||||
|
_CONF['stake_currency'],
|
||||||
|
_CONF['fiat_display_currency']
|
||||||
|
)
|
||||||
|
profit_all_coin = round(sum(profit_all_coin), 8)
|
||||||
|
profit_all_percent = round(sum(profit_all_percent) * 100, 2)
|
||||||
|
profit_all_fiat = _FIAT_CONVERT.convert_amount(
|
||||||
|
profit_all_coin,
|
||||||
|
_CONF['stake_currency'],
|
||||||
|
_CONF['fiat_display_currency']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Message to display
|
||||||
markdown_msg = """
|
markdown_msg = """
|
||||||
*ROI Trade closed:* `{profit_closed_btc:.8f} BTC ({profit_closed_percent:.2f}%)`
|
*ROI:* Close trades
|
||||||
*ROI All trades:* `{profit_all_btc:.8f} BTC ({profit_all_percent:.2f}%)`
|
∙ `{profit_closed_coin:.8f} {coin} ({profit_closed_percent:.2f}%)`
|
||||||
|
∙ `{profit_closed_fiat:.3f} {fiat}`
|
||||||
|
*ROI:* All trades
|
||||||
|
∙ `{profit_all_coin:.8f} {coin} ({profit_all_percent:.2f}%)`
|
||||||
|
∙ `{profit_all_fiat:.3f} {fiat}`
|
||||||
|
|
||||||
*Total Trade Count:* `{trade_count}`
|
*Total Trade Count:* `{trade_count}`
|
||||||
*First Trade opened:* `{first_trade_date}`
|
*First Trade opened:* `{first_trade_date}`
|
||||||
*Latest Trade opened:* `{latest_trade_date}`
|
*Latest Trade opened:* `{latest_trade_date}`
|
||||||
*Avg. Duration:* `{avg_duration}`
|
*Avg. Duration:* `{avg_duration}`
|
||||||
*Best Performing:* `{best_pair}: {best_rate:.2f}%`
|
*Best Performing:* `{best_pair}: {best_rate:.2f}%`
|
||||||
""".format(
|
""".format(
|
||||||
profit_closed_btc=round(sum(profit_btc_closed), 8),
|
coin=_CONF['stake_currency'],
|
||||||
profit_closed_percent=round(sum(profit_closed_percent) * 100, 2),
|
fiat=_CONF['fiat_display_currency'],
|
||||||
profit_all_btc=round(sum(profit_all_btc), 8),
|
profit_closed_coin=profit_closed_coin,
|
||||||
profit_all_percent=round(sum(profit_all_percent) * 100, 2),
|
profit_closed_percent=profit_closed_percent,
|
||||||
|
profit_closed_fiat=profit_closed_fiat,
|
||||||
|
profit_all_coin=profit_all_coin,
|
||||||
|
profit_all_percent=profit_all_percent,
|
||||||
|
profit_all_fiat=profit_all_fiat,
|
||||||
trade_count=len(trades),
|
trade_count=len(trades),
|
||||||
first_trade_date=arrow.get(trades[0].open_date).humanize(),
|
first_trade_date=arrow.get(trades[0].open_date).humanize(),
|
||||||
latest_trade_date=arrow.get(trades[-1].open_date).humanize(),
|
latest_trade_date=arrow.get(trades[-1].open_date).humanize(),
|
||||||
|
@ -16,6 +16,7 @@ def default_conf():
|
|||||||
"max_open_trades": 1,
|
"max_open_trades": 1,
|
||||||
"stake_currency": "BTC",
|
"stake_currency": "BTC",
|
||||||
"stake_amount": 0.001,
|
"stake_amount": 0.001,
|
||||||
|
"fiat_display_currency": "USD",
|
||||||
"dry_run": True,
|
"dry_run": True,
|
||||||
"minimal_roi": {
|
"minimal_roi": {
|
||||||
"40": 0.0,
|
"40": 0.0,
|
||||||
|
111
freqtrade/tests/test_fiat_convert.py
Normal file
111
freqtrade/tests/test_fiat_convert.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
# pragma pylint: disable=missing-docstring, too-many-arguments, too-many-ancestors, C0103
|
||||||
|
|
||||||
|
import time
|
||||||
|
import pytest
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
from freqtrade.fiat_convert import CryptoToFiatConverter, CryptoFiat
|
||||||
|
|
||||||
|
|
||||||
|
def test_pair_convertion_object():
|
||||||
|
pair_convertion = CryptoFiat(
|
||||||
|
crypto_symbol='btc',
|
||||||
|
fiat_symbol='usd',
|
||||||
|
price=12345.0
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check the cache duration is 6 hours
|
||||||
|
assert pair_convertion.CACHE_DURATION == 6 * 60 * 60
|
||||||
|
|
||||||
|
# Check a regular usage
|
||||||
|
assert pair_convertion.crypto_symbol == 'BTC'
|
||||||
|
assert pair_convertion.fiat_symbol == 'USD'
|
||||||
|
assert pair_convertion.price == 12345.0
|
||||||
|
assert pair_convertion.is_expired() is False
|
||||||
|
|
||||||
|
# Update the expiration time (- 2 hours) and check the behavior
|
||||||
|
pair_convertion._expiration = time.time() - 2 * 60 * 60
|
||||||
|
assert pair_convertion.is_expired() is True
|
||||||
|
|
||||||
|
# Check set price behaviour
|
||||||
|
time_reference = time.time() + pair_convertion.CACHE_DURATION
|
||||||
|
pair_convertion.set_price(price=30000.123)
|
||||||
|
assert pair_convertion.is_expired() is False
|
||||||
|
assert pair_convertion._expiration >= time_reference
|
||||||
|
assert pair_convertion.price == 30000.123
|
||||||
|
|
||||||
|
|
||||||
|
def test_fiat_convert_is_supported():
|
||||||
|
fiat_convert = CryptoToFiatConverter()
|
||||||
|
assert fiat_convert._is_supported_fiat(fiat='USD') is True
|
||||||
|
assert fiat_convert._is_supported_fiat(fiat='usd') is True
|
||||||
|
assert fiat_convert._is_supported_fiat(fiat='abc') is False
|
||||||
|
assert fiat_convert._is_supported_fiat(fiat='ABC') is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_fiat_convert_add_pair():
|
||||||
|
fiat_convert = CryptoToFiatConverter()
|
||||||
|
|
||||||
|
assert len(fiat_convert._pairs) == 0
|
||||||
|
|
||||||
|
fiat_convert._add_pair(crypto_symbol='btc', fiat_symbol='usd', price=12345.0)
|
||||||
|
assert len(fiat_convert._pairs) == 1
|
||||||
|
assert fiat_convert._pairs[0].crypto_symbol == 'BTC'
|
||||||
|
assert fiat_convert._pairs[0].fiat_symbol == 'USD'
|
||||||
|
assert fiat_convert._pairs[0].price == 12345.0
|
||||||
|
|
||||||
|
fiat_convert._add_pair(crypto_symbol='btc', fiat_symbol='Eur', price=13000.2)
|
||||||
|
assert len(fiat_convert._pairs) == 2
|
||||||
|
assert fiat_convert._pairs[1].crypto_symbol == 'BTC'
|
||||||
|
assert fiat_convert._pairs[1].fiat_symbol == 'EUR'
|
||||||
|
assert fiat_convert._pairs[1].price == 13000.2
|
||||||
|
|
||||||
|
|
||||||
|
def test_fiat_convert_find_price(mocker):
|
||||||
|
api_mock = MagicMock(return_value={
|
||||||
|
'price_usd': 12345.0,
|
||||||
|
'price_eur': 13000.2
|
||||||
|
})
|
||||||
|
mocker.patch('freqtrade.fiat_convert.Pymarketcap.ticker', api_mock)
|
||||||
|
fiat_convert = CryptoToFiatConverter()
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match=r'The fiat ABC is not supported.'):
|
||||||
|
fiat_convert._find_price(crypto_symbol='BTC', fiat_symbol='ABC')
|
||||||
|
|
||||||
|
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 12345.0
|
||||||
|
assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 12345.0
|
||||||
|
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='EUR') == 13000.2
|
||||||
|
|
||||||
|
|
||||||
|
def test_fiat_convert_get_price(mocker):
|
||||||
|
api_mock = MagicMock(return_value={
|
||||||
|
'price_usd': 28000.0,
|
||||||
|
'price_eur': 15000.0
|
||||||
|
})
|
||||||
|
mocker.patch('freqtrade.fiat_convert.Pymarketcap.ticker', api_mock)
|
||||||
|
|
||||||
|
fiat_convert = CryptoToFiatConverter()
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match=r'The fiat US DOLLAR is not supported.'):
|
||||||
|
fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='US Dollar')
|
||||||
|
|
||||||
|
# Check the value return by the method
|
||||||
|
assert len(fiat_convert._pairs) == 0
|
||||||
|
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 28000.0
|
||||||
|
assert fiat_convert._pairs[0].crypto_symbol == 'BTC'
|
||||||
|
assert fiat_convert._pairs[0].fiat_symbol == 'USD'
|
||||||
|
assert fiat_convert._pairs[0].price == 28000.0
|
||||||
|
assert fiat_convert._pairs[0]._expiration is not 0
|
||||||
|
assert len(fiat_convert._pairs) == 1
|
||||||
|
|
||||||
|
# Verify the cached is used
|
||||||
|
fiat_convert._pairs[0].price = 9867.543
|
||||||
|
expiration = fiat_convert._pairs[0]._expiration
|
||||||
|
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 9867.543
|
||||||
|
assert fiat_convert._pairs[0]._expiration == expiration
|
||||||
|
|
||||||
|
# Verify the cache expiration
|
||||||
|
expiration = time.time() - 2 * 60 * 60
|
||||||
|
fiat_convert._pairs[0]._expiration = expiration
|
||||||
|
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 28000.0
|
||||||
|
assert fiat_convert._pairs[0]._expiration is not expiration
|
@ -192,6 +192,9 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker):
|
|||||||
}),
|
}),
|
||||||
buy=MagicMock(return_value='mocked_limit_buy'),
|
buy=MagicMock(return_value='mocked_limit_buy'),
|
||||||
sell=MagicMock(return_value='mocked_limit_sell'))
|
sell=MagicMock(return_value='mocked_limit_sell'))
|
||||||
|
mocker.patch.multiple('freqtrade.fiat_convert.Pymarketcap',
|
||||||
|
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
||||||
|
_cache_symbols=MagicMock(return_value={'BTC': 1}))
|
||||||
init(default_conf, create_engine('sqlite://'))
|
init(default_conf, create_engine('sqlite://'))
|
||||||
create_trade(0.001)
|
create_trade(0.001)
|
||||||
|
|
||||||
|
@ -164,6 +164,9 @@ def test_profit_handle(
|
|||||||
mocker.patch.multiple('freqtrade.main.exchange',
|
mocker.patch.multiple('freqtrade.main.exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker)
|
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}))
|
||||||
init(default_conf, create_engine('sqlite://'))
|
init(default_conf, create_engine('sqlite://'))
|
||||||
|
|
||||||
_profit(bot=MagicMock(), update=update)
|
_profit(bot=MagicMock(), update=update)
|
||||||
@ -194,9 +197,14 @@ def test_profit_handle(
|
|||||||
|
|
||||||
_profit(bot=MagicMock(), update=update)
|
_profit(bot=MagicMock(), update=update)
|
||||||
assert msg_mock.call_count == 1
|
assert msg_mock.call_count == 1
|
||||||
assert '*ROI Trade closed:* `0.00006217 BTC (6.20%)`' in msg_mock.call_args_list[-1][0][0]
|
assert '*ROI:* Close trades' in msg_mock.call_args_list[-1][0][0]
|
||||||
assert '*ROI All trades:* `0.00006217 BTC (6.20%)`' in msg_mock.call_args_list[-1][0][0]
|
assert '∙ `0.00006217 BTC (6.20%)`' in msg_mock.call_args_list[-1][0][0]
|
||||||
assert 'Best Performing:* `BTC_ETH: 6.20%`' in msg_mock.call_args_list[-1][0][0]
|
assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0]
|
||||||
|
assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0]
|
||||||
|
assert '∙ `0.00006217 BTC (6.20%)`' in msg_mock.call_args_list[-1][0][0]
|
||||||
|
assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0]
|
||||||
|
|
||||||
|
assert '*Best Performing:* `BTC_ETH: 6.20%`' in msg_mock.call_args_list[-1][0][0]
|
||||||
|
|
||||||
|
|
||||||
def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker):
|
def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker):
|
||||||
@ -210,6 +218,9 @@ def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker):
|
|||||||
mocker.patch.multiple('freqtrade.main.exchange',
|
mocker.patch.multiple('freqtrade.main.exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker)
|
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}))
|
||||||
init(default_conf, create_engine('sqlite://'))
|
init(default_conf, create_engine('sqlite://'))
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -228,7 +239,9 @@ def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker):
|
|||||||
|
|
||||||
assert rpc_mock.call_count == 2
|
assert rpc_mock.call_count == 2
|
||||||
assert 'Selling [BTC/ETH]' in rpc_mock.call_args_list[-1][0][0]
|
assert 'Selling [BTC/ETH]' in rpc_mock.call_args_list[-1][0][0]
|
||||||
assert '0.00001172 (profit: ~6.11%, 0.00006126)' in rpc_mock.call_args_list[-1][0][0]
|
assert '0.00001172' in rpc_mock.call_args_list[-1][0][0]
|
||||||
|
assert 'profit: ~6.11%, 0.00006126' in rpc_mock.call_args_list[-1][0][0]
|
||||||
|
assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0]
|
||||||
|
|
||||||
|
|
||||||
def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, mocker):
|
def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, mocker):
|
||||||
@ -242,6 +255,9 @@ def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, m
|
|||||||
mocker.patch.multiple('freqtrade.main.exchange',
|
mocker.patch.multiple('freqtrade.main.exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker)
|
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}))
|
||||||
init(default_conf, create_engine('sqlite://'))
|
init(default_conf, create_engine('sqlite://'))
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -260,7 +276,9 @@ def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, m
|
|||||||
|
|
||||||
assert rpc_mock.call_count == 2
|
assert rpc_mock.call_count == 2
|
||||||
assert 'Selling [BTC/ETH]' in rpc_mock.call_args_list[-1][0][0]
|
assert 'Selling [BTC/ETH]' in rpc_mock.call_args_list[-1][0][0]
|
||||||
assert '0.00001044 (profit: ~-5.48%, -0.00005492)' in rpc_mock.call_args_list[-1][0][0]
|
assert '0.00001044' in rpc_mock.call_args_list[-1][0][0]
|
||||||
|
assert 'profit: ~-5.48%, -0.00005492' in rpc_mock.call_args_list[-1][0][0]
|
||||||
|
assert '-0.824 USD' in rpc_mock.call_args_list[-1][0][0]
|
||||||
|
|
||||||
|
|
||||||
def test_exec_forcesell_open_orders(default_conf, ticker, mocker):
|
def test_exec_forcesell_open_orders(default_conf, ticker, mocker):
|
||||||
@ -298,6 +316,9 @@ def test_forcesell_all_handle(default_conf, update, ticker, mocker):
|
|||||||
mocker.patch.multiple('freqtrade.main.exchange',
|
mocker.patch.multiple('freqtrade.main.exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker)
|
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}))
|
||||||
init(default_conf, create_engine('sqlite://'))
|
init(default_conf, create_engine('sqlite://'))
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -310,7 +331,9 @@ def test_forcesell_all_handle(default_conf, update, ticker, mocker):
|
|||||||
|
|
||||||
assert rpc_mock.call_count == 4
|
assert rpc_mock.call_count == 4
|
||||||
for args in rpc_mock.call_args_list:
|
for args in rpc_mock.call_args_list:
|
||||||
assert '0.00001098 (profit: ~-0.59%, -0.00000591)' in args[0][0]
|
assert '0.00001098' in args[0][0]
|
||||||
|
assert 'profit: ~-0.59%, -0.00000591 BTC' in args[0][0]
|
||||||
|
assert '-0.089 USD' in args[0][0]
|
||||||
|
|
||||||
|
|
||||||
def test_forcesell_handle_invalid(default_conf, update, mocker):
|
def test_forcesell_handle_invalid(default_conf, update, mocker):
|
||||||
@ -397,6 +420,9 @@ def test_daily_handle(
|
|||||||
mocker.patch.multiple('freqtrade.main.exchange',
|
mocker.patch.multiple('freqtrade.main.exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker)
|
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}))
|
||||||
init(default_conf, create_engine('sqlite://'))
|
init(default_conf, create_engine('sqlite://'))
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -418,7 +444,9 @@ def test_daily_handle(
|
|||||||
_daily(bot=MagicMock(), update=update)
|
_daily(bot=MagicMock(), update=update)
|
||||||
assert msg_mock.call_count == 1
|
assert msg_mock.call_count == 1
|
||||||
assert 'Daily' in msg_mock.call_args_list[0][0][0]
|
assert 'Daily' in msg_mock.call_args_list[0][0][0]
|
||||||
assert str(datetime.utcnow().date()) + ' 0.00006217 BTC' in msg_mock.call_args_list[0][0][0]
|
assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0]
|
||||||
|
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
|
||||||
|
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
|
||||||
|
|
||||||
# Try invalid data
|
# Try invalid data
|
||||||
msg_mock.reset_mock()
|
msg_mock.reset_mock()
|
||||||
|
@ -19,6 +19,7 @@ hyperopt==0.1
|
|||||||
# do not upgrade networkx before this is fixed https://github.com/hyperopt/hyperopt/issues/325
|
# do not upgrade networkx before this is fixed https://github.com/hyperopt/hyperopt/issues/325
|
||||||
networkx==1.11
|
networkx==1.11
|
||||||
tabulate==0.8.2
|
tabulate==0.8.2
|
||||||
|
pymarketcap==3.3.139
|
||||||
|
|
||||||
# Required for plotting data
|
# Required for plotting data
|
||||||
#matplotlib==2.1.0
|
#matplotlib==2.1.0
|
||||||
|
Loading…
Reference in New Issue
Block a user