Skip to content
Vishesh
Selected work

SwingTraderVCP

A trading workstation that connects screening, review and execution for Indian equities.

Status
Personal project
My role
Sole developer.
Built with
React, TypeScript, FastAPI, PostgreSQL, Redis, Fyers API

A personal trading workstation for one operator. It is engineering evidence for interface, integrations, and background work. It is not a product, and nothing here is an investment-performance claim.

Screening, plan drafting, human approval, and a headless worker that manages positions all run. Live broker placement stays behind separate flags.

  1. The order path has to join an approval row. The research jobs can run unattended and still hold no order authority of their own.
  2. Stops and targets are enforced by a worker process, not by the browser that showed the chart.
The SwingTraderVCP workstation: a Nifty sector watchlist beside a daily candlestick chart for Infosys, with a buy and sell ticket below it.
The workstation: sector breadth on the left, the chart and order ticket on the right.

What it is

SwingTraderVCP is a swing-trading workstation for Indian equities, built for one operator: me. It screens the Nifty 500 after the close, drafts trade plans, then manages the ones I approve through the Fyers broker API. It isn't a product and it has no users, and most of its machinery answers one question: where does the machine stop and where do I start?

Where the machine stops

The daily loop is mostly automatic. After the close, candles sync and a technical screen scores the Nifty 500 on Stage-2 structure, relative strength and volume behavior. A worker renders a chart, makes one model call to audit the base, and computes entry, stop and targets in plain Python.

The one step that needs me is the approval. Approving a proposal records a decision and arms the first entry leg without placing an order. A supervisor watches five-minute bars for a confirmed breakout while a separate monitor enforces stops, targets and a trailing stop tick by tick. Everything before the approval row is automatic, and everything after it needs that row.

Why the machine leaves one decision to me

The strategy is Mark Minervini's: Stage-2 uptrends, a base that contracts, a breakout on volume. The rules aren't complicated. Following them while real money is moving is the hard part, and most of this system exists because of specific ways that goes wrong.

Chasing is the first one. A stock that has already run 4% past the pivot looks like confirmation, and buying it puts the stop too far away to take. So the supervisor enforces a chase ceiling and the fill is refused.

Moving the stop is the second. As price approaches it, the case for the trade gets more creative. So the stop is enforced by a process I can't reach from the browser.

Revenge is the third. After a stop-out the next setup looks irresistible, usually because the last one hurt. So plans expire, a second decision on the same proposal is impossible at the database level, and only the version I approved can execute.

Taking a winner off too early is the fourth, and letting it round-trip is the same mistake at a different price. So the exits are mechanical: three targets taking a quarter each, and a trailing stop that only ratchets up.

The design answer is timing. Decide after the close, when nothing is moving and there's no reason to hurry. Execute during the session, when there is. The plan is written down, hashed and frozen at approval, so the machine acts on the version I signed off on and not on whatever I'd talk myself into at 09:45.

The approval is a row the order path has to join

A flag would have been easier, except that a flag enforces nothing. Creating an entry intent inner-joins the decisions table for a row marked approved, re-checks the proposal's status, live eligibility and hash, and raises ExecutionSafetyError if any of it fails. A unique index blocks a second decision, and a database constraint makes the execution engine the only component allowed to create order intents, so order placement can't happen for a plan nobody approved. The schema enforces that, not the review screen.

One caveat: the invariant only holds in live mode. Paper mode writes a synthetic approval so the system can gather data without a human clicking, and it can't fire live since auto-arming requires paper mode. The manual order ticket skips the join, so the README's claim about web requests isn't true for that path.

The plan is hashed, and it expires

Every proposal carries a SHA-256 over the fields approval accepts. The approval request has to echo that hash back, so if the plan changed underneath the review screen the decision is rejected with a 409. A database trigger then freezes the payload of anything approved or rejected.

Plans also expire. Miss the morning deadline and the proposal ages out as expired_unapproved, and an entry leg that never triggered is cancelled when the entry window closes. That's more state than a personal tool needs, and it's what keeps a stale browser tab from arming a trade I looked at yesterday.

The position belongs to a worker, not a browser

The position monitor is its own process. It takes a Redis singleton lease, subscribes to the live tick channel, and shuts down if it loses the lease. It opens no broker socket and reads no HTTP request or UI flag, so positions are managed whether or not I have a tab open.

A software stop exits the remaining quantity. The three targets each take a quarter and consolidate into one order when a gap crosses several at once. The trailing stop is a two-ATR high-water mark that only ratchets up.

The kill switch halts the monitor too, so it stops new entries and software stops at once. That makes it no substitute for being flat, which the interface says out loud.

Why this scan doesn't live in TradingView

There's a version of this screen people build as a Pine Script, and it falls over exactly where the scan needs the whole market.

The trend template wants price above the 50, 150 and 200-day averages, a 200-day that's trending up, price near the 52-week high and well off the low, and a relative strength rank against every other stock in the universe. That last one is the problem. A Pine script sees one symbol at a time, with a budget of 40 unique request.*() calls, 64 on the Ultimate plan, 20 to 40 seconds of total execution and 500 ms per loop per bar. You can't pull 500 symbols of history into one script and rank them.

TradingView's screener has the same shape from the other side. It gives you preset columns and a filter bar, not a place to run a custom contraction detector across the Nifty 500.

The VCP half is harder, because it's iterative. You're fitting successive contractions over months of daily bars, measuring how much each pullback gives back, watching volume dry up into the pivot, then re-checking all of it the next evening on new data. That wants a database and a job that runs after the close, not a label on a chart.

So the screen runs server-side over the full Nifty 500 with the full history in Postgres. TradingView still does the part it's good at: the workstation charts are its lightweight-charts library, and the headless chart the model audits is rendered with mplfinance.

What works and what does not

The pipeline runs end to end: candle sync, screening, plan generation, the approval checkpoint, breakout confirmation with a chase ceiling, idempotent order intents, and tick-by-tick management in a headless worker. Paper trading and a paper broker are built in, and live placement needs two separate flags.

Nothing in the repository shows the strategy makes money. There's no backtest engine, no walk-forward study, and the journal's win rate and R-multiples are arithmetic over trades that happened. The security audit says keep live order placement off until a list of findings is closed, and the rollout starts at a shadow stage where approval is blocked. The money-path tests are mostly mocked and nothing in CI runs them.

What is next

Closing the audit findings, then working up the rollout stages one at a time. The bigger gap is validation: the system can't yet tell me what the strategy would have done over the last few years.

If this kind of full-stack work is relevant to a role, email me.