TradeObservations – Al Brooks Snapshot

Instrument: ES 12-25
Last bar: 2025-12-16T07:15:00

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

AI Analysis

  1. Context: The first ~15–18 bars are a tight, slightly up-sloping trading range around the EMA. Then a bull spike around bars 16–17 leads into a steady bull channel with price and EMA both rising. The last 15–20 bars are a grinding bull channel with small pullbacks and closes mostly above the EMA, but momentum is weakening and the channel is flattening into a possible developing trading range near the highs.

  2. Clear patterns:

  • Bull spike and channel starting at bar 16 (6866.25 close) through ~bar 35.
  • Multiple small bull flags / H1-H2 style pullbacks in the channel (e.g., minor dips around bars 18–22, 30–32, 37–42).
  • Late bull channel with possible “final flag” behavior: higher highs around 35–39, then overlapping bars and small pullbacks (40–47) suggesting loss of momentum and increased risk of a trading-range top or minor reversal.
  1. Probability: Bulls still have the edge because the EMA is rising and pullbacks are shallow, but the buy climax / late-channel context means upside follow-through is less certain and reward is shrinking. Bears are more likely to get a 2–3 leg pullback than an immediate trend reversal, but risk selling too early into a strong bull trend.

  2. With-trend entries / avoids:

  • Best with-trend buys were earlier: pullback after the spike (around bars 18–22), and subsequent higher low pullbacks within the channel (around bars 30–32, 37–39).
  • At current bars (mid–high 6870s, 40–47), new longs are late in a mature bull channel and near a possible trading-range top—scalp-only and only on clear bull reversal bars off the EMA.
  • Avoid initiating fresh shorts until you see a clear bear breakout with follow-through below the recent higher lows (e.g., a strong break and close below the lows of bars 45–47); before that, it’s still “buy the pullback” but with reduced size and modest profit targets.
import json
import pandas as pd
import matplotlib.pyplot as plt
import mplfinance as mpf

instrument = 'ES 12-25'

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

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 06:55:00 43 6875.75 6878.75 6875.00 6877.00 6872.593150 6872.700780 6867.760488 6862.717468
2025-12-16 07:00:00 44 6876.75 6877.50 6874.25 6874.75 6872.798564 6872.895944 6868.045774 6862.837195
2025-12-16 07:05:00 45 6874.50 6876.25 6871.75 6872.25 6872.746320 6872.834425 6868.217375 6862.930855
2025-12-16 07:10:00 46 6872.25 6872.50 6870.75 6871.25 6872.603813 6872.683528 6868.341156 6863.013632
2025-12-16 07:15:00 47 6871.00 6873.00 6868.75 6872.50 6872.593926 6872.666049 6868.510904 6863.108024
# 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()
# 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()