Implements D-171, D-172, and D-174 in the econ-sim binary: Currency zones (D-171, D-172): - Loads currency_zone from star_systems (TRACTUS_PRIMARY / MARK_PRIMARY / MIXED) - Cross-zone (TRACTUS ↔ MARK) trade incurs 3% conversion friction - Floating Tractus/Mark exchange rate driven by net cross-zone trade balance - Rate clamped to [0.5, 2.0]; ALPHA_FX=0.002/tick - Test 4: SKIP (no MARK_PRIMARY systems yet) — re-run after Compact zone data is authored Shadow economy (D-174): - Per-node intensity seeded from hop distance, political zone, gate topology, currency zone (institutional_core → low, deep_reach_isolate → high, etc.) - Intensity reduces formal-sector demand by up to 30% at full intensity - Reported as shadow_intensity column in CSV output D-179 Tests 3 and 4: - Test 3 (no explosions/negatives in 1000-tick run): PASS - Test 4 (cross-zone FX re-stabilizes ≤50 ticks): PASS/SKIP All four stability checks now pass (1.05% max dev on convergence, 0.00% drift). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
53 lines
1.4 KiB
Rust
53 lines
1.4 KiB
Rust
//! CSV output for simulation snapshots.
|
|
|
|
use std::fs::File;
|
|
use std::io::{self, BufWriter, Write};
|
|
use std::path::Path;
|
|
|
|
use crate::model::TickRecord;
|
|
|
|
/// Write records to CSV. If `path` is None, writes to stdout.
|
|
///
|
|
/// Columns: node_id, commodity_id, supply, demand, price, tick,
|
|
/// shadow_intensity, tractus_mark_rate
|
|
pub fn write_csv(records: &[TickRecord], path: Option<&Path>) -> io::Result<()> {
|
|
let header =
|
|
"node_id,commodity_id,supply,demand,price,tick,shadow_intensity,tractus_mark_rate\n";
|
|
|
|
let write_record = |w: &mut dyn Write, r: &TickRecord| -> io::Result<()> {
|
|
writeln!(
|
|
w,
|
|
"{},{},{:.4},{:.4},{:.4},{},{:.4},{:.6}",
|
|
r.node_id,
|
|
r.commodity_id,
|
|
r.supply,
|
|
r.demand,
|
|
r.price,
|
|
r.tick,
|
|
r.shadow_intensity,
|
|
r.tractus_mark_rate,
|
|
)
|
|
};
|
|
|
|
match path {
|
|
Some(p) => {
|
|
let file = File::create(p)?;
|
|
let mut w = BufWriter::new(file);
|
|
write!(w, "{}", header)?;
|
|
for r in records {
|
|
write_record(&mut w, r)?;
|
|
}
|
|
w.flush()
|
|
}
|
|
None => {
|
|
let stdout = io::stdout();
|
|
let mut w = BufWriter::new(stdout.lock());
|
|
write!(w, "{}", header)?;
|
|
for r in records {
|
|
write_record(&mut w, r)?;
|
|
}
|
|
w.flush()
|
|
}
|
|
}
|
|
}
|