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
| Metric | Value | Description |
|---|---|---|
| Bid processing throughput | 50000 entries/s | Single node |
| Clearing computation latency | 2.5s | Day-ahead market |
| Settlement precision | 0.01 yuan | Tiered precision |
| Risk assessment frequency | 1 per minute | Real-time monitoring |
| New Crates | 6 | Trading-related |
| New Tests | 700+ | 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
| Strategy | Type | Applicable Scenarios | Risk Level |
|---|---|---|---|
| Cost-plus | Classic | Conservative operation | Low |
| Marginal cost | Classic | Perfect competition | Low |
| Competitor analysis | Game theory | Oligopoly market | Medium |
| Reinforcement learning | AI | Complex markets | Medium-high |
| Portfolio strategy | Hybrid | Multi-objective | Adjustable |
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
| Mechanism | Pricing Method | Applicable Market | Complexity |
|---|---|---|---|
| Uniform clearing | System marginal price | Simple markets | Low |
| LMP | Locational marginal price | Spot markets | High |
| Zonal pricing | Regional price | Inter-provincial markets | Medium |
| Dual settlement | Virtual + physical | Financial + physical | High |
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 Type | Frequency | Basis | Description |
|---|---|---|---|
| Day-ahead settlement | Daily | Clearing result + schedule | Main settlement |
| Real-time settlement | Hourly | Real-time clearing + metering | Supplementary settlement |
| Deviation settlement | Daily | Plan vs actual | Penalty settlement |
| Ancillary services | Monthly | Dispatch records | Frequency regulation/reserve |
| Capacity settlement | Annual | Available capacity | Capacity 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
| Interface | Direction | Frequency | Description |
|---|---|---|---|
| Bid submission | Uplink | Daily | Day-ahead bid |
| Bid modification | Uplink | Daily | Can modify before deadline |
| Clearing query | Downlink | Daily | Clearing result |
| Schedule issuance | Downlink | Daily | Dispatch schedule |
| Metering data | Downlink | Hourly | Actual energy |
| Settlement bill | Downlink | Daily | Settlement details |
| Ancillary services | Bidirectional | Real-time | Frequency regulation/reserve |
| Dispute submission | Uplink | On-demand | Settlement 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(¤t_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 Type | Description | Control Measures |
|---|---|---|
| Price risk | Market price fluctuation | Hedging |
| Energy risk | Actual vs plan deviation | Deviation management |
| Credit risk | Counterparty default | Margin |
| Operational risk | System failure | Redundant design |
| Compliance risk | Violation of market rules | Rule engine |
| Liquidity risk | Funding turnover | Budget control |
Risk Hedging
// Generate risk hedging recommendations
let hedge = risk_mgr.suggest_hedge(¤t_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-bidreinforcement learning model abnormal bidding under extreme prices (#4003) - Fixed
eneros-trade-clearingLMP calculation instability with reactive power constraints (#4010) - Fixed
eneros-trade-settlementdeviation settlement calculation error across time periods (#4016) - Fixed
eneros-trademarket interface duplicate submission on retry under weak network (#4022) - Fixed
eneros-trade-riskVaR calculation crash on missing data (#4028)
Breaking Changes
BidEngine::generate_bid: Parameter added&LoadForecastClearingEngine::clear: Return type changed fromClearingResulttoResult<ClearingResult>SettlementEngine::settle_day_ahead: Parameter changed fromDatetoMarketDate
Upgrade Guide
- Update the
enerosdependency inCargo.tomlto0.40.0 - Run
eneros trade initto initialize the trading engine - Configure market interface and risk parameters in
eneros.toml - Run
eneros trade registerto register market participant account
Acknowledgments
Thanks to the 42 contributors who submitted 640+ commits, and to the electricity market experts who provided rule validation.