refactor(monorepo): reorganización lógica + renames + SDDs + split CHANGELOG
Reorganización física de crates/: - core/ (mezclaba 6 propósitos) se divide en protocol/, init/, runtime/, compat/ - shared/ (3 crates) se redistribuye en protocol/ e init/ - lapaloma (sub-módulo de ui_engine) se promueve a modules/pineal/ Renames de proyectos: - shipote → shuma (runtime de sandboxes) - nouser → akasha (explorador de Mónadas) - yahweh → nahual (motor GPUI, antes ui_engine/) - lapaloma → pineal (data-viz agnóstica) Fraccionamiento UI → core agnóstico: - vista-core (DeckState + snap, 175 LOC, 5 tests verdes) - barra-core (Task + render_html + sanitize, 90 LOC, 5 tests verdes) - vista-web y barra-web ahora son thin DOM bindings Documentación nueva: - 16 SDDs por subdirectorio (≤80 LOC c/u): protocol/init/runtime/compat + 10 módulos + apps/ - docs/STATUS.md con cifras reales por proyecto - docs/ROADMAP.md con plan a finalización (6 hitos, ~6-8 semanas) - CHANGELOG.md particionado en docs/changelog/<proyecto>.md (7 buckets) Automatización: - scripts/reorg.py — script idempotente que: git mv directorios, renombra package names, recomputa path = refs, reescribe imports rust, actualiza workspace Cargo.toml. Soporta --dry-run. - scripts/split-changelog.py — particiona CHANGELOG por componente. Validación: - cargo check --workspace pasa (124 crates + 2 nuevos cores). - 10 tests adicionales (5 en vista-core + 5 en barra-core) verdes. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
//! `OhlcBuffer` — buffer plano de bars con stride 6 `f32`.
|
||||
//!
|
||||
//! Memoria contigua: `[t0, o0, h0, l0, c0, v0, t1, o1, …]`.
|
||||
//! Acceso O(1) por índice; un memcpy completo para hidratar desde
|
||||
//! una fuente externa.
|
||||
|
||||
/// Una barra OHLC + volumen. Valor leído del buffer; no es la
|
||||
/// representación de almacenamiento (que vive como `[f32; 6]`).
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Bar {
|
||||
pub t: f32,
|
||||
pub o: f32,
|
||||
pub h: f32,
|
||||
pub l: f32,
|
||||
pub c: f32,
|
||||
pub v: f32,
|
||||
}
|
||||
|
||||
impl Bar {
|
||||
pub fn is_bull(self) -> bool {
|
||||
self.c > self.o
|
||||
}
|
||||
pub fn is_bear(self) -> bool {
|
||||
self.c < self.o
|
||||
}
|
||||
}
|
||||
|
||||
pub const STRIDE: usize = 6;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct OhlcBuffer {
|
||||
bars: Vec<f32>,
|
||||
revision: u64,
|
||||
}
|
||||
|
||||
impl OhlcBuffer {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn with_capacity(n: usize) -> Self {
|
||||
Self {
|
||||
bars: Vec::with_capacity(n * STRIDE),
|
||||
revision: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_raw(bars: Vec<f32>) -> Self {
|
||||
assert!(bars.len() % STRIDE == 0, "OhlcBuffer: stride 6 required");
|
||||
Self { bars, revision: 0 }
|
||||
}
|
||||
|
||||
pub fn push_bar(&mut self, b: Bar) {
|
||||
self.bars.push(b.t);
|
||||
self.bars.push(b.o);
|
||||
self.bars.push(b.h);
|
||||
self.bars.push(b.l);
|
||||
self.bars.push(b.c);
|
||||
self.bars.push(b.v);
|
||||
self.revision = self.revision.wrapping_add(1);
|
||||
}
|
||||
|
||||
pub fn push_values(&mut self, t: f32, o: f32, h: f32, l: f32, c: f32, v: f32) {
|
||||
self.push_bar(Bar { t, o, h, l, c, v });
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.bars.len() / STRIDE
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.bars.is_empty()
|
||||
}
|
||||
|
||||
pub fn bar(&self, i: usize) -> Bar {
|
||||
let off = i * STRIDE;
|
||||
Bar {
|
||||
t: self.bars[off],
|
||||
o: self.bars[off + 1],
|
||||
h: self.bars[off + 2],
|
||||
l: self.bars[off + 3],
|
||||
c: self.bars[off + 4],
|
||||
v: self.bars[off + 5],
|
||||
}
|
||||
}
|
||||
|
||||
/// Slice plano del buffer subyacente.
|
||||
pub fn bars(&self) -> &[f32] {
|
||||
&self.bars
|
||||
}
|
||||
|
||||
pub fn revision(&self) -> u64 {
|
||||
self.revision
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.bars.clear();
|
||||
self.revision = self.revision.wrapping_add(1);
|
||||
}
|
||||
|
||||
/// Min/max de `low` y `high` sobre todo el buffer.
|
||||
/// Útil para autoscale del Y axis.
|
||||
pub fn price_range(&self) -> Option<(f32, f32)> {
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let mut lo = f32::INFINITY;
|
||||
let mut hi = f32::NEG_INFINITY;
|
||||
for i in 0..self.len() {
|
||||
let b = self.bar(i);
|
||||
if b.l < lo {
|
||||
lo = b.l;
|
||||
}
|
||||
if b.h > hi {
|
||||
hi = b.h;
|
||||
}
|
||||
}
|
||||
Some((lo, hi))
|
||||
}
|
||||
|
||||
/// Rango temporal `[t_min, t_max]`. None si vacío.
|
||||
pub fn time_range(&self) -> Option<(f32, f32)> {
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let first = self.bars[0];
|
||||
let last = self.bars[self.bars.len() - STRIDE];
|
||||
Some((first, last))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn push_y_lectura() {
|
||||
let mut b = OhlcBuffer::with_capacity(2);
|
||||
b.push_values(1.0, 10.0, 12.0, 9.0, 11.0, 100.0);
|
||||
b.push_values(2.0, 11.0, 13.0, 10.0, 10.5, 80.0);
|
||||
assert_eq!(b.len(), 2);
|
||||
assert_eq!(b.bar(0).c, 11.0);
|
||||
assert_eq!(b.bar(1).h, 13.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bull_y_bear() {
|
||||
let bull = Bar { t: 0.0, o: 10.0, h: 11.0, l: 9.0, c: 10.5, v: 0.0 };
|
||||
let bear = Bar { t: 0.0, o: 10.0, h: 11.0, l: 9.0, c: 9.5, v: 0.0 };
|
||||
assert!(bull.is_bull());
|
||||
assert!(!bull.is_bear());
|
||||
assert!(bear.is_bear());
|
||||
assert!(!bear.is_bull());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn price_range_correcto() {
|
||||
let mut b = OhlcBuffer::new();
|
||||
b.push_values(0.0, 10.0, 15.0, 8.0, 12.0, 0.0);
|
||||
b.push_values(1.0, 12.0, 14.0, 7.0, 9.0, 0.0);
|
||||
b.push_values(2.0, 9.0, 11.0, 9.0, 10.0, 0.0);
|
||||
let (lo, hi) = b.price_range().unwrap();
|
||||
assert_eq!(lo, 7.0);
|
||||
assert_eq!(hi, 15.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn time_range() {
|
||||
let mut b = OhlcBuffer::new();
|
||||
b.push_values(10.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||
b.push_values(50.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||
b.push_values(100.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||
assert_eq!(b.time_range(), Some((10.0, 100.0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn revision_bumps_en_push_y_clear() {
|
||||
let mut b = OhlcBuffer::new();
|
||||
let r0 = b.revision();
|
||||
b.push_values(0.0, 1.0, 1.0, 1.0, 1.0, 1.0);
|
||||
assert_ne!(r0, b.revision());
|
||||
let r1 = b.revision();
|
||||
b.clear();
|
||||
assert_ne!(r1, b.revision());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user