Skip to content

Commit

Permalink
closed loop control
Browse files Browse the repository at this point in the history
  • Loading branch information
bertiqwerty committed Aug 10, 2023
1 parent 0cb081e commit 2acc224
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 8 deletions.
16 changes: 12 additions & 4 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ fn trigger_dl(url: &str, rx: Sender<ehttp::Result<ehttp::Response>>, ctx: Contex
struct SimInput {
vola: Vola,
expected_yearly_return: String,
is_eyr_independent: bool,
start_month_slider: MonthSlider,
n_months: String,
}
Expand All @@ -72,6 +73,7 @@ impl SimInput {
SimInput {
vola: Vola::Mi,
expected_yearly_return: "7.0".to_string(),
is_eyr_independent: false,
n_months: "360".to_string(),
start_month_slider: MonthSlider::new(
Date::new(1950, 1).unwrap(),
Expand All @@ -80,10 +82,11 @@ impl SimInput {
),
}
}
fn parse(&self) -> BlcResult<(f64, f64, Date, usize)> {
fn parse(&self) -> BlcResult<(f64, f64, bool, Date, usize)> {
Ok((
self.vola.to_float(),
self.expected_yearly_return.parse().map_err(to_blc)?,
self.is_eyr_independent,
self.start_month_slider
.selected_date()
.ok_or_else(|| blcerr!("no date selected"))?,
Expand Down Expand Up @@ -281,6 +284,9 @@ impl<'a> eframe::App for BalanceApp<'a> {
ui.label("expected yearly return [%]");
ui.text_edit_singleline(&mut self.sim.expected_yearly_return);
ui.end_row();
ui.label("Return independent of previous returns?");
ui.checkbox(&mut self.sim.is_eyr_independent, "");
ui.end_row();
ui.label("#months");
ui.text_edit_singleline(&mut self.sim.n_months);
ui.end_row();
Expand All @@ -298,9 +304,9 @@ impl<'a> eframe::App for BalanceApp<'a> {
self.rebalance_stats = None;
match self.sim.parse() {
Ok(data) => {
let (noise, expected_yearly_return, start_date, n_months) =
let (noise, expected_yearly_return, is_eyr_independent, start_date, n_months) =
data;
match random_walk(expected_yearly_return, noise, n_months) {
match random_walk(expected_yearly_return, is_eyr_independent, noise, n_months) {
Ok(values) => {
let tmp = Chart::new(
format!(
Expand Down Expand Up @@ -655,8 +661,10 @@ impl<'a> eframe::App for BalanceApp<'a> {
}
ui.separator();
ui.horizontal(|ui| {
ui.label("code on");
ui.label("Code on");
ui.hyperlink_to("Github", "https://github.com/bertiqwerty/balance");
ui.label("-");
ui.hyperlink_to("Impressum", "https://bertiqwerty.com/impressum");
});
egui::warn_if_debug_build(ui);
});
Expand Down
16 changes: 12 additions & 4 deletions src/compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ const SIGMA_WINDOW_SIZE: usize = 12;

pub fn random_walk(
expected_yearly_return: f64,
is_return_indpendent: bool,
sigma_mean: f64,
n_months: usize,
) -> BlcResult<Vec<f64>> {
Expand All @@ -198,18 +199,25 @@ pub fn random_walk(
let mut rv_rng = StdRng::seed_from_u64(unix_to_now_nanos()?);
let start_price = 1.0;
let mut res = vec![start_price; n_months + 1];

let expected_monthly_return = (1.0 + (expected_yearly_return / 100.0)).powf(1.0 / 12.0);
// let mut mu_base = 1.0 + expected_yearly_return;
let mut mu = expected_monthly_return;
for (i, sigma) in (1..(n_months + 1)).zip(sigma_distribution.sample_iter(&mut sigma_rng)) {
for i in 0..9 {
last_sigmas[i] = last_sigmas[i + 1];
}
last_sigmas[9] = sigma;
last_sigmas.sort_by(|a, b| a.partial_cmp(b).unwrap());
let sigma = last_sigmas[SIGMA_WINDOW_SIZE / 2].abs();
let mu = (1.0 + expected_yearly_return / 100.0).powf(1.0 / 12.0);
let d = Normal::new(mu, sigma).map_err(to_blc)?;
let rv = d.sample(&mut rv_rng);
res[i] = (res[i - 1] * rv).max(1e-1);

if is_return_indpendent && sigma - sigma_mean > 0.0 {
let actual_total_return: f64 = (1..=i).map(|j| res[j] / res[j - 1]).product::<f64>().powf(1.0/(n_months-i) as f64);
let expected_total_return = expected_monthly_return.powf(n_months as f64 / (n_months-i) as f64);
mu = expected_total_return / actual_total_return;
}
}
Ok(res)
}
Expand Down Expand Up @@ -547,13 +555,13 @@ fn test_compute_balance() {

#[test]
fn test_compound() {
let compound_interest: Vec<f64> = random_walk(5.0, 0.0, 240).unwrap();
let compound_interest: Vec<f64> = random_walk(5.0, true, 0.0, 240).unwrap();
let (b, p) =
compute_total_balance(&[&compound_interest], &[10000.0], None, NONE_REBALANCE_DATA);
assert!((b - 26532.98).abs() < 1e-2);
assert!((p - 10000.0).abs() < 1e-12);

let compound_interest: Vec<f64> = random_walk(5.0, 0.0, 360).unwrap();
let compound_interest: Vec<f64> = random_walk(5.0, true, 0.0, 360).unwrap();
let ci_len = compound_interest.len();
let monthly_payments: Vec<f64> = vec![1000.0; ci_len - 1];
let (b, _) = compute_total_balance(
Expand Down

0 comments on commit 2acc224

Please sign in to comment.