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.

AI-readable reference available
/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.
We follow the llms.txt standard — a plain-text spec file at the root of the domain that AI assistants can read. So when users ask ChatGPT "help me modify my QuantifyMe strategy", they paste the URL and the AI already understands our signal convention, section structure, and all common patterns. It's like having documentation that's written for both humans and machines.
View llms.txt quantifyme.ai/llms.txt
No PineScript here. The generated code is Python. The logic is the same — entries, exits, signals, indicators — but the syntax is different. This page maps the two side by side.

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 writeWhat 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()dictOne 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 onlydirection = "long" / "short"
Stop loss / take profitstop_loss = 0.005 (0.5%) · take_profit = 0.01 (1.0%) — decimal ratios
Fewer / higher-conviction tradesraise signal_threshold (0.60–0.70) and/or set cooldown > 0
Trade only a sessionsession_filter = [7, 16] (UTC hours, e.g. London)
Only trade with the trendtrend_filter = "sma_50"
Change the prediction horizontarget_horizon = N (1 = next bar, 4 ≈ 1h, 16 ≈ 4h on 15-min bars)
Add an indicator / candle patternadd the column inside feature_engineering() — the model learns it automatically
Use a different modelmodel_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.

ConceptPineScriptPython (this file)
Go longstrategy.entry("L", strategy.long)signal = 1
Go shortstrategy.entry("S", strategy.short)signal = -1
Close / flatstrategy.close_all()signal = 0
Current closecloseclose or ohlc['close']
Previous closeclose[1]close.shift(1)
Current openopenohlc['open']
Bar's high / lowhigh / lowohlc['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 indexbar_indexdf.index (datetime)
Day of weekdayofweekdf.index.dayofweek (0=Mon, 4=Fri)
Hour of dayhourdf.index.hour
Minuteminutedf.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",
    }
What you don't write: no 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()

GoalChange
Long only / short onlydirection = "long" / "short"
Add / change stop loss & take profitstop_loss = 0.005, take_profit = 0.01 (decimal ratios; on EUR/USD 0.005 ≈ 50 pips). None disables.
Trade less often / higher convictionraise signal_threshold (0.60–0.70), and/or set cooldown > 0
Trade more oftenlower 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 periodsmin_atr = 0.0003 (minimum NATR)
Trade only with the trendtrend_filter = "sma_50"
Predict further / nearer aheadtarget_horizon = N — 1 = next bar, 4 ≈ 1h, 16 ≈ 4h on 15-min bars
Reverse vs go flat on the opposite signalon_opposite = "reverse" | "close_only"
Different model classmodel_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()
Custom indicators from Pine Script or natural language: 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.

Logged in: copies your personal key. Otherwise: creates a trial key.
# Base URL
https://api.quantifyme.ai/api/v1/

# Auth header
X-API-Key: qm_xxxxxxxxxxxxxxxx
Quick start: Scroll to Full Example at the bottom for a complete copy-pastable script that covers generate → train → predict.

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
What you get out of the box:
  • Subcommands: mint, whoami, one-shot, generate, train, fork, watch
  • Typed exceptions (AuthError, RateLimitError with .retry_after, BudgetError, NetworkError) so wrappers can branch on failure mode
  • Auto-retry with exponential backoff; honours Retry-After on 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"]
indicators is optional. When provided, Claude receives exact parameters (e.g. "RSI with period=14") instead of parsing free text. See GET /api/v1/indicator-schema for all available indicators.

pos_rules options

FieldOptionsDefault
max_pos1, 2, 31
on_oppositereverse, close_only, ignorereverse
directionboth, long, shortboth
cooldown0100 (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

KeyNameDescription
macd_momentumMACD MomentumTrend-following with MACD crossovers + RSI confirmation
mean_reversionMean ReversionFade extremes with Bollinger Bands + RSI
trend_followingTrend FollowingEMA crossovers + ATR position sizing
scalperScalperHigh-frequency with Stochastic + BB
conservative_longConservative Long-OnlySMA 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

IndicatorParamsFeatures generated
SMAsma_periods = "20,50,200"sma_{P}, dm_sma_{P}
EMAema_periods = "9,21,50"ema_{P}, dm_ema_{P}
Bollinger Bandsbb_period=20, bb_std=2.0bb_width, bb_pct
RSIrsi_period = 14rsi_{P}
MACDmacd_fast=12, slow=26, signal=9macd_line, macd_hist
Stochasticstoch_k=14, stoch_d=3stoch_k, stoch_d
ATRatr_period = 14atr_{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.

EndpointReturns
GET /api/v1/indicator-schemaAll 8 indicators with param names, types, defaults, and generated feature columns
GET /api/v1/presetsChip presets (same as website UI), pos_rules schema, 5 strategy templates
GET /api/v1/docsMachine-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
Tip: This script takes ~10-30 seconds total. Step 1 (generate) takes ~5s, Step 3 (training) takes ~5-15s depending on data size. The 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.

Demo
Try it out

Pick examples from each section, drag them into the composer, and hit "Try It" to generate & backtest a strategy.

Browse
Backtesting
Strategy Builder
API Workflows

    
Strategy Composer
Drag examples into slots or click "+ Add"
1. Features x
empty
2. Signals x
empty
3. Model x
empty
4. Optimisation x
empty
5. Risk x
empty
6. Position Rules x
empty
7. Filter x
empty
demo key — no account needed

  
0. Get your API key
checking...

All snippets below need an X-API-Key header. Click below to copy yours.

Run with Updates every Run button + visible code below
1. Build & Test

Generate a strategy, train it, and see backtest results. Updates live from the Strategy Composer.





Paste Strategy Code
(paste generated code here to skip generation)





2. Deploy & Automate

Deploy your trained model for live signal delivery via webhook or Telegram.





3. Community Script
M
EMA Trend + Session Filter
Shared by @malco
+1.01%
Return
39.83
Sharpe
-0.34%
Max DD
24
Trades
EMA 50/200 crossover with RSI filter (40-60 zone). ATR-based stop loss & take profit. London-NY overlap only, no Friday trades. 3-bar cooldown.

This proven strategy runs on EURUSD 15-min data (Jan-Apr 2026). Click "Run Now" to see the same results live.






4. Quick Deploy
From idea to live signals in 60 seconds
Describe your strategy in one line. We'll generate the code, train an ML model on the past 28 days, and deploy it live.
Try: