battery_optimizer.uk.request#

Classes

BMActivationRatio(*, start[, activation_ratio])

BalancingMechanism(*[, ...])

DCParameters(*[, ...])

DMParameters(*[, ...])

DRParameters(*[, ...])

DSEnergyThroughput(*, start[, ...])

DSEnergyThroughputBuffer(*[, enabled, ...])

DynamicServicesParameters(*[, ...])

Parameters that all UK dynamic services (DC, DM, DR) share.

MelMilNowParameters(*, timestamp_now, soc_now)

PVForecast(*, start, technical_maximum_kw, ...)

PowerSwingLimit(*, applies, value_kw)

PrequalifiedPowerHighKW(*, start, ...)

PrequalifiedPowerLowKW(*, start, ...)

TraderExclusion(*, start, dch, dcl[, dmh, ...])

UKAsset(*[, asset_id, price_buckets, ...])

UKBatteryCommercials(*, start[, ...])

UKBatteryInitialConditions(*, initial_soe[, ...])

UKBatteryMarketed(*, start, ...[, ...])

UKBatteryParameters(*, min_soe, max_soe[, ...])

UKCommercialObjective(*[, commercial_columns])

UKFeatureFlags(*[, ...])

UKMelMilNowRequest(*, request_id[, country, ...])

UKObjective(*[, name, ...])

UKPriceForecast(*, start[, intraday, ...])

UKRampRates(*[, type, ...])

UKRequest(*, request_id[, country, ...])

UKStrategyOptimization(*, frequency_min, horizon)

class battery_optimizer.uk.request.BMActivationRatio(*, start, activation_ratio=0.1)#

Bases: BaseModel

Parameters:
activation_ratio: float#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

start: datetime#
class battery_optimizer.uk.request.BalancingMechanism(*, activation_time_minutes=Timedelta('0 days 00:32:00'), activation_ratio=1.0)#

Bases: BaseModel

Parameters:
activation_ratio: float | list[BMActivationRatio] | DataFrame#
activation_time_minutes: int | str | Timedelta#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class battery_optimizer.uk.request.DCParameters(*, prequalified_power_high_kw=None, prequalified_power_low_kw=None, ramp_rate_perc_min=0.05, delivery_duration_sec=900, baseline_buffer_perc=0.12, delivery_duration_buffer_perc=1.1, min_energy_recovery_perc=0.2)#

Bases: DynamicServicesParameters

Parameters:
baseline_buffer_perc: float#
delivery_duration_buffer_perc: float#
delivery_duration_sec: int | str | Timedelta#
min_energy_recovery_perc: float#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

ramp_rate_perc_min: float#
class battery_optimizer.uk.request.DMParameters(*, prequalified_power_high_kw=None, prequalified_power_low_kw=None, delivery_duration_sec=1800, baseline_buffer_perc=0.22, delivery_duration_buffer_perc=1.1, min_energy_recovery_perc=0.2)#

Bases: DynamicServicesParameters

Parameters:
baseline_buffer_perc: float#
delivery_duration_buffer_perc: float#
delivery_duration_sec: int | str | Timedelta#
min_energy_recovery_perc: float#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class battery_optimizer.uk.request.DRParameters(*, prequalified_power_high_kw=None, prequalified_power_low_kw=None, delivery_duration_sec=3600, baseline_buffer_perc=0.44, delivery_duration_buffer_perc=1.1, min_energy_recovery_perc=0.2)#

Bases: DynamicServicesParameters

Parameters:
baseline_buffer_perc: float#
delivery_duration_buffer_perc: float#
delivery_duration_sec: int | str | Timedelta#
min_energy_recovery_perc: float#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class battery_optimizer.uk.request.DSEnergyThroughput(*, start, dcl_MWh_per_MW=0.0, dch_MWh_per_MW=0.0, dml_MWh_per_MW=0.0, dmh_MWh_per_MW=0.0, drl_MWh_per_MW=0.0, drh_MWh_per_MW=0.0)#

Bases: BaseModel

Parameters:
dch_MWh_per_MW: float#
dcl_MWh_per_MW: float#
dmh_MWh_per_MW: float#
dml_MWh_per_MW: float#
drh_MWh_per_MW: float#
drl_MWh_per_MW: float#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

start: datetime#
class battery_optimizer.uk.request.DSEnergyThroughputBuffer(*, enabled=False, rev_factor=0.06, rev_factor_mdo_mdb=1.0, efficiency_correct=False)#

Bases: BaseModel

Parameters:
efficiency_correct: bool#
enabled: bool#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

rev_factor: float#
rev_factor_mdo_mdb: float#
class battery_optimizer.uk.request.DynamicServicesParameters(*, prequalified_power_high_kw=None, prequalified_power_low_kw=None)#

Bases: BaseModel

Parameters that all UK dynamic services (DC, DM, DR) share.

Parameters:
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

prequalified_power_high_kw: Annotated[float, FieldInfo(annotation=NoneType, required=True, metadata=[Ge(ge=0.0)])] | list[PrequalifiedPowerHighKW] | Series | None#
prequalified_power_low_kw: Annotated[float, FieldInfo(annotation=NoneType, required=True, metadata=[Ge(ge=0.0)])] | list[PrequalifiedPowerLowKW] | Series | None#
class battery_optimizer.uk.request.MelMilNowParameters(*, timestamp_now, soc_now)#

Bases: BaseModel

Parameters:
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

soc_now: float#
timestamp_now: datetime#
class battery_optimizer.uk.request.PVForecast(*, start, technical_maximum_kw, production_forecast_kw)#

Bases: BaseModel

Parameters:
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

production_forecast_kw: float#
start: datetime#
technical_maximum_kw: float#
class battery_optimizer.uk.request.PowerSwingLimit(*, applies, value_kw)#

Bases: BaseModel

Parameters:
applies: bool#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

value_kw: float#
class battery_optimizer.uk.request.PrequalifiedPowerHighKW(*, start, prequalified_power_high_kw)#

Bases: BaseModel

Parameters:
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

prequalified_power_high_kw: Annotated[float, FieldInfo(annotation=NoneType, required=True, metadata=[Ge(ge=0.0)])]#
start: datetime#
class battery_optimizer.uk.request.PrequalifiedPowerLowKW(*, start, prequalified_power_low_kw)#

Bases: BaseModel

Parameters:
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

prequalified_power_low_kw: Annotated[float, FieldInfo(annotation=NoneType, required=True, metadata=[Ge(ge=0.0)])]#
start: datetime#
class battery_optimizer.uk.request.TraderExclusion(*, start, dch, dcl, dmh=False, dml=False, drh=False, drl=False)#

Bases: BaseModel

Parameters:
dch: bool#
dcl: bool#
dmh: bool#
dml: bool#
drh: bool#
drl: bool#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

start: datetime#
class battery_optimizer.uk.request.UKAsset(*, asset_id='Unique ID of the asset', battery_parameters, battery_initial_conditions, battery_marketed, battery_availabilities, price_forecast, price_buckets=None, strategy_optimization, asset_states=[], skipped_validations={'ensure_battery_marketed_respects_battery_parameters'}, battery_commercials, PV_forecast, dynamic_services_energy_throughput=Empty DataFrame Columns: [] Index: [], allow_pv_to_battery=False)#

Bases: Asset

Parameters:
PV_forecast: list[PVForecast] | DataFrame#
property all_markets#
allow_pv_to_battery: bool#
battery_commercials: list[UKBatteryCommercials] | DataFrame#
battery_initial_conditions: UKBatteryInitialConditions#
battery_marketed: list[UKBatteryMarketed] | DataFrame#
battery_parameters: UKBatteryParameters#
property day_ahead_products#
property dc_max_basepoint_change_per_dc_kW_trapezium#
property dc_max_basepoint_change_per_dc_kW_triangle#
dynamic_services_energy_throughput: list[DSEnergyThroughput] | DataFrame#
property energy_products#
ensure_PV_forecast_respects_grid_connection()#

PV production forecasts are currently provided as-is as fixed input. Since we cannot control the quality of the these PV production forecasts we need to guard our optimization routine against PV forecasts that would render our mixed-integer problem infeasible. Infeasible solutions arise e.g. when the provided PV forecast is greater than the grid connection export limit. To this end, we refuse obviously wrong PV forecast inputs (e.g. negative values) and clip the PV forecast from above to the grid connection export limit - thus ensuring that the PV forecast does not render our problem infeasible.

classmethod ensure_ds_marketed_in_integer_increments_of_MW(battery_marketed)#
ensure_energy_cost_provided_if_customer_perspective()#
property frequency_markets#
handle_country_specific_market_already_auctioned(market)#
Parameters:

market (str)

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

price_forecast: list[UKPriceForecast] | DataFrame#
strategy_optimization: UKStrategyOptimization#
property uk_dynamic_services_variables_high#
property uk_dynamic_services_variables_low#
class battery_optimizer.uk.request.UKBatteryCommercials(*, start, min_revenue_per_intraday_sell=0.01, min_revenue_per_virtual_wholesale_sell=0.01, BSUoS, FiT, RO, AAHEDC, CfD, CM_Levy, LLF, TLM, Mgmt_Fee, Imbal_risk, RCRC, Elexon, TNuOS_charg, TNuOS_disch, DUoS_ch, DUoS_disch, energy_cost, min_revenue_per_n2ex1h_sell=0.01, min_revenue_per_epex30min_sell=0.01)#

Bases: BatteryCommercials

Parameters:
AAHEDC: float#
BSUoS: float#
CM_Levy: float#
CfD: float#
DUoS_ch: float#
DUoS_disch: float#
Elexon: float#
FiT: float#
Imbal_risk: float#
LLF: float#
Mgmt_Fee: float#
RCRC: float#
RO: float#
TLM: float#
TNuOS_charg: float#
TNuOS_disch: float#
energy_cost: float#
min_revenue_per_epex30min_sell: float#
min_revenue_per_n2ex1h_sell: float#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class battery_optimizer.uk.request.UKBatteryInitialConditions(*, initial_soe, initial_soe_kwh=None, perform_initial_soe_checks=False, initial_dch_kw, initial_dcl_kw, initial_dmh_kw=0.0, initial_dml_kw=0.0, initial_drh_kw=0.0, initial_drl_kw=0.0, battery_marketed=Empty DataFrame Columns: [] Index: [], dynamic_services_energy_throughput=Empty DataFrame Columns: [] Index: [])#

Bases: BatteryInitialConditions

Parameters:
battery_marketed: list[UKBatteryMarketed] | DataFrame#
dynamic_services_energy_throughput: list[DSEnergyThroughput] | DataFrame#
classmethod ensure_battery_marketed_dataframe(v)#
classmethod ensure_dynamic_services_energy_throughput_dataframe(v)#
initial_dch_kw: float#
initial_dcl_kw: float#
initial_dmh_kw: float#
initial_dml_kw: float#
initial_drh_kw: float#
initial_drl_kw: float#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class battery_optimizer.uk.request.UKBatteryMarketed(*, start, bought_intraday_kw, sold_intraday_kw, bought_virtual_wholesale_kw=0.0, sold_virtual_wholesale_kw=0.0, sold_n2ex1h_kw, bought_n2ex1h_kw, dcl_kw, dch_kw, dml_kw=0.0, dmh_kw=0.0, drl_kw=0.0, drh_kw=0.0, sold_epex30min_kw, bought_epex30min_kw)#

Bases: BatteryMarketed

Parameters:
bought_epex30min_kw: float#
bought_n2ex1h_kw: float#
dch_kw: float#
dcl_kw: float#
dmh_kw: float#
dml_kw: float#
drh_kw: float#
drl_kw: float#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

sold_epex30min_kw: float#
sold_n2ex1h_kw: float#
class battery_optimizer.uk.request.UKBatteryParameters(*, min_soe, max_soe, min_soe_kwh=None, max_soe_kwh=None, charging_efficiency, discharging_efficiency, max_charging_power_kw, max_discharging_power_kw, capacity_kwh, capacity_nominal_kwh=None, max_daily_cycle, daily_cycle_limit_applies=True, daily_cycle_limit_penalty=False, grid_connection_export_kw, grid_connection_import_kw, ramp_rates=UKRampRates(type=<RampingLogicName.dc_only: 'dc_only'>, charging_perf_related_kw_per_second=None, discharging_perf_related_kw_per_second=None, charging_tech_limited_kw_per_second=None, discharging_tech_limited_kw_per_second=None), power_swing_limit)#

Bases: BatteryParameters

Parameters:
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

power_swing_limit: PowerSwingLimit#
ramp_rates: UKRampRates#
class battery_optimizer.uk.request.UKCommercialObjective(*, commercial_columns=['revenue_PV', 'dc_high_revenue', 'dc_low_revenue', 'commodity_revenue', 'third_party_revenue', 'third_party_cost', 'commodity_cost'])#

Bases: CommercialObjective

Parameters:

commercial_columns (list[str])

commercial_columns: list[str]#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class battery_optimizer.uk.request.UKFeatureFlags(*, return_pickled_optimizer=False, perform_check_battery_marketed_is_in_correct_unit=True, da_ds_rounding_method='da_rerun', check_ds_energy_constraints_within_settlement_period=True, account_for_ds_energy_throughput_third_party_cost=False, mel_mil_version=MelMilVersion.v1)#

Bases: FeatureFlags

Parameters:
  • return_pickled_optimizer (bool)

  • perform_check_battery_marketed_is_in_correct_unit (bool)

  • da_ds_rounding_method (Literal['da_rerun', 'imbalance_rerun'])

  • check_ds_energy_constraints_within_settlement_period (bool)

  • account_for_ds_energy_throughput_third_party_cost (bool)

  • mel_mil_version (MelMilVersion)

account_for_ds_energy_throughput_third_party_cost: bool#
check_ds_energy_constraints_within_settlement_period: bool#
da_ds_rounding_method: Literal['da_rerun', 'imbalance_rerun']#
mel_mil_version: MelMilVersion#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class battery_optimizer.uk.request.UKMelMilNowRequest(*, request_id, country=Country.UK, user_id, request_creation_time, elastic_filter=False, already_auctioned_as_constants=False, asset, verbose=False, epsilon=1e-06, commercial_objective=UKCommercialObjective(commercial_columns=['revenue_PV', 'dc_high_revenue', 'dc_low_revenue', 'commodity_revenue', 'third_party_revenue', 'third_party_cost', 'commodity_cost']), multiplier_buy_sell_exceed_limit=10.0, upper_bound_risk_increment=None, imbalance=Imbalance(imbalance_cost_per_MWh=1000000.0, min_imbalance_cost_per_MWh=100000.0, imbalance_cost_shaping=True, provide_imbalance_cost_in_result=False), solve_optimization_problem_max_seconds=90.0, intraday_strategy=IntradayStrategy(name=<IntradayStrategyName.vwap: 'vwap'>, delivery_length=<IntradayDeliveryLength.half_hour: 'HalfHour'>, tradeable_time_window_minutes=NaT), intraday_result_aggregation=IntradayResultAggregation(name=<IntradayResultAggregationName.minmax: 'minmax'>, no_buckets=1), objective=UKObjective(name=<ObjectiveName.pnl: 'pnl'>, frozen_window_plus_buffer=Timedelta('0 days 01:30:00'), melmil_optimization_horizon=Timedelta('0 days 01:00:00')), solver_settings=SolverSettings(gapRel=0.01), do_input_for_intraday_bucketing_correct_bucket=True, elastic_soe_constraints=False, global_max_charging_power_kw=None, global_max_discharging_power_kw=None, feature_flags=UKFeatureFlags(return_pickled_optimizer=False, perform_check_battery_marketed_is_in_correct_unit=True, da_ds_rounding_method='da_rerun', check_ds_energy_constraints_within_settlement_period=True, account_for_ds_energy_throughput_third_party_cost=False, mel_mil_version=<MelMilVersion.v1: 'v1'>), use_case=UseCase.BESS, iterative_commodity_and_ds=True, keep_n2ex_constant_hourly=True, default_penalty_for_discharge_during_dc=1000000.0, apply_penalty_for_discharge_during_dc=False, balancing_mechanism=BalancingMechanism(activation_time_minutes=Timedelta('0 days 00:32:00'), activation_ratio=1.0), mel_mil_now_parameters)#

Bases: UKRequest

Parameters:
property first_timestamp_full_sp_after_timestamp: datetime#
property last_timestamp_full_sp_before_timestamp: datetime#
mel_mil_now_parameters: MelMilNowParameters#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

property remaining_fraction_of_sp: float#
class battery_optimizer.uk.request.UKObjective(*, name=ObjectiveName.pnl, frozen_window_plus_buffer=Timedelta('0 days 01:30:00'), melmil_optimization_horizon=Timedelta('0 days 01:00:00'))#

Bases: Objective

Parameters:
frozen_window_plus_buffer: int | str | Timedelta#
melmil_optimization_horizon: int | str | Timedelta#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class battery_optimizer.uk.request.UKPriceForecast(*, start, intraday=None, intraday_sell=None, intraday_buy=None, virtual_wholesale=None, virtual_wholesale_sell=None, virtual_wholesale_buy=None, dch=None, dcl=None, dmh=None, dml=None, drh=None, drl=None, n2ex1h=None, n2ex1h_sell=None, n2ex1h_buy=None, epex30min=None, epex30min_sell=None, epex30min_buy=None)#

Bases: PriceForecast

Parameters:
dch: float | None#
dcl: float | None#
dmh: float | None#
dml: float | None#
drh: float | None#
drl: float | None#
epex30min: float | None#
epex30min_buy: float | None#
epex30min_sell: float | None#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

n2ex1h: float | None#
n2ex1h_buy: float | None#
n2ex1h_sell: float | None#
class battery_optimizer.uk.request.UKRampRates(*, type=RampingLogicName.dc_only, charging_perf_related_kw_per_second=None, discharging_perf_related_kw_per_second=None, charging_tech_limited_kw_per_second=None, discharging_tech_limited_kw_per_second=None)#

Bases: RampRates

Parameters:
  • type (Literal[RampingLogicName.performance_related, RampingLogicName.dc_only])

  • charging_perf_related_kw_per_second (Annotated[float | None, Gt(gt=0.0)])

  • discharging_perf_related_kw_per_second (Annotated[float | None, Gt(gt=0.0)])

  • charging_tech_limited_kw_per_second (Annotated[float | None, Gt(gt=0.0)])

  • discharging_tech_limited_kw_per_second (Annotated[float | None, Gt(gt=0.0)])

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

type: Literal[RampingLogicName.performance_related, RampingLogicName.dc_only]#
class battery_optimizer.uk.request.UKRequest(*, request_id, country=Country.UK, user_id, request_creation_time, elastic_filter=False, already_auctioned_as_constants=False, asset, verbose=False, epsilon=1e-06, commercial_objective=UKCommercialObjective(commercial_columns=['revenue_PV', 'dc_high_revenue', 'dc_low_revenue', 'commodity_revenue', 'third_party_revenue', 'third_party_cost', 'commodity_cost']), multiplier_buy_sell_exceed_limit=10.0, upper_bound_risk_increment=None, imbalance=Imbalance(imbalance_cost_per_MWh=1000000.0, min_imbalance_cost_per_MWh=100000.0, imbalance_cost_shaping=True, provide_imbalance_cost_in_result=False), solve_optimization_problem_max_seconds=90.0, intraday_strategy=IntradayStrategy(name=<IntradayStrategyName.vwap: 'vwap'>, delivery_length=<IntradayDeliveryLength.half_hour: 'HalfHour'>, tradeable_time_window_minutes=NaT), intraday_result_aggregation=IntradayResultAggregation(name=<IntradayResultAggregationName.minmax: 'minmax'>, no_buckets=1), objective=UKObjective(name=<ObjectiveName.pnl: 'pnl'>, frozen_window_plus_buffer=Timedelta('0 days 01:30:00'), melmil_optimization_horizon=Timedelta('0 days 01:00:00')), solver_settings=SolverSettings(gapRel=0.01), do_input_for_intraday_bucketing_correct_bucket=True, elastic_soe_constraints=False, global_max_charging_power_kw=None, global_max_discharging_power_kw=None, feature_flags=UKFeatureFlags(return_pickled_optimizer=False, perform_check_battery_marketed_is_in_correct_unit=True, da_ds_rounding_method='da_rerun', check_ds_energy_constraints_within_settlement_period=True, account_for_ds_energy_throughput_third_party_cost=False, mel_mil_version=<MelMilVersion.v1: 'v1'>), use_case=UseCase.BESS, iterative_commodity_and_ds=True, keep_n2ex_constant_hourly=True, default_penalty_for_discharge_during_dc=1000000.0, apply_penalty_for_discharge_during_dc=False, balancing_mechanism=BalancingMechanism(activation_time_minutes=Timedelta('0 days 00:32:00'), activation_ratio=1.0))#

Bases: Request

Parameters:
apply_penalty_for_discharge_during_dc: bool#
asset: UKAsset#
property asset_time_series_input_matches_asset_strategy_optimization_names#
balancing_mechanism: BalancingMechanism | None#
property capacity_product_variables#
property capacity_products#
commercial_objective: UKCommercialObjective#
country: Country#
default_penalty_for_discharge_during_dc: float#
property energy_markets#
ensure_ds_marketed_respects_soe_management_rules()#
ensure_initial_dynamic_services_commitments_respect_initial_soe()#
ensure_time_dependent_prequalified_power()#

Ensure that the prequalified power of every dynamic service is either None, meaning no prequalification limit, or a pd.Series aligned with the optimization time range, so that optimization methods can always call .loc[timestamp] without type guards. Scalar inputs apply to the entire horizon. Records and Series must contain exactly one value for every settlement period; missing timestamps are not filled.

In contrast to the German FCR / aFRR prequalified_power_kW we do NOT default None to the maximal (dis-)charging power: in the UK those time series are already scaled with the battery availabilities, so their minimum would be zero for any availability gap and would silently forbid dynamic services. Availability is therefore handled by the (dis-)charging power limits alone, and unset prequalified power means “not limited by prequalification”.

feature_flags: UKFeatureFlags#
get_reserved_power_fraction_simultaneous_ds_both(ds_field_name)#
initialize_optional_time_series_input()#
intraday_strategy: IntradayStrategy#
iterative_commodity_and_ds: bool#
keep_n2ex_constant_hourly: bool#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

objective: UKObjective#
property reserved_power_fraction_simultaneous_dc_both#
property reserved_power_fraction_simultaneous_dm_both#
property reserved_power_fraction_simultaneous_dr_both#
use_case: UseCase#
warn_about_already_auctioned_ds_volumes_above_prequalified_power()#

Already auctioned dynamic service volumes are fixed in the optimization and hence cannot be limited to a lower prequalified power without rendering the problem infeasible. We only warn about them. Needs to run after “ensure_time_dependent_prequalified_power”.

class battery_optimizer.uk.request.UKStrategyOptimization(*, frequency_min, horizon, markets={}, optimization_id=None, ds_energy_throughput_buffer=DSEnergyThroughputBuffer(enabled=False, rev_factor=0.06, rev_factor_mdo_mdb=1.0, efficiency_correct=False), dc_parameters, dm_parameters=DMParameters(prequalified_power_high_kw=None, prequalified_power_low_kw=None, delivery_duration_sec=Timedelta('0 days 00:30:00'), baseline_buffer_perc=0.22, delivery_duration_buffer_perc=1.1, min_energy_recovery_perc=0.2), dr_parameters=DRParameters(prequalified_power_high_kw=None, prequalified_power_low_kw=None, delivery_duration_sec=Timedelta('0 days 01:00:00'), baseline_buffer_perc=0.44, delivery_duration_buffer_perc=1.1, min_energy_recovery_perc=0.2), trader_exclusion, perspective)#

Bases: StrategyOptimization

Parameters:
dc_parameters: DCParameters#
dm_parameters: DMParameters#
dr_parameters: DRParameters#
ds_energy_throughput_buffer: DSEnergyThroughputBuffer#
ensure_dc_ramp_rate_not_too_steep()#
horizon_min_max_in_trader_exclusion_timeframe()#
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'frozen': False, 'json_encoders': {<class 'datetime.datetime'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timedeltas.Timedelta'>: <function BaseModel.<lambda>>, <class 'pandas._libs.tslibs.timestamps.Timestamp'>: <function BaseModel.<lambda>>, <class 'pandas.core.frame.DataFrame'>: <function BaseModel.<lambda>>, <class 'pandas.core.series.Series'>: <function BaseModel.<lambda>>}, 'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

perspective: Literal['Customer', 'EON']#
trader_exclusion: list[TraderExclusion] | DataFrame#