Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change twap avg to weighted #103

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 97 additions & 5 deletions contracts/auction/price_oracle/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use crate::state::{Config, PriceStep, ASTRO_PRICE_PATHS, CONFIG};
const CONTRACT_NAME: &str = "crates.io:oracle";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

const TEN_DAYS: u64 = 60 * 60 * 24 * 10;

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut,
Expand Down Expand Up @@ -71,7 +73,7 @@ pub fn execute(

let price = if can_update_price_from_auction(&config, &env, &twap_prices) {
source = "auction";
get_avg_price(twap_prices)
get_weighted_avg_price(twap_prices, &env)
} else {
let steps = ASTRO_PRICE_PATHS
.load(deps.storage, pair.clone())
Expand Down Expand Up @@ -248,14 +250,32 @@ fn can_update_price_from_auction(
true
}

fn get_avg_price(vec: VecDeque<Price>) -> Price {
let (total_count, prices_sum) = vec.iter().fold(
/// Calculate the weighted average price of the last 10 days
/// Each day weight is reduced by 1, so yesterdays price would be 9, the day before 8, etc.
/// Giving more value to the most recent prices
fn get_weighted_avg_price(vec: VecDeque<Price>, env: &Env) -> Price {
let (total_weight_count, prices_sum) = vec.iter().fold(
(Decimal::zero(), Decimal::zero()),
|(total_count, prices_sum), price| (total_count + Decimal::one(), prices_sum + price.price),
|(total_weight_count, prices_sum), price| {
// If the price is older than 10 days, we don't consider it
if price.time.seconds() + TEN_DAYS <= env.block.time.seconds() {
return (total_weight_count, prices_sum);
}

let weight = Decimal::from_ratio(
TEN_DAYS - (env.block.time.seconds() - price.time.seconds()),
1_u64,
);

(
total_weight_count + weight,
prices_sum + (price.price * weight),
)
},
);

Price {
price: prices_sum / total_count,
price: prices_sum / total_weight_count,
time: vec[0].time,
}
}
Expand Down Expand Up @@ -339,3 +359,75 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result<Response, Co
MigrateMsg::NoStateChange {} => Ok(Response::default()),
}
}

#[cfg(test)]
mod test {
use std::collections::VecDeque;

use auction_package::Price;
use cosmwasm_std::{testing::mock_env, Decimal};

#[test]
fn test_avg_price() {
let env = mock_env();

let mut prices = VecDeque::new();
// yesterday price was 1
prices.push_back(Price {
price: Decimal::from_ratio(1_u64, 1_u64),
time: env.block.time.minus_days(1),
});
// 2 days ago, price was 0.5
prices.push_back(Price {
price: Decimal::from_ratio(5_u64, 10_u64),
time: env.block.time.minus_days(2),
});
// weighted avg price should be more then 0.75
let avg_price = super::get_weighted_avg_price(prices, &env);
assert!(avg_price.price > Decimal::from_ratio(75_u64, 100_u64));

// Save as above, but the 2nd price is way older
prices = VecDeque::new();
// yesterday price was 1
prices.push_back(Price {
price: Decimal::from_ratio(1_u64, 1_u64),
time: env.block.time.minus_days(1),
});
// 9 days ago, price was 0.5
prices.push_back(Price {
price: Decimal::from_ratio(5_u64, 10_u64),
time: env.block.time.minus_days(9),
});
// weighted avg price should be more then 0.9,
let avg_price = super::get_weighted_avg_price(prices, &env);
assert!(avg_price.price > Decimal::from_ratio(9_u64, 10_u64));

// Save as above, but the 2nd price is way older
prices = VecDeque::new();
// yesterday price was 1
prices.push_back(Price {
price: Decimal::from_ratio(1_u64, 1_u64),
time: env.block.time.minus_days(1),
});
// 5 days ago, price was 0.5
prices.push_back(Price {
price: Decimal::from_ratio(5_u64, 10_u64),
time: env.block.time.minus_days(5),
});

// The weighted avg so far would be above 0.75 (which is the normal avg)
let avg_price = super::get_weighted_avg_price(prices.clone(), &env);
assert!(avg_price.price > Decimal::from_ratio(75_u64, 100_u64));

// 9 days ago, price was 2
prices.push_back(Price {
price: Decimal::from_ratio(2_u64, 1_u64),
time: env.block.time.minus_days(9),
});

// regular avg price should be 1.166
// but the weighted avg would be 0.9
let avg_price = super::get_weighted_avg_price(prices, &env);
assert!(avg_price.price >= Decimal::from_ratio(9_u64, 10_u64));
}
}
4 changes: 3 additions & 1 deletion tests/rust-tests/src/live_debugging/test_specific_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ use crate::{
use super::types::Balances;

const ACCOUNT_ADDR: &str = "neutron1yd27kgnsjkeddwtuy29q9yzrnd247v6hcml6swl54dfkcwuuytqs8ha94x";
const HEIGHT: &str = "";
const HEIGHT: &str = "a";

#[ignore = "For debugging mainnet data"]
#[test]
fn live_debugging() {
// If we have specifig height, query by that height
let mut height_arg = vec![];
let mut block_height = vec![];

#[allow(clippy::const_is_empty)]
if !HEIGHT.is_empty() {
height_arg = vec!["--height", HEIGHT];
block_height = vec![HEIGHT];
Expand Down
6 changes: 3 additions & 3 deletions tests/rust-tests/src/tests_rebalancer/test_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ fn test_update_whitelist() {
let whitelist = suite.query_rebalancer_whitelists().unwrap();

// lets make sure the whitelist is what we expect for the tests
assert!(whitelist.denom_whitelist.contains(&ATOM.to_string()));
assert!(whitelist.denom_whitelist.contains(ATOM));
assert!(whitelist.denom_whitelist.len() == 3);
assert!(whitelist
.base_denom_whitelist
Expand All @@ -282,8 +282,8 @@ fn test_update_whitelist() {
.unwrap();

let whitelist = suite.query_rebalancer_whitelists().unwrap();
assert!(!whitelist.denom_whitelist.contains(&ATOM.to_string()));
assert!(!whitelist.denom_whitelist.contains(&NTRN.to_string()));
assert!(!whitelist.denom_whitelist.contains(ATOM));
assert!(!whitelist.denom_whitelist.contains(NTRN));
assert!(whitelist.denom_whitelist.len() == 2);

// remove atom, add random
Expand Down
34 changes: 32 additions & 2 deletions tests/rust-tests/src/tests_rebalancer/test_rebalancing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use std::{collections::HashSet, str::FromStr};
use auction_package::Pair;
use cosmwasm_std::{Decimal, Event, Uint128};

use valence_package::services::rebalancer::PID;
use valence_package::services::rebalancer::{Target, PID};

use crate::suite::{
suite::{Suite, ATOM, NTRN},
suite::{Suite, ATOM, NTRN, OSMO},
suite_builder::SuiteBuilder,
};

Expand Down Expand Up @@ -138,3 +138,33 @@ fn test_targets_saved_after_rebalance() {
.unwrap();
assert!(config.targets[0].last_input.is_some());
}

#[test]
fn test_base_denom_not_in_target_list() {
let mut config = SuiteBuilder::get_default_rebalancer_register_data();
let mut new_targets = HashSet::with_capacity(2);

new_targets.insert(Target {
denom: ATOM.to_string(),
bps: 7500,
min_balance: None,
});

new_targets.insert(Target {
denom: OSMO.to_string(),
bps: 2500,
min_balance: None,
});

config.base_denom = NTRN.to_string();

config.targets = new_targets;

// Register with the config where the base denom is whitelisted but not in target list
let mut suite = SuiteBuilder::default()
.with_rebalancer_data(vec![config])
.build_default();

// Do a rebalance just to make sure that it doesn't panic
suite.rebalance(None).unwrap();
}
Loading