TradeObservations – Al Brooks Snapshot

Instrument: ES 03-26
Last bar: 2025-12-16T07:20:00

This notebook is an archived snapshot of a 48-bar window and its Al Brooks–style AI analysis.

AI Analysis

  1. Context: The EMA is rising the entire sample and price is mostly above it, so this is a bull trend / bull spike-and-channel that evolved into a tighter bull channel from about bar 27 onward. The early part (0–10) is a flat, slightly down drift around EMA that becomes a clear upside spike around 15–18, then a channel up into the high 6870s.

  2. Clear patterns:

  • Bull spike at 15–16 followed by a pullback to 18–21 and then a channel up (spike & channel bull trend).
  • Repeated small bull flags / H2-like setups around 18–21 and again around 25–27.
  • Latest bars (40–47) form a weak trading-range-like pause / small pullback just above a flattening EMA, potentially a final flag in a mature bull channel.
  1. Probabilities: Bulls still have a slight edge because the EMA is up and price is holding above it, but the channel is aging and momentum is waning; odds of a deeper pullback or transition into a trading range are increasing. This is no longer an early, high-probability bull breakout phase.

  2. Entries / avoid: With-trend buys were best on the pullback near 18–21 and again on the shallow pullback around 36–41. At the current bars (44–47), risk–reward for new longs is mediocre: buying here is late in the channel and vulnerable to a test down toward the EMA or the 6868–6870 area. Avoid fading (shorting) aggressively in the middle of this range; if shorting, it’s better to wait for a clear lower high and strong bear breakout below the EMA rather than guessing tops in this tight structure.

import json
import pandas as pd
import matplotlib.pyplot as plt
import mplfinance as mpf

instrument = 'ES 03-26'

bars_json = '''[
  {
    "index": 0,
    "datetime": "2025-12-16T03:25:00",
    "open": 6860.5,
    "high": 6861.0,
    "low": 6858.75,
    "close": 6860.0,
    "ema": 6853.47592049547
  },
  {
    "index": 1,
    "datetime": "2025-12-16T03:30:00",
    "open": 6859.75,
    "high": 6860.0,
    "low": 6858.0,
    "close": 6858.5,
    "ema": 6853.95440425781
  },
  {
    "index": 2,
    "datetime": "2025-12-16T03:35:00",
    "open": 6858.75,
    "high": 6860.75,
    "low": 6858.0,
    "close": 6860.25,
    "ema": 6854.55398480468
  },
  {
    "index": 3,
    "datetime": "2025-12-16T03:40:00",
    "open": 6860.0,
    "high": 6860.0,
    "low": 6856.75,
    "close": 6857.0,
    "ema": 6854.78693863281
  },
  {
    "index": 4,
    "datetime": "2025-12-16T03:45:00",
    "open": 6857.0,
    "high": 6857.75,
    "low": 6854.75,
    "close": 6856.25,
    "ema": 6854.92627781064
  },
  {
    "index": 5,
    "datetime": "2025-12-16T03:50:00",
    "open": 6856.0,
    "high": 6857.0,
    "low": 6853.25,
    "close": 6853.5,
    "ema": 6854.79044182867
  },
  {
    "index": 6,
    "datetime": "2025-12-16T03:55:00",
    "open": 6853.5,
    "high": 6853.75,
    "low": 6849.5,
    "close": 6849.5,
    "ema": 6854.28659022594
  },
  {
    "index": 7,
    "datetime": "2025-12-16T04:00:00",
    "open": 6849.5,
    "high": 6850.75,
    "low": 6846.75,
    "close": 6850.5,
    "ema": 6853.92596258537
  },
  {
    "index": 8,
    "datetime": "2025-12-16T04:05:00",
    "open": 6850.5,
    "high": 6853.0,
    "low": 6847.5,
    "close": 6853.0,
    "ema": 6853.83777567248
  },
  {
    "index": 9,
    "datetime": "2025-12-16T04:10:00",
    "open": 6852.75,
    "high": 6853.75,
    "low": 6851.25,
    "close": 6852.75,
    "ema": 6853.73417798939
  },
  {
    "index": 10,
    "datetime": "2025-12-16T04:15:00",
    "open": 6852.75,
    "high": 6856.25,
    "low": 6852.25,
    "close": 6856.0,
    "ema": 6853.94997056183
  },
  {
    "index": 11,
    "datetime": "2025-12-16T04:20:00",
    "open": 6856.25,
    "high": 6860.75,
    "low": 6855.5,
    "close": 6858.25,
    "ema": 6854.35949717499
  },
  {
    "index": 12,
    "datetime": "2025-12-16T04:25:00",
    "open": 6858.5,
    "high": 6861.0,
    "low": 6857.5,
    "close": 6861.0,
    "ema": 6854.99192601546
  },
  {
    "index": 13,
    "datetime": "2025-12-16T04:30:00",
    "open": 6861.0,
    "high": 6862.5,
    "low": 6860.75,
    "close": 6861.5,
    "ema": 6855.61174258542
  },
  {
    "index": 14,
    "datetime": "2025-12-16T04:35:00",
    "open": 6861.75,
    "high": 6862.5,
    "low": 6860.0,
    "close": 6860.0,
    "ema": 6856.029671863
  },
  {
    "index": 15,
    "datetime": "2025-12-16T04:40:00",
    "open": 6859.75,
    "high": 6866.5,
    "low": 6859.25,
    "close": 6866.25,
    "ema": 6857.00303644748
  },
  {
    "index": 16,
    "datetime": "2025-12-16T04:45:00",
    "open": 6866.0,
    "high": 6869.0,
    "low": 6865.75,
    "close": 6869.0,
    "ema": 6858.14560440486
  },
  {
    "index": 17,
    "datetime": "2025-12-16T04:50:00",
    "open": 6869.25,
    "high": 6870.0,
    "low": 6865.75,
    "close": 6865.75,
    "ema": 6858.86983255678
  },
  {
    "index": 18,
    "datetime": "2025-12-16T04:55:00",
    "open": 6865.75,
    "high": 6867.0,
    "low": 6863.75,
    "close": 6863.75,
    "ema": 6859.33461040851
  },
  {
    "index": 19,
    "datetime": "2025-12-16T05:00:00",
    "open": 6863.5,
    "high": 6865.75,
    "low": 6862.25,
    "close": 6864.5,
    "ema": 6859.82655227437
  },
  {
    "index": 20,
    "datetime": "2025-12-16T05:05:00",
    "open": 6864.25,
    "high": 6866.25,
    "low": 6863.0,
    "close": 6864.5,
    "ema": 6860.27164253395
  },
  {
    "index": 21,
    "datetime": "2025-12-16T05:10:00",
    "open": 6864.25,
    "high": 6864.5,
    "low": 6862.5,
    "close": 6864.5,
    "ema": 6860.674343245
  },
  {
    "index": 22,
    "datetime": "2025-12-16T05:15:00",
    "open": 6864.25,
    "high": 6865.0,
    "low": 6863.0,
    "close": 6864.75,
    "ema": 6861.06250103119
  },
  {
    "index": 23,
    "datetime": "2025-12-16T05:20:00",
    "open": 6864.75,
    "high": 6865.0,
    "low": 6862.75,
    "close": 6864.0,
    "ema": 6861.34226283775
  },
  {
    "index": 24,
    "datetime": "2025-12-16T05:25:00",
    "open": 6864.0,
    "high": 6864.25,
    "low": 6862.5,
    "close": 6864.0,
    "ema": 6861.59538066272
  },
  {
    "index": 25,
    "datetime": "2025-12-16T05:30:00",
    "open": 6863.75,
    "high": 6864.75,
    "low": 6863.25,
    "close": 6864.75,
    "ema": 6861.89582059961
  },
  {
    "index": 26,
    "datetime": "2025-12-16T05:35:00",
    "open": 6865.0,
    "high": 6865.75,
    "low": 6863.0,
    "close": 6865.0,
    "ema": 6862.19145673298
  },
  {
    "index": 27,
    "datetime": "2025-12-16T05:40:00",
    "open": 6865.0,
    "high": 6868.5,
    "low": 6864.5,
    "close": 6868.25,
    "ema": 6862.76846085365
  },
  {
    "index": 28,
    "datetime": "2025-12-16T05:45:00",
    "open": 6868.25,
    "high": 6869.75,
    "low": 6867.5,
    "close": 6869.0,
    "ema": 6863.36194077235
  },
  {
    "index": 29,
    "datetime": "2025-12-16T05:50:00",
    "open": 6868.75,
    "high": 6869.25,
    "low": 6867.75,
    "close": 6868.75,
    "ema": 6863.87508927022
  },
  {
    "index": 30,
    "datetime": "2025-12-16T05:55:00",
    "open": 6868.75,
    "high": 6871.5,
    "low": 6867.0,
    "close": 6871.0,
    "ema": 6864.55365219686
  },
  {
    "index": 31,
    "datetime": "2025-12-16T06:00:00",
    "open": 6870.75,
    "high": 6873.75,
    "low": 6870.5,
    "close": 6873.75,
    "ema": 6865.42949484478
  },
  {
    "index": 32,
    "datetime": "2025-12-16T06:05:00",
    "open": 6873.75,
    "high": 6874.0,
    "low": 6871.0,
    "close": 6872.75,
    "ema": 6866.12668581195
  },
  {
    "index": 33,
    "datetime": "2025-12-16T06:10:00",
    "open": 6873.0,
    "high": 6874.5,
    "low": 6872.25,
    "close": 6873.0,
    "ema": 6866.78128716319
  },
  {
    "index": 34,
    "datetime": "2025-12-16T06:15:00",
    "open": 6872.75,
    "high": 6878.0,
    "low": 6872.75,
    "close": 6877.0,
    "ema": 6867.75449790955
  },
  {
    "index": 35,
    "datetime": "2025-12-16T06:20:00",
    "open": 6877.0,
    "high": 6879.0,
    "low": 6877.0,
    "close": 6877.5,
    "ema": 6868.68264096578
  },
  {
    "index": 36,
    "datetime": "2025-12-16T06:25:00",
    "open": 6877.5,
    "high": 6878.0,
    "low": 6875.5,
    "close": 6875.5,
    "ema": 6869.33191325476
  },
  {
    "index": 37,
    "datetime": "2025-12-16T06:30:00",
    "open": 6875.5,
    "high": 6876.5,
    "low": 6875.0,
    "close": 6876.25,
    "ema": 6869.99077865907
  },
  {
    "index": 38,
    "datetime": "2025-12-16T06:35:00",
    "open": 6876.0,
    "high": 6878.0,
    "low": 6875.5,
    "close": 6878.0,
    "ema": 6870.75356164392
  },
  {
    "index": 39,
    "datetime": "2025-12-16T06:40:00",
    "open": 6877.75,
    "high": 6878.0,
    "low": 6875.25,
    "close": 6876.25,
    "ema": 6871.27703196354
  },
  {
    "index": 40,
    "datetime": "2025-12-16T06:45:00",
    "open": 6876.25,
    "high": 6879.75,
    "low": 6876.0,
    "close": 6876.5,
    "ema": 6871.77445749082
  },
  {
    "index": 41,
    "datetime": "2025-12-16T06:50:00",
    "open": 6876.5,
    "high": 6877.75,
    "low": 6874.5,
    "close": 6875.5,
    "ema": 6872.12927106313
  },
  {
    "index": 42,
    "datetime": "2025-12-16T06:55:00",
    "open": 6875.75,
    "high": 6878.75,
    "low": 6875.0,
    "close": 6877.0,
    "ema": 6872.5931500095
  },
  {
    "index": 43,
    "datetime": "2025-12-16T07:00:00",
    "open": 6876.75,
    "high": 6877.5,
    "low": 6874.25,
    "close": 6874.75,
    "ema": 6872.79856429431
  },
  {
    "index": 44,
    "datetime": "2025-12-16T07:05:00",
    "open": 6874.5,
    "high": 6876.25,
    "low": 6871.75,
    "close": 6872.25,
    "ema": 6872.7463200758
  },
  {
    "index": 45,
    "datetime": "2025-12-16T07:10:00",
    "open": 6872.25,
    "high": 6872.5,
    "low": 6870.75,
    "close": 6871.25,
    "ema": 6872.60381340191
  },
  {
    "index": 46,
    "datetime": "2025-12-16T07:15:00",
    "open": 6871.0,
    "high": 6873.0,
    "low": 6868.75,
    "close": 6872.5,
    "ema": 6872.59392641126
  },
  {
    "index": 47,
    "datetime": "2025-12-16T07:20:00",
    "open": 6872.5,
    "high": 6874.5,
    "low": 6868.25,
    "close": 6872.5,
    "ema": 6872.58498103876
  }
]
'''

bars = json.loads(bars_json)
df = pd.DataFrame(bars)

# Ensure datetime column is proper datetime and set as index
if 'datetime' in df.columns:
    df['datetime'] = pd.to_datetime(df['datetime'])
    df = df.set_index('datetime').sort_index()

# Rename OHLC columns to the format mplfinance expects
df_ohlc = df.rename(columns={
    'open': 'Open',
    'high': 'High',
    'low': 'Low',
    'close': 'Close'
})

# Compute a few EMAs on Close
df_ohlc['EMA20']  = df_ohlc['Close'].ewm(span=20, adjust=False).mean()
df_ohlc['EMA48']  = df_ohlc['Close'].ewm(span=48, adjust=False).mean()
df_ohlc['EMA200'] = df_ohlc['Close'].ewm(span=200, adjust=False).mean()

df_ohlc.tail()
index Open High Low Close ema EMA20 EMA48 EMA200
datetime
2025-12-16 07:00:00 43 6876.75 6877.50 6874.25 6874.75 6872.798564 6872.886770 6867.925896 6862.354169
2025-12-16 07:05:00 44 6874.50 6876.25 6871.75 6872.25 6872.746320 6872.826125 6868.102390 6862.452635
2025-12-16 07:10:00 45 6872.25 6872.50 6870.75 6871.25 6872.603813 6872.676018 6868.230864 6862.540171
2025-12-16 07:15:00 46 6871.00 6873.00 6868.75 6872.50 6872.593926 6872.659254 6868.405114 6862.639274
2025-12-16 07:20:00 47 6872.50 6874.50 6868.25 6872.50 6872.584981 6872.644087 6868.572252 6862.737391
# Full-session candlestick chart with multi-EMA overlays

ema_overlays = [
    mpf.make_addplot(df_ohlc['EMA20'],  width=1),
    mpf.make_addplot(df_ohlc['EMA48'],  width=1),
    mpf.make_addplot(df_ohlc['EMA200'], width=1),
]

fig, axlist = mpf.plot(
    df_ohlc,
    type='candle',
    style='classic',
    addplot=ema_overlays,
    figsize=(10, 5),
    title=f"{instrument} – Last {len(df_ohlc)} bars (full window)",
    ylabel='Price',
    returnfig=True
)

plt.show()

# RTH-only candlestick chart (example 08:30–15:15 session hours)
# Note: adjust the times to your actual RTH / time zone if needed.

if isinstance(df_ohlc.index, pd.DatetimeIndex):
    # Example: filter between 08:30 and 15:15 (local time) for each day
    df_rth = df_ohlc.between_time("08:30", "15:15")
else:
    df_rth = df_ohlc.copy()  # fallback

if len(df_rth) == 0:
    print("No RTH bars found with the given time window; showing full window instead.")
    df_rth = df_ohlc.copy()

ema_overlays_rth = [
    mpf.make_addplot(df_rth['EMA20'],  width=1),
    mpf.make_addplot(df_rth['EMA48'],  width=1),
    mpf.make_addplot(df_rth['EMA200'], width=1),
]

fig, axlist = mpf.plot(
    df_rth,
    type='candle',
    style='classic',
    addplot=ema_overlays_rth,
    figsize=(10, 5),
    title=f"{instrument} – RTH subset (example 08:30–15:15)",
    ylabel='Price',
    returnfig=True
)

plt.show()
No RTH bars found with the given time window; showing full window instead.

# Simple pattern detection: double tops/bottoms and 3-swing wedges
#
# This is intentionally conservative and approximate:
# - Finds local swing highs/lows
# - Looks for last pair of swing highs within a small price band  => DT
# - Looks for last pair of swing lows within a small price band   => DB
# - Looks for last 3 swing highs that are successively higher     => wedge up
# - Looks for last 3 swing lows that are successively lower       => wedge down

import numpy as np

df_sw = df_ohlc.copy()

# Identify swing highs/lows
swing_high = []
swing_low = []

close = df_sw['Close'].values
high  = df_sw['High'].values
low   = df_sw['Low'].values

for i in range(1, len(df_sw) - 1):
    # simple 3-bar swing definition
    if high[i] > high[i-1] and high[i] >= high[i+1]:
        swing_high.append(i)
    if low[i] < low[i-1] and low[i] <= low[i+1]:
        swing_low.append(i)

swing_high = np.array(swing_high, dtype=int)
swing_low  = np.array(swing_low, dtype=int)

dt_indices = []
db_indices = []
wedge_up_indices = []
wedge_down_indices = []

# Double tops: last 2 swing highs within a band
if len(swing_high) >= 2:
    i1, i2 = swing_high[-2], swing_high[-1]
    price1, price2 = high[i1], high[i2]
    # tolerance as fraction of price
    if abs(price2 - price1) <= 0.25 * df_sw['Close'].iloc[-1]:  # adjust tolerance as needed
        dt_indices = [i1, i2]

# Double bottoms: last 2 swing lows within a band
if len(swing_low) >= 2:
    i1, i2 = swing_low[-2], swing_low[-1]
    price1, price2 = low[i1], low[i2]
    if abs(price2 - price1) <= 0.25 * df_sw['Close'].iloc[-1]:  # adjust tolerance as needed
        db_indices = [i1, i2]

# Wedge up: last 3 swing highs successively higher
if len(swing_high) >= 3:
    i1, i2, i3 = swing_high[-3], swing_high[-2], swing_high[-1]
    if high[i1] < high[i2] < high[i3]:
        wedge_up_indices = [i1, i2, i3]

# Wedge down: last 3 swing lows successively lower
if len(swing_low) >= 3:
    i1, i2, i3 = swing_low[-3], swing_low[-2], swing_low[-1]
    if low[i1] > low[i2] > low[i3]:
        wedge_down_indices = [i1, i2, i3]

print("Swing highs:", swing_high)
print("Swing lows :", swing_low)
print("Double top indices   :", dt_indices)
print("Double bottom indices:", db_indices)
print("Wedge up indices     :", wedge_up_indices)
print("Wedge down indices   :", wedge_down_indices)

# Plot with annotations on the full window
fig, axlist = mpf.plot(
    df_ohlc,
    type='candle',
    style='classic',
    figsize=(10, 5),
    returnfig=True
)

ax = axlist[0]

x_vals = np.arange(len(df_ohlc))

# Annotate DTs
for i in dt_indices:
    dt_x = x_vals[i]
    dt_y = high[i]
    ax.scatter(dt_x, dt_y, s=50, marker='^')
    ax.text(dt_x, dt_y, 'DT', fontsize=8, va='bottom', ha='center')

# Annotate DBs
for i in db_indices:
    db_x = x_vals[i]
    db_y = low[i]
    ax.scatter(db_x, db_y, s=50, marker='v')
    ax.text(db_x, db_y, 'DB', fontsize=8, va='top', ha='center')

# Annotate wedge up
for i in wedge_up_indices:
    wx = x_vals[i]
    wy = high[i]
    ax.scatter(wx, wy, s=40, marker='o')
    ax.text(wx, wy, 'WU', fontsize=8, va='bottom', ha='center')

# Annotate wedge down
for i in wedge_down_indices:
    wx = x_vals[i]
    wy = low[i]
    ax.scatter(wx, wy, s=40, marker='o')
    ax.text(wx, wy, 'WD', fontsize=8, va='top', ha='center')

ax.set_title(f"{instrument} – Simple DT/DB & wedge annotations")

plt.tight_layout()
plt.show()
Swing highs: [ 2 13 17 20 22 28 35 38 40 42]
Swing lows : [ 1  7 15 19 21 24 26 30 37 39 41]
Double top indices   : [40, 42]
Double bottom indices: [39, 41]
Wedge up indices     : []
Wedge down indices   : []
C:\Users\markl\AppData\Local\Temp\ipykernel_44724\1182744269.py:114: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.
  plt.tight_layout()