Python client for Site-Calc operational optimization (day-ahead bidding, ANS)
Project description
Site-Calc Operational Client
Python client for Site-Calc operational optimization API - day-ahead bidding and short-term dispatch with ancillary services.
Installation
Note: the package is not yet published to PyPI. Install from the GitHub source until the first PyPI release.
# From source (recommended while v1.x is unreleased on PyPI):
pip install git+https://github.com/stranma/site-calc-operational.git
# Or, for local development inside this repo:
git clone https://github.com/stranma/site-calc-operational.git
cd site-calc-operational
pip install -e ".[dev]"
Quick Start
from datetime import date
from site_calc_operational import OperationalClient
from site_calc_operational.models import (
TimeSpan, Resolution, Site, Battery, ElectricityImport,
OptimalBiddingRequest, MarketForecasts, OptimizationConfig
)
# Initialize client
client = OperationalClient(
base_url="https://api.site-calc.example.com",
api_key="op_your_api_key_here"
)
# Create 1-day optimization at 15-minute resolution
timespan = TimeSpan.for_day(date(2025, 11, 6), Resolution.MINUTES_15)
# Define devices
battery = Battery(
name="Battery1",
properties={
"capacity": 10.0,
"max_power": 5.0,
"efficiency": 0.90,
"initial_soc": 0.5
},
ancillary_services={
"afrr_plus": {
"can_provide": [1, 1, 1, 1, 1, 1],
"expected_activation_profit": [80.0, 85.0, 88.0, 90.0, 87.0, 82.0]
}
}
)
grid_import = ElectricityImport(
name="GridImport",
properties={"price": [30.0]*96, "max_import": 8.0}
)
site = Site(site_id="my_site", devices=[battery, grid_import])
# Market forecasts
forecasts = MarketForecasts(
ancillary_services={
"afrr_plus": {
"max_accepted_price_forecast": [15.0, 18.0, 22.0, 25.0, 23.0, 16.0],
"period_duration_hours": 4
}
}
)
# Create and submit optimization request
request = OptimalBiddingRequest(
sites=[site],
timespan=timespan,
market_forecasts=forecasts,
optimization_config=OptimizationConfig(max_bid_steps=10)
)
job = client.create_optimal_bidding_job(request)
result = client.wait_for_completion(job.job_id, timeout=600)
# Display results
print(f"Total Profit: €{result.summary.expected_profit:,.2f}")
print(f"DA Revenue: €{result.summary.total_da_revenue:,.2f}")
print(f"ANS Revenue: €{result.summary.total_ancillary_revenue:,.2f}")
Features
- ✅ Day-ahead market bidding optimization
- ✅ Ancillary services (aFRR, mFRR) optimization
- ✅ Multi-step bid curve generation
- ✅ Device scheduling with constraints
- ✅ 15-minute or 1-hour resolution
- ✅ Multi-site optimization
- ✅ Type-safe Pydantic models
- ✅ Automatic retry and error handling
Capabilities
| Feature | Value |
|---|---|
| Max Horizon | 296 intervals (~3 days at 15-min) |
| Resolution | 15-minute or 1-hour |
| ANS Support | Yes (aFRR±, mFRR±) |
| Binary Variables | Yes (CHP on/off) |
| Timeout | 300 seconds |
Supported Devices
- Battery (with ANS capabilities)
- CHP - Combined Heat and Power (with ANS capabilities)
- Heat Accumulator
- Photovoltaic
- Heat Demand
- Electricity Demand
- Electricity Import/Export (market interface)
- Gas Import (market interface)
- Heat Export (market interface)
Documentation
Full documentation available at: https://docs.site-calc.example.com/operational-client
Examples
See examples/ directory for complete examples:
optimal_bidding.py- Complete optimal bidding workflowdevice_planning.py- Device scheduling with locked ANS reservationsmulti_site.py- Multi-site optimization
Requirements
- Python ≥ 3.10
- API key with
op_prefix (operational client)
Development
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Format code
ruff format .
# Type check
mypy site_calc_operational
On-prem (sync) client
For self-hosted deployments running server-onprem:
from site_calc_operational import OnPremClient
with OnPremClient(base_url="https://onprem.example.com", api_key="op_...") as client:
health = client.health()
print(health.site_calc_version)
result = client.device_planning(request_payload)
print(result["summary"])
The on-prem client is sync (no polling). For the SaaS server, keep using OperationalClient (async).
MCP server (LLM-driven scenario building)
The package ships an optional Model Context Protocol server that exposes 17 tools for building and submitting operational device-planning scenarios from an LLM (Claude Desktop, ChatGPT, Cursor, etc.). It wraps OnPremClient and runs locally on the user's machine, so the LLM can persist generated profile data to disk and reference it in subsequent tool calls.
Install
# From source with the MCP extra:
pip install "site-calc-operational[mcp] @ git+https://github.com/stranma/site-calc-operational.git"
# Or in this checkout:
pip install -e ".[mcp]"
The extra pulls fastmcp>=2.0. Once installed, the package exposes both a console script (site-calc-operational-mcp) and a module entry point (python -m site_calc_operational.mcp).
Configure the MCP client
Set environment variables so the server can reach your on-prem deployment:
export SITE_CALC_OPERATIONAL_API_URL="http://localhost:8000" # or your on-prem URL
export SITE_CALC_OPERATIONAL_API_KEY="op_..." # mint with: site-calc-op create-user
export SITE_CALC_OPERATIONAL_DATA_DIR="$HOME/.site-calc/data" # optional; for save_data_file output
Then register the server with your MCP-aware client. For Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):
{
"mcpServers": {
"site-calc-operational": {
"command": "site-calc-operational-mcp",
"env": {
"SITE_CALC_OPERATIONAL_API_URL": "http://localhost:8000",
"SITE_CALC_OPERATIONAL_API_KEY": "op_..."
}
}
}
}
If your environment requires python -m, swap command for python and add "args": ["-m", "site_calc_operational.mcp"].
Tools exposed
| Category | Tools |
|---|---|
| Server info | health, get_version |
| Scenario assembly | create_scenario, add_device, remove_device, set_timespan, set_optimization_config, review_scenario, delete_scenario, list_scenarios |
| Solving | solve (synchronous), cancel_active |
| Run inspection | get_run, list_runs |
| Schema / data | get_device_schema, save_data_file, fetch_url |
Workflow
The LLM is expected to follow this loop:
create_scenario(name, site_id)– starts a fresh draft.add_device(...)– one call per device. Useget_device_schema(device_type)first to discover required properties.set_timespan(period_start, period_end, resolution)– ISO datetimes; resolution is"15min" | "30min" | "1h".- (optional)
set_optimization_config(...)– override solver / objective defaults. review_scenario(scenario_id)– inspectvalidation.valid; fix any reported errors before submitting.solve(scenario_id, idempotency_key=...)– synchronous solve; returnsrun_idplus the server-side summary.get_run(run_id)– fetch full per-device schedules from the on-prem server when needed.
Profile-shaped properties (price, demand_profile, generation_profile, …) accept three forms:
- a scalar (broadcast to the timespan), e.g.
"price": 50.0; - an explicit list (validated against the interval count), e.g.
"price": [55, 50, 48, ...]; - a file reference, e.g.
"price": {"file": "C:/data/prices.csv", "column": "price_eur_mwh"}.
save_data_file and fetch_url exist so the LLM can persist arrays it has generated, or download remote CSVs, and then reference them in the file form above.
License
MIT License
Support
- Issues: https://github.com/site-calc/operational-client/issues
- Documentation: https://docs.site-calc.example.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file site_calc_operational-0.1.0.tar.gz.
File metadata
- Download URL: site_calc_operational-0.1.0.tar.gz
- Upload date:
- Size: 163.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
74631cd73041bf2d637e7e7e5712146cec0f4b285e9e0821e21a19fc5728fc50
|
|
| MD5 |
8945f52baff96f5a878e68d1dcc819ef
|
|
| BLAKE2b-256 |
1f07d2a83cf4a4427162c13cf02b9dce6bf52cd3297207b7a0d29c1dcb3f5888
|
Provenance
The following attestation bundles were made for site_calc_operational-0.1.0.tar.gz:
Publisher:
publish.yml on stranma/site-calc-operational
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
site_calc_operational-0.1.0.tar.gz -
Subject digest:
74631cd73041bf2d637e7e7e5712146cec0f4b285e9e0821e21a19fc5728fc50 - Sigstore transparency entry: 1459173102
- Sigstore integration time:
-
Permalink:
stranma/site-calc-operational@14dc27a2c43599ea1072b1990c7c8496d4c20492 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/stranma
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@14dc27a2c43599ea1072b1990c7c8496d4c20492 -
Trigger Event:
release
-
Statement type:
File details
Details for the file site_calc_operational-0.1.0-py3-none-any.whl.
File metadata
- Download URL: site_calc_operational-0.1.0-py3-none-any.whl
- Upload date:
- Size: 38.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
270319dfc4c510ecb4552d4586246ab9315d66dc6c97ea7e8d8f223f25e88a02
|
|
| MD5 |
7ca2628a25e9baf970b1b3a557f96c1b
|
|
| BLAKE2b-256 |
55fba378a97db63b59130da95f83b9547cc76f0919074044710f485749944ecd
|
Provenance
The following attestation bundles were made for site_calc_operational-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on stranma/site-calc-operational
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
site_calc_operational-0.1.0-py3-none-any.whl -
Subject digest:
270319dfc4c510ecb4552d4586246ab9315d66dc6c97ea7e8d8f223f25e88a02 - Sigstore transparency entry: 1459173199
- Sigstore integration time:
-
Permalink:
stranma/site-calc-operational@14dc27a2c43599ea1072b1990c7c8496d4c20492 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/stranma
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@14dc27a2c43599ea1072b1990c7c8496d4c20492 -
Trigger Event:
release
-
Statement type: