Exchanges
Every trade, order, asset instance, and strategy is parameterized against an ExchangeID
, which is a type constructed from the name (Symbol
) of an exchange. Currently, the bot supports CCXT with exchanges subtypes of CcxtExchange
.
There is only one exchange instance (one sandbox and one non-sandbox) constructed per exchange, so calling Planar.Engine.Exchanges.getexchange!
will always return the same object for each exchange (w.r.t. sandbox
and account
options). The sandbox instance is generally a test-net with synthetic markets. The account name indicates which api keys to use.
We try to parse as much info from the (CCXT) exchange such that we can fill attributes such as:
- Markets
- Timeframes
- Asset trading fees, limits, precision
- Funding rates
The support for exchanges is a best-effort basis. To overview if the exchange is likely compatible with the bot, call check
:
using Planar
@environment!
e = getexchange!(:bybit)
exs.check(e, type=:basic) # for backtesting and paper trading
exs.check(e, type=:live) # for live support
The bot tries to use the WebSocket API if available, otherwise, it falls back to the basic REST API. The API keys are read from a file in the user/
directory named after the exchange name like user/bybit.json
for the Bybit exchange or user/bybit_sandbox.json
for the respective sandbox API keys. The JSON file has to contain the fields apiKey
, secret
, and password
.
The strategy quote currency and each asset currency is a subtype of Planar.Engine.Exchanges.CurrencyCash
, which is a Number
where operations respect the precision defined by the exchange.
Some commonly fetched information is cached with a TTL, like tickers, markets, and balances.
Exchange Types
Basic exchange types, and global exchange vars.
ExchangeTypes.HOOKS
— ConstantFunctions f(::Exchange)
to call when an exchange is loaded
ExchangeTypes.exchangeIds
— ConstantAll possible exchanges that can be instantiated by ccxt.
ExchangeTypes.exchanges
— ConstantGlobal var holding Exchange instances. Used as a cache.
ExchangeTypes.sb_exchanges
— ConstantGlobal var holding Sandbox Exchange instances. Used as a cache.
ExchangeTypes.CcxtExchange
— TypeThe CcxtExchange
type wraps a ccxt exchange instance. Some attributes frequently accessed are copied over to avoid round tripping python. More attributes might be added in the future. To instantiate an exchange call getexchange!
or setexchange!
.
ExchangeTypes.ExcPrecisionMode
— TypeSame as ccxt precision mode enums.
ExchangeTypes.Exchange
— TypeInstantiates a new Exchange
wrapper for the provided x
Python object.
This constructs a CcxtExchange
struct with the provided Python object. It extracts the exchange ID, name, and other metadata. It runs any registered hook functions for that exchange. It sets a finalizer to close the exchange when garbage collected.
Returns the new Exchange
instance, or an empty one if x
is None.
ExchangeTypes.Exchange
— TypeAbstract exchange type.
Defines the interface for interacting with crypto exchanges. Implemented for CCXT in CcxtExchange.
ExchangeTypes.ExchangeID
— TypeA structure for handling Exchange IDs in CCXT.
This structure is used to manage Exchange IDs in the CCXT library. It contains methods for creating an Exchange ID from a symbol, a Python object, or directly from a symbol type. It ensures that the symbol is in the list of valid exchange IDs.
Base.first
— MethodReturn the first available property from a variable number of Symbol arguments in the given Exchange.
first(exc::Exchange, args::Symbol...) -> Any
This function iterates through the provided Symbols and returns the value of the first property that exists in the Exchange object.
Base.getproperty
— MethodAttributes not matching the Exchange
struct fields are forwarded to the wrapped ccxt class instance.
Base.hash
— MethodThe hash of an exchange object is reduced to its symbol (the function used to instantiate the object from ccxt).
ExchangeTypes._has
— MethodChecks if the specified feature feat
is supported by any of the exchanges available through the ccxt library.
Arguments
s::Symbol
: The feature to check for support across exchanges.full::Bool=true
: Iftrue
, checks both static and instantiated properties of the exchange for support.
Returns
Vector{String}
: A list of exchange names that support the specified feature.
ExchangeTypes.close_exc
— MethodCloses the given exchange.
close_exc(
exc::ExchangeTypes.CcxtExchange
) -> Union{Nothing, Task}
This function attempts to close the given exchange if it exists. It checks if the exchange has a 'close' attribute and if so, it schedules the 'close' coroutine for execution.
ExchangeTypes.decimal_to_size
— MethodConverts value v to integer size with precision p.
decimal_to_size(v, p::ExcPrecisionMode; exc) -> Any
Used when converting exchange API responses to integer sizes for orders.
ExchangeTypes.eids
— MethodUnion type of many exchange ids (from Symbol
arguments)
ExchangeTypes.exchangeid
— MethodReturn the given ExchangeID instance.
ExchangeTypes.exchangeid
— MethodCreate an ExchangeID instance from a symbol.
Construct and query exchanges
Helper module for downloading data off exchanges.
Exchanges.LEVERAGED_PAIR_OPTIONS
— ConstantA leveraged pair is a pair like BTC3L/USD
.
:yes
: Leveraged pairs will not be filtered.:only
: ONLY leveraged will be kept.:from
: Selects non leveraged pairs, that also have at least one leveraged sibling.
Exchanges.activeCache1Min
— ConstantCaches active states (1minute).
Exchanges.marketsCache1Min
— ConstantCaches markets (1minute).
Exchanges.tickersCache10Sec
— ConstantCaches tickers (10seconds).
Exchanges.tickersLockDict
— ConstantLock held when fetching tickers (per ticker).
Exchanges.has_leverage
— MethodTrue if pair
is a leveraged pair.
Exchanges.hasvolume
— MethodTrue if symbol sym
has a quote volume less than min_vol
.
Exchanges.is_pair_active
— MethodCheck if a currency pair is active on an exchange.
is_pair_active(pair::AbstractString, exc::Exchange) -> Any
Exchanges.ismargin
— MethodTrue if mkt
is a leveraged market.
Exchanges.isquote
— MethodTrue if id
is a quote id.
Exchanges.lastprice
— MethodFetch the latest price for a specific pair from an exchange.
lastprice(
pair::AbstractString,
exc::Exchange;
kwargs...
) -> Any
pair
: a string representing the currency pair to fetch the latest price for.exc
: an Exchange object to fetch the latest price from.kwargs
(optional): any additional keyword arguments are passed on to the underlying fetch operation.
Exchanges.leverage_func
— FunctionConstructor that returns a function that checks if a pair is leveraged.
Exchanges.market!
— MethodRetrieves a cached market (1minute) or fetches it from exchange.
market!(pair, exc::Exchange) -> Any
Exchanges.market_fees
— MethodFetch the market fees for a specific pair from an exchange.
market_fees(
pair::AbstractString,
exc::Exchange;
only_taker
) -> NamedTuple{(:taker, :maker, :min, :max), <:NTuple{4, Any}}
pair
: a string representing the currency pair to fetch the market fees for.exc
(optional, default is the current exchange): an Exchange object to fetch the market fees from.only_taker
(optional, default isnothing
): a boolean indicating whether to fetch only the taker fee. Ifnothing
, both maker and taker fees are fetched.
Exchanges.market_limits
— MethodFetch the market limits for a specific pair from an exchange.
market_limits(
pair::AbstractString,
exc::Exchange;
precision,
default_leverage,
default_amount,
default_price,
default_cost
) -> NamedTuple
pair
: a string representing the currency pair to fetch the market limits for.exc
: an Exchange object to fetch the market limits from.precision
(optional, default isprice=nothing, amount=nothing
): a named tuple specifying the precision for price and amount.default_leverage
(optional, default isDEFAULT_LEVERAGE
): the default leverage to use if not specified in the market data.default_amount
(optional, default isDEFAULT_AMOUNT
): the default amount to use if not specified in the market data.default_price
(optional, default isDEFAULT_PRICE
): the default price to use if not specified in the market data.default_cost
(optional, default isDEFAULT_COST
for non-fiat quote pairs andDEFAULT_FIAT_COST
for fiat quote pairs): the default cost to use if not specified in the market data.
Exchanges.market_precision
— MethodPrecision of the (base, quote) currencies of the market.
market_precision(
pair::AbstractString,
exc::Exchange
) -> NamedTuple{(:amount, :price), <:Tuple{Any, Any}}
Exchanges.marketsid
— MethodGet the exchange market ids.
Exchanges.quoteid
— MethodQuote id of the market.
Exchanges.ticker!
— MethodFetch the ticker for a specific pair from an exchange.
ticker!(pair, exc::Exchange; timeout, func, delay) -> Any
The ticker!
function takes the following parameters:
pair
: a string representing the currency pair to fetch the ticker for.exc
: an Exchange object to fetch the ticker from.timeout
(optional, default is 3 seconds): the maximum time to wait for the ticker fetch operation.func
(optional, default is the result of_tickerfunc(exc)
): the function to use to fetch the ticker.
Exchanges.tickers
— MethodGet the exchange tickers.
tickers(
exc::Exchange,
quot;
min_vol,
skip_fiat,
with_margin,
with_leverage,
as_vec,
verbose,
type,
cross_match
)
exc
: an Exchange object to fetch the tickers from.quot
: only choose pairs where the quote currency equalsquot
.min_vol
: the minimum volume of each pair.skip_fiat
(optional, default is true): ignore fiat/fiat pairs.with_margin
(optional, default is the result ofconfig.margin != NoMargin()
): only choose pairs enabled for margin trading.with_leverage
(optional, default is:no
): if:no
, skip all pairs where the base currency matches theleverage_pair_rgx
regex.as_vec
(optional, default is false): return the pair list as a Vector instead of as a Dict.verbose
(optional, default is true): print detailed output about the operation.type
(optional, default is the result ofmarkettype(exc)
): the type of markets to fetch tickers for.cross_match
list of other exchanges where the filter pairs must also be present in
Exchanges.tickers
— MethodGet the tickers matching quote currency quot
.
Fetching data from exchanges
Helper module for downloading data off exchanges.
Fetch.FUNDING_RATES_CACHE
— ConstantInitializes a safe TTL cache for storing multiple funding rates with a specified TTL.
Fetch.FUNDING_RATE_CACHE
— ConstantInitializes a safe TTL cache for storing funding rates with a specified TTL.
Fetch.FUNDING_RATE_TTL
— ConstantDefines the time-to-live (TTL) for a funding rate as 5 seconds.
Fetch.MAX_ORDERS
— ConstantDefines an array representing possible numbers of orders to fetch.
Fetch.OB_CACHE
— ConstantInitializes a safe TTL cache for storing order book data with the default eviction TTL.
Fetch.OB_EVICTION_TTL
— ConstantDefines the eviction time-to-live (TTL) for an order book as 5 minutes.
Fetch.OB_FUNCTIONS
— ConstantInitializes a LittleDict for storing order book functions keyed by order book level and exchange ID.
Fetch.OB_TTL
— ConstantDefines the time-to-live (TTL) for an order book as 5 seconds (after which it is stale).
Fetch.OHLCVTupleTypes
— ConstantDefines the tuple type for OHLCV data, where each element represents a specific metric (Open, High, Low, Close, Volume).
Fetch.SINCE_MIN_PERIOD
— ConstantUsed to slide the since
param forward when retrying fetching (in case the requested timestamp is too old).
Fetch.futures_limits
— ConstantDefines limit values for fetching futures data from exchanges.
Fetch.OrderBookTuple
— TypeDefines a NamedTuple structure for order book data.
Base.convert
— MethodThis is the fastest (afaik) way to convert ccxt lists to dataframe friendly format.
convert(
_::Type{Tuple{Vector{Dates.DateTime}, Vararg{Vector{Float64}, 5}}},
py::Py
) -> Tuple{Vector{Dates.DateTime}, Vararg{Vector{Float64}}}
This function converts the provided Python object to a tuple format suitable for dataframes, specifically tailored for OHLCV data.
Data.propagate_ohlcv!
— MethodPropagates OHLCV data to all timeframes in a data structure.
propagate_ohlcv!(
data::SortedDict,
pair::AbstractString,
exc::Exchange
) -> Union{Nothing, SortedDict{TimeFrames.TimeFrame, DataFrames.DataFrame}}
The propagate_ohlcv!
function propagates OHLCV data for a given pair
from an exchange exc
to all timeframes in the data
SortedDict data structure.
Data.to_ohlcv
— MethodConverts a Python object to a DataFrame with OHLCV columns
to_ohlcv(py::Py)
Fetch.__ensure_dates
— MethodEnsures dates are within valid range for the exchange and timeframe.
__ensure_dates(exc, tf, from, to) -> Tuple{Any, Any}
The __ensure_dates
function checks if the dates from
and to
are within the valid range for the given exchange exc
and timeframe tf
. If the dates are not within the valid range, the function adjusts them accordingly.
Fetch.__from_date_func
— MethodDetermines the starting date for fetching data.
__from_date_func(
update,
timeframe,
from,
to,
zi,
exc_name,
reset
) -> Union{Returns{<:Tuple{Nothing, Any}}, Fetch.var"#19#20"}
The __from_date_func
function determines the starting date from
for fetching data based on various parameters. If update
is true, it will fetch data from the latest date available. If reset
is true, it will fetch data from the earliest date possible. The function also considers the timeframe
, to
date, timezone zi
, and exchange name exc_name
in its calculations.
Fetch.__get_ohlcv
— MethodFetches OHLCV data for a specified exchange within a date range.
__get_ohlcv(
exc,
name,
timeframe,
from_date,
to;
out,
cleanup,
ohlcv_kind
) -> Tuple{Any, Any}
This function fetches OHLCV data for a given exchange exc
, with the specified name
and timeframe
, within the date range specified by from_date
and to
. The fetched data is appended to the out
data structure. The cleanup
parameter determines if any post-processing should be done on the data before returning. The ohlcv_kind
parameter determines the type of OHLCV data to fetch.
Fetch.__get_since
— MethodDetermines the 'since' parameter for fetching data from an exchange.
__get_since(
exc,
fetch_func,
pair,
limit,
from,
out,
is_df,
converter
) -> Any
This function calculates the 'since' parameter based on the specified 'from' timestamp, or finds the appropriate 'since' value if 'from' is 0.0.
Fetch.__handle_error
— MethodHandles errors during fetch operations.
__handle_error(
e,
fetch_func,
pair,
since,
df,
sleep_t,
limit,
converter,
retry
) -> Any
This function takes an error e
occurred during data fetching, and decides whether to retry the fetch_func
based on the retry
flag. If retry
is true, it calls the fetch_func
again with the same parameters.
Fetch.__handle_fetch
— MethodHandles fetch operations for specified exchange and pair.
__handle_fetch(
fetch_func,
pair,
since,
limit,
sleep_t,
df,
converter,
retry,
usetimeframe
) -> Tuple{Bool, Any}
This function calls the fetch_func
for a given pair
, starting from the since
timestamp with a maximum limit of limit
data points. It employs a delay sleep_t
between fetches. The function also applies a given converter
to the fetched data. If the retry
flag is true, the function will try to fetch data again in case of an empty response. The usetimeframe
flag indicates whether to use timeframe for fetching.
Fetch.__ordered_timeframes
— MethodReturns an ordered list of timeframes for a given exchange
__ordered_timeframes(exc::Exchange) -> Tuple{Any, Any}
This function collects the timeframes from the exchange, converts them into periods, and sorts them in descending order. It then returns these sorted timeframes and periods.
Fetch.__pairdata!
— MethodProcesses OHLCV data for a pair.
__pairdata!(
zi,
data,
ohlcv,
name,
timeframe,
z,
exc_name,
reset
) -> Data.PairData
The __pairdata!
function processes the OHLCV data ohlcv
for a pair name
over a timeframe
. It takes into account the timezone zi
, data data
, timezone offset z
, exchange name exc_name
, and a reset
flag. If reset
is true, it will reset the data for the pair before processing.
Fetch._cleanup_funding_history
— MethodCleans up fetched funding history data.
_cleanup_funding_history(
df,
name,
half_tf,
f_tf
) -> Union{DataFrames.DataFrame, DataFrames.GroupedDataFrame, DataFrames.SubDataFrame}
The _cleanup_funding_history
function takes a DataFrame df
of fetched funding history data for a name
and performs cleanup operations on it. The half_tf
and f_tf
parameters are used in the cleanup process.
Fetch._fetch_loop
— MethodIteratively fetches data over a specified date range.
_fetch_loop(
fetch_func::Function,
exc::Exchange,
pair;
from,
to,
sleep_t,
out,
converter,
limit
)
This function calls the fetch_func
function repeatedly until it has fetched data for the entire date range specified by from
and to
. Note: The total data points fetched may not match the expected number based on the date range.
Fetch._fetch_ohlcv_from_to
— MethodEnsure a to
date is set, before fetching.
_fetch_ohlcv_from_to(
exc::Exchange,
pair,
timeframe;
from,
to,
params,
sleep_t,
cleanup,
out,
ohlcv_kind
) -> Any
This function verifies that a 'to' date is set before attempting to fetch OHLCV data.
Fetch._fetch_ohlcv_with_delay
— MethodFetches OHLCV data with delay for a given exchange and arguments.
_fetch_ohlcv_with_delay(
exc::Exchange,
args...;
ohlcv_kind,
kwargs...
) -> Any
This function fetches OHLCV data for a specified exchange exc
and additional args
. The type of OHLCV data to fetch is determined by ohlcv_kind
. It applies a delay between fetches as specified in kwargs
.
Fetch._fetch_with_delay
— MethodWraps fetching function with error handling and backoff delay.
_fetch_with_delay(
fetch_func::Function,
pair;
since,
df,
sleep_t,
limit,
converter,
retry,
usetimeframe
) -> Any
This function wraps a fetching function fetch_func
with error handling and a backoff delay sleep_t
. The fetch_func
takes three parameters: pair
, since
, and limit
, and returns a PyList. The converter
function is used to tabulate the data such that the first column is the timestamp. The function will retry fetching in case of an error if retry
is set to true.
Fetch._levelname
— MethodReturns the name of an order book level.
_levelname(level) -> String
The _levelname
function takes an order book level
and returns its name.
Fetch._orderbook
— MethodGenerates an order book of depth N
.
_orderbook(
N
) -> @NamedTuple{busy::Ref{Bool}, timestamp::Ref{Dates.DateTime}, asks::Vector{Tuple{Float64, Float64}}, bids::Vector{Tuple{Float64, Float64}}}
The _orderbook
function generates an order book of depth N
. The order book contains N
levels of bid and ask prices along with their corresponding quantities.
Fetch._since_timestamp
— MethodDetermines the start time for fetching data
_since_timestamp(
actual::Dates.DateTime,
p::Dates.Period
) -> Int64
This function calculates the timestamp from which to start fetching data. It ensures that the start time is not more than 20 years in the past or less than the given period.
Fetch._update_orderbook!
— MethodUpdates an order book in place with new data.
_update_orderbook!(exc, ob, sym, lvl, limit; init)
The _update_orderbook!
function takes an exchange exc
, an order book ob
, a symbol sym
, an order book level lvl
, and a limit limit
, and updates the order book in place with new data. If init
is set, the function will initialize the order book before updating it.
Fetch.extract_futures_data
— MethodExtracts futures data from a Python object.
extract_futures_data(data::Py) -> DataFrames.DataFrame
The extract_futures_data
function takes futures data data
from a Python object and extracts it into a format suitable for further processing or analysis.
Fetch.fetch_candles
— MethodFetches candlestick data for a list of pairs from an exchange.
fetch_candles(
exc::Exchange,
timeframe::AbstractString,
pairs::Union{AbstractString, AbstractSet, AbstractVector, Tuple{Vararg{T}} where T};
from,
to,
ohlcv_kind
) -> Any
The fetch_candles
function fetches candlestick data from a given exchange exc
for a list of pairs
over a specified timeframe
. The from
and to
parameters define the date range for the fetched data. If from
is not provided, it defaults to an empty string, which implies fetching data from the earliest available date. The type of candlestick data to fetch is determined by the ohlcv_kind
parameter.
Fetch.fetch_limit
— MethodDefines the fetch limit for an exchange.
fetch_limit(
exc::Exchange,
limit::Union{Nothing, Int64}
) -> Union{Nothing, Int64}
This function fetches the limit for an exchange. If no limit is specified, it retrieves the default limit for the exchange.
Fetch.fetch_ohlcv
— MethodFetches OHLCV data from an exchange for a list of pairs.
fetch_ohlcv(
exc::Exchange,
timeframe::String,
pairs::Union{AbstractSet{T}, AbstractVector{T}, Tuple{Vararg{T}}} where T;
zi,
from,
to,
update,
reset,
progress,
ohlcv_kind
) -> Dict{String, Data.PairData}
This function fetches OHLCV data from a given exchange exc
for a list of pairs
over a specified timeframe
. The from
and to
parameters can represent dates or, if from
is a negative number, the function fetches the last N=from
candles. If update
is true, the function checks for cached data and only fetches missing candles. If reset
is true, the function removes cached data before fetching. The progress
parameter determines whether a progress bar is shown. The type of OHLCV data to fetch is defined by the ohlcv_kind
parameter.
Fetch.fetch_ohlcv
— MethodPrompts user for confirmation before fetching OHLCV data.
fetch_ohlcv(::Val{:ask}, args...; kwargs...) -> Any
This function prompts the user for confirmation before fetching OHLCV data for the specified arguments args
and keyword arguments kwargs
. If the user inputs 'Y', 'y', or simply presses Enter, it proceeds with the fetch_ohlcv
function. If any other input is given, the function returns nothing
.
Fetch.fetch_ohlcv
— MethodFetches OHLCV data for multiple exchanges on the same timeframe.
fetch_ohlcv(
excs::Vector{Exchange},
timeframe;
account,
sandbox,
parallel,
wait_task,
kwargs...
) -> Union{Nothing, Task}
This function fetches OHLCV data for multiple exchanges over the same timeframe. It accepts:
- A vector of exchange instances
excs
. - The desired timeframe
timeframe
.
The function can run in parallel if parallel
is set to true. If wait_task
is set to true, the function will wait for all tasks to complete before returning.
You can provide additional parameters using kwargs
.
Fetch.find_since
— MethodReturns the oldest possible timestamp for a pair.
find_since(exc::Exchange, pair) -> Any
This function iterates over the timeframes and periods of the exchange to find the oldest available timestamp for a given pair. If no data is found in any timeframe, it defaults to 1 day in the past.
Fetch.funding_data
— MethodRetrieves all or a subset of funding data for a symbol from an exchange.
funding_data(exc::Exchange, sym::AbstractString) -> Any
The funding_data
function retrieves all funding data returned by an exchange exc
for a symbol sym
.
Fetch.funding_history
— MethodFetches funding rate history from an exchange for a list of Derivative
pairs.
funding_history(
exc::Exchange,
assets::Vector;
from,
to,
params,
sleep_t,
limit,
cleanup
) -> Dict
The funding_history
function fetches funding rate history from a given exchange exc
for a list of assets
. The from
and to
parameters define the date range for which to fetch the funding rate history. Additional parameters can be specified through the params
dictionary. The function will wait for sleep_t
seconds between each request to the exchange. The limit
parameter can be used to limit the amount of data fetched. If cleanup
is set to true, the function will perform a cleanup on the fetched data before returning it.
Fetch.funding_rate
— MethodRetrieves the funding rate for a symbol from an exchange.
funding_rate(exc::Exchange, s::AbstractString) -> Any
The funding_rate
function retrieves the funding rate for a symbol s
from an exchange exc
.
Fetch.ohlcv_func_bykind
— MethodReturns the appropriate OHLCV fetching function based on the specified kind.
ohlcv_func_bykind(exc, kind) -> Any
The ohlcv_func_bykind
function determines and returns the appropriate OHLCV fetching function for the given exchange exc
and kind
.
Fetch.orderbook
— MethodFetches an order book from an exchange for a symbol.
orderbook(exc, sym; limit, level) -> Any
The orderbook
function fetches an order book from an exchange exc
for a symbol sym
. The limit
parameter can be used to limit the depth of the order book. The level
parameter specifies the level of the order book to fetch.
Fetch.parse_funding_row
— MethodParses a row of funding data from a Python object.
parse_funding_row(r::Py) -> Tuple{Int64, String, Float64}
The parse_funding_row
function takes a row of funding data r
from a Python object and parses it into a format suitable for further processing or analysis.
Fetch.update_ohlcv!
— MethodUpdates the tail of an OHLCV DataFrame with the most recent candles.
update_ohlcv!(
df::DataFrames.DataFrame,
pair,
exc,
tf;
ohlcv_kind,
from
) -> DataFrames.DataFrame
The update_ohlcv!
function updates the tail of an OHLCV DataFrame df
with the most recent candles for a given pair
from an exchange exc
over a timeframe tf
. The type of OHLCV data to update is determined by the ohlcv_kind
parameter.
[1]: It is possible that in the future the bot will work with the hummingbot gateway for DEX support, and at least another exchange type natively implemented (from psydyllic).