Multi-Day Battery Arbitrage Analysis
Simulate a full year of constrained battery operation to understand profit variability and realistic annual expectations.
Daily arbitrage profits swing from €6 to €84—a 14x difference. This chart shows why single-day analysis misleads: you need a full year of simulation to understand what battery storage really earns.

The conclusion? At current Spanish market prices, pure arbitrage barely covers operating costs. Annual net profit of ~€7,000 on a €360,000 investment means arbitrage alone doesn’t justify the battery. But this analysis sets the foundation for understanding where battery storage does work.
Questions
- How do constrained daily profits vary across a month?
- What’s the distribution of daily profits (best days vs worst days)?
- Are there weekly patterns in profitability?
- What’s a realistic annual profit projection based on simulated data?
- How does this compare to theoretical (unconstrained) analysis?
Implementation
Load price data
We’ll analyze 30 days of OMIE electricity prices at 15-minute resolution. See Fetching Electricity Prices from OMIE for the data source.
import pandas as pd
import numpy as np
df_prices = pd.read_csv('data/prices.csv', index_col='datetime', parse_dates=True) Loaded 35040 periods across 365 days (2025-01-01 to 2025-12-31).
Define constrained simulation
We reuse the constrained model from the single-day article. The function finds optimal charge/discharge windows and applies:
- SOC limits: 10%-90% (80% usable capacity)
- Power limit: 150 kW maximum charge/discharge rate
- Efficiency: 90% round-trip (10% energy loss)
def simulate_day_constrained(prices, capacity=1000, soc_min=0.10, soc_max=0.90,
max_power=150, efficiency=0.90,
charge_periods=16, discharge_periods=16):
"""Simulate one day with all constraints applied."""
n = len(prices)
if n < charge_periods + discharge_periods:
return None
usable_capacity = capacity * (soc_max - soc_min)
hours = charge_periods * 0.25
# Energy limited by power constraint
energy_charged = min(max_power * hours, usable_capacity)
energy_discharged = energy_charged * efficiency
# Find optimal charge window
min_avg, best_charge_start = float('inf'), 0
for start in range(n - charge_periods + 1):
avg = prices[start:start + charge_periods].mean()
if avg < min_avg:
min_avg, best_charge_start = avg, start
charge_end = best_charge_start + charge_periods
# Find optimal discharge window (after charging)
max_avg, best_discharge_start = -float('inf'), charge_end
for start in range(charge_end, n - discharge_periods + 1):
avg = prices[start:start + discharge_periods].mean()
if avg > max_avg:
max_avg, best_discharge_start = avg, start
# Calculate profits
charge_cost = energy_charged * min_avg / 1000
discharge_revenue = energy_discharged * max_avg / 1000
theoretical_profit = capacity * (max_avg - min_avg) / 1000
constrained_profit = discharge_revenue - charge_cost
return {
'charge_price': min_avg,
'discharge_price': max_avg,
'spread': max_avg - min_avg,
'theoretical_profit': theoretical_profit,
'constrained_profit': constrained_profit,
'efficiency_ratio': constrained_profit / theoretical_profit if theoretical_profit > 0 else 0
} Run 30-day simulation
daily_results = []
for date, day_data in df_prices.groupby(df_prices.index.date):
prices = day_data['price_spain'].values
result = simulate_day_constrained(prices)
if result:
result['date'] = pd.Timestamp(date)
daily_results.append(result)
df_daily = pd.DataFrame(daily_results).set_index('date')
30-day totals:
- Theoretical profit: €29,084
- Constrained profit: €15,131 (51% of theoretical)
Profit variability
How much do daily constrained profits vary?

The standard deviation of €17.29 shows significant day-to-day variability. Some days yield over €84, while the worst day produced only €6.
Weekly patterns
Do constrained profits follow a weekly pattern?

Weekday vs Weekend:
- Weekdays: €41.46/day average
- Weekends: €41.45/day average
Annual projection
Using 30 days of simulated data, we project annual returns:

Financial analysis
Can arbitrage alone justify a battery investment?

After 15 years (with 2% annual degradation), cumulative returns are €-287,653.
Conclusions
Multi-day simulation reveals the reality of battery arbitrage:
- Average daily profit: €41.45 (constrained)
- Variability: €6 to €84 per day
- Annual projection (median): €14,078 gross
- Annual net (after O&M): €6,878
Key insight: With median daily profits of €41, annual gross revenue (€14,078) barely covers O&M costs (€7,200). Arbitrage alone doesn’t justify battery investment at current Spanish market prices.
Successful BESS projects stack multiple revenue streams. In Spain, PV hybridization emerges as the most viable near-term path—combining solar self-consumption with storage fundamentally changes the economics. We’ll explore this in the next article.