config

package
v2.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package config loads and validates operator-owned TOML configuration for gateway identity, daemon behavior, and capability enablement.

A missing file or absent gateway field leaves that dimension discoverable; an explicitly configured value is binding. Runtime platform preferences are separate daemon.db state and are not read or written by this package.

Index

Constants

View Source
const (
	TradingModeDisabled = "disabled"
	TradingModePaper    = "paper"
	TradingModeLive     = "live"
)

Supported local trading modes.

Variables

This section is empty.

Functions

func DefaultPath

func DefaultPath() string

DefaultPath returns the canonical config path for the current user.

func SPXMembersAutoRefreshFromEnv

func SPXMembersAutoRefreshFromEnv() (enabled bool, forced bool)

SPXMembersAutoRefreshFromEnv resolves CANARY_SPX_MEMBERS_AUTO_REFRESH as a bidirectional override of the [spx] members_auto_refresh TOML field:

  • "1" → returns (true, true): explicit force-on.
  • "0" → returns (false, true): explicit force-off.
  • unset / other → returns (false, false): defer to TOML. Garbage values are silently ignored rather than rejected; env-var typos are a CI friction we'd rather not fail-loud on, and there's no realistic compliance posture that wants "fail when the env is present but malformed."

The second return ("forced") distinguishes "env actively overrode the TOML" from "env unset, TOML governs." The status renderer uses this to pick the "disabled (env)" vs "disabled (config)" suffix.

Lives next to the SPX type so the precedence rules don't have to be re-derived at every call site.

Types

type AutoTrade

type AutoTrade struct {
	// ProposalsEnabled controls whether the daemon may produce advisory protection proposals; default true, and proposals are not broker orders unless separately submitted by an explicitly enabled trading path — the `[auto_trade]` section name is historical: nothing auto-trades, and the policy's auto_submit stays false.
	ProposalsEnabled *bool `toml:"proposals_enabled"`
	// PolicyFile points to the local protection-policy TOML; default ~/.config/ibkr/policies/protection-policy.toml.
	PolicyFile string `toml:"policy_file"`
	// HotReload controls whether policy changes are reloaded while the daemon runs; default true.
	HotReload *bool `toml:"hot_reload"`
	// ReloadInterval controls how often the daemon checks policy-file changes; default 30s.
	ReloadInterval duration `toml:"reload_interval"`
	// ProposalCadence controls how often the daemon refreshes protection proposals; default 30s.
	ProposalCadence duration `toml:"proposal_cadence"`
	// FastPathEnabled allows manual proposal preview/submit to use the immediate
	// revalidation path; default true so paper protection stops remain usable.
	// Trading write gates still own broker-submit authority.
	FastPathEnabled *bool `toml:"fast_path_enabled"`
}

AutoTrade configures advisory protection-proposal production and policy reloads. Despite the historical section name, these fields do not authorize automatic broker submission.

func (AutoTrade) FastPathEnabledResolved

func (a AutoTrade) FastPathEnabledResolved() bool

FastPathEnabledResolved reports whether manual proposal actions may use the immediate revalidation path; absence defaults to enabled.

func (AutoTrade) HotReloadEnabled

func (a AutoTrade) HotReloadEnabled() bool

HotReloadEnabled reports the effective protection-policy reload toggle; absence defaults to enabled.

func (AutoTrade) ProposalCadenceDuration

func (a AutoTrade) ProposalCadenceDuration() time.Duration

ProposalCadenceDuration returns the effective advisory-proposal refresh cadence.

func (AutoTrade) ProposalsEnabledResolved

func (a AutoTrade) ProposalsEnabledResolved() bool

ProposalsEnabledResolved reports the effective advisory-proposal toggle; absence defaults to enabled.

func (AutoTrade) ReloadIntervalDuration

func (a AutoTrade) ReloadIntervalDuration() time.Duration

ReloadIntervalDuration returns the effective protection-policy reload interval.

func (AutoTrade) WithDefaults

func (a AutoTrade) WithDefaults() AutoTrade

WithDefaults returns the protection-proposal configuration with missing operational values resolved. It does not enable broker submission.

type Config

type Config struct {
	Gateway       Gateway         `toml:"gateway"`
	Daemon        Daemon          `toml:"daemon"`
	Trading       Trading         `toml:"trading"`
	Rulebook      Rulebook        `toml:"rulebook"`
	AutoTrade     AutoTrade       `toml:"auto_trade"`
	Opportunities Opportunities   `toml:"opportunities"`
	Flex          Flex            `toml:"flex"`
	SPX           SPX             `toml:"spx"`
	Scans         map[string]Scan `toml:"scans"`
}

Config is the on-disk shape of ~/.config/ibkr/config.toml.

func Load

func Load(path string) (*Config, error)

Load reads and parses the config file at path. A missing file yields a zero-value Config — every field nil/empty, meaning "fully auto."

func (*Config) Resolve

func (c *Config) Resolve() (*Resolved, error)

Resolve applies daemon-level defaults and returns the Resolved view. Gateway is passed through verbatim (pointer fields preserved) so discovery can see what was pinned vs. left auto.

type Daemon

type Daemon struct {
	// IdleTimeout is how long the auto-spawned daemon stays alive between CLI calls (default 15m, accepts any Go duration string like "1h" or "0s"); set "0s" to disable idle-shutdown when running long cold-start jobs such as the first breadth fan-out under `canary daemon --foreground`.
	IdleTimeout duration `toml:"idle_timeout"`
	// LogLevel is the daemon's log verbosity — one of "debug", "info" (default), "warn", or "error".
	LogLevel string `toml:"log_level"`
}

Daemon holds runtime knobs for the daemon process.

func (*Daemon) SetIdleTimeout

func (d *Daemon) SetIdleTimeout(t time.Duration)

SetIdleTimeout overrides the daemon's idle timeout. Used by --foreground (set 0 to disable idle-shutdown) and by tests that want a fast-firing idle watcher; the underlying field type is unexported so callers outside this package cannot construct it directly.

type Flex

type Flex struct {
	// Enabled turns the daily Flex statement fetch on; default false.
	Enabled bool `toml:"enabled"`
	// QueryID is the IBKR Flex query id to fetch (create the query in
	// Account Management with cash transactions, transfers, and equity
	// summary sections); required when enabled.
	QueryID string `toml:"query_id"`
	// TokenPath points to a file holding only the Flex Web Service token;
	// default ~/.config/ibkr/flex-token (mode 0600). The token itself never
	// belongs in config.toml, and no surface ever echoes it.
	TokenPath string `toml:"token_path"`
}

Flex configures daily IBKR Flex statement ingestion for post-trade reconciliation (internal-docs/design/post-trade-truth.md). Read-only toward the broker: statements feed the recon report; nothing here can touch order entry.

func (Flex) WithDefaults

func (f Flex) WithDefaults() Flex

WithDefaults returns the Flex configuration with its default token path.

type Gateway

type Gateway struct {
	// Host pins the IB Gateway / TWS host; empty (the default) defers to auto-discovery on loopback (127.0.0.1), any non-empty value skips probing.
	Host string `toml:"host"`
	// Port pins the IB Gateway / TWS API port (typically 4001/4002 for IB Gateway live/paper, 7496/7497 for TWS live/paper); absent (nil) defers to port-probing during discovery.
	Port *int `toml:"port"`
	// ClientID pins the IBKR API clientID for the primary connection (default 15); collisions are treated as a stale-client/operator issue and are not auto-walked to neighboring reserved IDs.
	ClientID *int `toml:"client_id"`
	// BreadthClientID is the IBKR clientID used by the dedicated
	// historical-bar connector that backs the SPX breadth refresh.
	// Default 16 (one above the primary's default 15). Pinned to its
	// own connection so breadth's 503-name fan-out runs against a
	// separate 40-msg/sec and 60-historical/10-min rate-limit budget
	// rather than competing with interactive RPCs and the gamma
	// option-leg fan-out on the primary client. Collisions are treated
	// as stale-client/operator issues, not silently shifted to another
	// role's reserved ID.
	BreadthClientID *int `toml:"breadth_client_id"`
	// Account pins the IBKR account ID like "U1234567"; empty (default) defers to the gateway's managedAccounts list — fine for single-account logins, required disambiguator when the login carries multiple accounts.
	Account string `toml:"account"`
	// TLS pins TLS mode for the API socket: absent (nil) auto-tries plain first then TLS, `true` forces TLS-only with no plain fallback, `false` forces plain — setting the field disables fallback in either direction.
	TLS *bool `toml:"tls"`
}

Gateway holds the four pinnable connection knobs. Pointer fields distinguish "user wrote this value" (binding) from "field absent" (auto).

Host is plain string because "" and "127.0.0.1" carry the same meaning: auto-discovery only probes loopback. A non-loopback host implies "I know where my gateway is" and is treated as pinned.

Account is plain string because empty already means "auto-detect via managedAccounts" in the SDK.

func (Gateway) BreadthClientIDOrDefault

func (g Gateway) BreadthClientIDOrDefault() int

BreadthClientIDOrDefault returns the clientID for the bulk-historical breadth connector. Default 16 — one above the primary default — so a fresh install gets two non-colliding role-owned IDs without any config tweak.

func (Gateway) ClientIDOrDefault

func (g Gateway) ClientIDOrDefault() int

ClientIDOrDefault returns ClientID if pinned, else 15.

func (Gateway) HostOrDefault

func (g Gateway) HostOrDefault() string

HostOrDefault returns Host if set, else 127.0.0.1.

func (Gateway) PortOrZero

func (g Gateway) PortOrZero() int

PortOrZero returns Port (dereferenced) or 0 if unset. Callers should check PortPinned first; the zero is a sentinel for "discover."

func (Gateway) PortPinned

func (g Gateway) PortPinned() bool

PortPinned reports whether the user pinned a port. Discovery skips the port-probe step when true and uses Port directly.

func (Gateway) TLSOrFalse

func (g Gateway) TLSOrFalse() bool

TLSOrFalse returns TLS (dereferenced) or false if unset. Callers should check TLSPinned first; the false is a sentinel meaning "auto, try plain first" — distinct from a binding tls=false.

func (Gateway) TLSPinned

func (g Gateway) TLSPinned() bool

TLSPinned reports whether the user pinned a TLS mode. The SDK's EnableTLSFallback is set to false (no fallback) when true, regardless of which mode was pinned — matches the strict-on-explicit-set contract.

type Opportunities

type Opportunities struct {
	// Enabled controls whether the daemon may produce advisory opportunities; default true, and opportunities are not broker writes unless separately submitted by an explicitly enabled trading path.
	Enabled *bool `toml:"enabled"`
	// PolicyFile points to the local opportunity-policy TOML; default ~/.config/ibkr/policies/opportunity-policy.toml.
	PolicyFile string `toml:"policy_file"`
	// RefreshCadence controls how often the daemon refreshes opportunities; default 2m.
	RefreshCadence duration `toml:"refresh_cadence"`
	// HotReload controls whether opportunity policy changes are reloaded while the daemon runs; default true.
	HotReload *bool `toml:"hot_reload"`
	// ReloadInterval controls how often the daemon checks the opportunity policy file for changes; default 30s.
	ReloadInterval duration `toml:"reload_interval"`
}

Opportunities configures advisory opportunity production and policy reloads. Broker submission remains subject to the independent trading path.

func (Opportunities) EnabledResolved

func (o Opportunities) EnabledResolved() bool

EnabledResolved reports the effective advisory-opportunity toggle; absence defaults to enabled.

func (Opportunities) HotReloadEnabled

func (o Opportunities) HotReloadEnabled() bool

HotReloadEnabled reports the effective opportunity-policy reload toggle; absence defaults to enabled.

func (Opportunities) RefreshCadenceDuration

func (o Opportunities) RefreshCadenceDuration() time.Duration

RefreshCadenceDuration returns the effective advisory-opportunity refresh cadence.

func (Opportunities) ReloadIntervalDuration

func (o Opportunities) ReloadIntervalDuration() time.Duration

ReloadIntervalDuration returns the effective opportunity-policy reload interval.

func (Opportunities) WithDefaults

func (o Opportunities) WithDefaults() Opportunities

WithDefaults returns the opportunity configuration with missing operational values resolved. It does not enable broker submission.

type Resolved

type Resolved struct {
	Gateway       Gateway
	Daemon        Daemon
	Trading       Trading
	Rulebook      Rulebook
	AutoTrade     AutoTrade
	Opportunities Opportunities
	Flex          Flex
	SPX           SPX
	Scans         map[string]Scan
}

Resolved is the validated, defaults-applied view a daemon actually uses. Gateway carries the raw (pointer-fielded) user input; discovery happens later in internal/discover and produces concrete values.

type Rulebook

type Rulebook struct {
	// TerminalEvidenceFile points to an optional JSON document of reviewed,
	// exact-contract terminal/non-reporting issuer evidence for rules 6-8. An
	// empty path leaves the retained daemon.db authority unchanged.
	TerminalEvidenceFile string `toml:"terminal_evidence_file"`
}

Rulebook configures operator-owned evidence inputs for the advisory trading rulebook. The import file is not read on snapshot paths: daemon startup validates and transactionally publishes it into daemon.db, which remains the live authority.

type SPX

type SPX struct {
	// MembersAutoRefresh controls whether the daemon refreshes the S&P 500 constituent list from Wikipedia daily at 02:30 ET (default true; set false to pin the embedded baseline) — overridden symmetrically by the `CANARY_SPX_MEMBERS_AUTO_REFRESH` env var (`1` force-on, `0` force-off).
	//
	// Pointer type lets the daemon distinguish an explicit `members_auto_refresh = true` from "field absent" — both enable the refresher today, but a future "user opted in" vs "default behaviour" distinction stays additive.
	MembersAutoRefresh *bool `toml:"members_auto_refresh"`
}

SPX holds the SPX-related daemon knobs. Currently just the members auto-refresh toggle; grouping under [spx] gives future SPX-scoped configs (e.g. fetcher concurrency, sweep tunables) a natural home without proliferating top-level TOML sections.

When MembersAutoRefresh is true (default) the daemon fetches Wikipedia's constituent list daily at 02:30 ET plus on startup if the cached file is stale; when false it loads whatever is on disk (or the binary's embedded fallback) and never reaches out.

Use case for pinning off: regulated traders running reproducibility audits, air-gapped boxes, anyone debugging breadth drift. The CANARY_SPX_MEMBERS_AUTO_REFRESH env var overrides this field at runtime: "1" force-enables, "0" force-disables, anything else (including unset) defers to the TOML value. Symmetric semantics — the env is a bidirectional override, not a one-way kill switch.

func (SPX) MembersAutoRefreshEnabled

func (s SPX) MembersAutoRefreshEnabled() bool

MembersAutoRefreshEnabled returns the resolved value of [spx] members_auto_refresh. Defaults to true when the field is absent — the refresher is opt-out, not opt-in.

type Scan

type Scan struct {
	// Type is the IBKR scanner code, such as TOP_PERC_GAIN; dump your gateway's catalog with `canary scan params`.
	Type string `toml:"type"`
	// Exchange is the IBKR scanner locationCode, such as STK.US.MAJOR or STK.NASDAQ.
	Exchange string `toml:"exchange"`
	// Instrument is the IBKR scanner instrument token, such as STK for US stocks or STOCK.EU for European stocks; empty defaults to STK.
	Instrument string `toml:"instrument"`
	// Limit caps returned rows for this preset.
	Limit int `toml:"limit"`
	// Timeout is the per-preset scan timeout; <=0 falls back to the daemon default (20s).
	Timeout duration `toml:"timeout"`
}

Scan holds a single scanner preset. Timeout is per-preset and optional; <=0 falls back to the daemon's default (20s).

type Trading

type Trading struct {
	// Mode selects the local order-entry state: "disabled" (default), "paper", or "live".
	Mode string `toml:"mode"`
	// MaxNotional caps every equity/ETF order before broker WhatIf; apparent close/reduce orders are not exempt because this client cannot prove that a manual TWS order has not already consumed the exit capacity. Default 10000 in account currency.
	MaxNotional float64 `toml:"max_notional"`
	// MaxOptionContracts caps every single-leg option order; apparent close/reduce orders are not exempt because account-global working-order authority is incomplete. Default 5.
	MaxOptionContracts int `toml:"max_option_contracts"`
	// AllowStockShort permits stock short/opening flip previews when true. Default false.
	AllowStockShort bool `toml:"allow_stock_short"`
	// AllowOptionSellToOpen permits option sell-to-open previews when true. Default false.
	AllowOptionSellToOpen bool `toml:"allow_option_sell_to_open"`
	// PaperSmokeMaxAge is the freshness window for the paper-smoke evidence reported in trading status. The smoke is informational at runtime and enforced by the release pipeline; it does not gate live enablement. Defaults to 168h.
	PaperSmokeMaxAge duration `toml:"paper_smoke_max_age"`
}

Trading holds local order-entry gates for experimental trading builds. Stable ibkr releases are read-only; a missing [trading] section resolves to mode="disabled". Treat active trading config as an explicit, as-is operator override. TWS / Gateway broker permissions remain the final authority even after these local gates pass.

func (Trading) OrderEntryEnabled

func (t Trading) OrderEntryEnabled() bool

OrderEntryEnabled reports whether the configured trading mode can progress beyond read-only status/preview diagnostics.

func (Trading) PaperSmokeMaxAgeDuration

func (t Trading) PaperSmokeMaxAgeDuration() time.Duration

PaperSmokeMaxAgeDuration returns the resolved paper-smoke freshness window.

func (Trading) WithDefaults

func (t Trading) WithDefaults() Trading

WithDefaults returns t with default values applied without granting trading.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL