Catalyst is a backtesting / paper-trading bot whose entire decision engine is built out of chemistry. A trade is treated as a reversible chemical reaction:
CASH + STOCK ⇌ POSITION
(forward = buy, reverse = sell)
Whether the reaction proceeds — and in which direction — is decided by the Gibbs free energy of the trade. Everything that feeds into it is a chemistry concept mapped onto a market signal.
Not financial advice. Catalyst only does backtesting and paper trading against historical or synthetic data. It never touches real money or a real brokerage. It is an educational toy for exploring strategy ideas.
| Chemistry concept | Market meaning | Module |
|---|---|---|
| Le Chatelier's principle — a system at equilibrium shifts to oppose stress | Mean reversion: price displaced from its moving-average "equilibrium price" is pushed back | chemistry/equilibrium.py |
Reaction kinetics / Arrhenius equation k = A·e^(−Eₐ/RT) |
Momentum gated by "temperature" (volatility): a trade only proceeds fast enough when the market is hot | chemistry/kinetics.py |
| pH (0–14 acid/base scale) | Sentiment from RSI. Acidic (low pH) = oversold = buy; basic (high pH) = overbought = sell | chemistry/ph.py |
| Catalysts lower activation energy | Volume spikes lower the energy barrier needed to fire a trade | chemistry/catalyst.py |
Radioactive half-life N = N₀·e^(−λt) |
Signals decay: old conviction loses potency over time | chemistry/decay.py |
Gibbs free energy ΔG = ΔH − TΔS |
The master decision. ΔG < 0 ⇒ spontaneous ⇒ buy; ΔG > 0 ⇒ sell |
strategy.py |
| Molarity / concentration | Position sizing — how concentrated the portfolio is in one name | portfolio.py |
| Titration — adding reagent drop by drop | Scaling into / out of a target position gradually instead of all at once | portfolio.py |
-
Compute the three directional signals (equilibrium, momentum, pH), each in
[-1, +1]. -
Blend them (configurable weights) and smooth with half-life decay → the Drive
D. -
Compute the Gibbs free energy of buying:
ΔG = −D + λ · Twhere
Tis the market temperature (volatility) andλis your risk aversion. A strong bullish Drive makesΔGnegative (a spontaneous, favorable reaction); high volatility raisesΔGand discourages diving into chaos. -
Cross the activation energy barrier
Eₐto actually trade — but a volume catalyst lowers that barrier (Eₐ_eff = Eₐ / catalyst_factor):ΔG < −Eₐ_eff→ BUY (build concentration toward the target)ΔG > +Eₐ_eff→ SELL (titrate the position back down)- otherwise → HOLD (stuck behind the energy barrier)
git clone <your-repo-url> catalyst-trader
cd catalyst-trader
python -m venv .venv && source .venv/bin/activate
pip install -e . # core (numpy, pandas, pyyaml)
pip install -e ".[data,plot]" # + yfinance for real data, matplotlib for chartsCatalyst runs end-to-end on synthetic data with zero extra dependencies, so you can try it immediately without an internet connection or any API keys.
# Backtest on a reproducible synthetic stock (no internet needed)
catalyst backtest --synthetic
# Backtest a real ticker (requires: pip install -e ".[data]")
catalyst backtest --ticker AAPL --start 2018-01-01 --end 2024-01-01
# Inspect today's chemistry signals for a synthetic series
catalyst signals --synthetic
# Save the equity curve to CSV and a PNG chart
catalyst backtest --synthetic --out outputs/run --plotOr from Python:
from catalyst.config import load_config
from catalyst.data.feed import load_prices
from catalyst.backtest import Backtester
cfg = load_config() # built-in defaults
prices = load_prices(cfg) # synthetic by default
result = Backtester(cfg).run(prices)
print(result.summary())All knobs live in config/default.yaml and mirror the
built-in defaults in catalyst/config.py. Copy it, edit it, and pass it with
--config my.yaml to retune the chemistry (EMA spans, activation energies,
half-life, signal weights, risk aversion, commissions, etc.).
config/default.yaml— the built-in mean-reversion config (Le Chatelier weighted heaviest). Conservative: low volatility, shallow drawdowns, modest upside.config/trend.yaml— a trend-following preset (momentum weighted heaviest, higher activation energy so winners run), found via the sweep below.
catalyst backtest --ticker AAPL --config config/trend.yamlThree scripts in examples/ explore and compare configurations:
| Script | What it does |
|---|---|
examples/compare.py [--config X] |
run a config across a 6-stock basket and the 2022 bear year |
examples/sweep.py |
grid momentum weight x risk aversion x activation energy and rank the trade-off |
examples/plot_compare.py --ticker AAPL |
plot default vs trend vs buy & hold equity curves |
Backtested across SPY/AAPL/MSFT/AMZN/TSLA/NVDA (2018-2024), both presets behave like low-volatility, capital-preserving strategies: strong Sharpe (~1.0-1.3) and shallow drawdowns, but they capture only ~15-20% of a raging bull market's upside because they titrate exposure and de-risk as volatility rises. Their edge shows in the 2022 bear year, where they cut losses roughly 2-4x versus buy & hold.
A sweep finding worth noting: lowering risk_lambda reduced returns. The temperature
term lambda*(theta-1) is negative when volatility is below average, so a high lambda
actually encourages buying into calm uptrends — which helps in a bull market.
catalyst/
config.py # defaults + YAML loader
data/feed.py # synthetic GBM generator + lazy yfinance loader
chemistry/
equilibrium.py # Le Chatelier mean reversion
kinetics.py # Arrhenius momentum + reaction-rate gate
ph.py # RSI → pH sentiment
catalyst.py # volume → activation-energy reducer
decay.py # half-life signal smoothing
strategy.py # Gibbs free-energy decision engine
portfolio.py # molarity sizing + titration
broker.py # paper broker (fills, commission, slippage)
backtest.py # event loop, equity curve, metrics
cli.py # `catalyst backtest|signals`
config/
default.yaml # mean-reversion defaults
trend.yaml # trend-following preset
examples/
run_backtest.py # minimal end-to-end run
compare.py # basket + bear-year comparison
sweep.py # parameter sweep / trade-off
plot_compare.py # equity-curve chart
tests/
This is a learning project. Backtested / paper results are not indicative of real performance, the synthetic data is not a real market, and nothing here is investment advice. Do not point this at real funds.
