optionchain_simulator 0.2.8

OptionChain-Simulator is a lightweight REST API service that simulates an evolving option chain with every request. It is designed for developers building or testing trading systems, backtesters, and visual tools that depend on option data streams but want to avoid relying on live data feeds.
# OptionChain-Simulator — environment variables
#
# A BLANK VALUE MEANS UNSET. `KNOB=` and `KNOB="   "` are read exactly as if the
# line were not here at all, and the documented default applies, so an optional
# knob is switched off by commenting it out rather than by emptying it.
#
# ONE EXCEPTION: CLICKHOUSE_PASSWORD. An empty ClickHouse password is a real
# configuration — a stock `default` user has none — so a PRESENT variable is
# taken as written, empty included, and only an absent one falls back.
#
# VALUES ARE NOT TRIMMED. Whitespace decides whether a value is blank, and
# nothing more: a credential written with a leading space authenticates with
# that space. Numbers and host names are trimmed where they are parsed, so
# `REDIS_PORT= 6379 ` still works.
#
# Every variable the service reads is listed here with its default and its
# accepted range. Copy this file to `.env` and change what you need; anything
# left unset takes the default shown.
#
# Two behaviours to know before editing:
#
#   * The v2 knobs (OCS_V2_*, OCS_MAX_CACHED_*) and the server knobs
#     (OCS_BIND_ADDRESS, OCS_PORT) are VALIDATED AT STARTUP. An
#     invalid value fails the process with a message naming the variable,
#     because silently reverting to a default would change how long simulations
#     live or how much memory the service holds without anyone noticing.
#
#   * The v1 request caps (OCS_MAX_STEPS, OCS_MAX_CHAIN_SIZE,
#     OCS_MAX_HISTORICAL_PRICES) and OCS_MAX_CACHED_WALKS warn and fall back to
#     their defaults instead, because a bad value there degrades one request
#     rather than the service.


# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------

# Verbosity of the tracing subscriber. Case-insensitive. An unrecognised value
# warns once at startup and falls back to the default rather than aborting. The
# effective level is logged at that same level, so setting WARN or ERROR still
# confirms itself.
#
# DEBUG is loud: it includes hyper connection traces on every request, which for
# a batch consumer materialising hundreds of tapes buries anything worth reading.
# Values: TRACE | DEBUG | INFO | WARN | ERROR.  Default: INFO
LOGLEVEL=INFO


# ---------------------------------------------------------------------------
# HTTP server
# ---------------------------------------------------------------------------

# The interface to bind. Accepts an IP address, or the words `all` (0.0.0.0)
# and `localhost` (127.0.0.1). Unlike the request caps, an unusable value here
# FAILS STARTUP naming the variable: a service that quietly ignored it would
# listen somewhere nobody asked for.
#
# The default is loopback, which is a CHANGE: the address used to be a constant
# 0.0.0.0. This service has no authentication and no rate limiting, so being
# reachable off the host is now a decision. A container must set 0.0.0.0 for
# its published port to lead anywhere; Docker/docker-compose.yml does.
# Default: localhost
# OCS_BIND_ADDRESS=localhost

# The port to bind, 1-65535. Also fails startup when unusable. 0 is refused
# along with the rest: the operating system would pick a free port, which is
# the opposite of what someone running several instances on known ports wants.
# Default: 7070
# OCS_PORT=7070


# ---------------------------------------------------------------------------
# Binary exports
# ---------------------------------------------------------------------------

# Rows per block in the `arrow` and `packed` export encodings. Both are
# columnar, so a column cannot be written until its last row is known: rows are
# buffered a block at a time, and this is what an export's memory is a function
# of rather than the number of steps. Wider blocks amortise the per-block
# overhead; narrower ones lower the memory floor. An invalid value warns and
# falls back.
# Default: 4096
# OCS_EXPORT_BLOCK_ROWS=4096


# ---------------------------------------------------------------------------
# Request caps (v1 and v2)
# ---------------------------------------------------------------------------
# These bound what a single request may ask for, so a pathological one cannot
# exhaust memory or CPU. Unset or invalid values fall back to the default and
# emit a warning.

# Maximum simulation steps per session or simulation.
# Range: >= 1.  Default: 10000
OCS_MAX_STEPS=10000

# Maximum option-chain size (strikes) per request.
# Range: >= 1.  Default: 500
OCS_MAX_CHAIN_SIZE=500

# Maximum number of embedded prices a Historical walk request may carry.
# Range: >= 1.  Default: 100000
OCS_MAX_HISTORICAL_PRICES=100000

# How many CPU-heavy pricing jobs may run at once, across the whole service.
# One job is up to OCS_MAX_SNAPSHOT_CONTRACTS contracts of pricing plus the
# encoding of the result, so this is the bound that stops a burst of requests
# from taking every core and starving the ones that asked for nothing expensive.
# It covers the v1 and v2 greek renderers AND the v2 snapshot build, since they
# compete for the same cores. Requests above the bound WAIT rather than fail,
# and they wait in async code, so a client that disconnects while queued costs
# nothing. A `greeks=none` render never takes a permit.
# Range: >= 1.  Default: 4
OCS_MAX_CONCURRENT_PRICING_JOBS=4


# ---------------------------------------------------------------------------
# v1 domain cache
# ---------------------------------------------------------------------------

# Maximum random walks held in the v1 simulation cache, evicted
# least-recently-used. Eviction never changes what a client sees: a rebuilt
# walk reproduces the identical tape from the same seed.
# Range: >= 1.  Default: 1000
OCS_MAX_CACHED_WALKS=1000


# ---------------------------------------------------------------------------
# v2 rolling simulations
# ---------------------------------------------------------------------------
# These are about REAL time and REAL memory. None of them touch the simulated
# clock, which may span years regardless of how long a simulation is kept.

# How long an idle v2 simulation is kept before the retention sweep reaps it,
# in seconds. Measured from the last WRITE, not the last read: peeking a
# snapshot persists nothing, so a client that only peeks does not refresh the
# window.
#
# Deliberately longer than v1's 1800 s: a v2 simulation is walked one request
# at a time over a long simulated horizon, and losing it to a short idle
# timeout would make that horizon unusable.
#
# Range: 1 .. 2592000 (30 days).  Default: 3600
OCS_V2_RETENTION_SECS=3600

# How often the retention sweep runs, in seconds. Each pass reaps expired
# simulations and evicts the factor tapes and snapshots they left behind.
# Range: 1 .. 3600.  Default: 60
OCS_V2_CLEANUP_INTERVAL_SECS=60

# Maximum v2 factor tapes held resident, evicted least-recently-used. A tape is
# one small row per step, so this bound is generous relative to the snapshot
# one.
# Range: 1 .. 1000000.  Default: 64
OCS_MAX_CACHED_TAPES=64

# Maximum v2 snapshots held resident, evicted least-recently-used. A snapshot
# holds every strike of every live expiration, so this bound is deliberately
# small. Eviction is never observable in what is served — a rebuilt snapshot
# prices identically, because it is a pure function of the factor row and the
# planner.
# Range: 1 .. 1000000.  Default: 256
OCS_MAX_CACHED_SNAPSHOTS=256

# Maximum contracts one v2 snapshot may price: strikes x the sum of every
# schedule rule's target_count. The chain-size and expiration caps are each
# reasonable alone and multiply into half a million contracts for one /snapshot
# call, which this bounds. Reported against chain_size, the field a client can
# lower without changing what the simulation means.
# Range: 1 .. 10000000.  Default: 200000
OCS_MAX_SNAPSHOT_CONTRACTS=200000

# Maximum contracts held resident across every cached v2 snapshot. This is the
# bound that protects memory — OCS_MAX_CACHED_SNAPSHOTS bounds the map, and one
# entry is a few hundred contracts in the reference configuration and hundreds
# of thousands in a large one. Roughly a gigabyte at the default: a contract is
# 848 bytes resident since the greek snapshots landed, so 1 200 000 of them is
# about 1.02 GB. A snapshot heavier than this whole budget is served without
# being cached, rather than evicting everything to make room for something that
# still would not fit.
# Range: 1 .. 100000000.  Default: 1200000
OCS_MAX_CACHED_SNAPSHOT_CONTRACTS=1200000

# Maximum steps one export request may cover. Bounds the WORK, not the memory:
# streaming already keeps the response bounded, but an option_chains export is
# steps x expirations x strikes priced contracts, so an unbounded range is
# minutes of CPU. A client wanting more pages the range with from_step/to_step.
# Range: 1 .. 10000000.  Default: 100000
OCS_MAX_EXPORT_ROWS=100000

# ---------------------------------------------------------------------------
# v2 snapshot persistence (ClickHouse)
# ---------------------------------------------------------------------------
# Files every ADVANCED v2 step in ClickHouse as an immutable, queryable tape:
# one metadata row per step plus one flattened row per expiration and strike.
# A peek and an export replay and persist nothing.
# OFF by default — a deployment without ClickHouse does not have to name the
# feature to not use it, and with it off nothing is added to the serving path.
# Turning it ON makes ClickHouse a HARD STARTUP DEPENDENCY: the tables are
# created at boot, so a schema or connectivity problem fails the boot rather
# than surfacing later as a warning. Writes themselves are detached from the
# request, so a warehouse that degrades later cannot delay a response.
# Values: true/false/1/0/yes/no/on/off.  Default: false
OCS_SNAPSHOT_PERSISTENCE_ENABLED=false

# Maximum quote rows one snapshot may carry into storage. Every quote row of a
# snapshot goes in ONE insert, so this bounds the snapshot and the statement
# alike: a snapshot above it is rejected before a row is written. It must be at
# least OCS_MAX_SNAPSHOT_CONTRACTS, or the API would accept simulations the
# warehouse could never persist — startup refuses that combination rather than
# leaving it to be found in the logs.
# Range: 1 .. 5000000.  Default: 200000 (matches OCS_MAX_SNAPSHOT_CONTRACTS)
OCS_SNAPSHOT_BATCH_ROWS=200000

# Maximum rows one read may return, so a range query cannot be unbounded.
# Range: 1 .. 10000000.  Default: 1000000
OCS_SNAPSHOT_MAX_READ_ROWS=1000000

# How long an insert may take before it is an error the advance tolerates.
# Range: 1 .. 600.  Default: 30
OCS_SNAPSHOT_INSERT_TIMEOUT_SECS=30

# How long persisted snapshots are kept, as a row-level TTL. Anchored on the
# ingestion timestamp, so a backfilled step is kept for its full window.
# Changing this on a live deployment needs an explicit ALTER TABLE MODIFY TTL.
# Range: 1 .. 3650.  Default: 90
# NOTE (issue #74): a quote row now stores the full twelve-greek set per option
# style, which measured 121 -> 363 compressed bytes per row on a real tape. The
# same RETENTION_DAYS therefore buys roughly a THIRD of the history it used to
# for the same disk. The greek columns are added to an existing table
# automatically at startup; nothing has to be run by hand.
OCS_SNAPSHOT_RETENTION_DAYS=90


# ---------------------------------------------------------------------------
# Redis — session and simulation storage
# ---------------------------------------------------------------------------
# Never commit a real password. This file documents the shape; `.env` holds the
# values and is gitignored.

# Host and port of the Redis server.  Defaults: 127.0.0.1 / 6379
REDIS_HOST=127.0.0.1
REDIS_PORT=6379

# Logical database index.  Default: 0
REDIS_DB=0

# Credentials. Both optional; leave them commented out for an unauthenticated
# server. A BLANK value means unset everywhere in this file, so writing
# `REDIS_PASSWORD=` is the same as not writing it — which is what you want
# against a password-less Redis, and used to be an AUTH attempt with an empty
# password instead.
#
# Write the raw credential: punctuation is percent-encoded into the connection
# URL for you, so a password containing `/`, `#`, `?`, `@`, `:` or a space is
# sent as written. Pre-encoding one by hand would send the escapes themselves.
#REDIS_USER=
#REDIS_PASSWORD=

# Connection timeout, in seconds.  Default: 30
REDIS_TIMEOUT=30

# Timeout for establishing the connection, in seconds.  Default: 5
REDIS_CONNECT_TIMEOUT=5


# ---------------------------------------------------------------------------
# MongoDB — event and step logging
# ---------------------------------------------------------------------------
# Logging is best-effort: a MongoDB failure never fails a request.

# Connection URI.  Default: mongodb://admin:password@localhost:27017
MONGODB_URI=mongodb://admin:password@localhost:27017

# Database and collections.
# Defaults: optionchain_simulator / steps / events
MONGODB_DATABASE=optionchain_simulator
MONGODB_STEPS_COLLECTION=steps
MONGODB_EVENTS_COLLECTION=events

# Server-selection timeout, in seconds. Bounded so a missing MongoDB cannot
# stall startup indefinitely.
# Default: 30
MONGODB_TIMEOUT=30


# ---------------------------------------------------------------------------
# ClickHouse — historical price data
# ---------------------------------------------------------------------------

# Host and port.  Defaults: localhost / 8123
CLICKHOUSE_HOST=localhost
CLICKHOUSE_PORT=8123

# Database and credentials.  Defaults: default / admin / password
#
# CLICKHOUSE_PASSWORD is the one variable where an empty value means something:
# `CLICKHOUSE_PASSWORD=` configures a user with NO password, which is how a
# stock ClickHouse `default` user ships. Comment the line out to get the
# default below instead.
CLICKHOUSE_DB=default
CLICKHOUSE_USER=admin
CLICKHOUSE_PASSWORD=password

# ─────────────────────────────────────────────────────────────────────────────
# Integration tests (examples/integration)
# ─────────────────────────────────────────────────────────────────────────────
#
# The service the integration suite tests, scheme and port included. The
# service itself never reads this: it belongs to `make test-integration`.
#
# Unset or blank, every integration test SKIPS and says so, which keeps
# `cargo test --workspace` hermetic and offline. Set, the suite runs against
# whatever is DEPLOYED there, which can be an older build than the working
# tree; the tests report the version they found and skip a feature that is not
# deployed yet rather than failing it.
#
# Where your deployment lives is your configuration; do not commit it.
# OCS_INTEGRATION_BASE_URL=http://localhost:7070