Cross-sectional factor research
Write momentum, value, and quality factors as typed .sig signals, combine them with explicit arithmetic, and get a reproducible ranked long-short backtest.
The problem
Factor research lives in notebooks that accumulate silent bugs: a mislagged return leaks lookahead, a calendar mismatch drops names, and a rerun three weeks later produces different numbers because a dependency moved. The signal logic is sound; the plumbing around it is not reproducible.
How sigc approaches it
- 01
Declare each factor as a named signal block — momentum from ret + zscore, value from a fundamentals column, quality from a stability measure — so the compiler checks shapes and calendars before any row is read.
- 02
Combine factors with explicit arithmetic (0.4 * momentum + 0.3 * value + 0.3 * quality) rather than hidden pandas joins.
- 03
Rank cross-sectionally and construct a long-short book with rank(...).long_short(top, bottom, cap), then attribute against a benchmark.
- 04
Every compile and run keys into the blake3 + sled content-addressed cache, so an identical .sig file and inputs reproduce the same numbers byte for byte.
signal momentum:
emit zscore(ret(prices, 60))
signal value:
emit zscore(-book_to_price)
signal combo:
emit 0.5 * momentum + 0.5 * value
portfolio main:
weights = rank(combo).long_short(top=0.2, bottom=0.2, cap=0.02)
backtest from 2020-01-01 to 2024-12-31 benchmark "SPY" What you get
- ✓Shape and calendar errors surface at compile time, not three hours into a backtest.
- ✓The same .sig source reproduces identical Total Return, Sharpe, Max Drawdown, and Turnover on any machine.
- ✓Factor libraries can be shared across a team as versioned .sig signals rather than copy-pasted notebook cells.
More use cases
One binary from research to production
Backtest a strategy with sigc run, then serve the exact same compiler and runtime in production as sigc daemon over nng — no rewrite, no second codebase.
Embed the runtime in a larger Rust system
Depend on the sigc crate, load a .sig file, override params, and pull weights or daily returns as plain Vec<f64> inside your own Rust trading stack.