Merge branch 'feat/short' into futures_pairlist
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
""" Bibox exchange subclass """
|
||||
import logging
|
||||
from typing import Dict, List
|
||||
from typing import Dict
|
||||
|
||||
from freqtrade.exchange import Exchange
|
||||
|
||||
@@ -23,6 +23,6 @@ class Bibox(Exchange):
|
||||
@property
|
||||
def _ccxt_config(self) -> Dict:
|
||||
# Parameters to add directly to ccxt sync/async initialization.
|
||||
return {"has": {"fetchCurrencies": False}}
|
||||
|
||||
funding_fee_times: List[int] = [0, 8, 16] # hours of the day
|
||||
config = {"has": {"fetchCurrencies": False}}
|
||||
config.update(super()._ccxt_config)
|
||||
return config
|
||||
|
@@ -1,6 +1,7 @@
|
||||
""" Binance exchange subclass """
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
@@ -27,35 +28,17 @@ class Binance(Exchange):
|
||||
"trades_pagination": "id",
|
||||
"trades_pagination_arg": "fromId",
|
||||
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
|
||||
"ccxt_futures_name": "future"
|
||||
}
|
||||
funding_fee_times: List[int] = [0, 8, 16] # hours of the day
|
||||
# but the schedule won't check within this timeframe
|
||||
|
||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
||||
# (TradingMode.FUTURES, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
||||
# (TradingMode.FUTURES, Collateral.ISOLATED) # TODO-lev: Uncomment once supported
|
||||
# TODO-lev: Uncomment once supported
|
||||
# (TradingMode.MARGIN, Collateral.CROSS),
|
||||
# (TradingMode.FUTURES, Collateral.CROSS),
|
||||
# (TradingMode.FUTURES, Collateral.ISOLATED)
|
||||
]
|
||||
|
||||
@property
|
||||
def _ccxt_config(self) -> Dict:
|
||||
# Parameters to add directly to ccxt sync/async initialization.
|
||||
if self.trading_mode == TradingMode.MARGIN:
|
||||
return {
|
||||
"options": {
|
||||
"defaultType": "margin"
|
||||
}
|
||||
}
|
||||
elif self.trading_mode == TradingMode.FUTURES:
|
||||
return {
|
||||
"options": {
|
||||
"defaultType": "future"
|
||||
}
|
||||
}
|
||||
else:
|
||||
return {}
|
||||
|
||||
def stoploss_adjust(self, stop_loss: float, order: Dict, side: str) -> bool:
|
||||
"""
|
||||
Verify stop_loss against stoploss-order value (limit or price)
|
||||
@@ -139,8 +122,8 @@ class Binance(Exchange):
|
||||
@retrier
|
||||
def fill_leverage_brackets(self):
|
||||
"""
|
||||
Assigns property _leverage_brackets to a dictionary of information about the leverage
|
||||
allowed on each pair
|
||||
Assigns property _leverage_brackets to a dictionary of information about the leverage
|
||||
allowed on each pair
|
||||
"""
|
||||
if self.trading_mode == TradingMode.FUTURES:
|
||||
try:
|
||||
@@ -174,9 +157,9 @@ class Binance(Exchange):
|
||||
|
||||
def get_max_leverage(self, pair: Optional[str], nominal_value: Optional[float]) -> float:
|
||||
"""
|
||||
Returns the maximum leverage that a pair can be traded at
|
||||
:param pair: The base/quote currency pair being traded
|
||||
:nominal_value: The total value of the trade in quote currency (collateral + debt)
|
||||
Returns the maximum leverage that a pair can be traded at
|
||||
:param pair: The base/quote currency pair being traded
|
||||
:nominal_value: The total value of the trade in quote currency (collateral + debt)
|
||||
"""
|
||||
if pair not in self._leverage_brackets:
|
||||
return 1.0
|
||||
@@ -195,8 +178,8 @@ class Binance(Exchange):
|
||||
trading_mode: Optional[TradingMode] = None
|
||||
):
|
||||
"""
|
||||
Set's the leverage before making a trade, in order to not
|
||||
have the same leverage on every trade
|
||||
Set's the leverage before making a trade, in order to not
|
||||
have the same leverage on every trade
|
||||
"""
|
||||
trading_mode = trading_mode or self.trading_mode
|
||||
|
||||
@@ -229,3 +212,11 @@ class Binance(Exchange):
|
||||
f"{arrow.get(since_ms // 1000).isoformat()}.")
|
||||
return await super()._async_get_historic_ohlcv(
|
||||
pair=pair, timeframe=timeframe, since_ms=since_ms, is_new_pair=is_new_pair)
|
||||
|
||||
def funding_fee_cutoff(self, open_date: datetime):
|
||||
"""
|
||||
# TODO-lev: Double check that gateio, ftx, and kraken don't also have this
|
||||
:param open_date: The open date for a trade
|
||||
:return: The cutoff open time for when a funding fee is charged
|
||||
"""
|
||||
return open_date.minute > 0 or (open_date.minute == 0 and open_date.second > 15)
|
||||
|
@@ -21,12 +21,12 @@ class Bybit(Exchange):
|
||||
|
||||
_ft_has: Dict = {
|
||||
"ohlcv_candle_limit": 200,
|
||||
"ccxt_futures_name": "linear"
|
||||
}
|
||||
|
||||
funding_fee_times: List[int] = [0, 8, 16] # hours of the day
|
||||
|
||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
# (TradingMode.FUTURES, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
||||
# (TradingMode.FUTURES, Collateral.ISOLATED) # TODO-lev: Uncomment once supported
|
||||
# TODO-lev: Uncomment once supported
|
||||
# (TradingMode.FUTURES, Collateral.CROSS),
|
||||
# (TradingMode.FUTURES, Collateral.ISOLATED)
|
||||
]
|
||||
|
@@ -7,7 +7,7 @@ import http
|
||||
import inspect
|
||||
import logging
|
||||
from copy import deepcopy
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from math import ceil
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
@@ -69,13 +69,11 @@ class Exchange:
|
||||
"trades_pagination_arg": "since",
|
||||
"l2_limit_range": None,
|
||||
"l2_limit_range_required": True, # Allow Empty L2 limit (kucoin)
|
||||
"mark_ohlcv_price": "mark",
|
||||
"ccxt_futures_name": "swap"
|
||||
}
|
||||
_ft_has: Dict = {}
|
||||
|
||||
# funding_fee_times is currently unused, but should ideally be used to properly
|
||||
# schedule refresh times
|
||||
funding_fee_times: List[int] = [] # hours of the day
|
||||
|
||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
]
|
||||
@@ -89,6 +87,7 @@ class Exchange:
|
||||
self._api: ccxt.Exchange = None
|
||||
self._api_async: ccxt_async.Exchange = None
|
||||
self._markets: Dict = {}
|
||||
self._leverage_brackets: Dict = {}
|
||||
|
||||
self._config.update(config)
|
||||
|
||||
@@ -179,7 +178,6 @@ class Exchange:
|
||||
self.markets_refresh_interval: int = exchange_config.get(
|
||||
"markets_refresh_interval", 60) * 60
|
||||
|
||||
self._leverage_brackets: Dict = {}
|
||||
if self.trading_mode != TradingMode.SPOT:
|
||||
self.fill_leverage_brackets()
|
||||
|
||||
@@ -234,7 +232,20 @@ class Exchange:
|
||||
@property
|
||||
def _ccxt_config(self) -> Dict:
|
||||
# Parameters to add directly to ccxt sync/async initialization.
|
||||
return {}
|
||||
if self.trading_mode == TradingMode.MARGIN:
|
||||
return {
|
||||
"options": {
|
||||
"defaultType": "margin"
|
||||
}
|
||||
}
|
||||
elif self.trading_mode == TradingMode.FUTURES:
|
||||
return {
|
||||
"options": {
|
||||
"defaultType": self._ft_has["ccxt_futures_name"]
|
||||
}
|
||||
}
|
||||
else:
|
||||
return {}
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
@@ -532,10 +543,10 @@ class Exchange:
|
||||
collateral: Optional[Collateral] # Only None when trading_mode = TradingMode.SPOT
|
||||
):
|
||||
"""
|
||||
Checks if freqtrade can perform trades using the configured
|
||||
trading mode(Margin, Futures) and Collateral(Cross, Isolated)
|
||||
Throws OperationalException:
|
||||
If the trading_mode/collateral type are not supported by freqtrade on this exchange
|
||||
Checks if freqtrade can perform trades using the configured
|
||||
trading mode(Margin, Futures) and Collateral(Cross, Isolated)
|
||||
Throws OperationalException:
|
||||
If the trading_mode/collateral type are not supported by freqtrade on this exchange
|
||||
"""
|
||||
if trading_mode != TradingMode.SPOT and (
|
||||
(trading_mode, collateral) not in self._supported_trading_mode_collateral_pairs
|
||||
@@ -1622,18 +1633,18 @@ class Exchange:
|
||||
until=until, from_id=from_id))
|
||||
|
||||
@retrier
|
||||
def get_funding_fees_from_exchange(self, pair: str, since: Union[datetime, int]) -> float:
|
||||
def _get_funding_fees_from_exchange(self, pair: str, since: Union[datetime, int]) -> float:
|
||||
"""
|
||||
Returns the sum of all funding fees that were exchanged for a pair within a timeframe
|
||||
:param pair: (e.g. ADA/USDT)
|
||||
:param since: The earliest time of consideration for calculating funding fees,
|
||||
in unix time or as a datetime
|
||||
Returns the sum of all funding fees that were exchanged for a pair within a timeframe
|
||||
Dry-run handling happens as part of _calculate_funding_fees.
|
||||
:param pair: (e.g. ADA/USDT)
|
||||
:param since: The earliest time of consideration for calculating funding fees,
|
||||
in unix time or as a datetime
|
||||
"""
|
||||
# TODO-lev: Add dry-run handling for this.
|
||||
|
||||
if not self.exchange_has("fetchFundingHistory"):
|
||||
raise OperationalException(
|
||||
f"fetch_funding_history() has not been implemented on ccxt.{self.name}")
|
||||
f"fetch_funding_history() is not available using {self.name}"
|
||||
)
|
||||
|
||||
if type(since) is datetime:
|
||||
since = int(since.timestamp()) * 1000 # * 1000 for ms
|
||||
@@ -1654,17 +1665,17 @@ class Exchange:
|
||||
|
||||
def fill_leverage_brackets(self):
|
||||
"""
|
||||
Assigns property _leverage_brackets to a dictionary of information about the leverage
|
||||
allowed on each pair
|
||||
Not used if the exchange has a static max leverage value for the account or each pair
|
||||
Assigns property _leverage_brackets to a dictionary of information about the leverage
|
||||
allowed on each pair
|
||||
Not used if the exchange has a static max leverage value for the account or each pair
|
||||
"""
|
||||
return
|
||||
|
||||
def get_max_leverage(self, pair: Optional[str], nominal_value: Optional[float]) -> float:
|
||||
"""
|
||||
Returns the maximum leverage that a pair can be traded at
|
||||
:param pair: The base/quote currency pair being traded
|
||||
:nominal_value: The total value of the trade in quote currency (collateral + debt)
|
||||
Returns the maximum leverage that a pair can be traded at
|
||||
:param pair: The base/quote currency pair being traded
|
||||
:param nominal_value: The total value of the trade in quote currency (collateral + debt)
|
||||
"""
|
||||
market = self.markets[pair]
|
||||
if (
|
||||
@@ -1676,6 +1687,25 @@ class Exchange:
|
||||
else:
|
||||
return 1.0
|
||||
|
||||
def _get_funding_fee(
|
||||
self,
|
||||
size: float,
|
||||
funding_rate: float,
|
||||
mark_price: float,
|
||||
time_in_ratio: Optional[float] = None
|
||||
) -> float:
|
||||
"""
|
||||
Calculates a single funding fee
|
||||
:param size: contract size * number of contracts
|
||||
:param mark_price: The price of the asset that the contract is based off of
|
||||
:param funding_rate: the interest rate and the premium
|
||||
- interest rate:
|
||||
- premium: varies by price difference between the perpetual contract and mark price
|
||||
:param time_in_ratio: Not used by most exchange classes
|
||||
"""
|
||||
nominal_value = mark_price * size
|
||||
return nominal_value * funding_rate
|
||||
|
||||
@retrier
|
||||
def _set_leverage(
|
||||
self,
|
||||
@@ -1684,8 +1714,8 @@ class Exchange:
|
||||
trading_mode: Optional[TradingMode] = None
|
||||
):
|
||||
"""
|
||||
Set's the leverage before making a trade, in order to not
|
||||
have the same leverage on every trade
|
||||
Set's the leverage before making a trade, in order to not
|
||||
have the same leverage on every trade
|
||||
"""
|
||||
if self._config['dry_run'] or not self.exchange_has("setLeverage"):
|
||||
# Some exchanges only support one collateral type
|
||||
@@ -1701,12 +1731,19 @@ class Exchange:
|
||||
except ccxt.BaseError as e:
|
||||
raise OperationalException(e) from e
|
||||
|
||||
def funding_fee_cutoff(self, open_date: datetime):
|
||||
"""
|
||||
:param open_date: The open date for a trade
|
||||
:return: The cutoff open time for when a funding fee is charged
|
||||
"""
|
||||
return open_date.minute > 0 or open_date.second > 0
|
||||
|
||||
@retrier
|
||||
def set_margin_mode(self, pair: str, collateral: Collateral, params: dict = {}):
|
||||
'''
|
||||
Set's the margin mode on the exchange to cross or isolated for a specific pair
|
||||
:param symbol: base/quote currency pair (e.g. "ADA/USDT")
|
||||
'''
|
||||
"""
|
||||
Set's the margin mode on the exchange to cross or isolated for a specific pair
|
||||
:param pair: base/quote currency pair (e.g. "ADA/USDT")
|
||||
"""
|
||||
if self._config['dry_run'] or not self.exchange_has("setMarginMode"):
|
||||
# Some exchanges only support one collateral type
|
||||
return
|
||||
@@ -1721,6 +1758,150 @@ class Exchange:
|
||||
except ccxt.BaseError as e:
|
||||
raise OperationalException(e) from e
|
||||
|
||||
@retrier
|
||||
def _get_mark_price_history(self, pair: str, since: int) -> Dict:
|
||||
"""
|
||||
Get's the mark price history for a pair
|
||||
:param pair: The quote/base pair of the trade
|
||||
:param since: The earliest time to start downloading candles, in ms.
|
||||
"""
|
||||
|
||||
try:
|
||||
candles = self._api.fetch_ohlcv(
|
||||
pair,
|
||||
timeframe="1h",
|
||||
since=since,
|
||||
params={
|
||||
'price': self._ft_has["mark_ohlcv_price"]
|
||||
}
|
||||
)
|
||||
history = {}
|
||||
for candle in candles:
|
||||
d = datetime.fromtimestamp(int(candle[0] / 1000), timezone.utc)
|
||||
# Round down to the nearest hour, in case of a delayed timestamp
|
||||
# The millisecond timestamps can be delayed ~20ms
|
||||
time = timeframe_to_prev_date('1h', d).timestamp() * 1000
|
||||
opening_mark_price = candle[1]
|
||||
history[time] = opening_mark_price
|
||||
return history
|
||||
except ccxt.NotSupported as e:
|
||||
raise OperationalException(
|
||||
f'Exchange {self._api.name} does not support fetching historical '
|
||||
f'mark price candle (OHLCV) data. Message: {e}') from e
|
||||
except ccxt.DDoSProtection as e:
|
||||
raise DDosProtection(e) from e
|
||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||
raise TemporaryError(f'Could not fetch historical mark price candle (OHLCV) data '
|
||||
f'for pair {pair} due to {e.__class__.__name__}. '
|
||||
f'Message: {e}') from e
|
||||
except ccxt.BaseError as e:
|
||||
raise OperationalException(f'Could not fetch historical mark price candle (OHLCV) data '
|
||||
f'for pair {pair}. Message: {e}') from e
|
||||
|
||||
def _calculate_funding_fees(
|
||||
self,
|
||||
pair: str,
|
||||
amount: float,
|
||||
open_date: datetime,
|
||||
close_date: Optional[datetime] = None
|
||||
) -> float:
|
||||
"""
|
||||
calculates the sum of all funding fees that occurred for a pair during a futures trade
|
||||
Only used during dry-run or if the exchange does not provide a funding_rates endpoint.
|
||||
:param pair: The quote/base pair of the trade
|
||||
:param amount: The quantity of the trade
|
||||
:param open_date: The date and time that the trade started
|
||||
:param close_date: The date and time that the trade ended
|
||||
"""
|
||||
|
||||
if self.funding_fee_cutoff(open_date):
|
||||
open_date += timedelta(hours=1)
|
||||
|
||||
open_date = timeframe_to_prev_date('1h', open_date)
|
||||
|
||||
fees: float = 0
|
||||
if not close_date:
|
||||
close_date = datetime.now(timezone.utc)
|
||||
open_timestamp = int(open_date.timestamp()) * 1000
|
||||
# close_timestamp = int(close_date.timestamp()) * 1000
|
||||
funding_rate_history = self.get_funding_rate_history(
|
||||
pair,
|
||||
open_timestamp
|
||||
)
|
||||
mark_price_history = self._get_mark_price_history(
|
||||
pair,
|
||||
open_timestamp
|
||||
)
|
||||
for timestamp in funding_rate_history.keys():
|
||||
funding_rate = funding_rate_history[timestamp]
|
||||
if timestamp in mark_price_history:
|
||||
mark_price = mark_price_history[timestamp]
|
||||
fees += self._get_funding_fee(
|
||||
size=amount,
|
||||
mark_price=mark_price,
|
||||
funding_rate=funding_rate
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"Mark price for {pair} at timestamp {timestamp} not found in "
|
||||
f"funding_rate_history Funding fee calculation may be incorrect"
|
||||
)
|
||||
|
||||
return fees
|
||||
|
||||
def get_funding_fees(self, pair: str, amount: float, open_date: datetime) -> float:
|
||||
"""
|
||||
Fetch funding fees, either from the exchange (live) or calculates them
|
||||
based on funding rate/mark price history
|
||||
:param pair: The quote/base pair of the trade
|
||||
:param amount: Trade amount
|
||||
:param open_date: Open date of the trade
|
||||
"""
|
||||
if self.trading_mode == TradingMode.FUTURES:
|
||||
if self._config['dry_run']:
|
||||
funding_fees = self._calculate_funding_fees(pair, amount, open_date)
|
||||
else:
|
||||
funding_fees = self._get_funding_fees_from_exchange(pair, open_date)
|
||||
return funding_fees
|
||||
else:
|
||||
return 0.0
|
||||
|
||||
@retrier
|
||||
def get_funding_rate_history(self, pair: str, since: int) -> Dict:
|
||||
"""
|
||||
:param pair: quote/base currency pair
|
||||
:param since: timestamp in ms of the beginning time
|
||||
:param end: timestamp in ms of the end time
|
||||
"""
|
||||
if not self.exchange_has("fetchFundingRateHistory"):
|
||||
raise ExchangeError(
|
||||
f"fetch_funding_rate_history is not available using {self.name}"
|
||||
)
|
||||
|
||||
# TODO-lev: Gateio has a max limit into the past of 333 days, okex has a limit of 3 months
|
||||
try:
|
||||
funding_history: Dict = {}
|
||||
response = self._api.fetch_funding_rate_history(
|
||||
pair,
|
||||
limit=1000,
|
||||
since=since
|
||||
)
|
||||
for fund in response:
|
||||
d = datetime.fromtimestamp(int(fund['timestamp'] / 1000), timezone.utc)
|
||||
# Round down to the nearest hour, in case of a delayed timestamp
|
||||
# The millisecond timestamps can be delayed ~20ms
|
||||
time = int(timeframe_to_prev_date('1h', d).timestamp() * 1000)
|
||||
|
||||
funding_history[time] = fund['fundingRate']
|
||||
return funding_history
|
||||
except ccxt.DDoSProtection as e:
|
||||
raise DDosProtection(e) from e
|
||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||
raise TemporaryError(
|
||||
f'Could not set margin mode due to {e.__class__.__name__}. Message: {e}') from e
|
||||
except ccxt.BaseError as e:
|
||||
raise OperationalException(e) from e
|
||||
|
||||
|
||||
def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool:
|
||||
return exchange_name in ccxt_exchanges(ccxt_module)
|
||||
|
@@ -20,13 +20,14 @@ class Ftx(Exchange):
|
||||
_ft_has: Dict = {
|
||||
"stoploss_on_exchange": True,
|
||||
"ohlcv_candle_limit": 1500,
|
||||
"mark_ohlcv_price": "index"
|
||||
}
|
||||
funding_fee_times: List[int] = list(range(0, 24))
|
||||
|
||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
||||
# (TradingMode.FUTURES, Collateral.CROSS) # TODO-lev: Uncomment once supported
|
||||
# TODO-lev: Uncomment once supported
|
||||
# (TradingMode.MARGIN, Collateral.CROSS),
|
||||
# (TradingMode.FUTURES, Collateral.CROSS)
|
||||
]
|
||||
|
||||
def stoploss_adjust(self, stop_loss: float, order: Dict, side: str) -> bool:
|
||||
|
@@ -26,33 +26,14 @@ class Gateio(Exchange):
|
||||
|
||||
_headers = {'X-Gate-Channel-Id': 'freqtrade'}
|
||||
|
||||
funding_fee_times: List[int] = [0, 8, 16] # hours of the day
|
||||
|
||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
||||
# (TradingMode.FUTURES, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
||||
# (TradingMode.FUTURES, Collateral.ISOLATED) # TODO-lev: Uncomment once supported
|
||||
# TODO-lev: Uncomment once supported
|
||||
# (TradingMode.MARGIN, Collateral.CROSS),
|
||||
# (TradingMode.FUTURES, Collateral.CROSS),
|
||||
# (TradingMode.FUTURES, Collateral.ISOLATED)
|
||||
]
|
||||
|
||||
@property
|
||||
def _ccxt_config(self) -> Dict:
|
||||
# Parameters to add directly to ccxt sync/async initialization.
|
||||
if self.trading_mode == TradingMode.MARGIN:
|
||||
return {
|
||||
"options": {
|
||||
"defaultType": "margin"
|
||||
}
|
||||
}
|
||||
elif self.trading_mode == TradingMode.FUTURES:
|
||||
return {
|
||||
"options": {
|
||||
"defaultType": "swap"
|
||||
}
|
||||
}
|
||||
else:
|
||||
return {}
|
||||
|
||||
def validate_ordertypes(self, order_types: Dict) -> None:
|
||||
super().validate_ordertypes(order_types)
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import logging
|
||||
from typing import Dict, List
|
||||
from typing import Dict
|
||||
|
||||
from freqtrade.exchange import Exchange
|
||||
|
||||
@@ -21,5 +21,3 @@ class Hitbtc(Exchange):
|
||||
"ohlcv_candle_limit": 1000,
|
||||
"ohlcv_params": {"sort": "DESC"}
|
||||
}
|
||||
|
||||
funding_fee_times: List[int] = [0, 8, 16] # hours of the day
|
||||
|
@@ -23,12 +23,12 @@ class Kraken(Exchange):
|
||||
"trades_pagination": "id",
|
||||
"trades_pagination_arg": "since",
|
||||
}
|
||||
funding_fee_times: List[int] = [0, 4, 8, 12, 16, 20] # hours of the day
|
||||
|
||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
||||
# (TradingMode.FUTURES, Collateral.CROSS) # TODO-lev: No CCXT support
|
||||
# TODO-lev: Uncomment once supported
|
||||
# (TradingMode.MARGIN, Collateral.CROSS),
|
||||
# (TradingMode.FUTURES, Collateral.CROSS)
|
||||
]
|
||||
|
||||
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
|
||||
@@ -146,8 +146,8 @@ class Kraken(Exchange):
|
||||
trading_mode: Optional[TradingMode] = None
|
||||
):
|
||||
"""
|
||||
Kraken set's the leverage as an option in the order object, so we need to
|
||||
add it to params
|
||||
Kraken set's the leverage as an option in the order object, so we need to
|
||||
add it to params
|
||||
"""
|
||||
return
|
||||
|
||||
@@ -156,3 +156,29 @@ class Kraken(Exchange):
|
||||
if leverage > 1.0:
|
||||
params['leverage'] = leverage
|
||||
return params
|
||||
|
||||
def _get_funding_fee(
|
||||
self,
|
||||
size: float,
|
||||
funding_rate: float,
|
||||
mark_price: float,
|
||||
time_in_ratio: Optional[float] = None
|
||||
) -> float:
|
||||
"""
|
||||
# ! This method will always error when run by Freqtrade because time_in_ratio is never
|
||||
# ! passed to _get_funding_fee. For kraken futures to work in dry run and backtesting
|
||||
# ! functionality must be added that passes the parameter time_in_ratio to
|
||||
# ! _get_funding_fee when using Kraken
|
||||
Calculates a single funding fee
|
||||
:param size: contract size * number of contracts
|
||||
:param mark_price: The price of the asset that the contract is based off of
|
||||
:param funding_rate: the interest rate and the premium
|
||||
- interest rate:
|
||||
- premium: varies by price difference between the perpetual contract and mark price
|
||||
:param time_in_ratio: time elapsed within funding period without position alteration
|
||||
"""
|
||||
if not time_in_ratio:
|
||||
raise OperationalException(
|
||||
f"time_in_ratio is required for {self.name}._get_funding_fee")
|
||||
nominal_value = mark_price * size
|
||||
return nominal_value * funding_rate * time_in_ratio
|
||||
|
@@ -1,6 +1,6 @@
|
||||
""" Kucoin exchange subclass """
|
||||
import logging
|
||||
from typing import Dict, List
|
||||
from typing import Dict
|
||||
|
||||
from freqtrade.exchange import Exchange
|
||||
|
||||
@@ -24,5 +24,3 @@ class Kucoin(Exchange):
|
||||
"order_time_in_force": ['gtc', 'fok', 'ioc'],
|
||||
"time_in_force_parameter": "timeInForce",
|
||||
}
|
||||
|
||||
funding_fee_times: List[int] = [4, 12, 20] # hours of the day
|
||||
|
@@ -17,29 +17,11 @@ class Okex(Exchange):
|
||||
_ft_has: Dict = {
|
||||
"ohlcv_candle_limit": 100,
|
||||
}
|
||||
funding_fee_times: List[int] = [0, 8, 16] # hours of the day
|
||||
|
||||
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
||||
# (TradingMode.FUTURES, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
||||
# (TradingMode.FUTURES, Collateral.ISOLATED) # TODO-lev: Uncomment once supported
|
||||
# TODO-lev: Uncomment once supported
|
||||
# (TradingMode.MARGIN, Collateral.CROSS),
|
||||
# (TradingMode.FUTURES, Collateral.CROSS),
|
||||
# (TradingMode.FUTURES, Collateral.ISOLATED)
|
||||
]
|
||||
|
||||
@property
|
||||
def _ccxt_config(self) -> Dict:
|
||||
# Parameters to add directly to ccxt sync/async initialization.
|
||||
if self.trading_mode == TradingMode.MARGIN:
|
||||
return {
|
||||
"options": {
|
||||
"defaultType": "margin"
|
||||
}
|
||||
}
|
||||
elif self.trading_mode == TradingMode.FUTURES:
|
||||
return {
|
||||
"options": {
|
||||
"defaultType": "swap"
|
||||
}
|
||||
}
|
||||
else:
|
||||
return {}
|
||||
|
Reference in New Issue
Block a user