mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
Added conversions from contract size to amount for objects returned from api
This commit is contained in:
parent
e10ceb2362
commit
4f6203e45f
|
@ -370,6 +370,30 @@ class Exchange:
|
||||||
else:
|
else:
|
||||||
return DataFrame()
|
return DataFrame()
|
||||||
|
|
||||||
|
def _get_contract_size(self, pair: str) -> int:
|
||||||
|
if self.trading_mode == TradingMode.FUTURES:
|
||||||
|
return self.markets[pair]['contract_size']
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def _trades_contracts_to_amount(self, trades: List) -> List:
|
||||||
|
if len(trades) > 0:
|
||||||
|
contract_size = self._get_contract_size(trades[0]['pair'])
|
||||||
|
if contract_size != 1:
|
||||||
|
for trade in trades:
|
||||||
|
trade['amount'] = trade['amount'] * contract_size
|
||||||
|
return trades
|
||||||
|
else:
|
||||||
|
return trades
|
||||||
|
|
||||||
|
def _order_contracts_to_amount(self, order: Dict) -> Dict:
|
||||||
|
contract_size = self._get_contract_size(order['pair'])
|
||||||
|
if contract_size != 1:
|
||||||
|
for prop in ['amount', 'cost', 'filled', 'remaining']:
|
||||||
|
if prop in order:
|
||||||
|
order[prop] = order[prop] * contract_size
|
||||||
|
return order
|
||||||
|
|
||||||
def set_sandbox(self, api: ccxt.Exchange, exchange_config: dict, name: str) -> None:
|
def set_sandbox(self, api: ccxt.Exchange, exchange_config: dict, name: str) -> None:
|
||||||
if exchange_config.get('sandbox'):
|
if exchange_config.get('sandbox'):
|
||||||
if api.urls.get('test'):
|
if api.urls.get('test'):
|
||||||
|
@ -872,13 +896,15 @@ class Exchange:
|
||||||
rate_for_order = self.price_to_precision(pair, rate) if needs_price else None
|
rate_for_order = self.price_to_precision(pair, rate) if needs_price else None
|
||||||
|
|
||||||
self._lev_prep(pair, leverage)
|
self._lev_prep(pair, leverage)
|
||||||
order = self._api.create_order(
|
order = self._order_contracts_to_amount(
|
||||||
pair,
|
self._api.create_order(
|
||||||
ordertype,
|
pair,
|
||||||
side,
|
ordertype,
|
||||||
amount,
|
side,
|
||||||
rate_for_order,
|
amount,
|
||||||
params
|
rate_for_order,
|
||||||
|
params
|
||||||
|
)
|
||||||
)
|
)
|
||||||
self._log_exchange_response('create_order', order)
|
self._log_exchange_response('create_order', order)
|
||||||
return order
|
return order
|
||||||
|
@ -927,7 +953,9 @@ class Exchange:
|
||||||
if self._config['dry_run']:
|
if self._config['dry_run']:
|
||||||
return self.fetch_dry_run_order(order_id)
|
return self.fetch_dry_run_order(order_id)
|
||||||
try:
|
try:
|
||||||
order = self._api.fetch_order(order_id, pair)
|
order = self._order_contracts_to_amount(
|
||||||
|
self._api.fetch_order(order_id, pair)
|
||||||
|
)
|
||||||
self._log_exchange_response('fetch_order', order)
|
self._log_exchange_response('fetch_order', order)
|
||||||
return order
|
return order
|
||||||
except ccxt.OrderNotFound as e:
|
except ccxt.OrderNotFound as e:
|
||||||
|
@ -981,7 +1009,9 @@ class Exchange:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
order = self._api.cancel_order(order_id, pair)
|
order = self._order_contracts_to_amount(
|
||||||
|
self._api.cancel_order(order_id, pair)
|
||||||
|
)
|
||||||
self._log_exchange_response('cancel_order', order)
|
self._log_exchange_response('cancel_order', order)
|
||||||
return order
|
return order
|
||||||
except ccxt.InvalidOrder as e:
|
except ccxt.InvalidOrder as e:
|
||||||
|
@ -1245,9 +1275,13 @@ class Exchange:
|
||||||
# since needs to be int in milliseconds
|
# since needs to be int in milliseconds
|
||||||
_params = params if params else {}
|
_params = params if params else {}
|
||||||
my_trades = self._api.fetch_my_trades(
|
my_trades = self._api.fetch_my_trades(
|
||||||
pair, int((since.replace(tzinfo=timezone.utc).timestamp() - 5) * 1000),
|
pair,
|
||||||
params=_params)
|
int((since.replace(tzinfo=timezone.utc).timestamp() - 5) * 1000),
|
||||||
matched_trades = [trade for trade in my_trades if trade['order'] == order_id]
|
params=_params
|
||||||
|
)
|
||||||
|
matched_trades = self._trades_contracts_to_amount(
|
||||||
|
trades=[trade for trade in my_trades if trade['order'] == order_id]
|
||||||
|
)
|
||||||
|
|
||||||
self._log_exchange_response('get_trades_for_order', matched_trades)
|
self._log_exchange_response('get_trades_for_order', matched_trades)
|
||||||
return matched_trades
|
return matched_trades
|
||||||
|
@ -1584,14 +1618,18 @@ class Exchange:
|
||||||
# fetch trades asynchronously
|
# fetch trades asynchronously
|
||||||
if params:
|
if params:
|
||||||
logger.debug("Fetching trades for pair %s, params: %s ", pair, params)
|
logger.debug("Fetching trades for pair %s, params: %s ", pair, params)
|
||||||
trades = await self._api_async.fetch_trades(pair, params=params, limit=1000)
|
trades = self._trades_contracts_to_amount(
|
||||||
|
trades=await self._api_async.fetch_trades(pair, params=params, limit=1000),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Fetching trades for pair %s, since %s %s...",
|
"Fetching trades for pair %s, since %s %s...",
|
||||||
pair, since,
|
pair, since,
|
||||||
'(' + arrow.get(since // 1000).isoformat() + ') ' if since is not None else ''
|
'(' + arrow.get(since // 1000).isoformat() + ') ' if since is not None else ''
|
||||||
)
|
)
|
||||||
trades = await self._api_async.fetch_trades(pair, since=since, limit=1000)
|
trades = self._trades_contracts_to_amount(
|
||||||
|
trades=await self._api_async.fetch_trades(pair, since=since, limit=1000),
|
||||||
|
)
|
||||||
return trades_dict_to_list(trades)
|
return trades_dict_to_list(trades)
|
||||||
except ccxt.NotSupported as e:
|
except ccxt.NotSupported as e:
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
|
@ -1727,7 +1765,7 @@ class Exchange:
|
||||||
self._async_get_trade_history(pair=pair, since=since,
|
self._async_get_trade_history(pair=pair, since=since,
|
||||||
until=until, from_id=from_id))
|
until=until, from_id=from_id))
|
||||||
|
|
||||||
@retrier
|
@ 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
|
Returns the sum of all funding fees that were exchanged for a pair within a timeframe
|
||||||
|
@ -1833,7 +1871,7 @@ class Exchange:
|
||||||
"""
|
"""
|
||||||
return open_date.minute > 0 or open_date.second > 0
|
return open_date.minute > 0 or open_date.second > 0
|
||||||
|
|
||||||
@retrier
|
@ retrier
|
||||||
def set_margin_mode(self, pair: str, collateral: Collateral, params: dict = {}):
|
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
|
Set's the margin mode on the exchange to cross or isolated for a specific pair
|
||||||
|
@ -1853,6 +1891,46 @@ class Exchange:
|
||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e) from 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(
|
def _calculate_funding_fees(
|
||||||
self,
|
self,
|
||||||
pair: str,
|
pair: str,
|
||||||
|
@ -1919,6 +1997,42 @@ class Exchange:
|
||||||
else:
|
else:
|
||||||
return 0.0
|
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:
|
def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool:
|
||||||
return exchange_name in ccxt_exchanges(ccxt_module)
|
return exchange_name in ccxt_exchanges(ccxt_module)
|
||||||
|
|
|
@ -3549,7 +3549,7 @@ def test__calculate_funding_fees(
|
||||||
assert pytest.approx(funding_fees) == expected_fees
|
assert pytest.approx(funding_fees) == expected_fees
|
||||||
|
|
||||||
|
|
||||||
@ pytest.mark.parametrize('exchange,expected_fees', [
|
@pytest.mark.parametrize('exchange,expected_fees', [
|
||||||
('binance', -0.0009140999999999999),
|
('binance', -0.0009140999999999999),
|
||||||
('gateio', -0.0009140999999999999),
|
('gateio', -0.0009140999999999999),
|
||||||
])
|
])
|
||||||
|
@ -3575,3 +3575,28 @@ def test__calculate_funding_fees_datetime_called(
|
||||||
time_machine.move_to("2021-09-01 08:00:00 +00:00")
|
time_machine.move_to("2021-09-01 08:00:00 +00:00")
|
||||||
funding_fees = exchange._calculate_funding_fees('ADA/USDT', 30.0, d1)
|
funding_fees = exchange._calculate_funding_fees('ADA/USDT', 30.0, d1)
|
||||||
assert funding_fees == expected_fees
|
assert funding_fees == expected_fees
|
||||||
|
|
||||||
|
|
||||||
|
def test__get_contract_size():
|
||||||
|
# TODO
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def test__trades_contracts_to_amount():
|
||||||
|
# TODO
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def test__order_contracts_to_amount():
|
||||||
|
# TODO
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def test__amount_to_contract_size():
|
||||||
|
# TODO
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def test__contract_size_to_amount():
|
||||||
|
# TODO
|
||||||
|
return
|
||||||
|
|
Loading…
Reference in New Issue
Block a user