Files
BlackboxAI 0206ef7cbb Initial commit: orderflow analysis system with 5 pattern detectors
Real-time orderflow trading system with absorption, initiative, sweep,
exhaustion, and divergence detection. Features volume profile framing,
state machine trade lifecycle, MT5 + Bybit feeds, FastAPI dashboard,
and Telegram alerts for 30+ instruments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:38:25 +03:00

783 lines
34 KiB
Python

"""
Demo data generator for standalone dashboard mode.
Provides realistic-looking orderflow data when no live feed is connected.
"""
import math
import random
import time
# ── Seed for reproducibility within a session ──
_session_seed = int(time.time()) % 10000
random.seed(_session_seed)
def _stable_rng(symbol: str, tf: int = 0, range_s: int = 0) -> random.Random:
"""Return a Random instance seeded by symbol + time-window.
The time-window reseeds every 60 seconds so the data drifts
slowly but repeated calls within the same minute return identical data.
"""
window = int(time.time()) // 60 # changes every 60 s
seed = hash((symbol, tf, range_s, window, _session_seed))
return random.Random(seed)
# ── Base prices for instruments (full MT5 coverage) ──
_BASE_PRICES = {
# Forex (main pairs)
"EURUSD": 1.0785, "GBPUSD": 1.2615, "USDJPY": 152.30,
"AUDUSD": 0.6540, "USDCAD": 1.3520, "USDCHF": 0.8850, "NZDUSD": 0.6120,
"EURGBP": 0.8550, "EURJPY": 164.20, "GBPJPY": 192.10,
# Metals
"XAUUSDT": 2920.0, "XAGUSD": 32.50,
# Indices
"NAS100USDT": 21450.0, "SP500": 6100.0, "DJ30": 44200.0,
"DAX40": 18900.0, "UK100": 8350.0,
# Crypto
"BTCUSDT": 98500.0, "ETHUSDT": 2680.0, "SOLUSDT": 195.0,
"XRPUSDT": 2.45, "BNBUSDT": 680.0,
# Stocks
"AAPL": 232.0, "TSLA": 365.0, "AMZN": 228.0, "MSFT": 415.0,
"NVDA": 138.0, "META": 680.0, "GOOGL": 185.0,
}
_TICK_SIZES = {
# Forex
**{s: 0.0001 for s in ["EURUSD","GBPUSD","AUDUSD","NZDUSD","USDCAD","USDCHF","EURGBP"]},
**{s: 0.01 for s in ["USDJPY","EURJPY","GBPJPY"]},
# Metals
"XAUUSDT": 0.10, "XAGUSD": 0.01,
# Indices
"NAS100USDT": 0.5, "SP500": 0.25, "DJ30": 1.0, "DAX40": 0.5, "UK100": 0.5,
# Crypto
"BTCUSDT": 0.5, "ETHUSDT": 0.1, "SOLUSDT": 0.01, "XRPUSDT": 0.0001, "BNBUSDT": 0.1,
# Stocks
**{s: 0.01 for s in ["AAPL","TSLA","AMZN","MSFT","NVDA","META","GOOGL"]},
}
def _base_price(symbol: str) -> float:
return _BASE_PRICES.get(symbol, 1000.0)
def _tick_size(symbol: str) -> float:
return _TICK_SIZES.get(symbol, 0.5)
def _gen_price_walk(base: float, n: int, volatility: float = 0.001, rng: random.Random | None = None) -> list:
"""Generate a random-walk price series."""
r = rng or random
prices = [base]
for _ in range(n - 1):
change = base * volatility * r.gauss(0, 1)
prices.append(prices[-1] + change)
return prices
# ════════════════════════════════════════════
# Public API — called by app.py endpoints
# ════════════════════════════════════════════
def demo_instruments():
"""Return a list of demo instruments."""
rng = _stable_rng("__instruments__")
now_ms = int(time.time() * 1000)
result = []
for sym, price in _BASE_PRICES.items():
drift = price * rng.uniform(-0.002, 0.002)
result.append({
"symbol": sym,
"price": round(price + drift, 5),
"volume_24h": rng.randint(50000, 500000),
"tick_count": rng.randint(10000, 100000),
"candle_count": rng.randint(200, 1000),
"data_source": "demo",
"trade_phase": "none",
"trade_direction": "none",
"last_update_ms": now_ms,
})
return result
def demo_candles(symbol: str, tf: int = 60, range_s: int = 86400):
"""Generate realistic OHLC candle data — stable per symbol/tf/range."""
rng = _stable_rng(symbol, tf, range_s)
base = _base_price(symbol)
tick = _tick_size(symbol)
now = int(time.time())
start = now - range_s
n_candles = range_s // tf
# Limit to reasonable number
n_candles = min(n_candles, 1500)
vol = 0.0004 if base > 1000 else 0.0008
walk = _gen_price_walk(base, n_candles + 1, vol, rng)
candles = []
for i in range(n_candles):
t = start + i * tf
o = walk[i]
c = walk[i + 1]
h = max(o, c) + abs(rng.gauss(0, base * vol * 0.5))
l = min(o, c) - abs(rng.gauss(0, base * vol * 0.5))
volume = rng.randint(50, 800)
delta = rng.randint(-200, 200)
candles.append({
"time": t,
"open": round(o, 5),
"high": round(h, 5),
"low": round(l, 5),
"close": round(c, 5),
"volume": volume,
"delta": delta,
})
return candles
def demo_delta(symbol: str, tf: int = 60, range_s: int = 86400):
"""Generate cumulative delta data — stable per symbol/tf/range."""
rng = _stable_rng(symbol, tf, range_s)
now = int(time.time())
start = now - range_s
n = min(range_s // tf, 1500)
cum = 0
result = []
for i in range(n):
bar_delta = rng.gauss(0, 50)
cum += bar_delta
result.append({
"time": start + i * tf,
"value": round(cum, 1),
"bar_delta": round(bar_delta, 1),
})
return result
def demo_volume_profile(symbol: str):
"""Generate a realistic volume profile."""
rng = _stable_rng(symbol, 0, 0)
base = _base_price(symbol)
tick = _tick_size(symbol)
n_levels = 60
# Bell-curve volume distribution (P-shape / D-shape / b-shape)
shape = rng.choice(["P-shape", "D-shape", "b-shape", "Balanced"])
center = base + rng.uniform(-base * 0.002, base * 0.002)
volume_at_price = {}
prices = []
for i in range(n_levels):
price = round(center - (n_levels // 2 - i) * tick, 5)
prices.append(price)
# Gaussian volume distribution
dist = abs(i - n_levels // 2) / (n_levels / 4)
vol = int(max(10, 500 * math.exp(-dist * dist) + rng.randint(5, 50)))
volume_at_price[str(price)] = vol
# Find POC (max volume)
poc_price = max(volume_at_price, key=volume_at_price.get)
poc = float(poc_price)
# Value area = 70% of volume
sorted_levels = sorted(volume_at_price.items(), key=lambda x: x[1], reverse=True)
total_vol = sum(v for _, v in sorted_levels)
va_vol = 0
va_prices = []
for p, v in sorted_levels:
va_vol += v
va_prices.append(float(p))
if va_vol >= total_vol * 0.7:
break
vah = max(va_prices)
val = min(va_prices)
return [{
"poc": round(poc, 5),
"vah": round(vah, 5),
"val": round(val, 5),
"total_volume": total_vol,
"shape": shape,
"poc_position_pct": 50.0 + rng.uniform(-15, 15),
"lvn_levels": [round(prices[n_levels // 4], 5), round(prices[3 * n_levels // 4], 5)],
"volume_at_price": volume_at_price,
}]
def demo_bias(symbol: str):
"""Generate daily bias data."""
rng = _stable_rng(symbol, 0, 1)
base = _base_price(symbol)
tick = _tick_size(symbol)
direction = rng.choice(["long", "short", "neutral"])
confidence = rng.randint(40, 95)
shape = rng.choice(["P-shape", "D-shape", "b-shape", "Balanced"])
poc = round(base + rng.uniform(-base * 0.001, base * 0.001), 5)
spread = base * 0.003
vah = round(poc + spread, 5)
val = round(poc - spread, 5)
levels = []
n_levels = rng.randint(1, 4)
for _ in range(n_levels):
lv_dir = rng.choice(["buy", "sell"])
lv_price = round(base + rng.uniform(-base * 0.005, base * 0.005), 5)
levels.append({
"price": lv_price,
"direction": lv_dir,
"level_type": rng.choice(["POC", "VAH", "VAL", "LVN", "Composite"]),
"strength": rng.randint(50, 100),
})
return {
"direction": direction,
"confidence": confidence,
"profile_shape": shape,
"poc": poc,
"vah": vah,
"val": val,
"qualified_levels": levels,
"notes": f"Demo bias — {shape} profile detected, {direction} bias at {confidence}%",
}
def demo_orderbook(symbol: str):
"""Generate a realistic orderbook snapshot."""
rng = _stable_rng(symbol, 0, 2)
base = _base_price(symbol)
tick = _tick_size(symbol)
n_levels = 20
mid = base + rng.uniform(-tick * 2, tick * 2)
bids = []
asks = []
for i in range(n_levels):
bid_price = round(mid - (i + 1) * tick, 5)
ask_price = round(mid + (i + 1) * tick, 5)
bid_size = rng.randint(5, 300)
ask_size = rng.randint(5, 300)
# Add some thin levels (sweep targets)
if rng.random() < 0.15:
bid_size = rng.randint(1, 5)
if rng.random() < 0.15:
ask_size = rng.randint(1, 5)
bids.append({"price": bid_price, "size": bid_size})
asks.append({"price": ask_price, "size": ask_size})
total_bid = sum(b["size"] for b in bids)
total_ask = sum(a["size"] for a in asks)
return {
"snapshot": True,
"bids": bids,
"asks": asks,
"last_price": round(mid, 5),
"spread": round(tick, 5),
"bid_total": total_bid,
"ask_total": total_ask,
"imbalance": round(total_bid / max(total_bid + total_ask, 1) * 100, 1),
}
def demo_strategy_status(symbol: str):
"""Generate a strategy status with step checklist."""
rng = _stable_rng(symbol, 0, 3)
base = _base_price(symbol)
direction = rng.choice(["buy", "sell"])
bias_dir = "long" if direction == "buy" else "short"
# Pick a random phase
phases = [
("WAITING_FOR_PRICE", 2),
("AT_LEVEL_SCANNING", 3),
("WATCHING", 4),
("ENTRY_READY", 5),
]
overall, steps_done = rng.choice(phases)
steps = [
{"name": "Volume Profile", "icon": "📊", "status": "completed", "detail": "D-shape identified, POC at " + str(round(base, 1))},
{"name": "Daily Bias", "icon": "🧭", "status": "completed", "detail": f"{bias_dir.upper()} bias — confidence 78%"},
{"name": "Qualified Level", "icon": "📍", "status": "completed" if steps_done >= 3 else "pending",
"detail": f"{'VAH rejection zone at ' + str(round(base * 1.002, 1)) if steps_done >= 3 else 'Scanning for level...'}"},
{"name": "Orderflow Confirm", "icon": "🔬", "status": "completed" if steps_done >= 4 else "pending",
"detail": "Absorption detected (3 attempts)" if steps_done >= 4 else "Waiting for orderflow signal..."},
{"name": "Entry Trigger", "icon": "🎯", "status": "active" if steps_done >= 5 else "pending",
"detail": "Initiative buying confirmed" if steps_done >= 5 else "Waiting for trigger..."},
{"name": "Trade Management", "icon": "⚙️", "status": "pending", "detail": "Not in trade"},
]
return {
"overall": overall,
"reason": f"Price approaching qualified level — {overall.replace('_', ' ').lower()}",
"steps": steps[:6],
"bias_direction": bias_dir,
"bias_confidence": rng.randint(60, 95),
"current_price": round(base, 5),
"trade": None,
}
def demo_scanner():
"""Generate scanner data for all demo instruments."""
rng = _stable_rng("__scanner__", 0, 0)
results = []
statuses = ["WAITING_FOR_PRICE", "AT_LEVEL_SCANNING", "WATCHING",
"ENTRY_READY", "IDLE", "WAITING_FOR_PRICE"]
for i, sym in enumerate(_BASE_PRICES):
overall = statuses[i % len(statuses)]
steps_done = rng.randint(0, 5)
bias_dir = rng.choice(["buy", "sell", "neutral"])
results.append({
"symbol": sym,
"overall": overall,
"reason": f"Demo — {overall.replace('_', ' ').lower()}",
"priority": rng.randint(10, 90),
"steps_done": steps_done,
"steps_total": 6,
"bias_direction": bias_dir,
"bias_confidence": rng.randint(30, 95),
"current_price": round(_base_price(sym), 5),
})
results.sort(key=lambda x: x["priority"], reverse=True)
return results
def demo_markers(symbol: str):
"""Generate a few chart markers for demo mode."""
rng = _stable_rng(symbol, 0, 4)
base = _base_price(symbol)
now = int(time.time())
markers = []
types = [
("ABS", "#26a69a", "arrowUp", "belowBar"),
("INIT", "#66bb6a", "arrowUp", "belowBar"),
("SWEEP", "#ab47bc", "arrowDown", "aboveBar"),
("EXHAUST", "#ffeb3b", "circle", "aboveBar"),
("DIV", "#ff9800", "circle", "aboveBar"),
]
for i in range(8):
t, color, shape, pos = rng.choice(types)
markers.append({
"time": now - rng.randint(300, 80000),
"position": pos,
"color": color,
"shape": shape,
"text": t,
})
markers.sort(key=lambda x: x["time"])
return markers
def demo_footprint(symbol: str, tf: int = 60, range_s: int = 86400):
"""Generate footprint chart data with bid/ask at each price level."""
rng = _stable_rng(symbol, tf, range_s)
base = _base_price(symbol)
tick = _tick_size(symbol)
now = int(time.time())
start = now - range_s
n_bars = min(range_s // tf, 200)
vol = 0.0004 if base > 1000 else 0.0008
walk = _gen_price_walk(base, n_bars + 1, vol, rng=rng)
bars = []
for i in range(n_bars):
t = start + i * tf
o = walk[i]
c = walk[i + 1]
h = max(o, c) + abs(rng.gauss(0, base * vol * 0.5))
l = min(o, c) - abs(rng.gauss(0, base * vol * 0.5))
# Generate levels from low to high at tick increments
n_levels = max(3, int((h - l) / tick))
n_levels = min(n_levels, 60) # cap
levels = []
max_vol = 0
poc_price = None
for j in range(n_levels):
price = round(l + j * tick, 5)
# Volume distribution — more near open/close, absorption zones
dist_from_mid = abs(price - (o + c) / 2) / max(h - l, tick)
base_vol = max(1, int(80 * math.exp(-dist_from_mid * 2)))
# Simulate bid/ask imbalance
if price < (o + c) / 2:
bid = base_vol + rng.randint(0, 40)
ask = max(1, base_vol - rng.randint(0, 20))
else:
bid = max(1, base_vol - rng.randint(0, 20))
ask = base_vol + rng.randint(0, 40)
# Random absorption spikes
if rng.random() < 0.08:
bid = bid * rng.randint(3, 6)
if rng.random() < 0.08:
ask = ask * rng.randint(3, 6)
total = bid + ask
if total > max_vol:
max_vol = total
poc_price = price
levels.append({"price": price, "bid": bid, "ask": ask})
bars.append({
"time": t,
"open": round(o, 5),
"high": round(h, 5),
"low": round(l, 5),
"close": round(c, 5),
"poc": poc_price,
"levels": levels,
})
return bars
def demo_tape_trades(symbol: str, count: int = 60):
"""Generate recent time & sales trades for initial tape fill."""
rng = _stable_rng(symbol, 0, 5)
base = _base_price(symbol)
tick = _tick_size(symbol)
now = time.time()
trades = []
price = base
for i in range(count):
price += tick * rng.choice([-2, -1, -1, 0, 1, 1, 2])
side = rng.choice(["buy", "sell"])
size = rng.randint(1, 50)
# occasional big trades
if rng.random() < 0.08:
size = rng.randint(80, 500)
trades.append({
"time": now - (count - i) * rng.uniform(0.3, 2.0),
"price": round(price, 5),
"size": size,
"side": side,
})
return trades
def demo_microstructure(symbol: str):
"""Generate a complete microstructure snapshot for initial panel fill."""
rng = _stable_rng(symbol, 0, 6)
base = _base_price(symbol)
direction = rng.choice(["buy", "sell"])
market_state = rng.choice(["TRENDING", "COMPRESSION", "REBALANCING"])
sessions = {
"London Open": 3600000 * 2,
"NY Open": 3600000 * 4,
"NY AM": 3600000 * 3,
"London PM": 3600000 * 1,
"Asia": 3600000 * 6,
}
session_name = rng.choice(list(sessions.keys()))
return {
"marketState": market_state,
"session": {
"name": session_name,
"remaining": sessions[session_name],
},
"absorption": {
"level": round(base + rng.uniform(-base * 0.001, base * 0.001), 5),
"attempts": rng.randint(1, 4),
"strength": rng.randint(30, 95),
"side": direction,
},
"initiative": {
"count": rng.randint(0, 5),
"direction": "up" if direction == "buy" else "down",
"strength": rng.randint(40, 90),
},
"delta": {
"cumulative": round(rng.uniform(-5000, 5000), 1),
"direction": rng.uniform(-1, 1),
"divergence": rng.random() < 0.2,
},
"exhaustion": rng.randint(10, 80),
"patterns": [
{
"type": rng.choice(["absorption", "initiative", "exhaustion", "sweep", "divergence"]),
"confidence": round(rng.uniform(0.5, 0.95), 2),
"price": round(base + rng.uniform(-base * 0.002, base * 0.002), 5),
}
for _ in range(rng.randint(1, 4))
],
}
def demo_signals():
"""Generate institutional-grade demo signals with deep trade analysis."""
now = int(time.time() * 1000)
signals = []
# ── Detailed pattern library with full narrative context ──
_setups = [
{
"pattern": "Bid Absorption",
"signal_type": "absorption",
"narrative": "Institutional buyers are defending {price:.2f} with repeated absorption. {abscount} rejection attempts in the last {minutes}min — each time sellers hit the bid, resting limit orders immediately refill. This is classic accumulation behavior at a key demand zone.",
"thesis": "Large passive buyers are accumulating. Once the selling pressure is exhausted, expect an aggressive markup move as trapped shorts cover.",
"edge": "Aggressive sellers are being absorbed at the bid, creating a floor. The orderbook shows {bookimb}% bid-heavy imbalance. Delta is confirming net buying pressure despite the flat price — this divergence suggests hidden accumulation.",
"invalidation": "Setup fails if {sl:.2f} breaks on volume > 2x average, indicating absorption wall has been removed and genuine supply is present.",
"htf_context": "HTF context: {htf_bias} on the {htf_tf} with price {htf_position} of the value area. {poc_context}",
},
{
"pattern": "Initiative Auction",
"signal_type": "initiative_auction",
"narrative": "Aggressive directional buying detected at {price:.2f}. Market orders are overwhelming the ask side — {init_count} initiative sweeps in the last {minutes}min. Order flow shows {delta_dir} delta acceleration with zero absorption resistance above.",
"thesis": "Smart money is initiating a move. Market-order aggression + thin liquidity above = high probability of follow-through. The auction is being driven, not responding.",
"edge": "Initiative buyers are lifting every ask level aggressively. The footprint shows {delta_val:+.0f} net delta in the most recent bars with ask-side depletion. This is not just buying — it's urgent, informed buying that sweeps through resting orders.",
"invalidation": "Watch for exhaustion candle (long upper wick, declining delta). If initiative volume drops >50% within 3 bars, the move may stall.",
"htf_context": "HTF alignment: {htf_bias} on {htf_tf}. Price breaking out of {htf_position}, confirmed by higher-timeframe delta momentum.",
},
{
"pattern": "Selling Exhaustion",
"signal_type": "exhaustion",
"narrative": "Selling pressure is dying at {price:.2f}. Despite making new lows, each successive push has declining delta: {delta_seq}. Volume is dropping on downside tests — sellers are losing conviction.",
"thesis": "Diminishing seller follow-through after multiple downside tests = exhaustion. The market is running out of sellers at this level. Expect mean reversion as shorts take profit and new buyers step in.",
"edge": "Three-test exhaustion pattern: each low is made on declining delta and volume. The delta divergence ({delta_div_pct}% weaker on last push vs first) indicates seller capitulation. Footprint shows ask volume shifting from initiative to responsive.",
"invalidation": "If fresh initiative selling appears with accelerating delta on the 4th push, exhaustion thesis is negated — treat as breakdown.",
"htf_context": "Higher timeframe: {htf_bias} with price at {htf_position}. {poc_context} Exhaustion at this level is consistent with HTF demand.",
},
{
"pattern": "Liquidity Sweep",
"signal_type": "book_sweep",
"narrative": "Stop-hunt complete at {price:.2f}. Price pierced below {sweep_level:.2f} to trigger clustered stops, then immediately reversed with {reversal_vol} contracts of aggressive buying. Classic institutional liquidity grab.",
"thesis": "Smart money engineered a liquidity sweep below the obvious support to fill their orders. The immediate reversal with high volume confirms this was a manufactured move, not a genuine breakdown.",
"edge": "The sweep cleared {stops_cleared} stops at {sweep_level:.2f} and the bid immediately reloaded with {reload_vol} contracts. The V-shaped reversal candle with positive delta ({sweep_delta:+.0f}) confirms aggressive re-entry. Book imbalance flipped from {pre_imb}% ask to {post_imb}% bid within seconds.",
"invalidation": "If price returns to the sweep low within 15min, the liquidity engineered thesis is invalid — real supply exists below.",
"htf_context": "HTF positioning: {htf_bias} with sweep occurring at {htf_position}. This level aligns with {poc_context}",
},
{
"pattern": "Delta Divergence",
"signal_type": "delta_divergence",
"narrative": "Bearish delta divergence at {price:.2f}. Price made a new high but cumulative delta is {delta_val:+.0f}{delta_pct}% lower than the previous swing high. Buyers are losing control despite higher prices.",
"thesis": "Divergence between price and orderflow is an early warning of trend exhaustion. Smart money is distributing into the rally — selling into strength while retail chases the breakout.",
"edge": "Three indicators confirm distribution: (1) Declining delta on new highs, (2) Increasing ask-side volume in the footprint, (3) Bid depth withdrawing from {depth_from:.2f}-{depth_to:.2f} range. The composite signal has been historically reliable at VP extremes.",
"invalidation": "Divergence thesis is invalidated if delta accelerates positive on a fresh breakout with initiative buying above {tp:.2f}.",
"htf_context": "HTF: {htf_bias}. Price is at {htf_position} — a common distribution zone. {poc_context}",
},
{
"pattern": "Composite POC Bounce",
"signal_type": "poc_bounce",
"narrative": "Price is testing the composite POC at {poc_level:.2f} — the highest volume node over {days}D. This level has attracted {poc_reactions} reactions in the last 5 sessions with an average bounce of {poc_avg_bounce:.1f}%.",
"thesis": "The composite POC is 'fair value' — where the most business was transacted. Price tends to rotate around this level. A reaction here with bid absorption confirmation suggests value-buyers are defending the level.",
"edge": "The POC at {poc_level:.2f} has a volume node of {poc_volume} contracts. The current test shows {abscount} absorption events with bid-side delta strengthening. The footprint profile shows responsive buying appearing at each POC test — institutions are re-accumulating at fair value.",
"invalidation": "If the POC breaks with initiative selling and delta acceleration, the value area is shifting. Expect a rotation to VAL at {val_level:.2f}.",
"htf_context": "HTF trend: {htf_bias}. The composite POC at {htf_position}. {poc_context}",
}
]
_sessions = [
{"name": "London Open", "detail": "High liquidity, typically large directional moves as European institutions set positioning"},
{"name": "NY Open", "detail": "Peak volatility as US institutions react to overnight flow and European positioning"},
{"name": "NY AM", "detail": "Continuation or reversal of NY Open initiative — strongest volume period"},
{"name": "London PM", "detail": "European close — profit-taking and positioning ahead of US session"},
{"name": "Asia", "detail": "Lower volume, range-bound. Watch for accumulation/distribution patterns"},
]
_models = ["Fabio Rejection", "Absorption → Initiative", "Sweep & Reverse", "LVN Bounce", "Value Area Rotation", "Composite"]
_htf_biases = [
("Bullish", "above POC", "Price is in the upper value area, favoring longs on pullbacks"),
("Bullish", "between POC and VAH", "Healthy uptrend — pullbacks to POC are high-probability entries"),
("Bearish", "below POC", "Price is below fair value, favoring shorts on rallies"),
("Bearish", "between VAL and POC", "Selling pressure dominant — rallies into POC are distribution zones"),
("Neutral", "at POC", "Price is at fair value with no clear directional edge — wait for initiative break"),
]
_market_regimes = [
{"state": "Trending", "detail": "Strong directional move underway — initiative activity dominating. Favor with-trend entries."},
{"state": "Ranging", "detail": "Balanced market, rotating between value extremes. Fade the edges, avoid the middle."},
{"state": "Balanced Volatile", "detail": "Wide range with high participation — institutional battle zone. Wait for resolution."},
{"state": "Low Volume Grind", "detail": "Thin market, easily manipulated. Reduce size, widen stops, avoid illiquid breakouts."},
{"state": "Breakout", "detail": "Value area migration in progress — new balance forming. Trail initiative entries."},
]
_blockers_pool = [
{"text": "High-impact news (NFP/FOMC) in next 30min — defer entry until after release", "severity": "high"},
{"text": "Spread widening to 3x average — liquidity deteriorating, slippage risk elevated", "severity": "high"},
{"text": "Counter-trend signal — trade against daily bias, reduce position to 50%", "severity": "medium"},
{"text": "Near session close — limited follow-through time, consider passing", "severity": "medium"},
{"text": "VIX spike detected — stop-hunt risk elevated, widen stops or reduce size", "severity": "medium"},
{"text": "Correlated asset divergence — BTCUSDT and NAS100 moving opposite, caution", "severity": "low"},
]
rng = _stable_rng("__signals__", 0, 7)
for i in range(8):
sym = rng.choice(list(_BASE_PRICES.keys()))
direction = rng.choice(["buy", "sell"])
setup = rng.choice(_setups)
score = rng.randint(45, 98)
session = rng.choice(_sessions)
regime = rng.choice(_market_regimes)
htf = rng.choice(_htf_biases)
model = rng.choice(_models)
base = _base_price(sym)
tick = _tick_size(sym)
entry = round(base + rng.uniform(-base * 0.001, base * 0.001), 5)
sl_dist = base * rng.uniform(0.001, 0.003)
tp_dist = sl_dist * rng.uniform(1.5, 3.5)
if direction == "buy":
sl = round(entry - sl_dist, 5)
tp = round(entry + tp_dist, 5)
else:
sl = round(entry + sl_dist, 5)
tp = round(entry - tp_dist, 5)
risk = abs(entry - sl)
reward = abs(tp - entry)
rr = round(reward / risk, 1) if risk > 0 else 0
bias_dir = "long" if direction == "buy" else "short"
# Generate rich context values for template formatting
ctx = {
"price": entry, "sl": sl, "tp": tp,
"abscount": rng.randint(2, 6),
"minutes": rng.randint(5, 30),
"bookimb": rng.randint(60, 88),
"htf_bias": htf[0],
"htf_tf": rng.choice(["4H", "1D", "Weekly"]),
"htf_position": htf[1],
"poc_context": htf[2],
"init_count": rng.randint(3, 8),
"delta_dir": "positive" if direction == "buy" else "negative",
"delta_val": rng.uniform(500, 5000) * (1 if direction == "buy" else -1),
"delta_seq": f"{rng.randint(-800,-200)}{rng.randint(-600,-100)}{rng.randint(-300,-30)}",
"delta_div_pct": rng.randint(30, 65),
"delta_pct": rng.randint(15, 50),
"sweep_level": round(entry - (sl_dist * 0.7 * (1 if direction == "buy" else -1)), 5),
"reversal_vol": rng.randint(150, 800),
"stops_cleared": rng.randint(40, 200),
"reload_vol": rng.randint(200, 600),
"sweep_delta": rng.uniform(300, 2000) * (1 if direction == "buy" else -1),
"pre_imb": rng.randint(55, 75),
"post_imb": rng.randint(60, 85),
"depth_from": round(entry - base * 0.002, 2),
"depth_to": round(entry + base * 0.002, 2),
"poc_level": round(entry + rng.uniform(-base * 0.001, base * 0.001), 5),
"val_level": round(entry - base * rng.uniform(0.003, 0.006), 5),
"days": rng.choice([5, 10, 20]),
"poc_reactions": rng.randint(3, 8),
"poc_avg_bounce": rng.uniform(0.2, 0.8),
"poc_volume": rng.randint(5000, 50000),
}
# Format the deep narrative templates
try:
narrative = setup["narrative"].format(**ctx)
thesis = setup["thesis"]
edge = setup["edge"].format(**ctx)
invalidation = setup["invalidation"].format(**ctx)
htf_context = setup["htf_context"].format(**ctx)
except (KeyError, ValueError):
narrative = f"{setup['pattern']} detected at {entry:.2f}"
thesis = setup["thesis"]
edge = f"Confidence {score}%"
invalidation = f"Stop loss at {sl:.2f}"
htf_context = f"{htf[0]} bias on higher timeframes"
# Build ordered reasons chain
reasons = [
f"1. {setup['pattern']} detected at {entry:.5g}",
f"2. Daily bias: {bias_dir.upper()} ({htf[0]} on {ctx['htf_tf']})",
f"3. Session: {session['name']}{session['detail'][:60]}",
f"4. {regime['state']}: {regime['detail'][:60]}",
]
if rng.random() > 0.3:
reasons.append(f"5. Orderbook imbalance {ctx['bookimb']}% on {'bid' if direction == 'buy' else 'ask'} side")
if rng.random() > 0.4:
reasons.append(f"6. Composite VP POC confluence at {ctx['poc_level']:.5g}")
# Blockers
blockers = []
if score < 60:
b = rng.choice(_blockers_pool)
blockers.append(b["text"])
if rng.random() < 0.25:
b = rng.choice(_blockers_pool)
if b["text"] not in blockers:
blockers.append(b["text"])
# Quality grade
grade = "A+" if score >= 85 else "A" if score >= 70 else "B" if score >= 55 else "C"
# Multi-timeframe confluence checklist
mtf_confluence = {
"weekly_bias": rng.choice(["Bullish", "Bearish", "Neutral"]),
"daily_bias": htf[0],
"h4_trend": rng.choice(["Uptrend", "Downtrend", "Sideways"]),
"h1_structure": rng.choice(["Higher highs", "Lower lows", "Range-bound", "Breakout"]),
"m15_trigger": setup["pattern"],
}
signals.append({
"id": now - i * 100000 + rng.randint(0, 999),
"timestamp": now - i * rng.randint(60000, 600000),
"timestamp_ms": now - i * rng.randint(60000, 600000),
"symbol": sym,
"direction": "long" if direction == "buy" else "short",
"confidence": score / 100,
"composite_score": score,
"action": "enter" if score >= 70 else "alert_only",
"pattern": setup["pattern"],
"signal_type": setup["signal_type"],
"entry": entry,
"stopLoss": sl,
"takeProfit": tp,
"entry_price": entry,
"suggested_sl": sl,
"suggested_tp": tp,
"riskReward": rr,
"bias": bias_dir.upper(),
"session": session["name"],
"session_detail": session["detail"],
"model": model,
"grade": grade,
"reasons": reasons,
"blockers": blockers,
"notes": f"{setup['pattern']}{grade} grade — score {score}%",
"signals": [{"signal_type": setup["signal_type"], "confidence": score}],
# ── Deep analysis fields ──
"narrative": narrative,
"thesis": thesis,
"edge": edge,
"invalidation": invalidation,
"htf_context": htf_context,
"market_regime": regime,
"mtf_confluence": mtf_confluence,
# ── Orderflow metrics ──
"market_state": regime["state"],
"volume_context": rng.choice(["Above Average", "Normal", "Below Average", "Spiking"]),
"key_level_type": rng.choice(["POC", "VAH", "VAL", "LVN", "Composite Node"]),
"key_level_price": round(entry + rng.uniform(-base * 0.001, base * 0.001), 5),
"absorption_count": ctx["abscount"] if "absorption" in setup["signal_type"] else rng.randint(0, 3),
"delta_confirm": rng.choice([True, False]),
"delta_value": round(ctx["delta_val"], 1),
"initiative_strength": rng.randint(40, 100),
"book_imbalance_pct": ctx["bookimb"],
"footprint_summary": f"{'Bid' if direction == 'buy' else 'Ask'}-heavy profile with {('absorption at bid' if direction == 'buy' else 'supply at ask')}. POC at {ctx['poc_level']:.5g}, delta {'+' if direction == 'buy' else '-'}{abs(ctx['delta_val']):.0f}",
})
return signals