Site-Calc Investment Client
Python client for Site-Calc investment planning API - long-term capacity planning and ROI analysis.
Installation
pip install site-calc-investment
Quick Start
from datetime import datetime
from zoneinfo import ZoneInfo
from site_calc_investment import InvestmentClient
from site_calc_investment.models import (
Resolution,
Site,
Battery,
ElectricityImport,
ElectricityExport,
InvestmentPlanningRequest,
InvestmentParameters,
OptimizationConfig,
)
from site_calc_investment.models.requests import TimeSpanInvestment
# Initialize client
client = InvestmentClient(base_url="https://api.site-calc.example.com", api_key="inv_your_api_key_here")
# Create 1-week planning horizon (1-hour resolution)
timespan = TimeSpanInvestment(
start=datetime(2025, 1, 1, tzinfo=ZoneInfo("Europe/Prague")),
intervals=168, # 1 week = 7 days × 24 hours
resolution=Resolution.HOUR_1,
)
# Generate hourly prices (example: day/night pattern)
prices = [30.0 if h % 24 < 6 else 80.0 if 8 <= h % 24 < 20 else 50.0 for h in range(168)]
# Define devices (NO ancillary_services field)
battery = Battery(
name="Battery1",
properties={"capacity": 10.0, "max_power": 5.0, "efficiency": 0.90, "initial_soc": 0.5},
investment={"capital_cost": 500000, "annual_opex": 5000}, # For client-side NPV
)
grid_import = ElectricityImport(name="GridImport", properties={"price": prices, "max_import": 10.0})
grid_export = ElectricityExport(name="GridExport", properties={"price": prices, "max_export": 10.0})
site = Site(site_id="investment_site", devices=[battery, grid_import, grid_export])
# Global investment parameters (per-device costs live on the devices)
inv_params = InvestmentParameters(
discount_rate=0.05,
project_lifetime_years=10, # Required field
)
# Create and submit optimization request
request = InvestmentPlanningRequest(
sites=[site],
timespan=timespan,
investment_parameters=inv_params,
optimization_config=OptimizationConfig(
objective="maximize_profit", # Options: maximize_profit, minimize_cost, maximize_self_consumption
time_limit_seconds=300, # Max 3600 seconds (60 min)
),
)
job = client.create_planning_job(request)
result = client.wait_for_completion(job.job_id, poll_interval=5, timeout=600)
print(f"Status: {result.status}")
print(f"Solver: {result.summary.solver_status}")
print(f"Profit: €{result.summary.expected_profit:,.2f}")
Features
- ✅ Long-term capacity planning (1-10 years)
- ✅ Investment ROI analysis (NPV, IRR, payback)
- ✅ Per-device investment costs (capital_cost / annual_opex) for client-side NPV
- ✅ Capacity reservations: per-period capacity limits and charges with automatic cheapest-tariff assignment
- ✅ BESS investment sizing: optimizer-sized battery power (MW) and energy capacity (MWh)
- ✅ Czech distribution-tariff import device (monthly T1/T2 capacity tariff, 2027 structure)
- ✅ Scenario comparison utilities
- ✅ Financial analysis helpers
- ✅ 1-hour resolution optimization
- ✅ Multi-site optimization
- ✅ Type-safe Pydantic models
- ✅ Automatic retry and error handling
- ✅ Job management (cancel single or all jobs)
Capabilities
| Feature | Value |
|---|---|
| Max Horizon | 100,000 intervals (~11 years at 1-hour) |
| Resolution | 1-hour only |
| ANS Support | No |
| Binary Variables | Relaxed to continuous |
| Timeout | 3600 seconds (60 minutes) max |
Supported Devices
- Battery (NO ANS; optional
power_sizing/capacity_sizingreservations let the optimizer size installed MW / MWh) - CHP - Combined Heat and Power (continuous operation)
- Heat Accumulator
- Photovoltaic:
photovoltaic_nonsteerable(exact profile) /photovoltaic_steerable(curtailable up to a max-power profile) - Fixed Production / Fixed Consumption (follow a power profile exactly)
- Max-Power Production / Consumption (steerable in [0, max] at a linear EUR/MWh cost or value)
- Heat Demand
- Electricity Demand
- Electricity Import/Export (market interface; optional
capacity_reservationfor per-period grid capacity tariffs) - Czech Distribution Import (
cz_distribution_import: electricity import billed under the Czech monthly T1/T2 capacity tariff) - Import with Overflow (
electricity_import_with_overflow: net-metered grid connection, consumption billed at the import price and the surplus paid at the overflow price; either importing or exporting in any hour, never both) - Gas Import (market interface)
- Heat Export (market interface)
Every device also accepts an optional investment block ({"capital_cost": EUR, "annual_opex": EUR/year}) used only for client-side NPV/IRR analysis -- it is stripped from the API payload.
Job Management
The client provides methods for managing optimization jobs:
# Create a job
job = client.create_planning_job(request)
print(f"Job ID: {job.job_id}")
# Check job status
status = client.get_job_status(job.job_id)
print(f"Status: {status.status}, Progress: {status.progress}%")
# Wait for completion
result = client.wait_for_completion(job.job_id, poll_interval=30, timeout=7200)
# Cancel a single job
cancelled = client.cancel_job(job.job_id)
# Cancel all pending/running jobs (bulk cancel)
result = client.cancel_all_jobs()
print(f"Cancelled {result['cancelled_count']} jobs")
Financial Analysis
from site_calc_investment.analysis import (
calculate_investment_metrics,
calculate_npv,
calculate_irr,
calculate_payback_period,
compare_scenarios,
)
# All-in-one: NPV/IRR/payback from annual aggregates + device investment blocks
metrics = calculate_investment_metrics(
annual_revenues=result.investment_metrics.annual_revenue_by_year,
annual_costs=result.investment_metrics.annual_costs_by_year, # already includes capacity-reservation charges
discount_rate=0.05,
devices=site.devices,
)
print(metrics["npv"], metrics["irr"], metrics["payback_period_years"])
# NPV calculation
npv = calculate_npv(cash_flows=annual_revenues, discount_rate=0.05, initial_investment=-1500000)
# IRR calculation
irr = calculate_irr([-1500000] + annual_revenues)
# Scenario comparison
comparison = compare_scenarios([result_5mw, result_10mw, result_15mw], names=["5 MW", "10 MW", "15 MW"])
print(comparison) # DataFrame with NPV, IRR, costs, revenues
MCP Server (LLM Integration)
The package includes an MCP server for use with Claude Desktop, ChatGPT, and other MCP-compatible LLM tools.
Installation
pip install site-calc-investment[mcp]
Claude Desktop Configuration
Add to claude_desktop_config.json:
{
"mcpServers": {
"site-calc-investment": {
"command": "uvx",
"args": ["--from", "site-calc-investment[mcp]", "site-calc-investment-mcp"],
"env": {
"INVESTMENT_API_URL": "http://your-api-url",
"INVESTMENT_API_KEY": "inv_your_key_here",
"INVESTMENT_DATA_DIR": "/path/to/data/directory"
}
}
}
}
For local development against a source checkout, use uv run --directory instead:
{
"mcpServers": {
"site-calc-investment": {
"command": "uv",
"args": ["run", "--directory", "/path/to/client-investment", "site-calc-investment-mcp"],
"env": {
"INVESTMENT_API_URL": "http://your-api-url",
"INVESTMENT_API_KEY": "inv_your_key_here",
"INVESTMENT_DATA_DIR": "/path/to/data/directory"
}
}
}
}
ChatGPT Configuration
ChatGPT supports MCP via streaming HTTP or SSE (stdio is not supported). This requires:
- An eligible ChatGPT plan with Developer Mode access (Pro, Plus, Business, Enterprise, or Education). Business plan restricts Developer Mode to admins/owners; other plans may have similar admin gating.
- Developer Mode enabled (navigate to Settings -- the exact path may vary by account type)
Step 1 -- Start the MCP server in HTTP mode:
cd /path/to/client-investment
INVESTMENT_API_URL="http://your-api-url" \
INVESTMENT_API_KEY="inv_your_key_here" \
INVESTMENT_DATA_DIR="/path/to/data" \
uv run fastmcp run site_calc_investment.mcp.server:mcp --transport http --port 8000
Step 2 -- Expose to the internet (for local development):
ngrok http 8000
For production, deploy the server to a publicly accessible host instead.
Step 3 -- Add to ChatGPT:
- Open ChatGPT Settings > Apps & Connectors (or Connectors) > Advanced settings > Enable Developer Mode
- Click Create app
- Enter the ngrok HTTPS URL (e.g.
https://abc123.ngrok.io/mcp) as the server endpoint - Name: "Site-Calc Investment", Description: "Investment planning optimization tools"
- Click Refresh to load the tool list
Note: ChatGPT shows tool call details and may require manual confirmation for actions. The
save_data_filetool requires the server to have local filesystem access.
Tools (17)
| Tool | Description |
|---|---|
get_version |
Get server and package version info |
create_scenario |
Create a new draft scenario |
add_device |
Add a device (battery, CHP, PV, etc.), optionally with an investment cost block |
set_timespan |
Set optimization time horizon |
set_investment_params |
Set global financial parameters (discount rate, project lifetime) |
review_scenario |
Review scenario before submission |
remove_device |
Remove a device |
delete_scenario |
Delete a scenario |
list_scenarios |
List all draft scenarios |
submit_scenario |
Submit for optimization |
get_job_status |
Check job progress |
get_job_result |
Get optimization results (incl. capacity reservation summaries) |
cancel_job |
Cancel a job |
list_jobs |
List all jobs |
get_device_schema |
Get device property schema |
save_data_file |
Save generated data as CSV |
fetch_url |
Download a file (e.g. CSV price data) from a URL |
save_data_file lets the LLM write generated data (price arrays, demand profiles) to local CSV files, which can then be referenced in add_device properties.
See docs/MCP_SERVER_SPEC.md for full specification.
Documentation
Full documentation available at: https://github.com/stranma/site-calc-investment#readme
Examples
See examples/ directory for complete examples:
01_basic_capacity_planning.py- one-week battery capacity planning workflow02_scenario_comparison.py- compare device configurations03_financial_analysis.py- investment ROI calculation04_import_with_overflow.py- net-metered import with overflow: the device, a hand-built pairing, and why it matters
Requirements
- Python ≥ 3.10
- API key with
inv_prefix (investment client)
Key Differences from Operational Client
- ❌ No
/optimal-biddingendpoint - ❌ No
ancillary_serviceson devices - ❌ No 15-minute resolution (1-hour only)
- ✅ Up to 100,000 intervals (vs. 296)
- ✅ Investment metrics (NPV, IRR, payback)
- ✅ Financial analysis helpers
Development
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Format code
ruff format .
# Type check
mypy site_calc_investment
License
MIT License
Support
- Issues: https://github.com/stranma/site-calc-investment/issues
- Documentation: https://github.com/stranma/site-calc-investment#readme
Part of the Site-Calc platform.
Metadata
Release files for site-calc-investment 1.5.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| site_calc_investment-1.5.2.tar.gz | 264.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| site_calc_investment-1.5.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 322.8 kB
Release files / site_calc_investment-1.5.2.tar.gz
| Download URL | site_calc_investment-1.5.2.tar.gz |
|---|---|
| Size | 264.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6456f686c4b165563b45b5ac7bec733b37b655cc3c0d602206d1087ab36717b2
|
|
BLAKE2b-256 checksum How to use checksums |
ce39ee68196283f30e8ab7f6b71f130a30f7b17c06d591fa351efb30462da5d1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.
Transparency logRelease files / site_calc_investment-1.5.2-py3-none-any.whl
| Download URL | site_calc_investment-1.5.2-py3-none-any.whl |
|---|---|
| Size | 58.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
20430212c6e860dc302b80fb0dfcce15502813c545a6bf06f9501e257f5576a3
|
|
BLAKE2b-256 checksum How to use checksums |
05129a610e1465faba09ae2df6b1267f64d5db556d31b2c7eb0337b3eb2ecc97
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.
Transparency log