Skip to main content

Stock replenishment simulation library.

Project description

replenishment

Stock replenishment simulation utilities.

Environment (uv + Python 3.12)

This repo is set up to use Python 3.12 via uv.

uv python install 3.12
uv venv --python 3.12
source .venv/bin/activate
uv pip install -e ".[dev]"

Run checks with:

uv run pytest

Usage

Plot-first usage

This README focuses on two visual workflows:

  • Mean forecast + safety stock optimization.
  • Percentile target optimization.

Example 1: mean forecast + safety stock

This example optimizes the safety stock factor on a backtest window, then applies the learned policy to the evaluation horizon.

from replenishment import (
    generate_standard_simulation_rows,
    optimize_point_forecast_policy_and_simulate_actuals,
    plot_replenishment_decisions,
    replenishment_decision_rows_to_dataframe,
    split_standard_simulation_rows,
    standard_simulation_rows_to_dataframe,
)

rows = generate_standard_simulation_rows(
    n_unique_ids=1,
    periods=18,
    start_date="2031-01-01",
    frequency_days=30,
    forecast_start_period=10,
    history_mean=52,
    history_std=6,
    forecast_mean=48,
    forecast_std=5,
    lead_time=2,
    initial_on_hand=30,
    current_stock=30,
    seed=7,
)
backtest_rows, eval_rows = split_standard_simulation_rows(rows)

optimized, _, decision_rows = optimize_point_forecast_policy_and_simulate_actuals(
    backtest_rows,
    eval_rows,
    candidate_factors=[0.8, 0.9, 1.0],
)

rows_df = standard_simulation_rows_to_dataframe(rows, library="pandas")
decision_df = replenishment_decision_rows_to_dataframe(decision_rows, library="pandas")

example_id = decision_df["unique_id"].iloc[0]
plot_replenishment_decisions(
    rows_df,
    decision_df,
    unique_id=example_id,
    title="Mean forecast + safety stock (optimized)",
    decision_style="line",
)

Mean forecast + safety stock example

Example 2: optimize percentile targets

This example learns the best percentile target on backtest rows and then plots replenishment decisions on forecast rows.

from replenishment import (
    PercentileForecastOptimizationPolicy,
    build_percentile_forecast_candidates_from_standard_rows,
    build_replenishment_decisions_from_simulations,
    generate_standard_simulation_rows,
    optimize_forecast_targets,
    plot_replenishment_decisions,
    replenishment_decision_rows_to_dataframe,
    simulate_replenishment_with_aggregation,
    split_standard_simulation_rows,
    standard_simulation_rows_to_dataframe,
)

rows = generate_standard_simulation_rows(
    n_unique_ids=1,
    periods=24,
    start_date="2031-01-01",
    frequency_days=30,
    forecast_start_period=14,
    history_mean=42,
    history_std=7,
    forecast_mean=40,
    forecast_std=5,
    lead_time=2,
    initial_on_hand=25,
    current_stock=25,
    seed=11,
    percentile_multipliers={"p50": 1.0, "p80": 1.2, "p95": 1.35},
)
backtest_rows, forecast_rows = split_standard_simulation_rows(rows)

percentile_configs = build_percentile_forecast_candidates_from_standard_rows(
    backtest_rows,
    include_mean=True,
    review_period=1,
    forecast_horizon=1,
)
optimized = optimize_forecast_targets(percentile_configs)

forecast_configs = build_percentile_forecast_candidates_from_standard_rows(
    forecast_rows,
    include_mean=True,
    review_period=1,
    forecast_horizon=1,
)
simulations = {}
for unique_id, config in forecast_configs.items():
    target = optimized[unique_id].target
    policy = PercentileForecastOptimizationPolicy(
        forecast=config.forecast_candidates[target],
        lead_time=config.lead_time,
    )
    simulations[unique_id] = simulate_replenishment_with_aggregation(
        periods=config.periods,
        demand=config.demand,
        initial_on_hand=config.initial_on_hand,
        lead_time=config.lead_time,
        policy=policy,
        aggregation_window=1,
        holding_cost_per_unit=config.holding_cost_per_unit,
        stockout_cost_per_unit=config.stockout_cost_per_unit,
        order_cost_per_order=config.order_cost_per_order,
        order_cost_per_unit=config.order_cost_per_unit,
    )

decision_rows = build_replenishment_decisions_from_simulations(
    forecast_rows,
    simulations,
    percentile_target={uid: optimized[uid].target for uid in simulations},
    review_period=1,
    forecast_horizon=1,
    rmse_window=1,
)
rows_df = standard_simulation_rows_to_dataframe(rows, library="pandas")
decision_df = replenishment_decision_rows_to_dataframe(decision_rows, library="pandas")

example_id = decision_df["unique_id"].iloc[0]
plot_replenishment_decisions(
    rows_df,
    decision_df,
    unique_id=example_id,
    title="Percentile forecast target (optimized)",
    decision_style="line",
)

Percentile target optimization example

Notebooks

For full runnable walkthroughs (including additional variants):

  • notebooks/mean_forecast_safety_stock_example.ipynb
  • notebooks/percentile_optimization_example.ipynb
  • notebooks/generated_data_example.ipynb

For data-loading and table-oriented flows (without plots), see:

  • notebooks/stock_replenishment_example.ipynb

Standard schema helpers

If you need CSV/DataFrame conversion helpers:

  • iter_standard_simulation_rows_from_csv
  • standard_simulation_rows_from_dataframe
  • build_point_forecast_article_configs_from_standard_rows

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

replenishment-0.1.1.tar.gz (7.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

replenishment-0.1.1-py3-none-any.whl (38.3 kB view details)

Uploaded Python 3

File details

Details for the file replenishment-0.1.1.tar.gz.

File metadata

  • Download URL: replenishment-0.1.1.tar.gz
  • Upload date:
  • Size: 7.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for replenishment-0.1.1.tar.gz
Algorithm Hash digest
SHA256 cf7dbdabf442e9825efb3dc152502aa95f38f77165e797f7512d64b7717e86bc
MD5 0ffb5a5c6c17fdf2c7dad3f38a80a8b0
BLAKE2b-256 ff11e9ba7cfdc04737d85e601c3e973d246377c568303a772ef92b061e70bd8b

See more details on using hashes here.

Provenance

The following attestation bundles were made for replenishment-0.1.1.tar.gz:

Publisher: release.yml on janrth/replenishment

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file replenishment-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: replenishment-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 38.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for replenishment-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6beb0a26fb8aa5c85c44aa65ac91eccd250832ceeda662bdf434384b0c96f7e9
MD5 2b77a8dbb5564a2bc2d78821a23ae913
BLAKE2b-256 ccfd92f05136e8733c839c54b454fd40609788d868d5e24a341a8ec776b347fc

See more details on using hashes here.

Provenance

The following attestation bundles were made for replenishment-0.1.1-py3-none-any.whl:

Publisher: release.yml on janrth/replenishment

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page