Skip to main content
The built-in generators cover a wide span of processes, but you will sometimes need a data-generating process they do not produce. BaseGenerator exists for that: subclass it, implement one method, and your generator inherits timestamp handling, reproducible seeding, parallel generation, pattern injection (changepoints, anomalies, missingness), exogenous variables, and every dataframe backend. This guide builds a small logistic-growth (“S-curve”) generator, a shape none of the built-ins produce directly.
The contract A generator needs three things:
  1. Subclass BaseGenerator.
  2. Declare its parameters as Pydantic Field attributes.
  3. Implement generate_single_series(self, length: int) -> np.ndarray, returning one series of the requested length. Draw randomness from self.rng so seeding and parallel generation stay deterministic.
Everything else is inherited.

What you implement, and what you get

generate_single_series is the only required method. It receives a target length — which varies per series between min_length and max_length — and returns a plain NumPy array. The base class attaches timestamps derived from freq, assigns the unique_id/ds/y columns, applies any requested pattern injection, and materializes the result in the chosen dataframe backend. The generator is a Pydantic model, so the shared parameters (min_length, freq, seed, engine, and the injection controls) are available without redeclaring them.

Generate a panel

Pattern injection is inherited

Because changepoint, anomaly, and missingness injection live in BaseGenerator, they work on the custom generator with no extra code: enabling them is a matter of setting the same parameters the built-in generators use. Below the S-curve is generated with injected spikes and dips and a level changepoint, and the ground-truth anomaly_flag column is requested through ExogenousConfig so the injected points can be marked.
Draw from self.rng, not np.random self.rng is a seeded NumPy Generator. Using it is what lets a fixed seed reproduce identical output and keeps parallel generation deterministic. Each series is generated with its own derived RNG state, so series differ from one another while the whole panel stays reproducible.

Contributing a generator back

To ship a generator as part of SynForecast rather than defining it inline, follow the same layout as the built-ins:
  1. Add the module under synforecast/generators/.
  2. Export the class from synforecast/generators/__init__.py.
  3. Add tests under tests/, and a page under nbs/docs/generators/<category>/.
See CONTRIBUTING and the generator reference for the conventions and verification expectations.