use crate::indicator::swing_structure::{SwingStructureEngine, SwingStructureOutput};
use crate::indicator::IndicatorOutput;
use crate::model::{Bar, MarketRegime};
use super::balance_migration::{build_balance_migration, BalanceMigrationOutput};
use super::liquidity_path::{build_free_space_score, FreeSpaceScore};
use super::market_context::{
build_market_context, classify_volume_node, AcceptanceLevel, AuctionPhase, MarketContextOutput,
};
use super::market_state::{
derive_playbook, ExpectedPlaybook, MarketStateOutput, OpeningType, VolatilityRegime,
};
use super::structural_stop::StructuralTrailingStop;
use super::vwap_regime::{VwapRegimeOutput, VwapRegimeTracker};
pub fn update_swing_structure(
engine: &mut SwingStructureEngine,
bar: &Bar,
atr_output: &IndicatorOutput,
) -> Option<SwingStructureOutput> {
let atr_raw = (atr_output.value / 100.0) * bar.close;
engine.update(bar, atr_raw)
}
pub fn build_market_context_from_profile(
regime: MarketRegime,
bar: &Bar,
atr_val: f64,
vp_output: &IndicatorOutput,
previous_acceptance: AcceptanceLevel,
auction_phase: AuctionPhase,
) -> MarketContextOutput {
let vpoc = vp_output
.extra
.get("vpoc")
.copied()
.unwrap_or(vp_output.value);
let density = vp_output
.extra
.get("current_density")
.copied()
.unwrap_or(0.05);
let node = classify_volume_node(density, 0.10, 0.02);
let atr_raw = if atr_val < 1.0 {
(atr_val / 100.0) * bar.close
} else {
atr_val
};
build_market_context(
regime,
bar.close,
vpoc,
atr_raw,
node,
previous_acceptance,
auction_phase,
)
}
pub fn derive_auction_phase(
msb_output: Option<&IndicatorOutput>,
ob_output: Option<&IndicatorOutput>,
current_phase: AuctionPhase,
) -> AuctionPhase {
if let Some(msb) = msb_output {
if msb.value.abs() >= 1.0 {
return AuctionPhase::Breakout;
}
}
if let Some(ob) = ob_output {
if let Some(&duration) = ob.extra.get("active_ob_duration") {
if duration > 0.0 {
return AuctionPhase::Acceptance {
duration_bars: duration as u32,
};
}
}
}
current_phase
}
pub fn build_market_state_and_playbook(
bars: &[Bar],
adx_val: f64,
atr_val: f64,
trend_stability: f64,
opening_type: OpeningType,
) -> (MarketStateOutput, ExpectedPlaybook) {
let regime = crate::regime::classify_regime(bars, adx_val, atr_val);
let playbook = derive_playbook(regime, trend_stability);
let vol_regime = if atr_val > 2.5 {
VolatilityRegime::High
} else if atr_val < 0.8 {
VolatilityRegime::Low
} else {
VolatilityRegime::Normal
};
let state_out = MarketStateOutput {
regime,
volatility_regime: vol_regime,
trend_stability,
balance_probability: if regime == MarketRegime::Consolidation {
0.8
} else {
0.2
},
opening_range_percentile: 0.5,
opening_type,
};
(state_out, playbook)
}
pub fn build_balance_migration_from_profiles(
prev_vp: &IndicatorOutput,
new_vp: &IndicatorOutput,
acceptance_bars: u32,
min_acceptance_bars: u32,
) -> BalanceMigrationOutput {
let prev_mid = prev_vp.extra.get("vpoc").copied().unwrap_or(prev_vp.value);
let new_mid = new_vp.extra.get("vpoc").copied().unwrap_or(new_vp.value);
build_balance_migration(prev_mid, new_mid, acceptance_bars, min_acceptance_bars)
}
pub fn advance_structural_stop(
stop: &mut StructuralTrailingStop,
new_pivot_price: f64,
) -> Option<f64> {
let old_stop = stop.current_stop;
stop.advance(new_pivot_price);
if (stop.current_stop - old_stop).abs() > 1e-9 {
Some(stop.current_stop)
} else {
None
}
}
pub fn build_free_space_from_profile(
vp_output: &IndicatorOutput,
atr_val: f64,
bar_close: f64,
) -> FreeSpaceScore {
let vpoc = vp_output
.extra
.get("vpoc")
.copied()
.unwrap_or(vp_output.value);
let lvn_width = vp_output.extra.get("lvn_width").copied().unwrap_or(0.0);
let density = vp_output
.extra
.get("current_density")
.copied()
.unwrap_or(0.05);
let atr_raw = if atr_val < 1.0 {
(atr_val / 100.0) * bar_close
} else {
atr_val
};
let lvn_width_atr = if atr_raw > 0.0 {
lvn_width / atr_raw
} else {
0.0
};
let distance_to_hvn_atr = if atr_raw > 0.0 {
(bar_close - vpoc).abs() / atr_raw
} else {
0.0
};
build_free_space_score(lvn_width_atr, distance_to_hvn_atr, density)
}
pub fn update_vwap_regime(
tracker: &mut VwapRegimeTracker,
vwap_output: &IndicatorOutput,
bar_close: f64,
atr_val: f64,
flat_threshold: f64,
strong_threshold: f64,
) -> VwapRegimeOutput {
let vwap = vwap_output.value;
let diff = bar_close - vwap;
let slope_raw = vwap_output.extra.get("slope").copied().unwrap_or(0.0);
let sigma = vwap_output
.extra
.get("sigma_1_upper")
.copied()
.unwrap_or(1.0)
- vwap;
let atr_raw = if atr_val < 1.0 {
(atr_val / 100.0) * bar_close
} else {
atr_val
};
let slope_atr = if atr_raw > 0.0 {
slope_raw / atr_raw
} else {
0.0
};
tracker.update(
diff,
atr_raw,
sigma,
slope_atr,
flat_threshold,
strong_threshold,
)
}