BrushAutomation Consulting

Projects

MSTock

A scanner that checks about 75 stocks, futures, and crypto markets for technical setups. It logs every signal it finds, follows each one forward to see how it turned out, and can place the trades in an Alpaca paper account. It only trades on paper.

Status
Paper trading only
Stack
Python, pandas and numpy, Streamlit, Alpaca paper trading, pytest
Tests
168 pytest tests, no network needed, run in CI on every push

Why I built it

I wanted to know whether the setups I was taking by hand actually had an edge, and looking at charts after the fact is not a way to find out.

So the design puts measurement ahead of automation. Every signal is logged when it is made and then checked against the real bars that came after it. The hit rate and average R come from trades the system actually called, not from a curve fit after the fact. Position sizing only reacts to that record once there is enough of it to mean anything.

How it works

No database. Everything is written to append-only CSV files. The indicators are written directly in pandas and numpy, because pandas-ta would not install on Python 3.14.

Watchlist
config/watchlist.py holds every instrument as one record. No other file keeps its own list of tickers.
Data
One DataSource interface, with versions for stocks and futures (yfinance), crypto (Binance), and intraday bars (Alpaca). All of them return price data in the same shape.
Filters
Stocks within 3 trading days of earnings are dropped before any strategy runs. If the earnings calendar is missing, the stock goes through with a warning instead of being dropped without a word.
Strategies
Trend pullback, Supertrend, and Donchian breakout. Each strategy sees only fully closed bars and returns a signal or nothing.
Logging
Every signal is written to a CSV with an entry, a stop, a target, and a position size. Running the scan twice in one day does not log the same signal twice.
Resolution
A resolver walks each signal forward bar by bar. Entry is the open of the next bar, never the signal bar. If one bar touches both the stop and the target, it checks 1 minute or 5 minute bars to see which came first, and counts a loss if it still cannot tell.
Sizing
Risk per trade comes from quarter Kelly on each asset class's own results, held between 0.25% and 1.5%. Below 30 closed trades it uses a fixed default.
Execution
Stock signals go to Alpaca as bracket orders. Alpaca has no bracket orders for crypto, so the code manages the stop and target itself. Each order uses the signal's ID, so matching orders back to signals is safe to run again.
Dashboard
A Streamlit app shows scan results, open positions, backtests, and a kill switch for each trading loop.

The scanner, the backtester, and the tests all run with no credentials. Only paper trading needs Alpaca keys.

Known limitations

These are real gaps in the code today.

The backtest has no slippage or commissions

Live fills on large stocks usually come in 0.05 to 0.15R worse per trade, so backtest results look better than they should by about that much.

A tie goes to the stop

A price bar cannot say whether the stop or the target came first. When finer data is missing, the loss is counted. That pushes measured results down, which is the safe direction, but it is not the truth.

Kelly sizing is not active yet

It needs 30 closed trades per asset class, and the log does not have that many yet. Until then every trade uses the default risk, so that part is written and tested but not proven.

Intraday volume is a sample

Alpaca's IEX feed is about 2 to 4% of all trading, so any filter built on volume is an estimate.

Intraday backtests are short

Alpaca only has so much intraday history, so windows run about 30 days. One news-driven week can swing the results.

Live trading takes a code change, on purpose

paper=True is hardcoded in the broker client, with no setting to flip it. A config mistake cannot send real money.

Read the code