use injective_cosmwasm::{InjectiveQuerier, InjectiveQueryWrapper, MarketId, OrderSide, PriceLevel, SpotMarket};
use crate::exact_math::{multiply_to_raw, ratio_to_tick, select_liquidity_levels, weighted_average_to_tick, LiquidityTarget, RoundingDirection};
SwapQuantity::OutputQuantity(_) => SwapEstimationAmount::ReceiveQuantity(current_swap.clone()),
let market = querier.query_spot_market(market_id)?.market.expect("market should be available");
let has_invalid_denom = balance_in.denom != market.quote_denom && balance_in.denom != market.base_denom;
let fee_multiplier = querier.query_market_atomic_execution_fee_multiplier(market_id)?.multiplier;
let fee_percent = market.taker_fee_rate * fee_multiplier * (FPDecimal::ONE - get_effective_fee_discount_rate(&market, is_self_relayer));
let is_estimating_from_target = matches!(swap_estimation_amount, SwapEstimationAmount::ReceiveQuantity(_));
let available_swap_quote_funds = ratio_to_tick(input_quote_quantity, fee_factor, raw_tick, RoundingDirection::Down)?;
let orders = querier.query_spot_market_orderbook(&market.market_id, OrderSide::Sell, None, Some(available_swap_quote_funds))?;
// lets overestimate amount for buys means rounding average price up -> higher buy price -> worse
let average_price = weighted_average_to_tick(&top_orders, market.min_price_tick_size, RoundingDirection::Up)?;
let worst_price_notional = multiply_to_raw(worst_price, result_quantity, RoundingDirection::Up)?;
let required_funds = multiply_to_raw(worst_price_notional, fee_factor, RoundingDirection::Up)?;
false => funds_in_contract, // in execution mode funds_in_contract already contain user funds so we don't want to count them double
let rounded_target_base_output_quantity = round_up_to_min_tick(target_base_output_quantity, market.min_quantity_tick_size);
let orders = querier.query_spot_market_orderbook(&market.market_id, OrderSide::Sell, Some(rounded_target_base_output_quantity), None)?;
// lets overestimate amount for buys means rounding average price up -> higher buy price -> worse
let average_price = weighted_average_to_tick(&top_orders, market.min_price_tick_size, RoundingDirection::Up)?;
let expected_exchange_quote_quantity = multiply_to_raw(rounded_target_base_output_quantity, average_price, RoundingDirection::Up)?;
let required_input_quote_quantity = multiply_to_raw(expected_exchange_quote_quantity, FPDecimal::ONE + fee_percent, RoundingDirection::Up)?;
let worst_price_notional = multiply_to_raw(worst_price, rounded_target_base_output_quantity, RoundingDirection::Up)?;
let required_funds = multiply_to_raw(worst_price_notional, FPDecimal::ONE + fee_percent, RoundingDirection::Up)?;
false => funds_in_contract, // in execution mode funds_in_contract already contain user funds so we don't want to count them double
let is_estimating_from_target = matches!(swap_estimation_amount, SwapEstimationAmount::ReceiveQuantity(_));
estimate_execution_buy_from_target(deps, querier, contract_address, market, amount_coin.amount, fee_percent, is_simulation)
estimate_execution_buy_from_source(deps, querier, contract_address, market, amount_coin.amount, fee_percent, is_simulation)
let orders = querier.query_spot_market_orderbook(&market.market_id, OrderSide::Buy, Some(input_base_quantity), None)?;
// lets overestimate amount for sells means rounding average price down -> lower sell price -> worse
let average_price = weighted_average_to_tick(&top_orders, market.min_price_tick_size, RoundingDirection::Down)?;
let expected_exchange_quantity = multiply_to_raw(input_base_quantity, average_price, RoundingDirection::Down)?;
let expected_quantity = multiply_to_raw(expected_exchange_quantity, FPDecimal::ONE - fee_percent, RoundingDirection::Down)?;
let orders = querier.query_spot_market_orderbook(&market.market_id, OrderSide::Buy, None, Some(required_swap_quantity_in_quote))?;
// lets overestimate amount for sells means rounding average price down -> lower sell price -> worse
let average_price = weighted_average_to_tick(&top_orders, market.min_price_tick_size, RoundingDirection::Down)?;
let is_estimating_from_target = matches!(swap_estimation_amount, SwapEstimationAmount::ReceiveQuantity(_));
fn get_effective_fee_discount_rate(market: &SpotMarket, is_self_relayer: bool) -> FPDecimal {
let levels = vec![create_price_level(1, 200), create_price_level(2, 200), create_price_level(3, 200)];
let avg = weighted_average_to_tick(&levels, FPDecimal::must_from_str("0.01"), RoundingDirection::Down).unwrap();
let levels = vec![create_price_level(1, 300), create_price_level(2, 200), create_price_level(3, 100)];
let avg = weighted_average_to_tick(&levels, FPDecimal::must_from_str("0.01"), RoundingDirection::Down).unwrap();
let levels = vec![create_price_level(1, 300), create_price_level(2, 200), create_price_level(3, 100)];
let avg = weighted_average_to_tick(&levels, FPDecimal::must_from_str("0.01"), RoundingDirection::Up).unwrap();
let levels = vec![create_price_level(1, 100), create_price_level(2, 200), create_price_level(3, 300)];
assert_eq!(result.unwrap_err().as_ref().to_string(), "Not enough liquidity to fulfill order");
let levels = vec![create_price_level(1, 100), create_price_level(3, 300), create_price_level(5, 500)];
let levels = vec![create_price_level(1, 100), create_price_level(3, 300), create_price_level(5, 500)];
let buy_levels = vec![create_price_level(5, 500), create_price_level(3, 300), create_price_level(1, 100)];