Skip to main content

v0.40.0 Release Notes

Release Notes

EnerOS v0.40.0

Release Date: 2026-06-20
Codename: Trade
Git Tag: v0.40.0
Support Status: Stable
Total Crates: 108 (6 new)
Test Cases: 13500+ (700 new)

Overview

EnerOS v0.40.0 “Trade” is the energy trading themed release, extending EnerOS capabilities from “grid operation control” to “electricity market trading”, enabling power generation companies, electricity retail companies, and power consumers to complete the entire trading workflow on the EnerOS platform—from bidding, clearing, settlement to risk management. This release marks EnerOS officially covering the dual-stack capabilities of “physical layer + market layer” of the power system.

The core design philosophy of the Trade release is “trading as system call + constraints as compliance” — market bidding, clearing calculations, settlement and clearing operations are completed through kernel system calls, deeply coupled with dispatching, power flow, and constraints. Trading decisions are enforced by the kernel constraint engine, ensuring that bidding strategies are physically feasible, contractually compliant, and risk-controllable. This makes EnerOS an energy trading platform that simultaneously satisfies physical constraints and market rules.

This release introduces five core capabilities: Bid Strategy Engine, Market Clearing System, Settlement System, Market Interface Gateway, and Risk Management Framework. All capabilities are implemented through five new crates: eneros-trade, eneros-trade-bid, eneros-trade-clearing, eneros-trade-settlement, and eneros-trade-risk.

Key Metrics

MetricValueDescription
Bid processing throughput50000 entries/sSingle node
Clearing computation latency2.5sDay-ahead market
Settlement precision0.01 yuanTiered precision
Risk assessment frequency1 per minuteReal-time monitoring
New Crates6Trading-related
New Tests700+Including 120 end-to-end

New Features

1. Bid Strategy Engine

Added the eneros-trade-bid crate, providing AI-based bid strategy generation capabilities, combining generation costs, load forecasts, market conditions, and competitor analysis to generate optimal bids.

Bid Generation

use eneros_trade_bid::{BidEngine, BidConfig, BidStrategy};

let engine = BidEngine::new(&ctx)
    .config(BidConfig {
        market: MarketType::DayAhead,
        strategy: BidStrategy::ReinforcementLearning {
            model: "bid-rl-dqn-v3",
            exploration: 0.05,
        },
        risk_appetite: RiskAppetite::Moderate,
        cost_model: CostModel::from_generator(&generator),
    })
    .build().await?;

// Generate day-ahead market bid
let bid = engine.generate_bid(
    MarketDate::tomorrow(),
    &load_forecast,
    &generation_capacity,
).await?;

println!("Bid proposal:");
for segment in &bid.segments {
    println!("  Period {}: {:.1} MW @ {:.2} yuan/MWh",
        segment.period, segment.volume_mw, segment.price);
}
println!("Estimated revenue: {:.0} yuan", bid.estimated_revenue);
println!("Estimated cost: {:.0} yuan", bid.estimated_cost);
println!("Estimated profit: {:.0} yuan", bid.estimated_profit);

Bid Strategy Types

StrategyTypeApplicable ScenariosRisk Level
Cost-plusClassicConservative operationLow
Marginal costClassicPerfect competitionLow
Competitor analysisGame theoryOligopoly marketMedium
Reinforcement learningAIComplex marketsMedium-high
Portfolio strategyHybridMulti-objectiveAdjustable

Bid Optimization Objective

// Multi-objective bid optimization
let bid = engine.optimize(BidObjective::MultiObjective {
    profit: 0.6,        // Profit maximization 60%
    risk: 0.3,          // Risk minimization 30%
    market_share: 0.1,  // Market share 10%
}).await?;

// Constraints
let constraints = BidConstraints {
    min_volume: 10.0,           // Minimum bid volume 10 MW
    max_volume: capacity,       // Maximum bid volume
    price_floor: 100.0,         // Minimum price 100 yuan/MWh
    price_ceiling: 1500.0,      // Maximum price 1500 yuan/MWh
    must_run: must_run_units,    // Must-run units
};

let bid = engine.optimize_with_constraints(constraints).await?;

2. Market Clearing System

Added the eneros-trade-clearing crate, providing electricity market clearing calculation capabilities, supporting marginal pricing, locational marginal pricing (LMP), zonal pricing, and other clearing mechanisms.

Clearing Calculation

use eneros_trade_clearing::{ClearingEngine, ClearingConfig, PricingMethod};

let engine = ClearingEngine::new(&ctx)
    .config(ClearingConfig {
        method: PricingMethod::LMP,  // Locational marginal price
        solver: ClearingSolver::MipSolver,
        security_constrained: true,   // Security-constrained clearing
        network_model: &network,
    })
    .build().await?;

// Collect all bids
let bids = market.collect_bids(MarketDate::tomorrow()).await?;

// Execute clearing
let result = engine.clear(bids).await?;

println!("Clearing result:");
println!("  System marginal price: {:.2} yuan/MWh", result.system_marginal_price);
println!("  Total cleared energy: {:.1} MWh", result.total_cleared_mwh);
println!("  Nodal prices:");
for (bus, lmp) in &result.lmp {
    println!("    Bus {}: {:.2} yuan/MWh", bus, lmp);
}

Clearing Mechanism Comparison

MechanismPricing MethodApplicable MarketComplexity
Uniform clearingSystem marginal priceSimple marketsLow
LMPLocational marginal priceSpot marketsHigh
Zonal pricingRegional priceInter-provincial marketsMedium
Dual settlementVirtual + physicalFinancial + physicalHigh

Security-Constrained Clearing

// Clearing with network constraints (SCUC)
let result = engine.security_constrained_clearing(bids)
    .constraints(&[
        Constraint::line_flow_limit("L-1", 100.0),  // MW
        Constraint::voltage_range(bus_id, 0.95, 1.05),
        Constraint::n_1_contingency(vec!["L-1", "L-2"]),
    ])
    .solve().await?;

println!("Clearing status: {:?}", result.status);
println!("Computation time: {:?}", result.solve_time);

// Output dispatch schedule
for unit in &result.committed_units {
    println!("Unit {}: output {:.1} MW, LMP {:.2} yuan/MWh",
        unit.id, unit.output_mw, unit.lmp);
}

3. Settlement System

Added the eneros-trade-settlement crate, providing electricity market settlement and clearing capabilities, supporting day-ahead settlement, real-time settlement, and ancillary services settlement.

Settlement Calculation

use eneros_trade_settlement::{SettlementEngine, SettlementConfig};

let engine = SettlementEngine::new(&ctx)
    .config(SettlementConfig {
        precision: 2,  // Precise to cents
        timezone: "Asia/Shanghai",
        settlement_rules: SettlementRules::latest(),
    })
    .build().await?;

// Day-ahead market settlement
let settlement = engine.settle_day_ahead(
    MarketDate::from("2026-06-19"),
    &clearing_result,
    &actual_metering,
).await?;

println!("Settlement details:");
println!("{:<15} {:<10} {:<10} {:<10} {:<12}",
    "Unit", "Bid MW", "Actual MW", "Price", "Settlement Amount");
for item in &settlement.items {
    println!("{:<15} {:<10.1} {:<10.1} {:<10.2} {:<12.2}",
        item.unit_id, item.bid_mw, item.actual_mw,
        item.price, item.amount);
}
println!("Total settlement: {:.2} yuan", settlement.total_amount);

// Deviation settlement (actual vs plan)
let deviation = engine.settle_deviation(
    &clearing_result,
    &actual_metering,
).await?;
println!("Deviation energy: {:.1} MWh", deviation.deviation_mwh);
println!("Deviation charge: {:.2} yuan", deviation.deviation_charge);

Settlement Types

Settlement TypeFrequencyBasisDescription
Day-ahead settlementDailyClearing result + scheduleMain settlement
Real-time settlementHourlyReal-time clearing + meteringSupplementary settlement
Deviation settlementDailyPlan vs actualPenalty settlement
Ancillary servicesMonthlyDispatch recordsFrequency regulation/reserve
Capacity settlementAnnualAvailable capacityCapacity market

Settlement Reconciliation

// Automatic reconciliation
let reconciliation = engine.reconcile(
    MarketDate::from("2026-06-19"),
    &internal_records,
    &market_operator_data,
).await?;

if reconciliation.has_discrepancies() {
    for disc in &reconciliation.discrepancies {
        println!("Discrepancy: {} internal {:.2} vs market center {:.2}",
            disc.item, disc.internal_amount, disc.market_amount);
    }
    // Automatically file a dispute
    engine.file_dispute(&reconciliation).await?;
}

4. Market Interface Gateway

Added the eneros-trade crate (trading core), providing standard interface integration capabilities with electricity trading centers.

Market Interface

use eneros_trade::{MarketGateway, MarketConfig, MarketApi};

let gateway = MarketGateway::new(MarketConfig {
    endpoint: "https://market.energy-exchange.cn/api/v2",
    auth: MarketAuth::Certificate {
        client_cert: "/etc/eneros/market-client.pem",
        ca: "/etc/eneros/market-ca.pem",
    },
    market: MarketOperator::Beijing,
}).await?;

// Submit bid
gateway.submit_bid(&bid).await?;

// Query clearing result
let clearing = gateway.query_clearing(MarketDate::tomorrow()).await?;

// Query settlement data
let settlement = gateway.query_settlement(MarketDate::today()).await?;

Interface Capabilities

InterfaceDirectionFrequencyDescription
Bid submissionUplinkDailyDay-ahead bid
Bid modificationUplinkDailyCan modify before deadline
Clearing queryDownlinkDailyClearing result
Schedule issuanceDownlinkDailyDispatch schedule
Metering dataDownlinkHourlyActual energy
Settlement billDownlinkDailySettlement details
Ancillary servicesBidirectionalReal-timeFrequency regulation/reserve
Dispute submissionUplinkOn-demandSettlement dispute

Multi-Market Access

// Access multiple provincial markets simultaneously
let markets = MarketGateway::multi(vec![
    MarketConfig::beijing(),
    MarketConfig::shanghai(),
    MarketConfig::guangdong(),
]).await?;

// Cross-market arbitrage analysis
let arbitrage = markets.analyze_arbitrage().await?;
for opp in &arbitrage.opportunities {
    println!("Arbitrage opportunity: {} -> {}, spread {:.2} yuan/MWh",
        opp.from_market, opp.to_market, opp.spread);
}

5. Risk Management Framework

Added the eneros-trade-risk crate, providing risk identification, assessment, and control capabilities throughout the trading process.

Risk Assessment

use eneros_trade_risk::{RiskManager, RiskConfig, RiskMetrics};

let risk_mgr = RiskManager::new(&ctx)
    .config(RiskConfig {
        var_confidence: 0.95,      // 95% confidence level
        var_horizon: Duration::days(1),
        max_var: 500_000,          // Maximum VaR 500000 yuan
        stress_scenarios: vec![
            StressScenario::price_spike(2.0),     // Price doubles
            StressScenario::load_drop(0.3),        // Load drops 30%
            StressScenario::unit_failure(0.2),     // 20% unit failure
        ],
    })
    .build().await?;

// Real-time risk assessment
let risk = risk_mgr.assess(&current_portfolio).await?;

println!("Risk metrics:");
println!("  VaR (95%, 1 day): {:.0} yuan", risk.var);
println!("  CVaR: {:.0} yuan", risk.cvar);
println!("  Max drawdown: {:.1}%", risk.max_drawdown * 100.0);
println!("  Risk rating: {:?}", risk.rating);

if risk.var > risk_mgr.config().max_var {
    // Trigger risk alert
    risk_mgr.alert(RiskAlert::VarExceeded(risk.var)).await?;
}

Risk Types

Risk TypeDescriptionControl Measures
Price riskMarket price fluctuationHedging
Energy riskActual vs plan deviationDeviation management
Credit riskCounterparty defaultMargin
Operational riskSystem failureRedundant design
Compliance riskViolation of market rulesRule engine
Liquidity riskFunding turnoverBudget control

Risk Hedging

// Generate risk hedging recommendations
let hedge = risk_mgr.suggest_hedge(&current_portfolio).await?;

for action in &hedge.actions {
    println!("Hedge recommendation: {} (expected risk reduction {:.0} yuan)",
        action.description, action.risk_reduction);
}

// Set risk limits
risk_mgr.set_limits(RiskLimits {
    max_position: 1000.0,          // MW
    max_daily_loss: 100_000,       // yuan
    max_deviation: 0.05,           // 5%
    max_exposure: 5_000_000,       // yuan
}).await?;

// Automatic closeout on limit breach
risk_mgr.on_breach(|breach| {
    log::warn!("Risk limit breached: {:?} = {:.0}", breach.type, breach.value);
    if breach.severity == Severity::Critical {
        risk_mgr.auto_closeout().await?;
    }
}).await?;

Improvements

  • Power flow computation: Clearing computation reuses the power flow engine, efficiency improved 40%
  • Constraint engine: Added market rule constraint types (price caps, capacity limits)
  • Time-series engine: Trading data supports querying by market calendar
  • Agent runtime: Added TradingAgent type, specifically for trading decisions
  • Observability: Full trading chain audit, meeting regulatory requirements

Bug Fixes

  • Fixed eneros-trade-bid reinforcement learning model abnormal bidding under extreme prices (#4003)
  • Fixed eneros-trade-clearing LMP calculation instability with reactive power constraints (#4010)
  • Fixed eneros-trade-settlement deviation settlement calculation error across time periods (#4016)
  • Fixed eneros-trade market interface duplicate submission on retry under weak network (#4022)
  • Fixed eneros-trade-risk VaR calculation crash on missing data (#4028)

Breaking Changes

  • BidEngine::generate_bid: Parameter added &LoadForecast
  • ClearingEngine::clear: Return type changed from ClearingResult to Result<ClearingResult>
  • SettlementEngine::settle_day_ahead: Parameter changed from Date to MarketDate

Upgrade Guide

  1. Update the eneros dependency in Cargo.toml to 0.40.0
  2. Run eneros trade init to initialize the trading engine
  3. Configure market interface and risk parameters in eneros.toml
  4. Run eneros trade register to register market participant account

Acknowledgments

Thanks to the 42 contributors who submitted 640+ commits, and to the electricity market experts who provided rule validation.