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

freqtrade / freqtrade / 9394559170

26 Apr 2024 06:36AM UTC coverage: 94.656% (-0.02%) from 94.674%
9394559170

push

github

xmatthias
Loader should be passed as kwarg for clarity

20280 of 21425 relevant lines covered (94.66%)

0.95 hits per line

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

96.0
/freqtrade/exchange/kraken.py
1
""" Kraken exchange subclass """
2
import logging
1✔
3
from datetime import datetime
1✔
4
from typing import Any, Dict, List, Optional, Tuple
1✔
5

6
import ccxt
1✔
7
from pandas import DataFrame
1✔
8

9
from freqtrade.constants import BuySell
1✔
10
from freqtrade.enums import MarginMode, TradingMode
1✔
11
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
1✔
12
from freqtrade.exchange import Exchange
1✔
13
from freqtrade.exchange.common import retrier
1✔
14
from freqtrade.exchange.types import Tickers
1✔
15

16

17
logger = logging.getLogger(__name__)
1✔
18

19

20
class Kraken(Exchange):
1✔
21

22
    _params: Dict = {"trading_agreement": "agree"}
1✔
23
    _ft_has: Dict = {
1✔
24
        "stoploss_on_exchange": True,
25
        "stop_price_param": "stopLossPrice",
26
        "stop_price_prop": "stopLossPrice",
27
        "stoploss_order_types": {"limit": "limit", "market": "market"},
28
        "order_time_in_force": ["GTC", "IOC", "PO"],
29
        "ohlcv_candle_limit": 720,
30
        "ohlcv_has_history": False,
31
        "trades_pagination": "id",
32
        "trades_pagination_arg": "since",
33
        "trades_pagination_overlap": False,
34
        "mark_ohlcv_timeframe": "4h",
35
    }
36

37
    _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [
1✔
38
        # TradingMode.SPOT always supported and not required in this list
39
        # (TradingMode.MARGIN, MarginMode.CROSS),
40
        # (TradingMode.FUTURES, MarginMode.CROSS)
41
    ]
42

43
    def market_is_tradable(self, market: Dict[str, Any]) -> bool:
1✔
44
        """
45
        Check if the market symbol is tradable by Freqtrade.
46
        Default checks + check if pair is darkpool pair.
47
        """
48
        parent_check = super().market_is_tradable(market)
1✔
49

50
        return (parent_check and
1✔
51
                market.get('darkpool', False) is False)
52

53
    def get_tickers(self, symbols: Optional[List[str]] = None, cached: bool = False) -> Tickers:
1✔
54
        # Only fetch tickers for current stake currency
55
        # Otherwise the request for kraken becomes too large.
56
        symbols = list(self.get_markets(quote_currencies=[self._config['stake_currency']]))
1✔
57
        return super().get_tickers(symbols=symbols, cached=cached)
1✔
58

59
    @retrier
1✔
60
    def get_balances(self) -> dict:
1✔
61
        if self._config['dry_run']:
1✔
62
            return {}
×
63

64
        try:
1✔
65
            balances = self._api.fetch_balance()
1✔
66
            # Remove additional info from ccxt results
67
            balances.pop("info", None)
1✔
68
            balances.pop("free", None)
1✔
69
            balances.pop("total", None)
1✔
70
            balances.pop("used", None)
1✔
71

72
            orders = self._api.fetch_open_orders()
1✔
73
            order_list = [(x["symbol"].split("/")[0 if x["side"] == "sell" else 1],
1✔
74
                           x["remaining"] if x["side"] == "sell" else x["remaining"] * x["price"],
75
                           # Don't remove the below comment, this can be important for debugging
76
                           # x["side"], x["amount"],
77
                           ) for x in orders]
78
            for bal in balances:
1✔
79
                if not isinstance(balances[bal], dict):
1✔
80
                    continue
1✔
81
                balances[bal]['used'] = sum(order[1] for order in order_list if order[0] == bal)
1✔
82
                balances[bal]['free'] = balances[bal]['total'] - balances[bal]['used']
1✔
83

84
            return balances
1✔
85
        except ccxt.DDoSProtection as e:
1✔
86
            raise DDosProtection(e) from e
1✔
87
        except (ccxt.OperationFailed, ccxt.ExchangeError) as e:
1✔
88
            raise TemporaryError(
1✔
89
                f'Could not get balance due to {e.__class__.__name__}. Message: {e}') from e
90
        except ccxt.BaseError as e:
1✔
91
            raise OperationalException(e) from e
1✔
92

93
    def _set_leverage(
1✔
94
        self,
95
        leverage: float,
96
        pair: Optional[str] = None,
97
        accept_fail: bool = False,
98
    ):
99
        """
100
        Kraken set's the leverage as an option in the order object, so we need to
101
        add it to params
102
        """
103
        return
×
104

105
    def _get_params(
1✔
106
        self,
107
        side: BuySell,
108
        ordertype: str,
109
        leverage: float,
110
        reduceOnly: bool,
111
        time_in_force: str = 'GTC'
112
    ) -> Dict:
113
        params = super()._get_params(
1✔
114
            side=side,
115
            ordertype=ordertype,
116
            leverage=leverage,
117
            reduceOnly=reduceOnly,
118
            time_in_force=time_in_force,
119
        )
120
        if leverage > 1.0:
1✔
121
            params['leverage'] = round(leverage)
1✔
122
        if time_in_force == 'PO':
1✔
123
            params.pop('timeInForce', None)
1✔
124
            params['postOnly'] = True
1✔
125
        return params
1✔
126

127
    def calculate_funding_fees(
1✔
128
        self,
129
        df: DataFrame,
130
        amount: float,
131
        is_short: bool,
132
        open_date: datetime,
133
        close_date: datetime,
134
        time_in_ratio: Optional[float] = None
135
    ) -> float:
136
        """
137
        # ! This method will always error when run by Freqtrade because time_in_ratio is never
138
        # ! passed to _get_funding_fee. For kraken futures to work in dry run and backtesting
139
        # ! functionality must be added that passes the parameter time_in_ratio to
140
        # ! _get_funding_fee when using Kraken
141
        calculates the sum of all funding fees that occurred for a pair during a futures trade
142
        :param df: Dataframe containing combined funding and mark rates
143
                   as `open_fund` and `open_mark`.
144
        :param amount: The quantity of the trade
145
        :param is_short: trade direction
146
        :param open_date: The date and time that the trade started
147
        :param close_date: The date and time that the trade ended
148
        :param time_in_ratio: Not used by most exchange classes
149
        """
150
        if not time_in_ratio:
1✔
151
            raise OperationalException(
1✔
152
                f"time_in_ratio is required for {self.name}._get_funding_fee")
153
        fees: float = 0
1✔
154

155
        if not df.empty:
1✔
156
            df = df[(df['date'] >= open_date) & (df['date'] <= close_date)]
1✔
157
            fees = sum(df['open_fund'] * df['open_mark'] * amount * time_in_ratio)
1✔
158

159
        return fees if is_short else -fees
1✔
160

161
    def _get_trade_pagination_next_value(self, trades: List[Dict]):
1✔
162
        """
163
        Extract pagination id for the next "from_id" value
164
        Applies only to fetch_trade_history by id.
165
        """
166
        if len(trades) > 0:
1✔
167
            if (
1✔
168
                isinstance(trades[-1].get('info'), list)
169
                and len(trades[-1].get('info', [])) > 7
170
            ):
171
                # Trade response's "last" value.
172
                return trades[-1].get('info', [])[-1]
1✔
173
            # Fall back to timestamp if info is somehow empty.
174
            return trades[-1].get('timestamp')
1✔
175
        return None
×
176

177
    def _valid_trade_pagination_id(self, pair: str, from_id: str) -> bool:
1✔
178
        """
179
        Verify trade-pagination id is valid.
180
        Workaround for odd Kraken issue where ID is sometimes wrong.
181
        """
182
        # Regular id's are in timestamp format 1705443695120072285
183
        # If the id is smaller than 19 characters, it's not a valid timestamp.
184
        if len(from_id) >= 19:
1✔
185
            return True
1✔
186
        logger.debug(f"{pair} - trade-pagination id is not valid. Fallback to timestamp.")
1✔
187
        return False
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

© 2025 Coveralls, Inc