Skip to content
v3 redesign is live — welcome to the trading cockpit.
Market updates, stock news, and futures insights — 3x/week, freeSubscribe free
Skip to content
build.logtraders.online=2,166trades.60s=74swings.ranked=308edge.latency_ms=42ms
// educational content · not financial advice

Everything on this page is published for educational and informational purposes only. Nothing here is investment, financial, legal, tax, or trading advice, a recommendation to buy or sell any security or contract, or a solicitation of any kind. Trading futures, options, equities, and crypto involves substantial risk of loss and is not suitable for every investor. Past performance — including any backtests, demos, or examples shown — does not guarantee future results. Consult a licensed professional before acting on anything you read here.

// automation · v1.0

Automation, Without the Blow-Up.

A complete, free guide to building trading automation that doesn’t orphan positions, fire duplicate orders, or silently lose money. Five stages. Seven-layer stack. Twelve weeks. No upsell.

5stages
12modules
195pages · free
$0forever
// unlock downloads + full curriculum

Get the 5 free PDFs.

Enter your email to unlock the PDF downloads instantly. Same email gate as the book — if you already unlocked that, refresh to see it here.

// we respect your inbox · unsubscribe anytime

// i am a:// page adapts: Python basics comes first
// getting started today · part 1

Your first automation. 15 minutes.

Three moving parts. One terminal window. By the end of this section you will see structured JSON alerts from TradingView land in your own log file — the first real piece of automation infrastructure most people never build.

  1. 015 min

    Run the receiver

    Save webhook_log.py, install Flask, run it on :5000. Expose it to the public internet with ngrok in one command.

  2. 025 min

    Wire a Pine alert

    Paste the Pine snippet into TradingView → Pine Editor. Create an alert pointing to your ngrok URL. Use bar-close frequency.

  3. 035 min

    Watch alerts arrive

    Tail the log. Every bar-close that triggers your condition will land as a structured JSON line you can grep, parse, or pipe to a broker.

Pythonwebhook_log.py
1# webhook_log.py — Minimal Flask receiver that logs every alert
2# Run: pip install flask && python webhook_log.py
3# Then expose with ngrok: ngrok http 5000
4 
5import json, time
6from flask import Flask, request
7 
8app = Flask(__name__)
9 
10@app.post("/webhook/signal")
11def signal():
12 payload = request.get_json(silent=True) or {}
13 payload["received_at"] = time.time()
14 # Structured log line — one JSON object per request
15 print(json.dumps(payload), flush=True)
16 return {"status": "logged"}
17 
18if __name__ == "__main__":
19 app.run(port=5000)
Pine Scriptfirst_alert.pine
1// pine_alert.pine — Alert with structured JSON payload
2// Run at bar close to avoid re-paints
3 
4//@version=5
5strategy("Asymmetric Entry v1", overlay=true, calc_on_every_tick=false)
6 
7// — Inputs
8rsi_len = input.int(14, "RSI Length")
9ema_fast = input.int(9, "Fast EMA")
10ema_slow = input.int(21, "Slow EMA")
11risk_pct = input.float(1.0, "Risk %", minval=0.1, maxval=5.0)
12 
13// — Indicators
14rsi = ta.rsi(close, rsi_len)
15ema_f = ta.ema(close, ema_fast)
16ema_s = ta.ema(close, ema_slow)
17 
18// — Entry conditions
19long_cond = ta.crossover(ema_f, ema_s) and rsi < 60
20short_cond = ta.crossunder(ema_f, ema_s) and rsi > 40
21 
22// — Structured JSON payload for webhook receiver
23if long_cond
24 alert(
25 str.format(
26 "{"action":"buy","symbol":"{0}","price":{1}," +
27 ""risk_pct":{2},"bar_time":"{3}"}",
28 syminfo.ticker, close, risk_pct, str.tostring(time)
29 ),
30 alert.freq_once_per_bar_close // no re-paints
31 )
32 strategy.entry("Long", strategy.long)
33 
34if short_cond
35 alert(
36 str.format(
37 "{"action":"sell","symbol":"{0}","price":{1}," +
38 ""risk_pct":{2},"bar_time":"{3}"}",
39 syminfo.ticker, close, risk_pct, str.tostring(time)
40 ),
41 alert.freq_once_per_bar_close
42 )
43 strategy.entry("Short", strategy.short)
JavaScriptwebhook.log
1// stdout: tail -f webhook.log
2{"action":"buy","symbol":"SPY","price":523.41,"risk_pct":1.0,"bar_time":"1716662400","received_at":1716662400.412}
3{"action":"sell","symbol":"SPY","price":524.18,"risk_pct":1.0,"bar_time":"1716663300","received_at":1716663300.221}
4// latency from bar close → receiver: ~400ms
5// dedup key: sha256(action:symbol:bar_time)[:24]
6// next: replace 'print' with submitOrder() in webhook.js
// 30-second ngrok cheat-sheet
  1. 1. brew install ngrok or download from ngrok.com (free tier is fine)
  2. 2. ngrok http 5000 → copies a public https URL into your clipboard
  3. 3. Paste that URL into TradingView → Alert → Webhook URL
// skip the typing — clone the full starter
view on github →

The receiver above is great for understanding the moving parts. When you want the full version — both Python FastAPI and Node Express, the Pine snippet, a shared .env schema, a notional cap, and a curl-based test script — clone the companion repo:

git clone https://github.com/JasonTeixeira/nexural-automation-starter.git
cd nexural-automation-starter/python
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp ../.env.example .env       # edit WEBHOOK_SECRET
uvicorn app:app --reload --port 8000

Prefer JavaScript? cd node && npm install && node server.js — same endpoints, same env file, same outcome.

Ready to swap print() for an actual broker call? That’s the next 10 minutes ↓

// getting started today · part 2

Your first paper order. 10 minutes.

One free account. Two API keys. Twelve lines of Python. By the end of this section a real broker will have accepted a real order against a real market — with zero capital at risk.

  1. 013 min

    Create an Alpaca account

    Sign up at alpaca.markets, verify your email, switch to Paper Trading. No funding, no SSN, no waiting period.

  2. 022 min

    Grab paper API keys

    Paper Trading → API Keys → Generate. Export them as ALPACA_KEY and ALPACA_SECRET environment variables. Never commit them.

  3. 035 min

    Submit your first order

    pip install alpaca-py, paste the 12-line script, run it during US market hours. Watch the order appear in the paper dashboard.

Pythonfirst_paper_order.py
1# first_paper_order.py — 12 lines from "have keys" to "got a fill"
2# Run: pip install alpaca-py
3# Get free paper keys at https://alpaca.markets (Paper trading → API keys)
4 
5import os
6from alpaca.trading.client import TradingClient
7from alpaca.trading.requests import MarketOrderRequest
8from alpaca.trading.enums import OrderSide, TimeInForce
9 
10client = TradingClient(
11 api_key=os.environ["ALPACA_KEY"],
12 secret_key=os.environ["ALPACA_SECRET"],
13 paper=True, # paper account — no real money at risk
14)
15 
16order = client.submit_order(MarketOrderRequest(
17 symbol="SPY", qty=1, side=OrderSide.BUY, time_in_force=TimeInForce.DAY,
18))
19print(f"submitted: {order.id} — status={order.status}")
JavaScriptstdout
1// stdout: python first_paper_order.py
2submitted: 9c4f2e1a-... — status=OrderStatus.ACCEPTED
3 
4// 200ms later, from your Alpaca paper dashboard:
5// SPY 1 share filled @ 523.42 fee $0.00
6// buying_power: $99,476.58 positions: 1
7//
8// you just placed a real order through a real broker API.
9// the only difference between this and live: paper=False + funded account.
// why paper first

The Alpaca paper environment is the production code path with paper=True. Every error you can hit live, you can hit here — invalid quantity, wrong session, rate-limit — with no dollar consequence.

// next: connect the two

Inside webhook_log.py, replace the print() with the Alpaca submit_order() call. That single substitution turns the receiver into a live signal→broker pipeline.

// the automation maturity ladder

Five stages. Where are you today?

Every trader starts at Stage 1. The goal isn’t to reach Stage 5 as fast as possible — it’s to advance only when the current stage is solid. Each stage compounds the previous one.

1

Manual

stage 1

You execute every trade by hand. Highest skill ceiling, lowest leverage.

// required skills
Chart ReadingRisk DisciplineExecution SpeedJournal Habit
// what breaks

Human error, emotional override, missed alerts while away.

// r:r impact

Baseline — your R:R is entirely execution-dependent.

2

Alerted

stage 2

TradingView alerts ping your phone. You still pull the trigger.

// required skills
Pine Script BasicsAlert ManagementWebhook SetupMobile Discipline
// what breaks

Phone not in hand, notification fatigue, slippage from manual lag.

// r:r impact

+0.1–0.2 R avg: alerts enforce entry discipline even when distracted.

3

Semi-Auto

stage 3

Alerts hit a webhook → broker submits limit orders. You manage exits manually.

// required skills
Webhook ReceiversREST API BasicsIdempotencyPartial Automation
// what breaks

Silent rejections, duplicate orders, no exit logic when phone is off.

// r:r impact

+0.2–0.3 R avg: consistent entries eliminate FOMO slippage.

4

Fully-Auto

stage 4

Strategy code owns entries, exits, sizing. You monitor and intervene.

// required skills
Strategy ArchitectureRisk MathBroker API DepthMonitoring Setup
// what breaks

Overfit backtest, regime shift, orphaned positions on crash.

// r:r impact

+0.3–0.5 R avg: removes all emotional override from execution.

5

Self-Healing

stage 5

Strategy detects regime shifts, auto-pauses, self-rebalances. Audit logs everything.

// required skills
Regime DetectionCircuit BreakersDeadman SwitchAudit Pipelines
// what breaks

Tail-risk correlation breakdown, black swans, exchange-level failures.

// r:r impact

+0.5–0.8 R avg vs baseline: compounding discipline over full market cycles.

// the visual story

Five diagrams. The whole game.

Before the matrices and the modules, see the territory. Every diagram is interactive — click anything to expand the underlying definition, code, or trade-off. Nothing here requires a login or a download.

MARKETSAuction theoryVWAP & valueRegimesSTRATEGYSignalFilterSizingR : RCODEGitPythonPine ScriptC# (NT8 / cAlgo)DATAVendorsStorageQualityRISKKelly / sizingKill switchDaily loss capEXECUTIONBroker APIIdempotencyReconcileOPERATIONSStructured logsAlertingDeadman switchRunbooks
// signal · to · fill · pipelinetotal budget ~560 ms
// stage 05 · 12 ms

Risk Gate

Pre-trade checks: daily loss, max position, account heat, kill switch.

// bottleneck

Multiple gate reads against state store.

// failure mode

What breaks here

Stale state — risk values from a previous bar block a valid trade.

// mitigation

How we teach you to handle it

Read state atomically. Use Redis for hot risk counters.

// decision tree

Pick your stack in 5 questions

// question 1 of 5

What do you trade?

Pick the asset class you spend most of your time on.

// latency budget

Where does 2,000 ms actually go?

From signal-fired to broker-ack. Click any segment to see what costs the latency and how a pro shop would cut it.

median total
600ms
worst-case total
2,265ms
floor (best case)
150ms
50 ms
150 ms
5 ms
10 ms
120 ms
150 ms
100 ms
15 ms
// webhook

Outbound webhook payload

The trading platform serializes your alert into JSON and POSTs it to your handler URL.

floor
50 ms
median
150 ms
worst
500 ms
// why it costs

TradingView batches webhooks through their servers in Ireland and queues during volatility. Cold TLS handshakes add 100+ ms.

// retail example

TradingView → custom webhook handler in us-east-1 typically lands at 120–180 ms.

// how to cut it

Drop webhooks entirely and run a Python or C# strategy in-process. If you must use webhooks, terminate TLS in the same region as the platform.

// pro target

2.0 ms median · 10 ms worst

Numbers are realistic estimates for a single-region cloud stack and a co-located pro shop. Variance dwarfs averages — measure your own pipeline before optimizing.
// risk gates

Seven gates between signal and submit

Every order runs this gauntlet. Click a gate to see the one-line rule, the failure mode it prevents, and the metric you should already be alerting on.

// start
Signal received
validated webhook or in-process event
discard
page operator
queue & retry
halve size
kill switch
halve size
kill switch
// end
Submit to broker
all gates passed
4
// gate 4

Position size & buying power

// one-line rule

Notional ≤ per-trade cap, post-fill position ≤ per-instrument cap, and broker buying power covers it.

// why it exists

Almost every retail blow-up story starts here. A divide-by-zero in volatility produces an insane size, the strategy fires, and the broker happily fills it because buying power exists.

// failure mode if missing

A single bad signal sizes a position the account cannot survive. Margin calls within minutes.

// reference implementationpython
if order.notional > limits.per_trade_max:
    order.qty = math.floor(limits.per_trade_max / order.price)
    audit.warn('size_capped', orig=payload.qty, capped=order.qty)
// on pass

Order moves to daily loss check.

// on reject

Halve the size or reject entirely. Never round up to fit the cap — that hides the bug.

// observability

Histogram: order_notional_distribution. Alert on p99 > 3× p50 — the strategy is sizing erratically.

Every gate is independent. Skipping one because it “seems redundant” is the single most common path to a five-figure loss day. Wire all seven.
// the 7-layer automation stack

Every layer is a failure point. Know them all.

Most automation failures aren’t strategy failures — they’re infrastructure failures. The stack below maps every layer, what it does, what breaks there, and which tools to use.

// platform comparison matrix

16 platforms. Honest trade-offs.

No rankings. No affiliate links. Emerald = good, amber = partial, rose = limitation. Use the filter chips to narrow by asset class, language, or cost.

Last reviewed: May 2026
// trader path

Start with TradingView for signals and Alpaca for execution. Once profitable on paper, evaluate NT8 or IBKR for lower fees and deeper order types.

PlatformLanguageAsset ClassPaper TradingReal-Money CostAuditabilityWhere It BreaksWho It's For
NinjaTrader 8
deep dive →
C# (NinjaScript)Futures, FXYes (Simulation)$50–100/mo or one-timeFull strategy logsWindows-only, NT8 version lockFutures traders with code skills
TradingView + Webhooks
deep dive →
Pine ScriptAll (view only)Paper mode only$15–60/mo planLimitedRequires external executor, delay free planSignal builders, no-code first step
MetaTrader 5
MQL5FX, Stocks, FuturesYes (Demo)Free (broker charges)Trade log onlyLimited crypto, old UI paradigmFX algo traders, legacy systems
IBKR API
deep dive →
Python, Java, C++Stocks, Options, Futures, FXYes (Paper acct)~$0.005/shareTWS + flex reportsComplex auth, TWS must runMulti-asset professionals
Alpaca
deep dive →
Python, JS, RESTStocks, CryptoYes (free)Free (spread)API event logUS stocks only, no optionsEquity algo starters
ProjectX
REST, WebSocketFuturesYes$0–30/moTrade history APISmaller ecosystem, fewer docsFutures API first-timers
Tradovate
REST/WebSocketFutures onlyYes$0–2/contractREST history endpointFutures only, rate limitsPure futures developers
QuantConnect / Lean
deep dive →
Python, C#Stocks, Options, Futures, CryptoYes (paper brokerage)$0 cloud or self-hostFull backtest + live logsCloud latency, data costs add upSystematic quants, researchers
Backtrader
PythonStocks, Futures (data-agnostic)Yes (built-in sim)Free (open source)Strategy log + analyzersNo live data, community-maintainedPython devs learning backtesting
Lean (standalone)
Python, C#All asset classesYesFree (self-host)Comprehensive logsDocker setup complexityDevs wanting local QuantConnect
cTrader / cAlgo
deep dive →
C# (cAlgo)FX, CFDs, CryptoYes (Demo)Free (broker spread)Full execution logBroker-specific availabilityFX devs wanting modern API
Sierra Chart
C++ (ACSIL)Futures, Stocks, FXYes (Sim)$26–54/moOrder log + journalSteep learning curve, dated UIPro futures traders, latency-sensitive
MultiCharts
EasyLanguage, C#Stocks, Futures, FXYes$1,497 once or $97/moStrategy log + reportsWindows-only, premium pricingMigrants from TradeStation
TradeStation
EasyLanguageStocks, Options, FuturesYes (Sim)$0–9.99/mo + commissionsTrade history + analyticsEasyLanguage lock-in, US-centricLegacy retail systematic traders
MetaTrader 4
MQL4FX, CFDsYes (Demo)Free (broker spread)Trade log onlyAging platform, no stocks/futuresRetail FX, legacy EAs
ThinkOrSwim
thinkScriptStocks, Options, Futures, FXYes (paperMoney)Free (Schwab account)Limited — study log onlyNo real automation — alerts onlyOptions traders, study authors

// emerald = good · amber = partial/caveat · rose = limitation · no rankings, no affiliates

// crypto venue matrix

8 crypto venues. CEX, DEX, broker.

Crypto demands different criteria: spot vs perps, KYC, taker fee, settlement model. Filter by product type, jurisdiction, or stack. Self-custody beats exchange custody when you can.

VenueTypeProductsAPIKYCTaker FeeRate LimitCustodyWhere It Breaks
Coinbase AdvancedCEXSpotREST, WebSocket, FIXRequired (US-friendly)0.40–0.80%30 req/sExchange-custodiedNo perpetuals on US accounts
KrakenCEXSpot, FuturesREST, WebSocketRequired (Pro tier)0.16–0.26%15–20 req/sExchange-custodiedLower liquidity vs Binance
BinanceCEXSpot, Perpetuals, Futures, OptionsREST, WebSocket, FIXRequired (non-US)0.04–0.10%1200/min weight-basedExchange-custodiedBanned for US users; geo issues
BybitCEXSpot, Perpetuals, OptionsREST, WebSocketTiered (verify for higher limits)0.055–0.10%120 req/s burstExchange-custodiedUS restricted, regional access varies
OKXCEXSpot, Perpetuals, Futures, OptionsREST, WebSocketRequired (jurisdiction-based)0.05–0.10%20 req/2s per endpointExchange-custodiedUS restricted; complex API surface
CCXT (multi-venue)BrokerSpot, Derivatives (varies)Python, JS, PHPPer underlying exchangePass-throughPer underlying exchangePer underlying exchangeAbstraction lag; new features take time
HyperliquidDEXPerpetualsREST, WebSocketNone (wallet-based)0.035%Generous, per-walletSelf-custody (on-chain)Single-chain; smart-contract risk
dYdX v4DEXPerpetualsREST, gRPC, WebSocketNone (wallet-based)0.025–0.05%Per-IP and per-walletSelf-custody (on-chain)Cosmos app-chain; bridging required

// cyan = CEX · lime = DEX · magenta = broker abstraction · self-custody > exchange custody when you can

// free reference · 14 pages

Platform Coverage PDF

All 16 platforms and 8 crypto venues in one branded PDF you can keep open while you decide. Includes a decision tree, language quick-pick, three runnable code samples (cAlgo, CCXT, Tradovate), and a clickable source list.

Download free PDF
// the 12-week path

Week by week. No skipping.

Each module builds on the previous. Check off deliverables as you go — progress is saved locally. Week 4 adapts to your track selection above.

// curriculum progress
0/12
0%
in progress
1

Week 1: Markets Are Auctions

Build the mental model: price is the output of an auction, not a random walk. Value areas, point of control, and what buyers vs sellers control at each bar.

// deliverables
Summarize auction theory in one page of your own words
Identify 3 value areas on a current chart
Calculate VWAP ± 1 std for today's ES session
+1 more on nexural.io/automation
2

Week 2: The Anatomy of a Strategy

Decompose any strategy into its atomic units: signal → filter → size → entry → exit → risk. Every decision point is a potential failure mode. Map them all before writing a line of code.

// deliverables
Write pseudocode for one strategy you currently trade
Identify every decision point and when it can fail
Define your minimum acceptable R:R (should be ≥ 2:1)
+1 more on nexural.io/automation
3

Week 3: From TradingView to Code

Get your first alert triggering a real action outside TradingView. Pine Script → JSON payload → webhook receiver → log file. The full signal pipeline in 7 days.

// deliverables
Write a TradingView alert with JSON payload
Set up a webhook receiver (Node or Python)
Log every received alert to a structured file
+1 more on nexural.io/automation
4

Week 4: Python Basics for Traders

Fill the gap in your weakest skill set. Traders learn pandas, NumPy, and basic data manipulation. Coders learn market structure, order types, and how fills actually work.

// deliverables
Complete 3 data manipulation exercises with pandas
Load a CSV of trade data and calculate expectancy
Write a function that computes position size given risk %
+1 more on nexural.io/automation
5

Week 5: Reading Order-Flow Data

Understand delta, footprint, cumulative delta, and how to use them as filters — not signals. Order flow confirms or invalidates what price structure is suggesting.

// deliverables
Identify 2 delta divergence examples on a past chart
Write a function that flags delta exhaustion
Back-test a simple delta-filtered entry on 20 trades
+1 more on nexural.io/automation
6

Week 6: Your First Backtest

Run a rigorous backtest with realistic assumptions, no look-ahead bias, and honest transaction costs. Define in-sample and out-of-sample before touching the data.

// deliverables
Define in-sample and out-of-sample periods before starting
Code the strategy in Python, Backtrader, or QuantConnect
Apply realistic slippage (0.5–1 tick) and commission model
+1 more on nexural.io/automation
7

Week 7: Risk Math

Build the risk layer from the ground up: R:R gate, position sizing via Kelly fraction, daily loss limit, and expectancy. The math that decides whether your system survives.

// deliverables
Code a pre_trade_risk_check() function
Implement Kelly Criterion with 0.25 fraction cap
Calculate your strategy's expectancy per trade
+1 more on nexural.io/automation
8

Week 8: The Broker API

Connect to a paper trading account and submit your first programmatic order. Test every order type, every rejection scenario, and every partial fill before touching real capital.

// deliverables
Set up paper trading on Alpaca OR IBKR OR NT8 Simulation
Submit a limit order via API and confirm execution
Test order rejection (outside market hours, insufficient margin)
+1 more on nexural.io/automation
9

Week 9: Webhooks and Order Routing

Build the full TradingView → webhook → broker pipeline, end-to-end. Deduplication, payload validation, error handling, and a round-trip latency target under 2 seconds.

// deliverables
Webhook receiver deduplicates by client_order_id
Payload validation rejects malformed alerts
Error handling sends alert on rejected order
+1 more on nexural.io/automation
10

Week 10: Telemetry and Journaling

Build the observability layer: structured JSON logs, real-time P&L dashboard, trade journal that records entry, exit, R:R, and context for every trade.

// deliverables
All events emitted as structured JSON logs
P&L updates in real-time (websocket or polling ≤ 5 seconds)
Trade journal records: entry, exit, R:R, notes, screenshot link
+1 more on nexural.io/automation
11

Week 11: Kill Switches and Circuit Breakers

Harden the system with kill switches, deadman switch, and drawdown circuit breakers. Every one of them must be tested before going live — not just coded.

// deliverables
Kill switch tested: flat-all executes within 5 seconds
Deadman switch tested: flattens if heartbeat absent > 60 seconds
Drawdown circuit breaker pauses signals at −5% drawdown
+1 more on nexural.io/automation
12

Week 12: Going Live with Real Capital

Structured go-live with minimum viable capital and full monitoring in place. Two weeks of paper in live-hours conditions. Start with ≤ 25% of intended capital. No strategy changes for 30 days.

// deliverables
Complete all 27 items on the Pre-Flight Checklist
Trade 2 weeks on paper in live-hours conditions
Start live with ≤ 25% of intended capital for first 2 weeks
+1 more on nexural.io/automation
// reference repositories

Read the source. Not just the docs.

// risk layer deep-dive

The section that saves accounts.

Every successful automated trader builds risk controls first and strategy logic second. The Risk Layer isn’t a feature — it’s the operating system. These five code patterns represent the minimum viable safety net before any strategy touches real capital.

1. Pre-Trade Risk Check

Every order submission must pass three gates: minimum R:R ratio, maximum position size as a percentage of account equity, and the daily loss limit check. If any gate fails, the function returns a rejection reason and the order never reaches the broker.

Pythonrisk_gate.py
1# risk_gate.py — Pre-trade risk validation
2# Rejects any trade that fails R:R, size, or daily-loss-limit checks
3 
4def pre_trade_risk_check(
5 account_equity: float,
6 entry_price: float,
7 stop_price: float,
8 target_price: float,
9 proposed_size: float,
10 daily_pnl: float,
11 daily_loss_limit: float = -0.02, # -2% account
12 max_risk_per_trade: float = 0.02, # 2% account
13 min_rr_ratio: float = 2.0,
14) -> tuple[bool, str]:
15 """
16 Returns (approved, reason). Raise before any order submission.
17 """
18 # 1. R:R check
19 risk_per_unit = abs(entry_price - stop_price)
20 reward_per_unit = abs(target_price - entry_price)
21 if risk_per_unit == 0:
22 return False, "Stop equals entry — invalid trade parameters"
23 
24 rr_ratio = reward_per_unit / risk_per_unit
25 if rr_ratio < min_rr_ratio:
26 return False, f"R:R {rr_ratio:.2f} below minimum {min_rr_ratio:.2f}"
27 
28 # 2. Position size check
29 dollar_risk = proposed_size * risk_per_unit
30 risk_pct = dollar_risk / account_equity
31 if risk_pct > max_risk_per_trade:
32 return False, f"Risk {risk_pct:.1%} exceeds max {max_risk_per_trade:.1%}"
33 
34 # 3. Daily loss limit check
35 daily_pnl_pct = daily_pnl / account_equity
36 if daily_pnl_pct <= daily_loss_limit:
37 return False, f"Daily loss {daily_pnl_pct:.1%} hit limit — no new trades today"
38 
39 return True, "approved"
40 
41# Usage
42approved, reason = pre_trade_risk_check(
43 account_equity=50_000,
44 entry_price=4_500.00,
45 stop_price=4_493.50,
46 target_price=4_517.00,
47 proposed_size=2,
48 daily_pnl=-450.00,
49)
50if not approved:
51 raise RuntimeError(f"Trade rejected: {reason}")

// try it — risk-gate playground

Drag the three sliders. Expectancy, full Kelly, quarter-Kelly, and the daily loss limit recompute live. A negative-expectancy combination turns the top metric coral — that’s the gate refusing the trade in cartoon form.

// playground · risk-gate math · pure client-side
drag · watch · learn
45%

Share of trades that close in your favor.

200

Mean $ realized on a winning trade.

100

Mean $ given back on a losing trade (enter as a positive number).

// reward : risk
2.00 : 1

A 1:1 system needs >50% win rate to be profitable. A 2:1 system only needs >33%. This is why R:R is the lever that matters.

Expectancy per trade
+$35.00

Positive expectancy — over many trades, the math is on your side.

Kelly fraction (full)
17.5%

Theoretically optimal fraction of capital to risk per trade. Painful in real drawdowns.

Quarter-Kelly (recommended)
4.38%

The cap most quants actually use. Smoother equity curve, ~75% of the geometric growth.

Daily loss limit (2% of $25,000)
$500

That’s 5 losing trades at your average loss before the daily kill-switch trips. When it trips, the strategy stops sending orders for the day. No negotiation.

// tip · drop your win rate by 5pp and watch quarter-Kelly collapse — the math is unforgiving on the right side of the curve. That’s the whole point of the gate.

2. Pine Script Alert with Structured Payload

The key detail most tutorials miss: use alert.freq_once_per_bar_close — not once_per_bar. The bar-close version prevents re-paint alerts from generating orders on candles that haven’t closed yet, which is one of the most common causes of unexpected entries.

Pine Scriptpine_alert.pine
1// pine_alert.pine — Alert with structured JSON payload
2// Run at bar close to avoid re-paints
3 
4//@version=5
5strategy("Asymmetric Entry v1", overlay=true, calc_on_every_tick=false)
6 
7// — Inputs
8rsi_len = input.int(14, "RSI Length")
9ema_fast = input.int(9, "Fast EMA")
10ema_slow = input.int(21, "Slow EMA")
11risk_pct = input.float(1.0, "Risk %", minval=0.1, maxval=5.0)
12 
13// — Indicators
14rsi = ta.rsi(close, rsi_len)
15ema_f = ta.ema(close, ema_fast)
16ema_s = ta.ema(close, ema_slow)
17 
18// — Entry conditions
19long_cond = ta.crossover(ema_f, ema_s) and rsi < 60
20short_cond = ta.crossunder(ema_f, ema_s) and rsi > 40
21 
22// — Structured JSON payload for webhook receiver
23if long_cond
24 alert(
25 str.format(
26 "{"action":"buy","symbol":"{0}","price":{1}," +
27 ""risk_pct":{2},"bar_time":"{3}"}",
28 syminfo.ticker, close, risk_pct, str.tostring(time)
29 ),
30 alert.freq_once_per_bar_close // no re-paints
31 )
32 strategy.entry("Long", strategy.long)
33 
34if short_cond
35 alert(
36 str.format(
37 "{"action":"sell","symbol":"{0}","price":{1}," +
38 ""risk_pct":{2},"bar_time":"{3}"}",
39 syminfo.ticker, close, risk_pct, str.tostring(time)
40 ),
41 alert.freq_once_per_bar_close
42 )
43 strategy.entry("Short", strategy.short)

3. Webhook Receiver with Idempotency

TradingView can fire the same alert multiple times for the same bar under network retry conditions. Without idempotency keys, each retry becomes a new order. This Express receiver generates a deterministic key from the signal content and rejects duplicates before they reach the broker. In production, replace the in-memory Set with Redis.

JavaScriptwebhook.js
1// webhook.js — Express webhook receiver with idempotency
2// Deduplicates by client_order_id to prevent double-orders on retry
3 
4const express = require("express");
5const crypto = require("crypto");
6const app = express();
7app.use(express.json());
8 
9// In-memory dedup cache (use Redis in production)
10const processedIds = new Set();
11 
12app.post("/webhook/signal", async (req, res) => {
13 const payload = req.body;
14 
15 // 1. Validate payload shape
16 const { action, symbol, price, risk_pct } = payload;
17 if (!action || !symbol || !price) {
18 return res.status(400).json({ error: "invalid_payload" });
19 }
20 
21 // 2. Generate deterministic idempotency key
22 const raw = `${action}:${symbol}:${payload.bar_time}`;
23 const client_order_id = crypto
24 .createHash("sha256")
25 .update(raw)
26 .digest("hex")
27 .slice(0, 24);
28 
29 // 3. Check for duplicate
30 if (processedIds.has(client_order_id)) {
31 console.log(`[dedup] Skipping duplicate signal: ${client_order_id}`);
32 return res.json({ status: "duplicate", client_order_id });
33 }
34 
35 processedIds.add(client_order_id);
36 
37 try {
38 // 4. Submit order to broker (Alpaca example)
39 const order = await submitOrder({
40 symbol,
41 side: action === "buy" ? "buy" : "sell",
42 type: "limit",
43 limit_price: price,
44 qty: computeQty(risk_pct, price),
45 client_order_id,
46 time_in_force: "day",
47 });
48 
49 console.log(`[order] Submitted ${action} ${symbol} → ${order.id}`);
50 res.json({ status: "submitted", order_id: order.id, client_order_id });
51 } catch (err) {
52 // Remove from dedup cache on failure so retry can proceed
53 processedIds.delete(client_order_id);
54 console.error(`[error] Order submission failed: ${err.message}`);
55 res.status(500).json({ error: "submission_failed", message: err.message });
56 }
57});
58 
59app.listen(3001, () => console.log("Webhook receiver listening on :3001"));

4. NinjaScript Circuit Breaker

NinjaTrader 8 strategies run inside the platform process. This circuit breaker pattern checks daily P&L before every OnBarUpdate entry signal. When the daily loss limit is hit, it flattens all positions and activates the kill switch flag, which blocks all future entries until manually reset.

C# (NinjaScript)AsymmetricStrategy.cs
1// CircuitBreaker.cs — NinjaScript kill switch + circuit breaker
2// Stops new entries when daily P&L hits limit or kill switch is active
3 
4#region Using declarations
5using System;
6using NinjaTrader.Cbi;
7using NinjaTrader.NinjaScript;
8#endregion
9 
10namespace NinjaTrader.NinjaScript.Strategies
11{
12 public class AsymmetricStrategy : Strategy
13 {
14 private double dailyLossLimit = -500; // dollars
15 private bool killSwitchActive = false;
16 
17 protected override void OnStateChange()
18 {
19 if (State == State.SetDefaults)
20 {
21 Name = "AsymmetricStrategy";
22 Calculate = Calculate.OnBarClose;
23 }
24 }
25 
26 protected override void OnBarUpdate()
27 {
28 // Gate 1: Kill switch — set via UI or external trigger
29 if (killSwitchActive)
30 {
31 Print("[KILL] Kill switch active — no new orders");
32 return;
33 }
34 
35 // Gate 2: Daily loss circuit breaker
36 double todayPnL = GetDailyPnL();
37 if (todayPnL <= dailyLossLimit)
38 {
39 Print($"[CIRCUIT] Daily P&L {todayPnL:F2} hit limit {dailyLossLimit:F2}");
40 FlattenAll();
41 killSwitchActive = true;
42 return;
43 }
44 
45 // Gate 3: Only trade in defined session hours
46 if (Time[0].Hour < 9 || Time[0].Hour >= 16)
47 return;
48 
49 // — Signal logic (simplified)
50 double rsi = RSI(14, 3)[0];
51 double ema9 = EMA(9)[0];
52 double ema21 = EMA(21)[0];
53 
54 if (rsi < 40 && ema9 > ema21 && Position.MarketPosition == MarketPosition.Flat)
55 EnterLongLimit(0, true, 1, Close[0] - 0.25, "LongEntry");
56 
57 if (rsi > 60 && ema9 < ema21 && Position.MarketPosition == MarketPosition.Flat)
58 EnterShortLimit(0, true, 1, Close[0] + 0.25, "ShortEntry");
59 }
60 
61 private double GetDailyPnL()
62 {
63 return Performance.AllTrades.TradesPerformance.GrossProfit
64 - Performance.AllTrades.TradesPerformance.GrossLoss;
65 }
66 
67 private void FlattenAll()
68 {
69 if (Position.MarketPosition != MarketPosition.Flat)
70 {
71 Print("[FLATTEN] Closing all positions");
72 ExitLong();
73 ExitShort();
74 }
75 }
76 }
77}

5. Deadman Switch

A deadman switch runs as a separate process and watches a heartbeat file. The main strategy touches this file on every bar update. If the heartbeat goes stale for more thanMAX_SILENCE_SECONDS— because the strategy crashed, the machine rebooted, or the network dropped — the deadman triggers a market-order flat-all on every open position.

Pythondeadman.py
1# deadman.py — Flat-all if heartbeat absent > N seconds
2# Run as a separate process alongside your main strategy
3 
4import time
5import signal
6import threading
7from datetime import datetime
8 
9HEARTBEAT_FILE = "/tmp/strategy_heartbeat"
10MAX_SILENCE_SECONDS = 60 # flat all if no heartbeat for 60s
11CHECK_INTERVAL = 5 # poll every 5 seconds
12 
13def flat_all_positions(broker_client):
14 """Close every open position. Implement for your broker."""
15 print(f"[DEADMAN] {datetime.now().isoformat()} — Flattening all positions")
16 positions = broker_client.get_all_positions()
17 for position in positions:
18 symbol = position.symbol
19 qty = abs(int(position.qty))
20 side = "sell" if float(position.qty) > 0 else "buy"
21 broker_client.submit_order(
22 symbol=symbol,
23 qty=qty,
24 side=side,
25 type="market",
26 time_in_force="day",
27 )
28 print(f"[DEADMAN] Closed {qty} {symbol} ({side})")
29 
30def deadman_monitor(broker_client):
31 """Watch heartbeat file. If stale > MAX_SILENCE_SECONDS, flatten."""
32 print(f"[DEADMAN] Monitor started — max silence: {MAX_SILENCE_SECONDS}s")
33 while True:
34 time.sleep(CHECK_INTERVAL)
35 try:
36 mtime = os.path.getmtime(HEARTBEAT_FILE)
37 age = time.time() - mtime
38 if age > MAX_SILENCE_SECONDS:
39 print(f"[DEADMAN] Heartbeat age {age:.0f}s — triggering flat-all")
40 flat_all_positions(broker_client)
41 # Keep monitoring — may need to re-flatten if positions reopen
42 except FileNotFoundError:
43 print("[DEADMAN] Heartbeat file missing — creating baseline")
44 with open(HEARTBEAT_FILE, "w") as f:
45 f.write(str(time.time()))
46 
47# In your main strategy loop, touch the heartbeat file each bar:
48# with open(HEARTBEAT_FILE, "w") as f:
49# f.write(str(time.time()))

6. cTrader cAlgo (C#) — RSI Bracket Bot

cAlgo is cTrader’s native automation API. Robots inherit fromRobotand react to bar/tick events. This bot reads RSI on every closed bar and submits a market order with stop-loss and take-profit in pips on extremes — with a duplicate-position guard via the Labelfield. Same shape works for Forex and CFDs.

C# (NinjaScript)RsiBracketBot.cs
1// RsiBracketBot.cs — cTrader cAlgo example
2// Trade RSI extremes with bracketed stop / take-profit (in pips).
3using cAlgo.API;
4using cAlgo.API.Indicators;
5 
6namespace cAlgo.Robots
7{
8 [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
9 public class RsiBracketBot : Robot
10 {
11 [Parameter("RSI Periods", DefaultValue = 14, MinValue = 2)]
12 public int RsiPeriods { get; set; }
13 
14 [Parameter("Lots", DefaultValue = 0.10, MinValue = 0.01, Step = 0.01)]
15 public double Lots { get; set; }
16 
17 [Parameter("Stop Loss (pips)", DefaultValue = 25)]
18 public double StopLossPips { get; set; }
19 
20 [Parameter("Take Profit (pips)", DefaultValue = 50)]
21 public double TakeProfitPips { get; set; }
22 
23 private RelativeStrengthIndex _rsi;
24 private const string Label = "RsiBracketBot";
25 
26 protected override void OnStart()
27 {
28 _rsi = Indicators.RelativeStrengthIndex(Bars.ClosePrices, RsiPeriods);
29 }
30 
31 protected override void OnBar()
32 {
33 // Skip if we already have a position with this label
34 var open = Positions.FindAll(Label, SymbolName);
35 if (open.Length > 0) return;
36 
37 var volume = Symbol.QuantityToVolumeInUnits(Lots);
38 var prev = _rsi.Result.Last(1);
39 
40 if (prev < 30)
41 ExecuteMarketOrder(TradeType.Buy, SymbolName, volume, Label,
42 StopLossPips, TakeProfitPips);
43 else if (prev > 70)
44 ExecuteMarketOrder(TradeType.Sell, SymbolName, volume, Label,
45 StopLossPips, TakeProfitPips);
46 }
47 
48 protected override void OnStop()
49 {
50 // Optional: flatten on shutdown for safety
51 foreach (var p in Positions.FindAll(Label, SymbolName))
52 ClosePosition(p);
53 }
54 }
55}

7. CCXT (Python) — Spot & Perp Order

CCXT is the unified Python/JS library for over 100 crypto venues. The same code targets Coinbase Advanced, Kraken, Bybit, OKX, Binance — you change one env var. Below: spot market-buy by quote-cost on one client, then a separate defaultType: "swap"client for a leveraged perpetual long with isolated margin. CatchesRateLimitExceededand InsufficientFundsseparately because they need different retry strategies.

Pythonccxt_order.py
1# ccxt_order.py — Spot + perp market order with proper error handling
2# pip install ccxt
3import os
4import ccxt
5 
6VENUE = os.environ["VENUE"] # e.g. "bybit", "okx", "binance"
7API_KEY = os.environ["API_KEY"]
8API_SECRET = os.environ["API_SECRET"]
9PASSPHRASE = os.environ.get("API_PASSPHRASE") # required for OKX, Coinbase
10 
11exchange_cls = getattr(ccxt, VENUE)
12exchange = exchange_cls({
13 "apiKey": API_KEY,
14 "secret": API_SECRET,
15 "password": PASSPHRASE,
16 "enableRateLimit": True, # CCXT will sleep to respect venue limits
17 "options": {"defaultType": "spot"},
18})
19 
20def place_spot_buy(symbol: str, quote_amount: float):
21 """Market-buy 'quote_amount' of the quote currency (e.g. 100 USDT of BTC)."""
22 try:
23 bal = exchange.fetch_balance()
24 free_quote = bal["free"].get(symbol.split("/")[1], 0)
25 if free_quote < quote_amount:
26 raise RuntimeError(f"Insufficient quote balance: {free_quote}")
27 
28 # 'createMarketBuyOrderRequiresPrice' is False on most venues when
29 # passing cost via params; check venue docs.
30 order = exchange.create_market_buy_order(
31 symbol, amount=None,
32 params={"cost": quote_amount},
33 )
34 return order
35 except ccxt.RateLimitExceeded as e:
36 # Back off and retry from caller
37 raise
38 except ccxt.InsufficientFunds as e:
39 raise RuntimeError(f"Exchange rejected order: {e}")
40 
41def place_perp_long(symbol: str, contracts: float, leverage: int = 3):
42 """Open a long perpetual position with isolated margin."""
43 perp = exchange_cls({
44 "apiKey": API_KEY, "secret": API_SECRET, "password": PASSPHRASE,
45 "enableRateLimit": True,
46 "options": {"defaultType": "swap"}, # perpetual futures
47 })
48 perp.set_leverage(leverage, symbol, params={"marginMode": "isolated"})
49 return perp.create_market_buy_order(symbol, contracts)
50 
51if __name__ == "__main__":
52 print(place_spot_buy("BTC/USDT", 100.0))
53 # print(place_perp_long("BTC/USDT:USDT", 0.001, leverage=3))

8. Tradovate (Python) — Futures REST + WebSocket

Tradovate is one of the most retail-friendly futures brokers and exposes a clean access-token REST API plus a WebSocket fill feed. This module handles three real production concerns: token-bearer auth with explicit 401 surfacing, anisAutomated: trueflag (required by Tradovate for bot orders), and a self-healingon_closeWebSocket reconnect for fill streams.

Pythontradovate_order.py
1# tradovate_order.py — Auth + place market order on Tradovate REST
2# Tradovate uses an access-token flow. Tokens expire — refresh on 401.
3import os
4import time
5import requests
6from websocket import WebSocketApp # pip install websocket-client
7 
8BASE = "https://demo.tradovateapi.com/v1" # live: https://live.tradovateapi.com/v1
9WS = "wss://demo.tradovateapi.com/v1/websocket"
10 
11def get_access_token() -> str:
12 payload = {
13 "name": os.environ["TRADOVATE_USERNAME"],
14 "password": os.environ["TRADOVATE_PASSWORD"],
15 "appId": os.environ["TRADOVATE_APP_ID"],
16 "appVersion": "1.0",
17 "cid": os.environ["TRADOVATE_CID"],
18 "sec": os.environ["TRADOVATE_SECRET"],
19 }
20 r = requests.post(f"{BASE}/auth/accesstokenrequest", json=payload, timeout=10)
21 r.raise_for_status()
22 body = r.json()
23 if "accessToken" not in body:
24 raise RuntimeError(f"Auth failed: {body}")
25 return body["accessToken"]
26 
27def place_market_order(token: str, account_id: int, symbol: str, qty: int, action: str = "Buy"):
28 """action: 'Buy' or 'Sell'. symbol e.g. 'MESM5' (Micro E-mini June '25)."""
29 headers = {"Authorization": f"Bearer {token}"}
30 body = {
31 "accountSpec": os.environ["TRADOVATE_USERNAME"],
32 "accountId": account_id,
33 "action": action,
34 "symbol": symbol,
35 "orderQty": qty,
36 "orderType": "Market",
37 "isAutomated": True,
38 }
39 r = requests.post(f"{BASE}/order/placeorder", json=body, headers=headers, timeout=10)
40 if r.status_code == 401:
41 raise PermissionError("Token expired — re-auth and retry")
42 r.raise_for_status()
43 return r.json()
44 
45def stream_fills(token: str, on_fill):
46 """Reconnect-on-drop fill stream. Caller passes a callback(message_dict)."""
47 def _on_open(ws):
48 ws.send(f"authorize\n1\n\n{token}")
49 def _on_message(ws, msg):
50 on_fill(msg)
51 def _on_close(ws, code, reason):
52 print(f"[WS] closed {code} {reason} — reconnecting in 3s")
53 time.sleep(3)
54 stream_fills(token, on_fill) # tail-call reconnect
55 WebSocketApp(WS, on_open=_on_open, on_message=_on_message, on_close=_on_close).run_forever()
56 
57if __name__ == "__main__":
58 tok = get_access_token()
59 print(place_market_order(tok, account_id=int(os.environ["TRADOVATE_ACCOUNT_ID"]),
60 symbol="MESM5", qty=1, action="Buy"))
// pitfalls hall of fame

Failures everyone makes. Pay the tuition here.

These aren’t edge cases. Every one of these has caused real capital loss for someone running automation without proper safeguards.

01

The Overfit Backtest

// what happened

The strategy produced a Sharpe ratio of 4.2 in-sample. Out-of-sample it lost money from day one. The developer had inadvertently optimized over 3 years of data without holding out a test set.

// the math

In-sample Sharpe 4.2 → Out-of-sample Sharpe −0.3. Overfitting penalty.

// the fix

Define in-sample and out-of-sample periods before you touch the data. Never optimize against the test set.

02

The Silent Reject

// what happened

Alerts fired. No orders appeared. The webhook receiver had silently crashed hours earlier. The strategy missed 12 trades before anyone noticed.

// the math

MTTR (mean time to detect): 4.5 hours. Missed P&L opportunity: ~$900.

// the fix

Heartbeat monitoring on the webhook receiver is non-negotiable. Alert within 60 seconds of any process failure.

03

Timezone Roulette

// what happened

The data feed returned timestamps in UTC. The broker expected EST. The system submitted orders 5 hours before intended session start, during pre-market when spreads were enormous.

// the math

Slippage on 1 contract: 4 ticks = $50. Over 30 trades: $1,500 unmodeled cost.

// the fix

Normalize all timestamps to UTC at ingestion. Convert to local session time only at the final display or session-filter layer.

04

The Duplicate Order Storm

// what happened

A TradingView alert fired three times on the same bar due to a misconfigured alert frequency. Three identical market orders executed, tripling the intended position size.

// the math

Intended exposure: 1 × $5,000 risk. Actual exposure: 3 × $5,000. Drawdown: $8,200.

// the fix

Use alert.freq_once_per_bar_close in Pine Script. Implement client_order_id deduplication on the receiver.

05

The Orphaned Position

// what happened

Strategy process crashed mid-trade. The exit logic never ran. An open short position sat overnight, then gapped up 2% against the strategy at the open.

// the math

Overnight gap: +2.1% × $10,000 position = $210 unmanaged loss.

// the fix

Deadman switch flattens all positions if the heartbeat stops. Startup code reconciles broker state against strategy state before resuming.

06

The Regime Blind Spot

// what happened

A mean-reversion strategy backtested beautifully on 2019-2022 data — a mostly range-bound equity market. It deployed into the 2022 bear market and lost steadily for 8 months.

// the math

Annualized return in-sample: +34%. Live during regime shift: −28%.

// the fix

Test across multiple regimes explicitly. Use a regime filter (e.g., 200-day SMA slope) to pause mean-reversion strategies during trending conditions.

07

The API Key Expiry

// what happened

API keys were rotated as part of a routine security audit. The strategy's config file wasn't updated. All order submissions returned 401 Unauthorized for 3 hours before anyone noticed.

// the math

3 hours of missed signal time. 4 valid setups unexecuted. Approx. $650 opportunity cost.

// the fix

Store credentials in a secrets manager with expiry alerts. Health-check the API connection at startup and on a 5-minute heartbeat.

08

The Slippage Model Gap

// what happened

Backtest used fill-at-close prices. Live trading used market orders. In the 30 minutes before the close, ES spreads widen and fills came in 2–4 ticks worse than modeled.

// the math

Modeled slippage: 0.5 ticks. Actual: 2.8 ticks. Profit factor dropped from 1.8 to 1.1.

// the fix

Model slippage at 2× your average spread for market orders. Switch to limit orders with a modest offset from current price.

// downloads

Five PDFs. Print them. Use them.

Reference documents designed to live on your desk, not in a folder you never open. Enter your email above to unlock instant download access.

Pre-Flight Checklist

pre-flight-checklist.pdf · ~5KB

27-point pre-deployment audit across Code, Data, Risk, Broker, Monitoring, and Recovery categories.

27 numbered checks
6 categories
Checkbox format
Print-ready
enter email to unlock

Broker API Comparison

broker-api-comparison.pdf · ~6KB

IBKR, Alpaca, Tradovate, NT8, and TradingView side-by-side across 10 dimensions. No affiliates.

5 platforms compared
10 dimensions
Where each breaks
Honest trade-offs
enter email to unlock

Incident Response Playbook

incident-response-playbook.pdf · ~8KB

Step-by-step runbook for the 6 most common automation failures. Severity levels, decision trees, recovery procedures.

6 incident types
SEV-1 through SEV-3
Step-by-step procedures
Decision tree format
enter email to unlock

Maturity Self-Assessment

maturity-self-assessment.pdf · ~8KB

Score yourself on the 5-stage ladder across 5 dimensions. 25 questions. Identifies exactly what to build next.

25 questions
5 dimensions
0–75 scoring rubric
Next-steps guidance
enter email to unlock

12-Week Curriculum

12-week-curriculum.pdf · ~9KB

Printable week-by-week plan with deliverables, resource links, and checkboxes for each module.

12 weeks
48 deliverables
Resource links per week
Printable format
enter email to unlock
// frequently asked questions

Honest answers.

// the theory behind the automation

This guide covers the implementation. If you want the underlying mental models — asymmetric R:R, why markets are auctions, how to build a system that survives — read the book first.

read The Asymmetric Investor — free