← Back to Blog

From Backtest to Live: Automated Execution

You've designed a strategy, backtested it, validated it with walk-forward analysis, and set your risk limits. Now for the part that turns all of that into reality: connecting your strategy to a broker so it can trade automatically.

This is where trading meets engineering. The challenges shift from "does this strategy work?" to "how do I make sure orders fire reliably, every time, without manual intervention?" Getting this right is the difference between a strategy that works on paper and one that works with real money.

The Execution Pipeline

Automated execution is a pipeline with four stages:

  1. Signal generation — AmiBroker detects a buy or sell condition in your AFL
  2. Signal transmission — the signal is sent from AmiBroker to your execution layer
  3. Order logic — a middleware layer translates the signal into a specific order (instrument, quantity, order type)
  4. Order placement — the order is sent to your broker via their API

Each stage can fail independently. A robust system handles failures at every stage — not just the happy path.

Three Levels of Automation

Not everyone needs (or should start with) full automation. Here are the three levels, from simplest to most complex:

Level 1: Alert-Based (Semi-Manual)

AmiBroker generates an alert (sound, popup, email). You see the alert and place the order manually at your broker. This is the safest starting point — you stay in the loop for every trade.

The downside is latency and human error. If you're away from the screen, you miss the signal. If you hesitate, you get a worse price. And the whole point of automation was to remove human judgment from execution.

Level 2: Webhook-Based (Semi-Automated)

AmiBroker sends a signal to a local server (webhook), which processes the signal and places the order via broker API. You monitor a dashboard but don't intervene on individual trades.

This is the sweet spot for most retail algo traders. It's fully automated for normal operation, but you can pause, override, or shut down the system from a dashboard. You're supervising, not executing.

Level 3: Fully Autonomous

The system runs on a cloud server, handles daily login and authentication automatically, recovers from errors without intervention, and operates during market hours with no human involvement.

This is the end goal, but it requires significant engineering to handle all the edge cases: broker API downtime, network interruptions, session expiry, and market data gaps. Build up to this gradually.

Sending Signals from AFL

AmiBroker provides InternetPostRequest() for sending HTTP requests directly from AFL. This is your bridge from strategy logic to the outside world.

// Send signal via HTTP POST
url = "http://127.0.0.1:5001/webhook";

if (Buy[BarCount-1]) {
    params = "strategy=MyStrategy&action=BUY";
    result = InternetPostRequest(url, params);
}

if (Sell[BarCount-1]) {
    params = "strategy=MyStrategy&action=SELL";
    result = InternetPostRequest(url, params);
}

A few critical points about this approach:

  • Check the last bar only. Buy[BarCount-1] checks the current (latest) bar. Without this, AFL would fire signals for every historical bar on every refresh.
  • Use localhost. The webhook server runs on the same machine. No signals leave your computer until the middleware decides to send them to the broker.
  • Handle failures gracefully. InternetPostRequest() returns null if the server is unreachable. Your AFL should detect this and retry on the next refresh, not silently swallow the failure.

Deduplication: The Silent Killer

AmiBroker refreshes your AFL on every new tick or bar. If a Buy signal is true on bar 100, it will be true on every refresh while bar 100 is the latest bar. Without deduplication, you'll send dozens of identical orders.

The solution is to track which signals you've already sent:

// Track sent signals using StaticVars
lastSentBar = Nz(StaticVarGet("LastBuyBar"));

if (Buy[BarCount-1] AND BarCount-1 != lastSentBar) {
    params = "strategy=MyStrategy&action=BUY";
    result = InternetPostRequest(url, params);

    if (result != "") {
        StaticVarSet("LastBuyBar", BarCount-1);
    }
    // If POST failed (result is empty), don't set the flag
    // so it retries on the next refresh
}

StaticVarGet/Set persists values across AFL refreshes (but not across AmiBroker restarts). The flag is only set after a successful POST — so if the server is down, the signal retries automatically.

The Middleware Layer

Between AmiBroker and your broker, you need a middleware server. This is where the real engineering lives. The middleware handles:

  • Signal validation — is this a legitimate signal? Is the strategy enabled? Is it market hours?
  • Position tracking — what's the current position? Is this a new entry, an exit, or a reversal?
  • Instrument selection — for options strategies, which strike price? Which expiry? The middleware translates "BUY" into "buy NIFTY 24000 CE current weekly."
  • Order sizing — how many lots, based on your position sizing rules?
  • Broker API calls — actually placing the order through the broker's REST API
  • Error handling — what happens when the broker rejects the order? When the API times out? When the instrument isn't available?

A simple webhook server in Python (using Flask or FastAPI) works well for this. It receives POST requests from AFL, processes the signal, and calls the broker API. A hundred lines of well-structured code can handle the core logic.

Broker API Integration

Most Indian brokers now offer REST APIs for order placement. The typical flow:

  1. Authentication — login with your credentials, receive an access token (usually valid for one trading day)
  2. Order placement — POST request with instrument, quantity, order type, price
  3. Order status — poll or receive callbacks for fill confirmation
  4. Position queries — check current open positions for reconciliation

Daily Authentication

Broker tokens expire daily, which means your system needs to authenticate every morning before market open. This typically involves:

  • Logging into the broker's web portal (often requires TOTP/2FA)
  • Extracting an access token
  • Storing the token for the day's API calls

This is one of the most annoying parts of automation. Some brokers make it easy with API-based login. Others require browser-based flows that need tools like Selenium to automate. Either way, build this into your morning startup routine so it happens without manual intervention.

Order Types

For automated systems, market orders are usually the right choice. You want speed and certainty of fill, not price optimization. Limit orders risk not filling, which creates a dangerous state — your strategy thinks it's in a position, but the order is sitting unfilled.

The exception is highly liquid instruments where the bid-ask spread is tight. There, a limit order at the best bid/ask gives you the fill speed of a market order with slightly better pricing.

Handling the Hard Parts

Stop-and-Reverse (S&R)

Many strategies don't just exit — they reverse. A buy signal closes the short and opens a long in one logical step. But at the broker level, this is two separate orders: exit the current position, then enter the new one.

The danger is partial execution. If the exit fills but the entry fails, you're flat when you should be in a position. Your middleware needs to handle this as an atomic operation — if the entry fails after a successful exit, it should retry the entry, log a critical alert, or both.

Position Reconciliation

Your middleware tracks positions internally ("I'm long 1 lot of NIFTY CE"). But is that actually true at the broker? Network failures, manual interventions, or broker-side rejections can create mismatches.

Build a reconciliation check: periodically query the broker's position API and compare against your internal state. If they diverge, alert immediately. A position mismatch that goes undetected can turn a small problem into a catastrophic one.

Network and Server Failures

What happens when your internet drops for 30 seconds? When your middleware crashes? When the broker API returns a 500 error?

Design for failure:

  • Retry with backoff — if an API call fails, wait a second and retry. Don't hammer the server with rapid retries.
  • Persist state to disk — if your middleware crashes and restarts, it should know what positions are open. Don't rely solely on in-memory state.
  • Log everything — every signal received, every order sent, every response from the broker. When something goes wrong (and it will), logs are the only way to reconstruct what happened.
  • Health checks — your middleware should expose a health endpoint. Monitor it. If it stops responding, you need to know immediately, not at 3:30 PM when you check your account.

Market Hours and Edge Cases

Your system should know when the market is open. Signals received outside market hours should be queued or rejected, not sent to the broker. Handle edge cases:

  • Pre-market and post-market sessions
  • Trading holidays (the exchange calendar changes yearly)
  • Early market closes
  • Data feed delays that make your AFL fire stale signals

Latency: How Fast is Fast Enough?

For high-frequency strategies, microseconds matter. For typical retail strategies — trading on 5-minute to daily bars, a handful of trades per day — they don't.

A typical retail automation pipeline:

AFL signal detection:     ~instant (within tick/bar refresh)
AFL → Middleware (HTTP):  ~10-20ms (localhost)
Middleware processing:    ~5-10ms
Middleware → Broker API:  ~300-500ms (network + broker processing)
Total end-to-end:         ~500ms - 1 second

Under one second from signal to order placement. For a strategy that trades on intraday or daily bars, this is more than fast enough. The signal was generated from a completed bar — a few hundred milliseconds of additional latency is noise.

Don't over-optimize for latency at the retail level. Focus on reliability instead. A system that's 200ms slower but never drops a signal beats one that's faster but crashes once a week.

Paper Trading: The Dress Rehearsal

Before any real money flows through your pipeline, run the entire system in paper trading mode. This means:

  • AFL generates real signals from live market data
  • Middleware receives and processes signals normally
  • Orders are logged but not actually sent to the broker (or sent in the broker's simulation mode)
  • The dashboard shows what would have happened

Paper trade for at least two weeks during live market hours. You're testing the full pipeline, not the strategy (that was already validated). Watch for:

  • Signals firing at the wrong time or on the wrong bar
  • Duplicate signals that would create double orders
  • Signals that fire in backtest but not in live (data feed differences)
  • Morning startup failures (authentication, data feed connection)
  • Mid-day recovery after brief disconnections

Every issue you catch in paper trading is an issue that won't cost you money in live trading.

The Morning Routine

A well-automated system reduces your daily involvement to a morning startup check:

  1. Run the startup script — authenticates with broker, starts middleware, opens dashboard
  2. Verify the dashboard shows "connected" and positions match expected state
  3. Confirm the data feed is streaming live prices
  4. Walk away

Ideally, steps 1-3 are a single script that takes under a minute. The goal is to make the daily process so simple and reliable that you never skip it, and so automated that human error can't corrupt it.

The ultimate step — putting the system on a cloud server with automated startup — eliminates even this morning routine. But that's a topic for another post.

Monitoring in Production

Once live, you need to know if something breaks without staring at a screen all day. Build monitoring for:

  • Middleware health — is the server running? Is it processing signals?
  • Broker connection — is the API token still valid? Are orders going through?
  • Data feed — is AmiBroker receiving live prices? A stale data feed means no signals.
  • Position state — does your internal state match the broker? Check at least twice daily.

A simple web dashboard that shows current status, today's signals, and open positions goes a long way. Check it at market open, once mid-day, and at close. That's the total time commitment for a well-built system.

Summary

  1. The pipeline has four stages — signal generation, transmission, order logic, and placement. Each can fail independently.
  2. Start with webhook-based semi-automation. Full autonomy is the goal, but build up to it. A monitored dashboard with manual override is the right starting point.
  3. Deduplication is critical. Without it, every AFL refresh fires the same signal again. Use StaticVars to track sent signals.
  4. Build middleware between AFL and the broker. It handles position tracking, instrument selection, sizing, and error recovery. Keep it simple — a small Python server does the job.
  5. Design for failure. Retry logic, persistent state, comprehensive logging, and health checks. The question isn't whether something will break — it's whether your system recovers when it does.
  6. Paper trade the full pipeline for two weeks minimum. You're testing the engineering, not the strategy. Every bug caught here is money saved later.
  7. Reliability beats speed. At retail scale, a 1-second pipeline that never drops a signal beats a 100ms pipeline that crashes weekly.

What's Next

With this post, the core series is complete: strategy design, backtesting, validation, risk management, and execution. Future posts will dive deeper into specific topics — options execution mechanics, cloud deployment, multi-strategy portfolio management, and lessons from live trading. Follow the blog to stay updated.