Add pivot retest + engulfing strategy with dual take-profit

New strategy (pivot_retest_engulfing) that enters long/short trades at
pivot level retests confirmed by SMA 50 alignment and engulfing candle
patterns. Uses ATR-based stop loss with two take-profit levels — at TP1
half the position closes and SL moves to breakeven, at TP2 the rest closes.

- data_engine: add detect_engulfing() for bullish/bearish pattern detection
- backtester: add generate_signals_pivot_retest(), run_backtest_dual_tp(),
  update signal dispatcher and metrics for dual-TP trade format
- order_executor: support signal=-1 (SHORT), attach SL/TP levels
- config: switch to pivot_retest_engulfing with default params
- chart_trades: new mplfinance script to visualize entries on candlesticks
- README: rewrite with full setup guide, project structure, strategy docs
- requirements.txt: make portable (remove conda file:// paths), add mplfinance
- .env.example: add template for secrets

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Brent Neale
2026-02-17 15:18:02 +10:00
co-authored by Claude Opus 4.6
parent d843e63e7b
commit b1f3a919bf
11 changed files with 1017 additions and 114 deletions
+13 -1
View File
@@ -305,6 +305,7 @@ def chart():
ohlc_json = "[]"
pivot_json = "{}"
sma_json = "{}"
error_msg = None
try:
@@ -325,7 +326,7 @@ def chart():
resp = (
sb.table(table)
.select("time,open,high,low,close")
.select("time,open,high,low,close,sma_3,sma_20,sma_21,sma_50,sma_100")
.eq("instrument", instrument)
.eq("granularity", granularity)
.order("time", desc=True)
@@ -355,6 +356,16 @@ def chart():
latest = df[pivot_cols].dropna().iloc[-1] if df[pivot_cols].dropna().shape[0] > 0 else None
if latest is not None:
pivot_json = latest.to_json()
# Prepare SMA data as {col_name: [values]} for overlay
sma_cols = [c for c in df.columns if c.startswith("sma_")]
if sma_cols:
sma_dict = {}
for c in sma_cols:
df[c] = pd.to_numeric(df[c], errors="coerce")
sma_dict[c] = df[c].where(df[c].notna(), None).tolist()
import json
sma_json = json.dumps(sma_dict)
else:
error_msg = "Supabase credentials not configured."
except Exception as e:
@@ -363,6 +374,7 @@ def chart():
return render_template("chart.html",
ohlc_json=ohlc_json,
pivot_json=pivot_json,
sma_json=sma_json,
instrument=instrument,
granularity=granularity,
instruments=VALID_INSTRUMENTS,