• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

freqtrade / freqtrade / 1557291412

pending completion
1557291412

push

github-actions

GitHub
Merge pull request #4703 from freqtrade/decimal_space

52 of 52 new or added lines in 3 files covered. (100.0%)

8910 of 9070 relevant lines covered (98.24%)

0.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.01
/freqtrade/rpc/fiat_convert.py
1
"""
2
Module that define classes to convert Crypto-currency to FIAT
3
e.g BTC to USD
4
"""
5

6
import logging
1✔
7
from typing import Dict
1✔
8

9
from cachetools.ttl import TTLCache
1✔
10
from pycoingecko import CoinGeckoAPI
1✔
11

12
from freqtrade.constants import SUPPORTED_FIAT
1✔
13

14

15
logger = logging.getLogger(__name__)
1✔
16

17

18
class CryptoToFiatConverter:
1✔
19
    """
20
    Main class to initiate Crypto to FIAT.
21
    This object contains a list of pair Crypto, FIAT
22
    This object is also a Singleton
23
    """
24
    __instance = None
1✔
25
    _coingekko: CoinGeckoAPI = None
1✔
26

27
    _cryptomap: Dict = {}
1✔
28

29
    def __new__(cls):
1✔
30
        """
31
        This class is a singleton - cannot be instantiated twice.
32
        """
33
        if CryptoToFiatConverter.__instance is None:
1✔
34
            CryptoToFiatConverter.__instance = object.__new__(cls)
1✔
35
            try:
1✔
36
                CryptoToFiatConverter._coingekko = CoinGeckoAPI()
1✔
37
            except BaseException:
×
38
                CryptoToFiatConverter._coingekko = None
×
39
        return CryptoToFiatConverter.__instance
1✔
40

41
    def __init__(self) -> None:
1✔
42
        # Timeout: 6h
43
        self._pair_price: TTLCache = TTLCache(maxsize=500, ttl=6 * 60 * 60)
1✔
44

45
        self._load_cryptomap()
1✔
46

47
    def _load_cryptomap(self) -> None:
1✔
48
        try:
1✔
49
            coinlistings = self._coingekko.get_coins_list()
1✔
50
            # Create mapping table from synbol to coingekko_id
51
            self._cryptomap = {x['symbol']: x['id'] for x in coinlistings}
1✔
52
        except (Exception) as exception:
1✔
53
            logger.error(
1✔
54
                f"Could not load FIAT Cryptocurrency map for the following problem: {exception}")
55

56
    def convert_amount(self, crypto_amount: float, crypto_symbol: str, fiat_symbol: str) -> float:
1✔
57
        """
58
        Convert an amount of crypto-currency to fiat
59
        :param crypto_amount: amount of crypto-currency to convert
60
        :param crypto_symbol: crypto-currency used
61
        :param fiat_symbol: fiat to convert to
62
        :return: float, value in fiat of the crypto-currency amount
63
        """
64
        if crypto_symbol == fiat_symbol:
1✔
65
            return float(crypto_amount)
1✔
66
        price = self.get_price(crypto_symbol=crypto_symbol, fiat_symbol=fiat_symbol)
1✔
67
        return float(crypto_amount) * float(price)
1✔
68

69
    def get_price(self, crypto_symbol: str, fiat_symbol: str) -> float:
1✔
70
        """
71
        Return the price of the Crypto-currency in Fiat
72
        :param crypto_symbol: Crypto-currency you want to convert (e.g BTC)
73
        :param fiat_symbol: FIAT currency you want to convert to (e.g USD)
74
        :return: Price in FIAT
75
        """
76
        crypto_symbol = crypto_symbol.lower()
1✔
77
        fiat_symbol = fiat_symbol.lower()
1✔
78
        inverse = False
1✔
79

80
        if crypto_symbol == 'usd':
1✔
81
            # usd corresponds to "uniswap-state-dollar" for coingecko.
82
            # We'll therefore need to "swap" the currencies
83
            logger.info(f"reversing Rates {crypto_symbol}, {fiat_symbol}")
1✔
84
            crypto_symbol = fiat_symbol
1✔
85
            fiat_symbol = 'usd'
1✔
86
            inverse = True
1✔
87

88
        symbol = f"{crypto_symbol}/{fiat_symbol}"
1✔
89
        # Check if the fiat convertion you want is supported
90
        if not self._is_supported_fiat(fiat=fiat_symbol):
1✔
91
            raise ValueError(f'The fiat {fiat_symbol} is not supported.')
1✔
92

93
        price = self._pair_price.get(symbol, None)
1✔
94

95
        if not price:
1✔
96
            price = self._find_price(
1✔
97
                crypto_symbol=crypto_symbol,
98
                fiat_symbol=fiat_symbol
99
            )
100
            if inverse and price != 0.0:
1✔
101
                price = 1 / price
1✔
102
            self._pair_price[symbol] = price
1✔
103

104
        return price
1✔
105

106
    def _is_supported_fiat(self, fiat: str) -> bool:
1✔
107
        """
108
        Check if the FIAT your want to convert to is supported
109
        :param fiat: FIAT to check (e.g USD)
110
        :return: bool, True supported, False not supported
111
        """
112

113
        return fiat.upper() in SUPPORTED_FIAT
1✔
114

115
    def _find_price(self, crypto_symbol: str, fiat_symbol: str) -> float:
1✔
116
        """
117
        Call CoinGekko API to retrieve the price in the FIAT
118
        :param crypto_symbol: Crypto-currency you want to convert (e.g btc)
119
        :param fiat_symbol: FIAT currency you want to convert to (e.g usd)
120
        :return: float, price of the crypto-currency in Fiat
121
        """
122
        # Check if the fiat convertion you want is supported
123
        if not self._is_supported_fiat(fiat=fiat_symbol):
1✔
124
            raise ValueError(f'The fiat {fiat_symbol} is not supported.')
1✔
125

126
        # No need to convert if both crypto and fiat are the same
127
        if crypto_symbol == fiat_symbol:
1✔
128
            return 1.0
1✔
129

130
        if crypto_symbol not in self._cryptomap:
1✔
131
            # return 0 for unsupported stake currencies (fiat-convert should not break the bot)
132
            logger.warning("unsupported crypto-symbol %s - returning 0.0", crypto_symbol)
1✔
133
            return 0.0
1✔
134

135
        try:
1✔
136
            _gekko_id = self._cryptomap[crypto_symbol]
1✔
137
            return float(
1✔
138
                self._coingekko.get_price(
139
                    ids=_gekko_id,
140
                    vs_currencies=fiat_symbol
141
                )[_gekko_id][fiat_symbol]
142
            )
143
        except Exception as exception:
1✔
144
            logger.error("Error in _find_price: %s", exception)
1✔
145
            return 0.0
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2024 Coveralls, Inc