Alpaca MCP server

alpaca:get_stock_bars (MCP)(symbol: “ZENA”, days: 1)
⎿ Historical Data for ZENA (Last 1 trading days):
---------------------------------------------------
Date: 2025-06-04, Open: $3.09, High: $3.19, Low: $3.00, Close: $3.18, Volume: 928524.0
Date: 2025-06-05, Open: $4.73, High: $6.92, Low: $4.15, Close: $4.45, Volume: 136897059.0

[DEBUG] Stream started - received first chunk

● ZENA Stock Bars (June 5, 2025):

  • Open: $4.73
  • High: $6.92
  • Low: $4.15
  • Close: $4.45
  • Volume: 136.9M shares

Large volume spike from 928K to 137M shares, with significant price movement from previous close of $3.18 to open
at $4.73.

How do I fetch bars with 1Min sample rate?
[DEBUG] Stream started - received first chunk

● The get_stock_bars function in the Alpaca MCP server only provides daily bars and doesn’t have a parameter for
different timeframes like 1-minute intervals. It returns daily OHLCV data for the specified number of trading
days.

For 1-minute bars, you would need to use a different endpoint or modify the MCP server to support intraday
timeframes. The current implementation is limited to daily historical data.

Get 1-minute bars for AAPL for the last hour

Show me intraday 1-minute data for TSLA from today
Get minute-level price data for NVDA
[DEBUG] Stream started - received first chunk

● The current Alpaca MCP server only provides daily bars, not intraday minute-level data. The get_stock_bars
function doesn’t support 1-minute timeframes - it only returns daily OHLCV data.

To get minute-level data, you’d need the MCP server to be updated with intraday bar endpoints that support
timeframes like “1Min”, “5Min”, etc.

For now, I can only show you the latest quotes or daily bars for these stocks.

@mcp.tool()
def get_stock_bars_intraday(symbol: str, timeframe: str = “1Min”, limit: int = 100) → str:
“”"
Get intraday stock bars with specified timeframe

Args:
    symbol: Stock ticker symbol (e.g., 'AAPL')
    timeframe: Timeframe (1Min, 5Min, 15Min, 30Min, 1Hour)
    limit: Number of bars to retrieve (max 1000)
"""
# Implementation would go here

Update - I made a new function for the Alpaca MCP server and fixed the calendar logic. Here is an example output from a prompt:

alpaca:get_stock_bars_intraday(symbol: “AAPL”, timeframe: “1Min”, limit: 60)
[DEBUG] Stream started - received first chunk

● I’ll fetch the intraday data for AAPL with 1-minute bars.
[DEBUG] MCP server “alpaca”: Calling MCP tool: get_stock_bars_intraday
[ERROR] MCP server “alpaca” Server stderr: Processing request of type CallToolRequest
[DEBUG] MCP server “alpaca”: Tool call succeeded: {“content”:[{“type”:“text”,“text”:“Intraday 1Min Bars for AAPL:\nPeriod: 2025-06-05 to 2025-06-05\n============================================================\n\nTime: 2025-06-05 23:59:00 ET\nOpen: $200.88, High: $200.90, Low: $200.80, Close: $200.80\nVolume: 1,546.0\nTrade Count: 54.0\nVWAP: $200.86\n────────────────────────────────────────\n\nTime: 2025-06-05 23:58:00 ET\nOpen: $200.85, High: $200.90, Low: $200.75, Close: $200.80\nVolume: 2,350.0\nTrade Count: 22.0\nVWAP: $200.81\n────────────────────────────────────────\n\nTime: 2025-06-05 23:57:00 ET\nOpen: $200.80, High: $200.82, Low: $200.80, Close: $200.82\nVolume: 2,379.0\nTrade Count: 36.0\nVWAP: $200.80\n────────────────────────────────────────\n\nTime: 2025-06-05 23:56:00 ET\nOpen: $200.98, High: $201.00, Low: $200.80, Close: $200.81\nVolume: 4,240.0\nTrade Count: 51.0\nVWAP: $200.96\n────────────────────────────────────────\n\nTime: 2025-06-05 23:55:00 ET\nOpen: $200.99, High: $200.99, Low: $200.80, Close: $200.85\nVolume: 565.0\nTrade Count: 11.0\nVWAP: $200.88\n────────────────────────────────────────\n\nTime: 2025-06-05 23:54:00 ET\nOpen: $200.81, High: $200.87, Low: $200.81, Close: $200.87\nVolume: 599.0\nTrade Count: 16.0\nVWAP: $200.85\n────────────────────────────────────────\n\nTime: 2025-06-05 23:53:00 ET\nOpen: $200.80, High: $200.82, Low: $200.80, Close: $200.81\nVolume: 728.0\nTrade Count: 16.0\nVWAP: $200.81\n────────────────────────────────────────\n\nTime: 2025-06-05 23:52:00 ET\nOpen: $200.90, High: $200.90, Low: $200.90, Close: $200.90\nVolume: 170.0\nTrade Count: 8.0\nVWAP: $200.90\n────────────────────────────────────────\n\nTime: 2025-06-05 23:51:00 ET\nOpen: $200.78, High: $200.79, Low: $200.76, Close: $200.76\nVolume: 3,920.0\nTrade Count: 29.0\nVWAP: $200.79\n────────────────────────────────────────\n\nTime: 2025-06-05 23:50:00 ET\nOpen: $200.79, High: $200.79, Low: $200.79, Close: $200.79\nVolume: 360.0\nTrade Count: 22.0\nVWAP: $200.79\n────────────────────────────────────────\n\nTime: 2025-06-05 23:49:00 ET\nOpen: $200.77, High: $200.77, Low: $200.77, Close: $200.77\nVolume: 231.0\nTrade Count: 16.0\nVWAP: $200.77\n────────────────────────────────────────\n\nTime: 2025-06-05 23:48:00 ET\nOpen: $200.78, High: $200.78, Low: $200.78, Close: $200.78\nVolume: 435.0\nTrade Count: 14.0\nVWAP: $200.78\n────────────────────────────────────────\n\nTime: 2025-06-05 23:46:00 ET\nOpen: $200.79, High: $200.79, Low: $200.79, Close: $200.79\nVolume: 562.0\nTrade Count: 9.0\nVWAP: $200.79\n────────────────────────────────────────\n\nTime: 2025-06-05 23:45:00 ET\nOpen: $200.75, High: $200.79, Low: $200.75, Close: $200.77\nVolume: 900.0\nTrade Count: 18.0\nVWAP: $200.76\n────────────────────────────────────────\n\nTime: 2025-06-05 23:43:00 ET\nOpen: $200.73, High: $200.73, Low: $200.73, Close: $200.73\nVolume: 555.0\nTrade Count: 21.0\nVWAP: $200.73\n────────────────────────────────────────\n\nTime: 2025-06-05 23:42:00 ET\nOpen: $200.73, High: $200.73, Low: $200.73, Close: $200.73\nVolume: 2,334.0\nTrade Count: 25.0\nVWAP: $200.73\n────────────────────────────────────────\n\nTime: 2025-06-05 23:41:00 ET\nOpen: $200.73, High: $200.73, Low: $200.69, Close: $200.73\nVolume: 1,924.0\nTrade Count: 14.0\nVWAP: $200.70\n────────────────────────────────────────\n\nTime: 2025-06-05 23:40:00 ET\nOpen: $200.73, High: $200.73, Low: $200.73, Close: $200.73\nVolume: 288.0\nTrade Count: 15.0\nVWAP: $200.73\n────────────────────────────────────────\n\nTime: 2025-06-05 23:39:00 ET\nOpen: $200.73, High: $200.73, Low: $200.73, Close: $200.73\nVolume: 1,266.0\nTrade Count: 18.0\nVWAP: $200.73\n────────────────────────────────────────\n\nTime: 2025-06-05 23:36:00 ET\nOpen: $200.73, High: $200.73, Low: $200.73, Close: $200.73\nVolume: 487.0\nTrade Count: 12.0\nVWAP: $200.73\n────────────────────────────────────────\n\nTime: 2025-06-05 23:34:00 ET\nOpen: $200.74, High: $200.74, Low: $200.74, Close: $200.74\nVolume: 183.0\nTrade Count: 11.0\nVWAP: $200.74\n────────────────────────────────────────\n\nTime: 2025-06-05 23:33:00 ET\nOpen: $200.79, High: $200.79, Low: $200.65, Close: $200.67\nVolume: 1,127.0\nTrade Count: 12.0\nVWAP: $200.77\n────────────────────────────────────────\n\nTime: 2025-06-05 23:32:00 ET\nOpen: $200.77, High: $200.79, Low: $200.68, Close: $200.68\nVolume: 437.0\nTrade Count: 18.0\nVWAP: $200.75\n────────────────────────────────────────\n\nTime: 2025-06-05 23:31:00 ET\nOpen: $200.84, High: $200.84, Low: $200.84, Close: $200.84\nVolume: 167.0\nTrade Count: 6.0\nVWAP: $200.84\n────────────────────────────────────────\n\nTime: 2025-06-05 23:30:00 ET\nOpen: $200.90, High: $200.90, Low: $200.70, Close: $200.70\nVolume: 215.0\nTrade Count: 8.0\nVWAP: $200.80\n────────────────────────────────────────\n\nTime: 2025-06-05 23:29:00 ET\nOpen: $200.85, High: $200.86, Low: $200.80, Close: $200.85\nVolume: 2,442.0\nTrade Count: 48.0\nVWAP: $200.84\n────────────────────────────────────────\n\nTime: 2025-06-05 23:28:00 ET\nOpen: $200.84, High: $200.85, Low: $200.84, Close: $200.85\nVolume: 963.0\nTrade Count: 29.0\nVWAP: $200.85\n────────────────────────────────────────\n\nTime: 2025-06-05 23:26:00 ET\nOpen: $200.84, High: $200.84, Low: $200.84, Close: $200.84\nVolume: 254.0\nTrade Count: 9.0\nVWAP: $200.84\n────────────────────────────────────────\n\nTime: 2025-06-05 23:25:00 ET\nOpen: $200.85, High: $200.85, Low: $200.79, Close: $200.84\nVolume: 2,286.0\nTrade Count: 16.0\nVWAP: $200.84\n────────────────────────────────────────\n\nTime: 2025-06-05 23:24:00 ET\nOpen: $200.85, High: $200.85, Low: $200.85, Close: $200.85\nVolume: 428.0\nTrade Count: 15.0\nVWAP: $200.85\n────────────────────────────────────────\n\nTime: 2025-06-05 23:23:00 ET\nOpen: $200.80, High: $200.80, Low: $200.80, Close: $200.80\nVolume: 679.0\nTrade Count: 13.0\nVWAP: $200.80\n────────────────────────────────────────\n\nTime: 2025-06-05 23:21:00 ET\nOpen: $200.78, High: $200.78, Low: $200.78, Close: $200.78\nVolume: 700.0\nTrade Count: 20.0\nVWAP: $200.78\n────────────────────────────────────────\n\nTime: 2025-06-05 23:20:00 ET\nOpen: $200.55, High: $200.61, Low: $200.55, Close: $200.61\nVolume: 430.0\nTrade Count: 18.0\nVWAP: $200.58\n────────────────────────────────────────\n\nTime: 2025-06-05 23:19:00 ET\nOpen: $200.70, High: $200.70, Low: $200.56, Close: $200.56\nVolume: 1,209.0\nTrade Count: 44.0\nVWAP: $200.67\n────────────────────────────────────────\n\nTime: 2025-06-05 23:18:00 ET\nOpen: $200.72, High: $200.72, Low: $200.72, Close: $200.72\nVolume: 484.0\nTrade Count: 37.0\nVWAP: $200.72\n────────────────────────────────────────\n\nTime: 2025-06-05 23:17:00 ET\nOpen: $200.78, High: $200.78, Low: $200.73, Close: $200.73\nVolume: 1,037.0\nTrade Count: 31.0\nVWAP: $200.75\n────────────────────────────────────────\n\nTime: 2025-06-05 23:16:00 ET\nOpen: $200.81, High: $200.81, Low: $200.80, Close: $200.80\nVolume: 888.0\nTrade Count: 25.0\nVWAP: $200.80\n────────────────────────────────────────\n\nTime: 2025-06-05 23:15:00 ET\nOpen: $200.90, High: $200.90, Low: $200.85, Close: $200.85\nVolume: 836.0\nTrade Count: 17.0\nVWAP: $200.89\n────────────────────────────────────────\n\nTime: 2025-06-05 23:14:00 ET\nOpen: $200.85, High: $200.90, Low: $200.85, Close: $200.90\nVolume: 1,353.0\nTrade Count: 10.0\nVWAP: $200.88\n────────────────────────────────────────\n\nTime: 2025-06-05 23:13:00 ET\nOpen: $200.88, High: $200.88, Low: $200.88, Close: $200.88\nVolume: 314.0\nTrade Count: 15.0\nVWAP: $200.88\n────────────────────────────────────────\n\nTime: 2025-06-05 23:12:00 ET\nOpen: $200.90, High: $200.90, Low: $200.90, Close: $200.90\nVolume: 809.0\nTrade Count: 19.0\nVWAP: $200.90\n────────────────────────────────────────\n\nTime: 2025-06-05 23:11:00 ET\nOpen: $200.86, High: $200.95, Low: $200.85, Close: $200.95\nVolume: 520.0\nTrade Count: 18.0\nVWAP: $200.90\n────────────────────────────────────────\n\nTime: 2025-06-05 23:10:00 ET\nOpen: $200.73, High: $200.73, Low: $200.73, Close: $200.73\nVolume: 644.0\nTrade Count: 10.0\nVWAP: $200.73\n────────────────────────────────────────\n\nTime: 2025-06-05 23:09:00 ET\nOpen: $200.87, High: $200.87, Low: $200.85, Close: $200.85\nVolume: 903.0\nTrade Count: 21.0\nVWAP: $200.87\n────────────────────────────────────────\n\nTime: 2025-06-05 23:08:00 ET\nOpen: $200.95, High: $200.98, Low: $200.95, Close: $200.98\nVolume: 1,492.0\nTrade Count: 24.0\nVWAP: $200.97\n────────────────────────────────────────\n\nTime: 2025-06-05 23:07:00 ET\nOpen: $200.97, High: $200.98, Low: $200.96, Close: $200.98\nVolume: 2,363.0\nTrade Count: 25.0\nVWAP: $200.96\n────────────────────────────────────────\n\nTime: 2025-06-05 23:06:00 ET\nOpen: $200.90, High: $200.96, Low: $200.90, Close: $200.96\nVolume: 641.0\nTrade Count: 22.0\nVWAP: $200.95\n────────────────────────────────────────\n\nTime: 2025-06-05 23:04:00 ET\nOpen: $200.90, High: $200.94, Low: $200.89, Close: $200.94\nVolume: 1,601.0\nTrade Count: 31.0\nVWAP: $200.92\n────────────────────────────────────────\n\nTime: 2025-06-05 23:03:00 ET\nOpen: $200.90, High: $200.90, Low: $200.90, Close: $200.90\nVolume: 959.0\nTrade Count: 26.0\nVWAP: $200.90\n────────────────────────────────────────\n\nTime: 2025-06-05 23:01:00 ET\nOpen: $200.80, High: $200.88, Low: $200.80, Close: $200.88\nVolume: 558.0\nTrade Count: 18.0\nVWAP: $200.86\n────────────────────────────────────────\n\nTime: 2025-06-05 23:00:00 ET\nOpen: $200.80, High: $200.80, Low: $200.80, Close: $200.80\nVolume: 381.0\nTrade Count: 25.0\nVWAP: $200.80\n────────────────────────────────────────\n\nTime: 2025-06-05 22:59:00 ET\nOpen: $200.80, High: $200.80, Low: $200.80, Close: $200.80\nVolume: 408.0\nTrade Count: 33.0\nVWAP: $200.80\n────────────────────────────────────────\n\nTime: 2025-06-05 22:57:00 ET\nOpen: $200.80, High: $200.80, Low: $200.80, Close: $200.80\nVolume: 226.0\nTrade Count: 25.0\nVWAP: $200.80\n────────────────────────────────────────\n\nTime: 2025-06-05 22:56:00 ET\nOpen: $200.81, High: $200.81, Low: $200.81, Close: $200.81\nVolume: 713.0\nTrade Count: 15.0\nVWAP: $200.81\n────────────────────────────────────────\n\nTime: 2025-06-05 22:55:00 ET\nOpen: $200.73, High: $200.73, Low: $200.73, Close: $200.73\nVolume: 303.0\nTrade Count: 13.0\nVWAP: $200.73\n────────────────────────────────────────\n\nTime: 2025-06-05 22:52:00 ET\nOpen: $200.81, High: $200.81, Low: $200.81, Close: $200.81\nVolume: 4,104.0\nTrade Count: 18.0\nVWAP: $200.81\n────────────────────────────────────────\n\nTime: 2025-06-05 22:50:00 ET\nOpen: $200.80, High: $200.81, Low: $200.76, Close: $200.81\nVolume: 2,593.0\nTrade Count: 40.0\nVWAP: $200.80\n────────────────────────────────────────\n\nTime: 2025-06-05 22:49:00 ET\nOpen: $200.77, High: $200.80, Low: $200.72, Close: $200.80\nVolume: 3,556.0\nTrade Count: 50.0\nVWAP: $200.78\n────────────────────────────────────────\n\nTime: 2025-06-05 22:46:00 ET\nOpen: $200.79, High: $200.79, Low: $200.79, Close: $200.79\nVolume: 217.0\nTrade Count: 12.0\nVWAP: $200.79\n────────────────────────────────────────\n\nTime: 2025-06-05 22:44:00 ET\nOpen: $200.80, High: $200.80, Low: $200.80, Close: $200.80\nVolume: 717.0\nTrade Count: 15.0\nVWAP: $200.80\n────────────────────────────────────────\n\nTotal bars retrieved: 60”}],“isError”:false}

● alpaca:get_stock_bars_intraday (MCP)(symbol: “AAPL”, timeframe: “1Min”, limit: 60)
⎿ Intraday 1Min Bars for AAPL:
Period: 2025-06-05 to 2025-06-05
alpaca:get_stock_bars_intraday (MCP)(symbol: “AAPL”, timeframe: “1Min”, limit: 60)
⎿ Intraday 1Min Bars for AAPL:
Period: 2025-06-05 to 2025-06-05

I will push the changes to the Alpaca Github repository and generate a PR. I want to add some more functionality first.

@Joe_O1 Great effort! The intraday bars data issue has been fixed and there is now support flexible timeframes (1Min, 5Min, 1Hour, etc.).

I have a huge 8 am spike on RXT 10/21/2025 with no follow through - looks like a late reported trade to me because the 1 bars around it look like nothing happened. I’ve actually noticed a few 8 am 1 minute bars that are suspect. it throws my ATR calculation off, so I’m having to figure out a way to ignore these spikes. Just like other similar posts, the bar doesn’t appear on TradingView

@Matt_Hibbs You asked about a spike in bar data around 8:00 AM.

The issue here is an artifact of how prices are received and timestamped by the SIPs. All Alpaca timestamps are the ‘participant timestamps’. Actually, more precisely, ‘Timestamp 1’ received from the SIPs. Below is the definition from the CTS Specification (the UTP Specification is similar).

Timestamp 1
2 x Integer (pair of integers).
Timestamp 1 is a Participant-provided timestamp representing the number of nanoseconds since Epoch. The first integer contains the number of seconds from epoch 1/1/1970, 00:00:00 UTC. The next integer contains the nanosecond portion of the time (e.g., 972402315). For any messages generated by CTS, e.g., Messages generated on behalf of a Participant, Price Band messages, Control messages and Market Status messages, the Timestamp 1 field will be set to current SIP time.

If from an Exchange: Timestamp 1 denotes the Exchange Matching Engine Publication timestamp for a transaction. Exchanges use a clock sync methodology ensuring that timestamps are accurate within tolerances of 100 microseconds or less. Exchanges shall provide the timestamp in terms of nanoseconds since Epoch.

If from the FINRA Alternative Display Facility (ADF) or a FINRA Trade Reporting Facility (TRF): Timestamp1 denotes the time of execution that a FINRA member reports to the FINRA ADF or a FINRA TRF. FINRA shall provide such times to the Processor in nanoseconds since Epoch.

We often generalize the timestamp to be the ‘time the trade executed’, but this isn’t exactly the definition. That is true for trades executed on an exchange. However, for trades not executed on an exchange and reported to a FINRA Alternative Display Facility (ADF) or a FINRA Trade Reporting Facility (TRF), it is the time which the trade was reported to the reporting facility. (These trades have an exchange code of “D”.) This time is typically very very close to the execution time, however it really distorts things for trades executed after 20:00 ET. Why? The ADF and TRF facilities aren’t open. They don’t open until 8:00 ET the following morning. Therefore, execution platforms don’t report overnight trades until 8:00 ET (even though they may have executed the previous evening). The timestamp is the time the trade is reported for those trades. All those trades get a timestamp of 8:00 (or shortly after) and therefore all get reported at once. If you look at trade volume between 8:00-8:15 AM ET you will often see a big spike along with swings in prices. That is because of trades from the previous evening all getting reported when the reporting facilities open.

The primary execution platform for overnight trades after 20:00 is the BOATS exchange. This anomaly, and how trades our time stamped, is specifically called out in the BOATS FAQ. Below is from the BOATS exchange FAQ. Note "Trades executed between 8:00 PM ET and 12:00 AM ET will carry a trade date of the following trade day."

The core offering is the Blue Ocean Session, which operates overnight in the US from 8:00 PM ET- 4:00 AM ET (Sunday-Thursday). It operates only on those calendar days when the NYSE Trade Report Facility (TRF) is open for reporting the following morning. Trades executed between 8:00 PM ET and 12:00 AM ET will carry a trade date of the following trade day. Settlement date will reflect the first business day following the transaction (T+1).

For example, when operating from Sunday evening at 8:00 PM to 4:00 AM ET Monday morning, the trades executed during that session will be reported to the NYSE TRF no later than 8:15 AM ET that same Monday morning.

This anomaly only occurs between 8:00 and 8:15 as represented in a typical bar chart shown below.

Other data providers may get data from other sources other than the SIPs, and may handle timestamps differently.

Hope that explains it, but I realize it probably doesn’t help.

Dan this IS SUPER helpful! I developed a script for “wild bars” that are premarket. I had noticed they all appear really close to 8 am, but spot checking is not enough. Understanding this helps me narrow down my wild bar script time window!

@Matt_Hibbs Glad that helps. Wish there was a better way to filter these overnight trades.

Here is my wild bar identification code - anyone can use this, but it is an educational example and i assume no liability for anyone using it. If some elements are helpful, wonderful, if anyone has suggestions or corrections, please let me know! This catches wild bars and normalizes OHLC. This particular code is for AmiBroker AFL, but the logic should be self-explanatory!

// Wild bar detection: large spike followed by immediate reversion
// Occurs 7:45-8:30 AM, can happen in clusters, price returns to pre-wild level
// Wild sequence resets when normal bar appears near the original clean price
// Distinguishes wild bars from legitimate breakouts (which move away from baseline)

// Time window for wild bars: 7:45 AM to 8:45 AM (225-345 minutes since PM open at 4:00 AM)
inWildBarWindow = MinutesSincePMopen >= 225 AND MinutesSincePMopen < 345;

// Calculate bar range
barRange = High - Low;

// Build clean ATR using only normal-sized bars (exclude wild bars from calculation)
// First pass: identify potentially oversized bars (can’t use isWildBar yet since it depends on cleanATR)
tempATR = ATR(20);
isPotentiallyOversized = barRange > 5 * tempATR AND inWildBarWindow;

// Track last normal bar’s range for clean ATR calculation
lastNormalRange = 0;
for(i = 0; i < BarCount; i++)
{
if(i == 0)
{
lastNormalRange[i] = barRange[i];
}
else
{
// Use current bar’s range if not oversized, else carry forward previous normal range
if(NOT isPotentiallyOversized[i])
{
lastNormalRange[i] = barRange[i];
}
else
{
lastNormalRange[i] = lastNormalRange[i-1];
}
}
}

// Calculate clean ATR using normal bar ranges (substitute last normal range for wild bars)
cleanBarRange = IIf(isPotentiallyOversized, lastNormalRange, barRange);
cleanATR20 = MA(cleanBarRange, 20); // Average includes substituted normal ranges
cleanATR20 = IIf(IsNull(cleanATR20) OR cleanATR20 == 0, tempATR, cleanATR20);

// Track the baseline “clean” OHLC - last normal bar’s values before wild activity
// Normal bar = smaller range AND close near previous clean level (not a breakout)
// Reset whenever we see a normal bar near the previous clean level
lastCleanClose = 0;
lastCleanHigh = 0;
lastCleanLow = 0;
lastCleanOpen = 0;
isNormalBar = False;

for(i = 0; i < BarCount; i++)
{
if(i == 0)
{
lastCleanClose[i] = Close[i];
lastCleanHigh[i] = High[i];
lastCleanLow[i] = Low[i];
lastCleanOpen[i] = Open[i];
isNormalBar[i] = True;
}
else
{
// Normal bar characteristics:
// 1. Range < 2x ATR (smaller bar)
// 2. Close near previous clean level (not breaking out)
isSmallerBar = barRange[i] < 2 * cleanATR20[i];
isNearCleanLevel = abs(Close[i] - lastCleanClose[i-1]) < 2 * cleanATR20[i];
isNormalBar[i] = isSmallerBar AND isNearCleanLevel;

    // Update clean OHLC: use current bar if normal, else carry forward previous clean values
    if(isNormalBar[i] OR NOT inWildBarWindow[i])
    {
        lastCleanClose[i] = Close[i];
        lastCleanHigh[i] = High[i];
        lastCleanLow[i] = Low[i];
        lastCleanOpen[i] = Open[i];
    }
    else
    {
        lastCleanClose[i] = lastCleanClose[i-1];
        lastCleanHigh[i] = lastCleanHigh[i-1];
        lastCleanLow[i] = lastCleanLow[i-1];
        lastCleanOpen[i] = lastCleanOpen[i-1];
    }
}

}

// Look ahead to see if price returns to near clean level within next 10 bars
// Check each future bar using Ref(), combine with OR to detect ANY return within window
priceReturned = False;
for(lookAhead = 1; lookAhead <= 10; lookAhead++)
{
futureClose = Ref(Close, lookAhead);
futureIsNormal = Ref(isNormalBar, lookAhead);
futureATR = Ref(cleanATR20, lookAhead);

// Return confirmed if: future bar is normal AND close to clean level
priceReturned = priceReturned OR (futureIsNormal AND abs(futureClose - lastCleanClose) < 2 * futureATR);

}

// A bar is “wild” if ALL conditions are met:
// 1. Within 7:45-8:45 AM time window
// 2. Bar range > 5x clean ATR (oversized move)
// 3. Price returns to near lastCleanClose within 10 bars via normal bar (not a breakout)
isOversized = barRange > 5 * cleanATR20;
isWildBar = inWildBarWindow AND isOversized AND priceReturned;

// Create cleaned sig variables by completely replacing wild bars with last clean OHLC
// This prevents wild bar extremes from contaminating ATR and range calculations
sigClose = IIf(isWildBar, lastCleanClose, Close);
sigHigh = IIf(isWildBar, lastCleanHigh, High);
sigLow = IIf(isWildBar, lastCleanLow, Low);
sigOpen = IIf(isWildBar, lastCleanOpen, Open);