Policies¶
A policy is an ordering rule: given the inventory state, how much should be ordered? Stockcast ships the classic rules of inventory theory and lets you write your own with the same interface.
At a glance¶
| Policy | Notation | When it orders | How much |
|---|---|---|---|
OrderUpToPolicy |
\((R, S)\) | every scheduled opportunity | \(\max(0, S - \mathit{IP})\) |
ReorderPointPolicy("sQ") |
\((s, Q)\) | when \(\mathit{IP} \le s\) | \(Q\) |
ReorderPointPolicy("sS") |
\((s, S)\) | when \(\mathit{IP} \le s\) | \(\max(0, S - \mathit{IP})\) |
ReorderPointPolicy("sS"), review_period=R |
\((R, s, S)\) | at scheduled opportunities when \(\mathit{IP} \le s\) | \(\max(0, S - \mathit{IP})\) |
SingleOrderPolicy |
newsvendor | once | up to a season target |
| Your own | – | your rule | your rule |
Choosing a policy¶
flowchart TD
A{"Can you order<br/>more than once?"} -- no --> N["SingleOrderPolicy<br/>(newsvendor)"]
A -- yes --> B{"Does each order have<br/>a fixed size, e.g. a pallet?"}
B -- yes --> SQ["ReorderPointPolicy (s, Q)"]
B -- no --> C{"Is placing an order<br/>costly compared with holding stock?"}
C -- "no, order whenever there is a chance" --> RS["OrderUpToPolicy (R, S)"]
C -- "yes, only order when low" --> D{"Fixed ordering days?"}
D -- yes --> RSS["ReorderPointPolicy (R, s, S)<br/>review_period=R"]
D -- "no, check every period" --> SS["ReorderPointPolicy (s, S)<br/>review_period=1"]
These are starting points. Because every policy runs through the
same engine and event table, the fastest way to choose is often to simulate the
candidates side by side with
run_comparison.
The shared interface¶
Every policy is configured with the same operational facts:
| Argument | Meaning |
|---|---|
lead_time |
\(L \ge 0\), periods from order to delivery |
review_period or schedule |
when the policy may order (see Decision schedules) |
service_level |
the probability \(\alpha\) its targets represent, or None for planner targets |
allow_backorders |
True for backorders, False for lost sales |
Then it follows the same lifecycle:
policy = PolicyClass(lead_time=..., review_period=..., ...) # configure
policy.fit(targets, ...) # bind forecast information
decision = policy.predict(state, current_period=...) # propose orders
predict returns an OrderDecision: one row per
SKU with order_quantity and the reasoning behind it (target_level,
inventory_position, reorder_point, order_period,
expected_delivery_period).
Fitted policies also let you inspect what they learned:
| Method | Returns |
|---|---|
get_target_levels() |
\(S\) per SKU (order-up-to and single-order) |
get_parameters() |
\(s\) with \(Q\) or \(S\) per SKU (reorder-point and periodic review) |
get_target_metadata() |
probability, horizon, origin, end date, source |
Policies and stock¶
Policies decide; they never change stock. They receive a copy of the state and return a request. Constraints, callbacks, suppliers, receipts, and demand are all applied by the engine, after the policy. As a result, every policy is judged by the same accounting.
See also: Walkthrough step 4 · Notebook 05b · Notebook 06