Merge get_buy_rate and get_sell_rate
This commit is contained in:
parent
d652e6fcc4
commit
7c27525bd8
@ -999,94 +999,61 @@ class Exchange:
|
||||
except ccxt.BaseError as e:
|
||||
raise OperationalException(e) from e
|
||||
|
||||
def get_buy_rate(self, pair: str, refresh: bool) -> float:
|
||||
def get_rate(self, pair: str, refresh: bool, side: str = "buy") -> float:
|
||||
"""
|
||||
Calculates bid target between current ask price and last price
|
||||
:param pair: Pair to get rate for
|
||||
:param refresh: allow cached data
|
||||
:param side: "buy" or "sell"
|
||||
:return: float: Price
|
||||
:raises PricingError if orderbook price could not be determined.
|
||||
"""
|
||||
cache_rate: TTLCache = self._buy_rate_cache if side == "buy" else self._sell_rate_cache
|
||||
[strat_name, name] = ['bid_strategy', 'Buy'] if side == "buy" else ['ask_strategy', 'Sell']
|
||||
|
||||
if not refresh:
|
||||
rate = self._buy_rate_cache.get(pair)
|
||||
rate = cache_rate.get(pair)
|
||||
# Check if cache has been invalidated
|
||||
if rate:
|
||||
logger.debug(f"Using cached buy rate for {pair}.")
|
||||
logger.debug(f"Using cached {side} rate for {pair}.")
|
||||
return rate
|
||||
|
||||
bid_strategy = self._config.get('bid_strategy', {})
|
||||
if 'use_order_book' in bid_strategy and bid_strategy.get('use_order_book', False):
|
||||
strategy = self._config.get(strat_name, {})
|
||||
|
||||
order_book_top = bid_strategy.get('order_book_top', 1)
|
||||
if strategy.get('use_order_book', False) and ('use_order_book' in strategy):
|
||||
|
||||
order_book_top = strategy.get('order_book_top', 1)
|
||||
order_book = self.fetch_l2_order_book(pair, order_book_top)
|
||||
logger.debug('order_book %s', order_book)
|
||||
# top 1 = index 0
|
||||
try:
|
||||
rate_from_l2 = order_book[f"{bid_strategy['price_side']}s"][order_book_top - 1][0]
|
||||
rate = order_book[f"{strategy['price_side']}s"][order_book_top - 1][0]
|
||||
except (IndexError, KeyError) as e:
|
||||
logger.warning(
|
||||
"Buy Price from orderbook could not be determined."
|
||||
f"Orderbook: {order_book}"
|
||||
)
|
||||
raise PricingError from e
|
||||
logger.info(f"Buy price from orderbook {bid_strategy['price_side'].capitalize()} side "
|
||||
f"- top {order_book_top} order book buy rate {rate_from_l2:.8f}")
|
||||
used_rate = rate_from_l2
|
||||
else:
|
||||
logger.info(f"Using Last {bid_strategy['price_side'].capitalize()} / Last Price")
|
||||
ticker = self.fetch_ticker(pair)
|
||||
ticker_rate = ticker[bid_strategy['price_side']]
|
||||
if ticker['last'] and ticker_rate > ticker['last']:
|
||||
balance = bid_strategy['ask_last_balance']
|
||||
ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate)
|
||||
used_rate = ticker_rate
|
||||
|
||||
self._buy_rate_cache[pair] = used_rate
|
||||
|
||||
return used_rate
|
||||
|
||||
def get_sell_rate(self, pair: str, refresh: bool) -> float:
|
||||
"""
|
||||
Get sell rate - either using ticker bid or first bid based on orderbook
|
||||
or remain static in any other case since it's not updating.
|
||||
:param pair: Pair to get rate for
|
||||
:param refresh: allow cached data
|
||||
:return: Bid rate
|
||||
:raises PricingError if price could not be determined.
|
||||
"""
|
||||
if not refresh:
|
||||
rate = self._sell_rate_cache.get(pair)
|
||||
# Check if cache has been invalidated
|
||||
if rate:
|
||||
logger.debug(f"Using cached sell rate for {pair}.")
|
||||
return rate
|
||||
|
||||
ask_strategy = self._config.get('ask_strategy', {})
|
||||
if ask_strategy.get('use_order_book', False):
|
||||
logger.debug(
|
||||
f"Getting price from order book {ask_strategy['price_side'].capitalize()} side."
|
||||
)
|
||||
order_book_top = ask_strategy.get('order_book_top', 1)
|
||||
order_book = self.fetch_l2_order_book(pair, order_book_top)
|
||||
try:
|
||||
rate = order_book[f"{ask_strategy['price_side']}s"][order_book_top - 1][0]
|
||||
except (IndexError, KeyError) as e:
|
||||
logger.warning(
|
||||
f"Sell Price at location {order_book_top} from orderbook could not be "
|
||||
f"{name} Price at location {order_book_top} from orderbook could not be "
|
||||
f"determined. Orderbook: {order_book}"
|
||||
)
|
||||
raise PricingError from e
|
||||
|
||||
logger.info(f"{name} price from orderbook {strategy['price_side'].capitalize()}"
|
||||
f"side - top {order_book_top} order book {side} rate {rate:.8f}")
|
||||
else:
|
||||
logger.info(f"Using Last {strategy['price_side'].capitalize()} / Last Price")
|
||||
ticker = self.fetch_ticker(pair)
|
||||
ticker_rate = ticker[ask_strategy['price_side']]
|
||||
if ticker['last'] and ticker_rate < ticker['last']:
|
||||
balance = ask_strategy.get('bid_last_balance', 0.0)
|
||||
ticker_rate = ticker[strategy['price_side']]
|
||||
if ticker['last']:
|
||||
if side == 'buy' and ticker_rate > ticker['last']:
|
||||
balance = strategy['ask_last_balance']
|
||||
ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate)
|
||||
elif side == 'sell' and ticker_rate < ticker['last']:
|
||||
balance = strategy.get('bid_last_balance', 0.0)
|
||||
ticker_rate = ticker_rate - balance * (ticker_rate - ticker['last'])
|
||||
rate = ticker_rate
|
||||
|
||||
if rate is None:
|
||||
raise PricingError(f"Sell-Rate for {pair} was empty.")
|
||||
self._sell_rate_cache[pair] = rate
|
||||
if rate is None and side == "sell":
|
||||
raise PricingError(f"{name}-Rate for {pair} was empty.")
|
||||
cache_rate[pair] = rate
|
||||
|
||||
return rate
|
||||
|
||||
# Fee handling
|
||||
|
@ -475,7 +475,7 @@ class FreqtradeBot(LoggingMixin):
|
||||
buy_limit_requested = price
|
||||
else:
|
||||
# Calculate price
|
||||
buy_limit_requested = self.exchange.get_buy_rate(pair, True)
|
||||
buy_limit_requested = self.exchange.get_rate(pair, refresh=True, side="buy")
|
||||
|
||||
if not buy_limit_requested:
|
||||
raise PricingError('Could not determine buy price.')
|
||||
@ -609,7 +609,7 @@ class FreqtradeBot(LoggingMixin):
|
||||
"""
|
||||
Sends rpc notification when a buy cancel occurred.
|
||||
"""
|
||||
current_rate = self.exchange.get_buy_rate(trade.pair, False)
|
||||
current_rate = self.exchange.get_rate(trade.pair, refresh=False, side="buy")
|
||||
|
||||
msg = {
|
||||
'trade_id': trade.id,
|
||||
@ -695,7 +695,7 @@ class FreqtradeBot(LoggingMixin):
|
||||
(buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df)
|
||||
|
||||
logger.debug('checking sell')
|
||||
sell_rate = self.exchange.get_sell_rate(trade.pair, True)
|
||||
sell_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell")
|
||||
if self._check_and_execute_sell(trade, sell_rate, buy, sell):
|
||||
return True
|
||||
|
||||
@ -1132,7 +1132,8 @@ class FreqtradeBot(LoggingMixin):
|
||||
profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested
|
||||
profit_trade = trade.calc_profit(rate=profit_rate)
|
||||
# Use cached rates here - it was updated seconds ago.
|
||||
current_rate = self.exchange.get_sell_rate(trade.pair, False) if not fill else None
|
||||
current_rate = self.exchange.get_rate(
|
||||
trade.pair, refresh=False, side="sell") if not fill else None
|
||||
profit_ratio = trade.calc_profit_ratio(profit_rate)
|
||||
gain = "profit" if profit_ratio > 0 else "loss"
|
||||
|
||||
@ -1177,7 +1178,7 @@ class FreqtradeBot(LoggingMixin):
|
||||
|
||||
profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested
|
||||
profit_trade = trade.calc_profit(rate=profit_rate)
|
||||
current_rate = self.exchange.get_sell_rate(trade.pair, False)
|
||||
current_rate = self.exchange.get_rate(trade.pair, refresh=False, side="sell")
|
||||
profit_ratio = trade.calc_profit_ratio(profit_rate)
|
||||
gain = "profit" if profit_ratio > 0 else "loss"
|
||||
|
||||
|
@ -154,7 +154,8 @@ class RPC:
|
||||
# calculate profit and send message to user
|
||||
if trade.is_open:
|
||||
try:
|
||||
current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False)
|
||||
current_rate = self._freqtrade.exchange.get_rate(
|
||||
trade.pair, refresh=False, side="sell")
|
||||
except (ExchangeError, PricingError):
|
||||
current_rate = NAN
|
||||
else:
|
||||
@ -213,7 +214,8 @@ class RPC:
|
||||
for trade in trades:
|
||||
# calculate profit and send message to user
|
||||
try:
|
||||
current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False)
|
||||
current_rate = self._freqtrade.exchange.get_rate(
|
||||
trade.pair, refresh=False, side="sell")
|
||||
except (PricingError, ExchangeError):
|
||||
current_rate = NAN
|
||||
trade_percent = (100 * trade.calc_profit_ratio(current_rate))
|
||||
@ -372,7 +374,8 @@ class RPC:
|
||||
else:
|
||||
# Get current rate
|
||||
try:
|
||||
current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False)
|
||||
current_rate = self._freqtrade.exchange.get_rate(
|
||||
trade.pair, refresh=False, side="sell")
|
||||
except (PricingError, ExchangeError):
|
||||
current_rate = NAN
|
||||
profit_ratio = trade.calc_profit_ratio(rate=current_rate)
|
||||
@ -551,7 +554,8 @@ class RPC:
|
||||
|
||||
if not fully_canceled:
|
||||
# Get current rate and execute sell
|
||||
current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False)
|
||||
current_rate = self._freqtrade.exchange.get_rate(
|
||||
trade.pair, refresh=False, side="sell")
|
||||
sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL)
|
||||
self._freqtrade.execute_sell(trade, current_rate, sell_reason)
|
||||
# ---- EOF def _exec_forcesell ----
|
||||
|
@ -1783,14 +1783,14 @@ def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid,
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
||||
return_value={'ask': ask, 'last': last, 'bid': bid})
|
||||
|
||||
assert exchange.get_buy_rate('ETH/BTC', True) == expected
|
||||
assert exchange.get_rate('ETH/BTC', refresh=True, side="buy") == expected
|
||||
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
|
||||
|
||||
assert exchange.get_buy_rate('ETH/BTC', False) == expected
|
||||
assert exchange.get_rate('ETH/BTC', refresh=False, side="buy") == expected
|
||||
assert log_has("Using cached buy rate for ETH/BTC.", caplog)
|
||||
# Running a 2nd time with Refresh on!
|
||||
caplog.clear()
|
||||
assert exchange.get_buy_rate('ETH/BTC', True) == expected
|
||||
assert exchange.get_rate('ETH/BTC', refresh=True, side="buy") == expected
|
||||
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
|
||||
|
||||
|
||||
@ -1825,12 +1825,12 @@ def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask,
|
||||
|
||||
# Test regular mode
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
rate = exchange.get_sell_rate(pair, True)
|
||||
rate = exchange.get_rate(pair, refresh=True, side="sell")
|
||||
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||
assert isinstance(rate, float)
|
||||
assert rate == expected
|
||||
# Use caching
|
||||
rate = exchange.get_sell_rate(pair, False)
|
||||
rate = exchange.get_rate(pair, refresh=False, side="sell")
|
||||
assert rate == expected
|
||||
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||
|
||||
@ -1848,11 +1848,11 @@ def test_get_sell_rate_orderbook(default_conf, mocker, caplog, side, expected, o
|
||||
pair = "ETH/BTC"
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
rate = exchange.get_sell_rate(pair, True)
|
||||
rate = exchange.get_rate(pair, refresh=True, side="sell")
|
||||
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||
assert isinstance(rate, float)
|
||||
assert rate == expected
|
||||
rate = exchange.get_sell_rate(pair, False)
|
||||
rate = exchange.get_rate(pair, refresh=False, side="sell")
|
||||
assert rate == expected
|
||||
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||
|
||||
@ -1868,7 +1868,7 @@ def test_get_sell_rate_orderbook_exception(default_conf, mocker, caplog):
|
||||
return_value={'bids': [[]], 'asks': [[]]})
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
with pytest.raises(PricingError):
|
||||
exchange.get_sell_rate(pair, True)
|
||||
exchange.get_rate(pair, refresh=True, side="sell")
|
||||
assert log_has_re(r"Sell Price at location 1 from orderbook could not be determined\..*",
|
||||
caplog)
|
||||
|
||||
@ -1881,18 +1881,18 @@ def test_get_sell_rate_exception(default_conf, mocker, caplog):
|
||||
return_value={'ask': None, 'bid': 0.12, 'last': None})
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
|
||||
exchange.get_sell_rate(pair, True)
|
||||
exchange.get_rate(pair, refresh=True, side="sell")
|
||||
|
||||
exchange._config['ask_strategy']['price_side'] = 'bid'
|
||||
assert exchange.get_sell_rate(pair, True) == 0.12
|
||||
assert exchange.get_rate(pair, refresh=True, side="sell") == 0.12
|
||||
# Reverse sides
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
||||
return_value={'ask': 0.13, 'bid': None, 'last': None})
|
||||
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
|
||||
exchange.get_sell_rate(pair, True)
|
||||
exchange.get_rate(pair, refresh=True, side="sell")
|
||||
|
||||
exchange._config['ask_strategy']['price_side'] = 'ask'
|
||||
assert exchange.get_sell_rate(pair, True) == 0.13
|
||||
assert exchange.get_rate(pair, refresh=True, side="sell") == 0.13
|
||||
|
||||
|
||||
def make_fetch_ohlcv_mock(data):
|
||||
@ -2203,7 +2203,7 @@ def test_cancel_order_dry_run(default_conf, mocker, exchange_name):
|
||||
({'status': 'canceled', 'filled': 10.0}, False),
|
||||
({'status': 'unknown', 'filled': 10.0}, False),
|
||||
({'result': 'testest123'}, False),
|
||||
])
|
||||
])
|
||||
def test_check_order_canceled_empty(mocker, default_conf, exchange_name, order, result):
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
assert exchange.check_order_canceled_empty(order) == result
|
||||
|
@ -109,7 +109,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'exchange': 'binance',
|
||||
}
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate',
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_rate',
|
||||
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
|
||||
results = rpc._rpc_trade_status()
|
||||
assert isnan(results[0]['current_profit'])
|
||||
@ -217,7 +217,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
||||
assert '-0.41% (-0.06)' == result[0][3]
|
||||
assert '-0.06' == f'{fiat_profit_sum:.2f}'
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate',
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_rate',
|
||||
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
|
||||
result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
|
||||
assert 'instantly' == result[0][2]
|
||||
@ -427,7 +427,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
||||
assert prec_satoshi(stats['best_rate'], 6.2)
|
||||
|
||||
# Test non-available pair
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate',
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_rate',
|
||||
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
|
||||
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
|
||||
assert stats['trade_count'] == 2
|
||||
|
@ -847,7 +847,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
|
||||
'exchange': 'binance',
|
||||
}
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate',
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_rate',
|
||||
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/status")
|
||||
|
@ -161,7 +161,7 @@ def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None:
|
||||
(True, 0.0022, 3, 0.5, [0.001, 0.001, 0.0]),
|
||||
(True, 0.0027, 3, 0.5, [0.001, 0.001, 0.000673]),
|
||||
(True, 0.0022, 3, 1, [0.001, 0.001, 0.0]),
|
||||
])
|
||||
])
|
||||
def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_buy_order_open,
|
||||
amend_last, wallet, max_open, lsamr, expected) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
@ -784,7 +784,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
|
||||
buy_mm = MagicMock(return_value=limit_buy_order_open)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
get_buy_rate=buy_rate_mock,
|
||||
get_rate=buy_rate_mock,
|
||||
fetch_ticker=MagicMock(return_value={
|
||||
'bid': 0.00001172,
|
||||
'ask': 0.00001173,
|
||||
@ -824,7 +824,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
|
||||
limit_buy_order_open['id'] = '33'
|
||||
fix_price = 0.06
|
||||
assert freqtrade.execute_buy(pair, stake_amount, fix_price)
|
||||
# Make sure get_buy_rate wasn't called again
|
||||
# Make sure get_rate wasn't called again
|
||||
assert buy_rate_mock.call_count == 0
|
||||
|
||||
assert buy_mm.call_count == 2
|
||||
@ -893,7 +893,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
|
||||
assert not freqtrade.execute_buy(pair, stake_amount)
|
||||
|
||||
# Fail to get price...
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_buy_rate', MagicMock(return_value=0.0))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(return_value=0.0))
|
||||
|
||||
with pytest.raises(PricingError, match="Could not determine buy price."):
|
||||
freqtrade.execute_buy(pair, stake_amount)
|
||||
@ -909,7 +909,7 @@ def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) -
|
||||
'last': 0.00001172
|
||||
}),
|
||||
buy=MagicMock(return_value=limit_buy_order),
|
||||
get_buy_rate=MagicMock(return_value=0.11),
|
||||
get_rate=MagicMock(return_value=0.11),
|
||||
get_min_pair_stake_amount=MagicMock(return_value=1),
|
||||
get_fee=fee,
|
||||
)
|
||||
@ -2513,7 +2513,7 @@ def test_handle_cancel_sell_limit(mocker, default_conf, fee) -> None:
|
||||
'freqtrade.exchange.Exchange',
|
||||
cancel_order=cancel_order_mock,
|
||||
)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', return_value=0.245441)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.245441)
|
||||
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
|
||||
@ -3956,7 +3956,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o
|
||||
|
||||
def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None:
|
||||
"""
|
||||
test if function get_buy_rate will return the order book price
|
||||
test if function get_rate will return the order book price
|
||||
instead of the ask rate
|
||||
"""
|
||||
patch_exchange(mocker)
|
||||
@ -3974,7 +3974,7 @@ def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None:
|
||||
default_conf['telegram']['enabled'] = False
|
||||
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
assert freqtrade.exchange.get_buy_rate('ETH/BTC', True) == 0.043935
|
||||
assert freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") == 0.043935
|
||||
assert ticker_mock.call_count == 0
|
||||
|
||||
|
||||
@ -3996,8 +3996,8 @@ def test_order_book_bid_strategy_exception(mocker, default_conf, caplog) -> None
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
# orderbook shall be used even if tickers would be lower.
|
||||
with pytest.raises(PricingError):
|
||||
freqtrade.exchange.get_buy_rate('ETH/BTC', refresh=True)
|
||||
assert log_has_re(r'Buy Price from orderbook could not be determined.', caplog)
|
||||
freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy")
|
||||
assert log_has_re(r'Buy Price at location 1 from orderbook could not be determined.', caplog)
|
||||
|
||||
|
||||
def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None:
|
||||
|
Loading…
Reference in New Issue
Block a user