Strategy Code Reference
Every strategy generated by QuantifyMe is a self-contained Python file. This guide explains how to read it, what each section does, and how to make common edits — especially if you're coming from PineScript.
/llms.txt is a plain-text version of this documentation written specifically for AI assistants (ChatGPT, Claude, Gemini).
Paste the URL into any AI chat and it can instantly help you write or modify your strategy code.
How it works
A generated strategy file has exactly two functions: feature_engineering() and strategy_config(). You describe the strategy through those — the framework does everything else: builds and trains the model, turns its probabilities into buy/sell/flat signals, applies your position/session/trend filters, runs the walk-forward backtest with your stop-loss / take-profit / cooldown, and computes every metric. There is no build_model, generate_signals, apply_risk, or train_and_backtest to edit anymore — older docs that mention "7 numbered sections" describe the pre-2026 framework.
| You write | What it does |
|---|---|
feature_engineering(df, close, open_, high, low) | Add indicator / pattern columns to df and return it. Every new column becomes an ML feature. (No look-ahead; end with .bfill().ffill(); don't drop rows.) |
strategy_config() → dict | One dict: model_type + model_params, signal_threshold, direction, stop_loss, take_profit, cooldown, max_positions, on_opposite, session_filter, min_atr, trend_filter, target_horizon (+ title/objective/notes). |
| Want to… | Change |
|---|---|
| Longs only / shorts only | direction = "long" / "short" |
| Stop loss / take profit | stop_loss = 0.005 (0.5%) · take_profit = 0.01 (1.0%) — decimal ratios |
| Fewer / higher-conviction trades | raise signal_threshold (0.60–0.70) and/or set cooldown > 0 |
| Trade only a session | session_filter = [7, 16] (UTC hours, e.g. London) |
| Only trade with the trend | trend_filter = "sma_50" |
| Change the prediction horizon | target_horizon = N (1 = next bar, 4 ≈ 1h, 16 ≈ 4h on 15-min bars) |
| Add an indicator / candle pattern | add the column inside feature_engineering() — the model learns it automatically |
| Use a different model | model_type + model_params (XGBClassifier / RandomForestClassifier / GradientBoostingClassifier / ExtraTreesClassifier / LogisticRegression) |
Backtest basics: walk-forward, no look-ahead — entry is on the next bar's open after a signal; SL/TP are checked intra-bar against the bar's high/low; commission on every position change. Data is forex OHLC for any of 7 majors (EUR/USD, GBP/USD, USD/JPY, USD/CHF, AUD/USD, NZD/USD, USD/CAD) at 1min / 5min / 15min / 1h — pick it with the symbol + timeframe fields. For the AI-readable version of all this, see /llms.txt.
PineScript vs Python — side by side
If you know PineScript, here's how the concepts map across.
| Concept | PineScript | Python (this file) |
|---|---|---|
| Go long | strategy.entry("L", strategy.long) | signal = 1 |
| Go short | strategy.entry("S", strategy.short) | signal = -1 |
| Close / flat | strategy.close_all() | signal = 0 |
| Current close | close | close or ohlc['close'] |
| Previous close | close[1] | close.shift(1) |
| Current open | open | ohlc['open'] |
| Bar's high / low | high / low | ohlc['high'] / ohlc['low'] |
| SMA(close, 20) | ta.sma(close, 20) | close.rolling(20).mean() |
| EMA(close, 20) | ta.ema(close, 20) | close.ewm(span=20).mean() |
| RSI(close, 14) | ta.rsi(close, 14) | see Add an indicator |
| Crossover(fast, slow) | ta.crossover(fast, slow) | (fast > slow) & (fast.shift(1) <= slow.shift(1)) |
| Bar index | bar_index | df.index (datetime) |
| Day of week | dayofweek | df.index.dayofweek (0=Mon, 4=Fri) |
| Hour of day | hour | df.index.hour |
| Minute | minute | df.index.minute |
Editing a generated strategy
The whole file is two functions plus a tiny header. You add features in the first and set knobs in the second — that's it.
# SECTION 0 — IMPORTS & CONSTANTS (don't change these) import numpy as np import pandas as pd DATA_PATH = "..."; START_DATE = "..."; END_DATE = "..."; VALIDATION_DATE = ""; TRAIN_SPLIT = 0.7 # SECTION 1 — FEATURE ENGINEERING (add columns here; each one becomes an ML feature) def feature_engineering(df, close, open_, high, low): df['rsi_14'] = _rsi(close, 14) df['ema_20'] = close.ewm(span=20, adjust=False).mean() df['ema_50'] = close.ewm(span=50, adjust=False).mean() df['ret_1'] = close.pct_change() df = df.bfill().ffill() # fill indicator warm-up NaNs — required return df # SECTION 2 — STRATEGY CONFIG (every key required) def strategy_config(): return { "model_type": "XGBClassifier", "model_params": {"n_estimators": 300, "max_depth": 4, "learning_rate": 0.05}, "signal_threshold": 0.55, # predict_proba cutoff for a signal "direction": "both", # "both" | "long" | "short" "stop_loss": 0.005, # 0.5% — decimal ratio, or None "take_profit": 0.01, # 1.0% — decimal ratio, or None "cooldown": 0, # bars to wait after a close before re-entry "max_positions": 1, "on_opposite": "reverse", # "reverse" (flip) | "close_only" (go flat) "session_filter": None, # [start_hour, end_hour] UTC, e.g. [7, 16] "min_atr": None, # minimum NATR to allow entries "trend_filter": None, # e.g. "sma_50" — longs above, shorts below "target_horizon": 4, # bars ahead the model predicts (4 ≈ 1h on 15-min) "title": "EUR/USD Momentum (RSI+EMA, XGBoost)", "objective": "capture intraday momentum continuation", "notes": "shallow trees + low LR for noisy FX", }
build_model, generate_signals, apply_risk, train_and_backtest, or any backtest/metric code. The framework loads the OHLC, calls your feature_engineering(), strips warm-up rows, builds the target (sign(close.shift(-target_horizon) - close)), splits train/test, fits the model, thresholds predict_proba to -1/0/1, applies your filters, runs the backtest with your SL/TP/cooldown, and returns all metrics.Common edits — all via strategy_config()
| Goal | Change |
|---|---|
| Long only / short only | direction = "long" / "short" |
| Add / change stop loss & take profit | stop_loss = 0.005, take_profit = 0.01 (decimal ratios; on EUR/USD 0.005 ≈ 50 pips). None disables. |
| Trade less often / higher conviction | raise signal_threshold (0.60–0.70), and/or set cooldown > 0 |
| Trade more often | lower signal_threshold toward 0.50 |
| Restrict to a session (UTC hours) | session_filter = [7, 16] (London) · [13, 17] (London/NY overlap) · [0, 9] (Tokyo) |
| Skip dead/low-volatility periods | min_atr = 0.0003 (minimum NATR) |
| Trade only with the trend | trend_filter = "sma_50" |
| Predict further / nearer ahead | target_horizon = N — 1 = next bar, 4 ≈ 1h, 16 ≈ 4h on 15-min bars |
| Reverse vs go flat on the opposite signal | on_opposite = "reverse" | "close_only" |
| Different model class | model_type: XGBClassifier | RandomForestClassifier | GradientBoostingClassifier | ExtraTreesClassifier | LogisticRegression (+ matching model_params) |
Common edit: add an indicator / pattern as a feature
Put it in feature_engineering() — every new column on df is automatically an ML feature. No look-ahead (no negative shifts), don't drop rows, and finish with .bfill().ffill().
def feature_engineering(df, close, open_, high, low): # MACD ema12 = close.ewm(span=12).mean(); ema26 = close.ewm(span=26).mean() df['macd'] = ema12 - ema26 df['macd_sig'] = df['macd'].ewm(span=9).mean() df['macd_hist'] = df['macd'] - df['macd_sig'] # Bollinger position mid = close.rolling(20).mean(); sd = close.rolling(20).std() df['bb_pos'] = (close - (mid - 2*sd)) / ((mid + 2*sd) - (mid - 2*sd) + 1e-9) # ATR / NATR (for volatility context) tr = pd.concat([high-low, (high-close.shift(1)).abs(), (low-close.shift(1)).abs()], axis=1).max(axis=1) df['natr_14'] = tr.rolling(14).mean() / close # Candle pattern: hammer body = (close-open_).abs() lower_wick = pd.concat([close, open_], axis=1).min(axis=1) - low upper_wick = high - pd.concat([close, open_], axis=1).max(axis=1) df['hammer'] = ((lower_wick > 2*body) & (upper_wick < 0.3*body) & (close > open_)).astype(float) # Time-of-day (cyclical) df['hour_sin'] = np.sin(2*np.pi*df.index.hour/24) df['hour_cos'] = np.cos(2*np.pi*df.index.hour/24) return df.bfill().ffill()
POST /api/v1/indicators/custom generates the feature code for you. Full indicator catalogue: GET /api/v1/indicator-schema.API Reference
All endpoints live under /api/v1/. Authenticated endpoints require an X-API-Key header.
# Base URL https://api.quantifyme.ai/api/v1/ # Auth header X-API-Key: qm_xxxxxxxxxxxxxxxx
Run a Bot — prebuilt CLI
Don't want to write the HTTP layer yourself? qm-bot-template is a stdlib-only Python CLI that wraps every endpoint on this page. Auto-mints a trial key, retries on transient errors, types every exception. 60 seconds to your first live signal:
# Clone, install, run git clone https://github.com/malcolm1232/HFTAgent.git cd HFTAgent/notebooks/bot_template pip install -e . qmbot mint # 50-credit trial key, cached qmbot one-shot "Buy when RSI<30 on EURUSD 15m" # generate → train → deploy qmbot fork --publish # fork top of /community, retrain, republish if profitable qmbot watch --kind FORKED # live tail of every other bot's activity
- Subcommands:
mint,whoami,one-shot,generate,train,fork,watch - Typed exceptions (
AuthError,RateLimitErrorwith.retry_after,BudgetError,NetworkError) so wrappers can branch on failure mode - Auto-retry with exponential backoff; honours
Retry-Afteron 429 / 503 - Typed exit codes (2=auth, 3=rate-limit, 4=budget, 5=validation, 6=network) for shell-script integration
- Zero runtime deps. Python 3.9+. Drop on any VPS.
Full source & README → github.com/malcolm1232/HFTAgent/tree/main/notebooks/bot_template
POST /api/v1/generate
Generate a full Python strategy from natural language. Each of the 7 sections accepts free-text descriptions. Optionally pass structured indicators for exact parameter control.
import requests resp = requests.post("https://api.quantifyme.ai/api/v1/generate", json={ "features": "MACD histogram slope, RSI divergence", "signals": "Long when MACD crosses above zero with RSI confirmation", "model": "XGBoost", "optimization": "Maximize Sharpe ratio", "risk": "Stop loss 0.3%, scale by ATR", "risk_function": "Skip trades on Fridays after 18:00 UTC", "indicators": { "macd_enabled": True, "macd_fast": 12, "macd_slow": 26, "macd_signal": 9, "rsi_enabled": True, "rsi_period": 14, "atr_enabled": True, "atr_period": 14, }, "pos_rules": {"max_pos": 1, "direction": "both", "on_opposite": "reverse", "cooldown": 0}, "start_date": "2025-01-01", "end_date": "2026-04-01", "train_split": 0.7, "claude_model": "sonnet", }, headers={"X-API-Key": "qm_xxx"}) code = resp.json()["code"]
GET /api/v1/indicator-schema for all available indicators.pos_rules options
| Field | Options | Default |
|---|---|---|
| max_pos | 1, 2, 3 | 1 |
| on_opposite | reverse, close_only, ignore | reverse |
| direction | both, long, short | both |
| cooldown | 0 — 100 (bars) | 0 |
POST /api/v1/generate/from-template
Generate strategy code from a curated template. Override any field — text fields replace, dict fields (indicators, pos_rules) merge.
resp = requests.post("https://api.quantifyme.ai/api/v1/generate/from-template", json={ "template": "scalper", "start_date": "2025-01-01", "end_date": "2026-04-01", "pos_rules": {"direction": "long"}, # override: long-only "indicators": {"rsi_period": 21}, # override: RSI 21 instead of 14 "claude_model": "sonnet", }, headers={"X-API-Key": "qm_xxx"})
Available templates
| Key | Name | Description |
|---|---|---|
| macd_momentum | MACD Momentum | Trend-following with MACD crossovers + RSI confirmation |
| mean_reversion | Mean Reversion | Fade extremes with Bollinger Bands + RSI |
| trend_following | Trend Following | EMA crossovers + ATR position sizing |
| scalper | Scalper | High-frequency with Stochastic + BB |
| conservative_long | Conservative Long-Only | SMA trend + strict drawdown control |
POST /api/v1/indicators
Compute built-in indicators on OHLC data without training a model. Returns indicator values as JSON.
resp = requests.post("https://api.quantifyme.ai/api/v1/indicators", json={ "indicators": {"rsi_enabled": True, "rsi_period": 14, "macd_enabled": True}, "symbol": "EURUSD", "timeframe": "15min", "start": "2026-04-01", "limit": 100, "format": "columns", # or "rows" }, headers={"X-API-Key": "qm_xxx"}) data = resp.json() # data["ind_rsi14"] → [45.2, 48.1, 52.3, ...] # data["timestamps"] → ["2026-04-01 00:00:00", ...]
Available indicators
| Indicator | Params | Features generated |
|---|---|---|
| SMA | sma_periods = "20,50,200" | sma_{P}, dm_sma_{P} |
| EMA | ema_periods = "9,21,50" | ema_{P}, dm_ema_{P} |
| Bollinger Bands | bb_period=20, bb_std=2.0 | bb_width, bb_pct |
| RSI | rsi_period = 14 | rsi_{P} |
| MACD | macd_fast=12, slow=26, signal=9 | macd_line, macd_hist |
| Stochastic | stoch_k=14, stoch_d=3 | stoch_k, stoch_d |
| ATR | atr_period = 14 | atr_{P}, natr_{P} |
POST /api/v1/indicators/custom
Convert Pine Script or natural language to a computed indicator via Claude. Returns overlays, panels, signals, and ML features.
# Natural language resp = requests.post("https://api.quantifyme.ai/api/v1/indicators/custom", json={ "source": "Supertrend indicator with ATR period 10 and multiplier 3", "timeframe": "15min", "fe_mode": "basic", # "full" | "basic" | "none" }, headers={"X-API-Key": "qm_xxx"}) # Pine Script resp = requests.post("https://api.quantifyme.ai/api/v1/indicators/custom", json={ "source": "//@version=5 indicator('My RSI') plot(ta.rsi(close, 14))", "provider": "anthropic", # "anthropic" | "groq" | "ollama" }, headers={"X-API-Key": "qm_xxx"}) result = resp.json() # result["name"] → "Supertrend" # result["overlays"] → [{id, color, data}] # result["panels"] → [{id, title, lines, h_lines}] # result["signals"] → [{time, type: "buy"|"sell"}] # result["features"] → {col_name: [{time, value}]}
Discovery Endpoints (no auth)
These endpoints are public — no API key required. Use them to discover available options before calling authenticated endpoints.
| Endpoint | Returns |
|---|---|
GET /api/v1/indicator-schema | All 8 indicators with param names, types, defaults, and generated feature columns |
GET /api/v1/presets | Chip presets (same as website UI), pos_rules schema, 5 strategy templates |
GET /api/v1/docs | Machine-readable JSON docs (core endpoints, with examples). Full path catalogue: /openapi.json |
# Discover available indicators schema = requests.get("https://api.quantifyme.ai/api/v1/indicator-schema").json() # Discover templates + presets presets = requests.get("https://api.quantifyme.ai/api/v1/presets").json() # presets["templates"]["macd_momentum"]["config"] → ready for /generate
Full Example — Copy, Paste, Run
Complete end-to-end script: generate strategy → train model → poll until done → check results. Replace API_KEY with yours.
# QuantifyMe API - Full Example # 1. Generate strategy code from natural language # 2. Submit for training # 3. Poll until complete # 4. View results import requests, time # ────────────────────────────────────────────────────────────────── # CONFIG — paste your API key here # ────────────────────────────────────────────────────────────────── BASE = "https://api.quantifyme.ai" API_KEY = "qm_PASTE_YOUR_KEY_HERE" # ← get from API Endpoint page HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"} # ────────────────────────────────────────────────────────────────── # STEP 1: Generate strategy code # ────────────────────────────────────────────────────────────────── print("Generating strategy...") resp = requests.post(f"{BASE}/api/v1/generate", json={ # Natural language — describe your strategy "features": "MACD (12,26,9), RSI 14, ATR 14, Volume Z-score", "signals": "Buy when MACD crosses above zero and RSI < 60", "model": "XGBoost", "optimization": "Maximize Sharpe ratio, keep max drawdown under 15%", "risk": "Stop loss 0.3%, fixed 1% risk per trade", # Structured indicators — exact params (optional, overrides NL) "indicators": { "macd_enabled": True, "macd_fast": 12, "macd_slow": 26, "macd_signal": 9, "rsi_enabled": True, "rsi_period": 14, "atr_enabled": True, "atr_period": 14, }, # Position rules "pos_rules": { "max_pos": 1, "direction": "both", "on_opposite": "reverse", "cooldown": 0, }, # Date range + model "start_date": "2025-06-01", "end_date": "2026-04-01", "train_split": 0.7, "claude_model": "sonnet", }, headers=HEADERS) code = resp.json()["code"] print(f"Strategy generated ({len(code)} chars)") # ────────────────────────────────────────────────────────────────── # STEP 2: Submit for training # ────────────────────────────────────────────────────────────────── print("Submitting for training...") resp = requests.post(f"{BASE}/api/v1/train", json={ "code": code, "model_name": "API Test Model", "start_date": "2025-06-01", "end_date": "2026-04-01", "train_split": 0.7, "timeframe": "15min", }, headers=HEADERS) job = resp.json() job_id = job["job_id"] print(f"Job {job_id} queued") # ────────────────────────────────────────────────────────────────── # STEP 3: Poll until done # ────────────────────────────────────────────────────────────────── while True: resp = requests.get(f"{BASE}/api/v1/train/{job_id}", headers=HEADERS) status = resp.json() print(f" Status: {status['status']}") if status["status"] in ("done", "failed"): break time.sleep(3) # ────────────────────────────────────────────────────────────────── # STEP 4: View results # ────────────────────────────────────────────────────────────────── if status["status"] == "done": m = status.get("metrics", {}) print("Training complete!") print(f" Return: {m.get('total_ret', 0):.2%}") print(f" Sharpe: {m.get('sharpe_strat', 0):.2f}") print(f" Drawdown: {m.get('mdd', 0):.2%}") print(f" Trades: {m.get('n_trades', 0)}") else: print(f"Training failed: {status.get('error', 'unknown')}") # ────────────────────────────────────────────────────────────────── # BONUS: Use a template instead (no NL needed) # ────────────────────────────────────────────────────────────────── # resp = requests.post(f"{BASE}/api/v1/generate/from-template", json={ # "template": "scalper", # "start_date": "2025-06-01", # "end_date": "2026-04-01", # "claude_model": "sonnet", # }, headers=HEADERS) # ────────────────────────────────────────────────────────────────── # BONUS: Compute indicators without training # ────────────────────────────────────────────────────────────────── # resp = requests.post(f"{BASE}/api/v1/indicators", json={ # "indicators": {"rsi_enabled": True, "macd_enabled": True}, # "timeframe": "15min", "limit": 100, # }, headers=HEADERS) # print(resp.json()["ind_rsi14"][:5]) # first 5 RSI values
while True loop polls every 3 seconds until done.More Examples
Browse all API examples by section. Drag examples into the Strategy Composer to build and test a strategy.
Pick examples from each section, drag them into the composer, and hit "Try It" to generate & backtest a strategy.
All snippets below need an X-API-Key header.
Click below to copy yours.
Generate a strategy, train it, and see backtest results. Updates live from the Strategy Composer.
Deploy your trained model for live signal delivery via webhook or Telegram.
This proven strategy runs on EURUSD 15-min data (Jan-Apr 2026). Click "Run Now" to see the same results live.