diff --git a/app/src/app/components/frames/chart/components/title.tsx b/app/src/app/components/frames/chart/components/title.tsx index 5bf5374df..45296aa1f 100644 --- a/app/src/app/components/frames/chart/components/title.tsx +++ b/app/src/app/components/frames/chart/components/title.tsx @@ -1,6 +1,11 @@ export function Title({ presets }: { presets: Presets }) { return ( -
+

{`/ ${[...presets.selected().path.map(({ name }) => name), presets.selected().name].join(" / ")}`}

{presets.selected().title}

diff --git a/app/src/scripts/presets/apply.ts b/app/src/scripts/presets/apply.ts index 84b24095e..3c0efd200 100644 --- a/app/src/scripts/presets/apply.ts +++ b/app/src/scripts/presets/apply.ts @@ -294,7 +294,7 @@ export function applySeriesList({ }); } - seriesConfigList.reverse().forEach((seriesConfig, index) => { + [...seriesConfigList].reverse().forEach((seriesConfig, index) => { const dataset = datasets.getOrImport(scale, seriesConfig.datasetPath); activeDatasets.push(dataset); diff --git a/app/src/scripts/presets/market/averages/index.ts b/app/src/scripts/presets/market/averages/index.ts index 86be71c43..c5de64dbd 100644 --- a/app/src/scripts/presets/market/averages/index.ts +++ b/app/src/scripts/presets/market/averages/index.ts @@ -16,7 +16,7 @@ export function createPresets(scale: ResourceScale): PartialPresetFolder { top: averages.map((average) => ({ title: average.key.toUpperCase(), color: colors[`_${average.key}`], - datasetPath: `/date-to-price-${average.key}-sma`, + datasetPath: `/${scale}-to-price-${average.key}-sma`, })), }, ...averages.map(({ name, key }) => @@ -111,17 +111,17 @@ function createPresetFolder({ bottom: [ { title: `1Y`, - color: colors.red, + color: colors._1y, datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio-1y-sma`, }, { title: `1M`, - color: colors.orange, + color: colors._1m, datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio-1m-sma`, }, { title: `1W`, - color: colors.yellow, + color: colors._1w, datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio-1w-sma`, }, { @@ -143,6 +143,108 @@ function createPresetFolder({ }, ], }, + { + scale, + name: "Momentum Oscillator", + description: "", + icon: IconTablerWaveSine, + title: `Market Price To ${name} Moving Average Ratio 1Y SMA Momentum Oscillator`, + top: [ + { + title: `SMA`, + color, + datasetPath: `/${scale}-to-price-${key}-sma`, + }, + ], + bottom: [ + { + title: `Momentum`, + seriesType: SeriesType.Based, + datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio-1y-sma-momentum-oscillator`, + }, + // { + // title: `Raw`, + // color: colors.white, + // datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio`, + // options: { + // base: 1, + // }, + // }, + { + title: `Base`, + color: colors.white, + datasetPath: `/${scale}-to-0`, + options: { + lineStyle: 3, + lastValueVisible: false, + }, + }, + ], + }, + { + scale, + name: "Percentiles", + description: "", + icon: IconTablerPercentage, + title: `Market Price To ${name} Moving Average Ratio Percentiles`, + top: [ + { + title: `SMA`, + color, + datasetPath: `/${scale}-to-price-${key}-sma`, + }, + ], + bottom: [ + { + title: `99.9%`, + color: colors.extremeMax, + datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio-99-9p`, + }, + { + title: `99.5%`, + color: colors.extremeMiddle, + datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio-99-5p`, + }, + { + title: `99%`, + color: colors.extremeMin, + datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio-99p`, + }, + { + title: `1%`, + color: colors.extremeMin, + datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio-1p`, + }, + + { + title: `0.5%`, + color: colors.extremeMiddle, + datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio-0-5p`, + }, + { + title: `0.1%`, + color: colors.extremeMax, + datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio-0-1p`, + }, + { + title: `Raw`, + color: colors.white, + datasetPath: `/${scale}-to-market-price-to-price-${key}-sma-ratio`, + options: { + base: 1, + }, + }, + { + title: `Even`, + color: colors.gray, + datasetPath: `/${scale}-to-1`, + options: { + lineStyle: 3, + lastValueVisible: false, + }, + }, + ], + }, ], }, ], diff --git a/app/src/types/auto-imports.d.ts b/app/src/types/auto-imports.d.ts index 17805a7b9..b5e11489d 100644 --- a/app/src/types/auto-imports.d.ts +++ b/app/src/types/auto-imports.d.ts @@ -238,6 +238,7 @@ declare global { const IconTablerVolume2: typeof import('~icons/tabler/volume2.jsx')['default'] const IconTablerWall: typeof import('~icons/tabler/wall.jsx')['default'] const IconTablerWallet: typeof import('~icons/tabler/wallet.jsx')['default'] + const IconTablerWaveSine: typeof import('~icons/tabler/wave-sine.jsx')['default'] const IconTablerWeight: typeof import('~icons/tabler/weight.jsx')['default'] const IconTablerWind: typeof import('~icons/tabler/wind.jsx')['default'] const IconTablerX: typeof import('~icons/tabler/x.jsx')['default'] diff --git a/parser/src/datasets/cointime.rs b/parser/src/datasets/cointime.rs index 44addc647..9f247a73d 100644 --- a/parser/src/datasets/cointime.rs +++ b/parser/src/datasets/cointime.rs @@ -86,7 +86,7 @@ impl CointimeDataset { cointime_value_stored: BiMap::new_bin(1, &f("cointime_value_stored")), concurrent_liveliness: BiMap::new_bin(1, &f("concurrent_liveliness")), concurrent_liveliness_2w_median: BiMap::new_bin( - 2, + 1, &f("concurrent_liveliness_2w_median"), ), cumulative_coinblocks_created: BiMap::new_bin(1, &f("cumulative_coinblocks_created")), diff --git a/parser/src/datasets/subs/ratio.rs b/parser/src/datasets/subs/ratio.rs index be792e9eb..acdd321fb 100644 --- a/parser/src/datasets/subs/ratio.rs +++ b/parser/src/datasets/subs/ratio.rs @@ -46,18 +46,18 @@ impl RatioDataset { 2, &f_ratio("ratio_1y_sma_momentum_oscillator"), ), - ratio_99p: BiMap::new_bin(2, &f_ratio("ratio_99p")), - ratio_99_5p: BiMap::new_bin(2, &f_ratio("ratio_99_5p")), - ratio_99_9p: BiMap::new_bin(2, &f_ratio("ratio_99_9p")), - ratio_1p: BiMap::new_bin(2, &f_ratio("ratio_1p")), - ratio_0_5p: BiMap::new_bin(2, &f_ratio("ratio_0_5p")), - ratio_0_1p: BiMap::new_bin(2, &f_ratio("ratio_0_1p")), - price_99p: BiMap::new_bin(2, &f_price("99p")), - price_99_5p: BiMap::new_bin(2, &f_price("99_5p")), - price_99_9p: BiMap::new_bin(2, &f_price("99_9p")), - price_1p: BiMap::new_bin(2, &f_price("1p")), - price_0_5p: BiMap::new_bin(2, &f_price("0_5p")), - price_0_1p: BiMap::new_bin(2, &f_price("0_1p")), + ratio_99p: BiMap::new_bin(1, &f_ratio("ratio_99p")), + ratio_99_5p: BiMap::new_bin(1, &f_ratio("ratio_99_5p")), + ratio_99_9p: BiMap::new_bin(1, &f_ratio("ratio_99_9p")), + ratio_1p: BiMap::new_bin(1, &f_ratio("ratio_1p")), + ratio_0_5p: BiMap::new_bin(1, &f_ratio("ratio_0_5p")), + ratio_0_1p: BiMap::new_bin(1, &f_ratio("ratio_0_1p")), + price_99p: BiMap::new_bin(1, &f_price("99p")), + price_99_5p: BiMap::new_bin(1, &f_price("99_5p")), + price_99_9p: BiMap::new_bin(1, &f_price("99_9p")), + price_1p: BiMap::new_bin(1, &f_price("1p")), + price_0_5p: BiMap::new_bin(1, &f_price("0_5p")), + price_0_1p: BiMap::new_bin(1, &f_price("0_1p")), }; s.min_initial_states diff --git a/parser/src/structs/date_map.rs b/parser/src/structs/date_map.rs index a50fd1029..62c3228f4 100644 --- a/parser/src/structs/date_map.rs +++ b/parser/src/structs/date_map.rs @@ -728,81 +728,80 @@ where let mut ordered_vec = None; let mut sorted_vec = None; - dates.iter().for_each(|date| { - if let Some(start) = days.map_or(chrono::NaiveDate::from_ymd_opt(2009, 1, 3), |size| { - date.checked_sub_days(Days::new(size as u64)) - }) { - if sorted_vec.is_none() { - let mut vec = start - .iter_days() - .take_while(|d| *d != **date) - .flat_map(|date| self.get_or_import(&WNaiveDate::wrap(date))) - .map(|f| OrderedFloat(f)) - .collect_vec(); + let min_percentile_date = chrono::NaiveDate::from_ymd_opt(2012, 1, 1).unwrap(); + let min_percentile_wdate = WNaiveDate::wrap(min_percentile_date); - if days.is_some() { - ordered_vec.replace(VecDeque::from(vec.clone())); + dates + .iter() + .cloned() + .filter(|date| date < &min_percentile_wdate) + .for_each(|date| { + if let Some(start) = days.map_or(Some(min_percentile_date), |size| { + date.checked_sub_days(Days::new(size as u64)) + }) { + if sorted_vec.is_none() { + let mut vec = start + .iter_days() + .take_while(|d| *d <= *date) + .flat_map(|date| self.get_or_import(&WNaiveDate::wrap(date))) + .map(|f| OrderedFloat(f)) + .collect_vec(); + + if days.is_some() { + ordered_vec.replace(VecDeque::from(vec.clone())); + } + + vec.sort_unstable(); + sorted_vec.replace(vec); + } else { + let float_value = OrderedFloat(self.get_or_import(&date).unwrap()); + + if let Some(days) = days { + if let Some(ordered_vec) = ordered_vec.as_mut() { + if ordered_vec.len() == days { + let first = ordered_vec.pop_front().unwrap(); + + let pos = + sorted_vec.as_ref().unwrap().binary_search(&first).unwrap(); + + sorted_vec.as_mut().unwrap().remove(pos); + } + + ordered_vec.push_back(float_value); + } + } + + let pos = sorted_vec + .as_ref() + .unwrap() + .binary_search(&float_value) + .unwrap_or_else(|pos| pos); + + sorted_vec.as_mut().unwrap().insert(pos, float_value); } - vec.sort_unstable(); - sorted_vec.replace(vec); - } else { - let float_value = OrderedFloat(self.get_or_import(date).unwrap()); + let vec = sorted_vec.as_ref().unwrap(); - if let Some(days) = days { - if let Some(ordered_vec) = ordered_vec.as_mut() { - if ordered_vec.len() == days { - let first = ordered_vec.pop_front().unwrap(); + let len = vec.len(); - let pos = - sorted_vec.as_ref().unwrap().binary_search(&first).unwrap(); - - sorted_vec.as_mut().unwrap().remove(pos); + map_and_percentiles + .iter_mut() + .for_each(|(map, percentile)| { + if !(0.0..=1.0).contains(percentile) { + panic!("The percentile should be between 0.0 and 1.0"); } - ordered_vec.push_back(float_value); - } - } + let value = { + if vec.is_empty() { + T::default() + } else { + let index = (len - 1) as f32 * *percentile; - let pos = sorted_vec - .as_ref() - .unwrap() - .binary_search(&float_value) - .unwrap_or_else(|pos| pos); + let fract = index.fract(); + let fract_t = T::from(fract).unwrap(); - sorted_vec.as_mut().unwrap().insert(pos, float_value); - } - - let vec = sorted_vec.as_ref().unwrap(); - - let len = vec.len(); - - map_and_percentiles - .iter_mut() - .for_each(|(map, percentile)| { - if !(0.0..=1.0).contains(percentile) { - panic!("The percentile should be between 0.0 and 1.0"); - } - - let value = { - if vec.is_empty() { - T::default() - } else { - let index = (len - 1) as f32 * *percentile; - - let fract = index.fract(); - let fract_t = T::from(fract).unwrap(); - - if fract != 0.0 { - (vec.get(index.ceil() as usize) - .unwrap_or_else(|| { - dbg!(vec, index, &self.path_all, &self.path_all, days); - panic!() - }) - .0 - * fract_t - + vec - .get(index.floor() as usize) + if fract != 0.0 { + (vec.get(index.ceil() as usize) .unwrap_or_else(|| { dbg!( vec, @@ -813,27 +812,41 @@ where ); panic!() }) - .0) - * T::from(1.0 - fract).unwrap() - } else { - vec.get(index.floor() as usize) - .unwrap_or_else(|| { - dbg!(vec, index); - panic!(); - }) - .0 + .0 + * fract_t + + vec + .get(index.floor() as usize) + .unwrap_or_else(|| { + dbg!( + vec, + index, + &self.path_all, + &self.path_all, + days + ); + panic!() + }) + .0) + * T::from(1.0 - fract).unwrap() + } else { + vec.get(index.floor() as usize) + .unwrap_or_else(|| { + dbg!(vec, index); + panic!(); + }) + .0 + } } - } - }; + }; - (*map).insert(*date, value); + (*map).insert(date, value); + }); + } else { + map_and_percentiles.iter_mut().for_each(|(map, _)| { + (*map).insert(date, T::default()); }); - } else { - map_and_percentiles.iter_mut().for_each(|(map, _)| { - (*map).insert(*date, T::default()); - }); - } - }); + } + }); } // diff --git a/parser/src/structs/height_map.rs b/parser/src/structs/height_map.rs index 3758d44c3..ef25df39e 100644 --- a/parser/src/structs/height_map.rs +++ b/parser/src/structs/height_map.rs @@ -717,104 +717,114 @@ where let mut ordered_vec = None; let mut sorted_vec = None; - heights.iter().for_each(|height| { - let height = *height; + let min_percentile_height = 160_000; - if let Some(start) = block_time.map_or(Some(0), |size| height.checked_sub(size)) { - if sorted_vec.is_none() { - let mut vec = (start..=height) - .map(|height| OrderedFloat(self.get_or_import(&height))) - .collect_vec(); + if min_percentile_height % HEIGHT_MAP_CHUNK_SIZE != 0 { + panic!("Should be 0"); + } - if block_time.is_some() { - ordered_vec.replace(VecDeque::from(vec.clone())); - } + heights + .iter() + .cloned() + .filter(|height| height < &min_percentile_height) + .for_each(|height| { + if let Some(start) = + block_time.map_or(Some(min_percentile_height), |size| height.checked_sub(size)) + { + if sorted_vec.is_none() { + let mut vec = (start..=height) + .map(|height| OrderedFloat(self.get_or_import(&height))) + .collect_vec(); - vec.sort_unstable(); - - sorted_vec.replace(vec); - } else { - let float_value = OrderedFloat(self.get_or_import(&height)); - - if block_time.is_some() { - let first = ordered_vec.as_mut().unwrap().pop_front().unwrap(); - let pos = sorted_vec.as_ref().unwrap().binary_search(&first).unwrap(); - sorted_vec.as_mut().unwrap().remove(pos); - - ordered_vec.as_mut().unwrap().push_back(float_value); - } - - let pos = sorted_vec - .as_ref() - .unwrap() - .binary_search(&float_value) - .unwrap_or_else(|pos| pos); - - sorted_vec.as_mut().unwrap().insert(pos, float_value); - } - - let vec = sorted_vec.as_ref().unwrap(); - - let len = vec.len(); - - map_and_percentiles - .iter_mut() - .for_each(|(map, percentile)| { - if !(0.0..=1.0).contains(percentile) { - panic!("The percentile should be between 0.0 and 1.0"); + if block_time.is_some() { + ordered_vec.replace(VecDeque::from(vec.clone())); } - let value = { - if vec.is_empty() { - T::default() - } else { - let index = (len - 1) as f32 * *percentile; + vec.sort_unstable(); - let fract = index.fract(); - let fract_t = T::from(fract).unwrap(); + sorted_vec.replace(vec); + } else { + let float_value = OrderedFloat(self.get_or_import(&height)); - if fract != 0.0 { - (vec.get(index.ceil() as usize) - .unwrap_or_else(|| { - dbg!( - index, - &self.path_all, - &self.path_all, - &self.to_insert, - block_time, - vec - ); - panic!() - }) - .0 - * fract_t - + vec - .get(index.floor() as usize) + if block_time.is_some() { + let first = ordered_vec.as_mut().unwrap().pop_front().unwrap(); + let pos = sorted_vec.as_ref().unwrap().binary_search(&first).unwrap(); + sorted_vec.as_mut().unwrap().remove(pos); + + ordered_vec.as_mut().unwrap().push_back(float_value); + } + + let pos = sorted_vec + .as_ref() + .unwrap() + .binary_search(&float_value) + .unwrap_or_else(|pos| pos); + + sorted_vec.as_mut().unwrap().insert(pos, float_value); + } + + let vec = sorted_vec.as_ref().unwrap(); + + let len = vec.len(); + + map_and_percentiles + .iter_mut() + .for_each(|(map, percentile)| { + if !(0.0..=1.0).contains(percentile) { + panic!("The percentile should be between 0.0 and 1.0"); + } + + let value = { + if vec.is_empty() { + T::default() + } else { + let index = (len - 1) as f32 * *percentile; + + let fract = index.fract(); + let fract_t = T::from(fract).unwrap(); + + if fract != 0.0 { + (vec.get(index.ceil() as usize) .unwrap_or_else(|| { dbg!( index, &self.path_all, &self.path_all, - block_time + &self.to_insert, + block_time, + vec ); panic!() }) - .0) - * T::from(1.0 - fract).unwrap() - } else { - vec.get(index as usize).unwrap().0 + .0 + * fract_t + + vec + .get(index.floor() as usize) + .unwrap_or_else(|| { + dbg!( + index, + &self.path_all, + &self.path_all, + block_time + ); + panic!() + }) + .0) + * T::from(1.0 - fract).unwrap() + } else { + vec.get(index as usize).unwrap().0 + } } - } - }; + }; - (*map).insert(height, value); + (*map).insert(height, value); + }); + } else { + map_and_percentiles.iter_mut().for_each(|(map, _)| { + (*map).insert(height, T::default()); }); - } else { - map_and_percentiles.iter_mut().for_each(|(map, _)| { - (*map).insert(height, T::default()); - }); - } - }); + } + }); } // pub fn insert_cumulative(&mut self, height: usize, source: &HeightMap) -> T