Appearance
Personalize your experience
Strategies
IDEPaste the code below into a new Strategy file. Click Run ▶ to backtest, then Apply to see trade markers on the chart.
⚡ Quick Start — Complete Working Strategy
1"""2SMA Crossover Strategy — Quatick Quick Start3Copy, paste, Run ▶, Apply ✓4"""5import numpy as np67METADATA = { "id": "sma-cross", "name": "SMA Crossover", "version": "1.0.0" }89DEFAULT_PARAMS = {10 "fast": 9, "slow": 21,11 "risk_pct": 2.0, "stop_atr": 1.5, "tp_atr": 3.0,12 "initial_equity": 100000,13}1415# ---- helpers ----------------------------------------------------------------16def _sma(arr, n): return np.convolve(arr, np.ones(n)/n, 'full')[:len(arr)]17def _atr(bars, n=14):18 hi = np.array([b['high'] for b in bars])19 lo = np.array([b['low'] for b in bars])20 cl = np.array([b['close'] for b in bars])21 tr = np.maximum(hi - lo, np.maximum(abs(hi - np.roll(cl, 1)), abs(lo - np.roll(cl, 1))))22 tr[0] = hi[0] - lo[0]23 atr = np.zeros(len(tr))24 atr[n - 1] = np.mean(tr[:n])25 for i in range(n, len(tr)):26 atr[i] = (atr[i-1] * (n-1) + tr[i]) / n27 return atr2829# ---- main entry point -------------------------------------------------------30def run(bars: list, params: dict = None) -> dict:31 p = {**DEFAULT_PARAMS, **(params or {})}32 closes = np.array([b['close'] for b in bars])33 fast = _sma(closes, p['fast'])34 slow = _sma(closes, p['slow'])35 atr = _atr(bars)3637 signals = []38 equity = p['initial_equity']39 trades = []40 position = None # {side, entry, stop, tp, qty}4142 warmup = max(p['fast'], p['slow'])4344 for i in range(warmup, len(bars)):45 bar = bars[i]46 time = bar['time']47 cl = closes[i]48 a = atr[i] if atr[i] > 0 else cl * 0.014950 # --- manage open position ---51 if position:52 hit_stop = (position['side'] == 'long' and cl <= position['stop']) or \53 (position['side'] == 'short' and cl >= position['stop'])54 hit_tp = (position['side'] == 'long' and cl >= position['tp']) or \55 (position['side'] == 'short' and cl <= position['tp'])5657 if hit_stop or hit_tp:58 pnl = (cl - position['entry']) * position['qty'] * (1 if position['side'] == 'long' else -1)59 equity += pnl60 trades.append({ 'pnl': pnl, 'side': position['side'] })61 signals.append({ 'time': time, 'type': 'LONG_EXIT' if position['side'] == 'long' else 'SHORT_EXIT', 'price': cl })62 position = None6364 # --- new entry signals ---65 if not position:66 golden = fast[i-1] <= slow[i-1] and fast[i] > slow[i] # bullish crossover67 death = fast[i-1] >= slow[i-1] and fast[i] < slow[i] # bearish crossover6869 if golden:70 risk_amt = equity * p['risk_pct'] / 10071 qty = max(1, int(risk_amt / (a * p['stop_atr'])))72 position = { 'side': 'long', 'entry': cl, 'qty': qty,73 'stop': cl - a * p['stop_atr'], 'tp': cl + a * p['tp_atr'] }74 signals.append({ 'time': time, 'type': 'LONG_ENTRY', 'price': cl })7576 elif death:77 risk_amt = equity * p['risk_pct'] / 10078 qty = max(1, int(risk_amt / (a * p['stop_atr'])))79 position = { 'side': 'short', 'entry': cl, 'qty': qty,80 'stop': cl + a * p['stop_atr'], 'tp': cl - a * p['tp_atr'] }81 signals.append({ 'time': time, 'type': 'SHORT_ENTRY', 'price': cl })8283 # close any open position at end84 if position:85 cl = closes[-1]86 pnl = (cl - position['entry']) * position['qty'] * (1 if position['side'] == 'long' else -1)87 equity += pnl88 trades.append({ 'pnl': pnl, 'side': position['side'] })8990 wins = [t for t in trades if t['pnl'] > 0]91 gross_p = sum(t['pnl'] for t in wins) if wins else 092 gross_l = abs(sum(t['pnl'] for t in trades if t['pnl'] <= 0)) or 19394 return {95 "signals": signals,96 "performance": {97 "total_trades": len(trades),98 "win_rate": round(len(wins) / len(trades) * 100, 2) if trades else 0,99 "profit_factor": round(gross_p / gross_l, 2),100 "total_pnl": round(equity - p['initial_equity'], 2),101 "return_pct": round((equity - p['initial_equity']) / p['initial_equity'] * 100, 2),102 }103 }📂 Python SDK — Built-in Helpers
| Function | Signature | Returns |
|---|---|---|
| np.array | np.array([b["close"] for b in bars]) | float array of prices |
| np.convolve | np.convolve(arr, np.ones(n)/n, "full") | Fast SMA via convolution |
| np.maximum / minimum | np.maximum(a, b) | Element-wise max/min (, range calcs) |
| np.roll | np.roll(arr, 1) | Shift array by 1 (yesterday vs today) |
| np.std | np.std(arr[-n:]) | of last n values |
| np.mean | np.mean(arr[-n:]) | Mean of last n values |
| np.cumsum | np.cumsum(arr) | sum () |
| np.argmax / argmin | np.argmax(arr) | Index of max/min |
📊 Signal Types Reference
| Signal type | Meaning | Chart marker |
|---|---|---|
| LONG_ENTRY | Open a | ▲ Green arrow below bar |
| LONG_EXIT | Close the | ▼ Gray arrow above bar |
| SHORT_ENTRY | Open a | ▼ Red arrow above bar |
| SHORT_EXIT | Close the | ▲ Gray arrow below bar |
Creating Trading Strategies
Strategies in Quatick define complete trading logic: when to enter and exit positions, how much capital to allocate, and how to manage risk. They combine one or more technical indicators with rule-based -making to generate actionable trading signals.
All strategies in Quatick are written in Python and run via the (WebAssembly Python) , giving you access to NumPy for high-performance numerical computation.
1. Strategy Architecture
A strategy script is a Python module that exports:
- — Identity dict with id, name, version,
- DEFAULT_PARAMS — Default parameter values the user can override
- run(bars, params) — Main entry : processes all bars and returns signals + performance
- Helper functions — sma(), (), (), etc. for technical calculations
2. Strategy Lifecycle
When a strategy is executed, the calls your run() function with the full bar dataset. The function iterates through each bar, evaluating conditions and generating signals:
1# Strategy execution flow:2#3# 1. INITIALIZATION4# - Load parameters, set initial equity, clear state5#6# 2. BAR ITERATION (for each bar in the dataset)7# ├── Calculate indicator values (SMA, RSI, etc.)8# ├── Check ENTRY conditions (and generate LONG_ENTRY / SHORT_ENTRY)9# ├── Check EXIT conditions (stop loss, take profit, signal exit)10# ├── Update position P&L11# └── Record signal if generated12#13# 3. PERFORMANCE CALCULATION14# - Compute win rate, profit factor, max drawdown, etc.15# - Return signals list + performance metrics3. Signal Types
| Signal | Action | Description | Marker |
|---|---|---|---|
| LONG_ENTRY | Buy | Open a — profit from price increase | ▲ Green arrow below bar |
| LONG_EXIT | Sell | Close an existing | ▼ Green arrow above bar |
| SHORT_ENTRY | Sell Short | Open a — profit from price decrease | ▼ Red arrow above bar |
| SHORT_EXIT | Buy to Cover | Close an existing | ▲ Red arrow below bar |
| None | No trading action — conditions not met | No marker |
4. Risk Management Parameters
Every professional strategy must implement proper risk management. These are the key parameters to define:
| Parameter | Type | Description | Typical Range |
|---|---|---|---|
| risk_percent | float | Maximum of equity to risk per trade | 0.5% – 3.0% |
| stop_loss_atr | float | distance as a multiple of | 1.0 – 3.0 |
| take_profit_atr | float | Take profit distance as a multiple of | 2.0 – 6.0 |
| max_positions | int | Maximum number of simultaneous open positions | 1 – 5 |
| cooldown_bars | int | Minimum bars to wait between | 1 – 20 |
| initial_equity | float | Starting account balance for | $10,000 – $1,000,000 |
| allow_shorts | bool | Whether the strategy can open short positions | True / False |
| max_drawdown_pct | float | switch — stop trading if exceeds this | 10% – 30% |
| trailing_stop | bool | Whether trails with | True / False |
5. Position Sizing Algorithms
Position sizing determines how many shares/contracts to trade per signal. Proper is critical for risk management.
5.1 Fixed Risk Position Sizing
1def calculate_position_size(equity: float, risk_pct: float, 2 entry_price: float, stop_price: float) -> int:3 """4 Risk-based position sizing.5 6 Args:7 equity: Current account equity8 risk_pct: Risk per trade as percentage (e.g., 2.0 for 2%)9 entry_price: Planned entry price10 stop_price: Stop loss price11 12 Returns:13 Number of shares to trade14 15 Example:16 equity = $100,00017 risk_pct = 2.0 (2%)18 entry = $50.0019 stop = $48.0020 21 risk_amount = $100,000 × 0.02 = $2,00022 risk_per_share = $50.00 - $48.00 = $2.0023 quantity = $2,000 / $2.00 = 1,000 shares24 """25 risk_amount = equity * (risk_pct / 100)26 risk_per_share = abs(entry_price - stop_price)27 28 if risk_per_share <= 0:29 return 030 31 quantity = int(risk_amount / risk_per_share)32 33 # Never use more than 10% of equity in a single position34 max_quantity = int((equity * 0.10) / entry_price)35 36 return min(quantity, max_quantity)5.2 Kelly Criterion Position Sizing
1def kelly_position_size(win_rate: float, avg_win: float, avg_loss: float,2 equity: float, fraction: float = 0.5) -> float:3 """4 Kelly Criterion — optimal bet sizing based on historical performance.5 Uses fractional Kelly (default 50%) for safety.6 7 Kelly % = W - [(1 - W) / R]8 where W = win rate, R = win/loss ratio9 """10 if avg_loss == 0:11 return 012 13 win_loss_ratio = avg_win / avg_loss14 kelly_pct = win_rate - ((1 - win_rate) / win_loss_ratio)15 16 # Apply fraction (half Kelly is common for safety)17 adjusted = kelly_pct * fraction18 19 # Clamp between 0% and 25% of equity20 return max(0, min(adjusted, 0.25)) * equity6. Stop Loss Strategies
| Type | Description | Code Pattern |
|---|---|---|
| Fixed ATR Stop | Stop at N × below/above entry | stop = entry - * multiplier |
| Percentage Stop | Stop at N% below/above entry | stop = entry * (1 - pct/100) |
| Trailing Stop | Stop moves up with price, never down | stop = max(stop, high - * N) |
| Chandelier Exit | minus N × | stop = max(highs[-22:]) - 3 * |
| Parabolic SAR | -based trailing stop | Complex — see SAR indicator |
| Support Level | Stop below nearest support level | stop = find_support(bars, lookback) |
1# Trailing stop implementation2def update_trailing_stop(current_stop: float, current_high: float,3 atr_value: float, atr_mult: float,4 side: str) -> float:5 """Move stop loss in profitable direction only."""6 if side == "long":7 new_stop = current_high - atr_value * atr_mult8 return max(current_stop, new_stop) # Only move UP9 else: # short10 new_stop = current_high + atr_value * atr_mult11 return min(current_stop, new_stop) # Only move DOWN7.1 Moving Average Crossover
1"""2Dual MA Crossover — The most classic strategy pattern.34Entry: Fast MA crosses ABOVE slow MA → Long5Exit: Fast MA crosses BELOW slow MA → Close long6"""7def detect_crossover(fast_values, slow_values, index):8 if index < 1:9 return None10 11 prev_fast, curr_fast = fast_values[index - 1], fast_values[index]12 prev_slow, curr_slow = slow_values[index - 1], slow_values[index]13 14 if prev_fast <= prev_slow and curr_fast > curr_slow:15 return "GOLDEN_CROSS" # Bullish16 elif prev_fast >= prev_slow and curr_fast < curr_slow:17 return "DEATH_CROSS" # Bearish18 19 return None7.2 Mean Reversion (Bollinger Bands)
1"""2Mean Reversion — Price tends to return to the mean.34Entry: Price touches lower Bollinger Band → Long (oversold)5Exit: Price reaches middle band (SMA) or upper band6"""7def check_mean_reversion(close, sma, upper, lower):8 if close <= lower:9 return "LONG_ENTRY" # Oversold — expect bounce10 elif close >= upper:11 return "SHORT_ENTRY" # Overbought — expect pullback12 elif close >= sma:13 return "LONG_EXIT" # Returned to mean14 return None7.3 Breakout Strategy
1"""2Breakout — Price breaks above resistance or below support.34Entry: Price > highest high of last N bars → Long5 Price < lowest low of last N bars → Short6Exit: Trailing stop or opposite signal7"""8def check_breakout(bars, index, lookback=20):9 if index < lookback:10 return None11 12 window = bars[index - lookback:index]13 highest = max(b["high"] for b in window)14 lowest = min(b["low"] for b in window)15 current = bars[index]["close"]16 17 if current > highest:18 return "LONG_ENTRY" # Upside breakout19 elif current < lowest:20 return "SHORT_ENTRY" # Downside breakout21 return None7.4 RSI Divergence
1"""2RSI Divergence — Price makes new high but RSI doesn't (bearish)3 Price makes new low but RSI doesn't (bullish)4"""5def detect_divergence(prices, rsi_values, index, lookback=14):6 if index < lookback * 2:7 return None8 9 recent_prices = prices[index - lookback:index + 1]10 recent_rsi = rsi_values[index - lookback:index + 1]11 12 price_higher_high = recent_prices[-1] > max(recent_prices[:-1])13 rsi_lower_high = recent_rsi[-1] < max(recent_rsi[:-1])14 15 price_lower_low = recent_prices[-1] < min(recent_prices[:-1])16 rsi_higher_low = recent_rsi[-1] > min(recent_rsi[:-1])17 18 if price_higher_high and rsi_lower_high:19 return "BEARISH_DIVERGENCE" # Potential SHORT20 elif price_lower_low and rsi_higher_low:21 return "BULLISH_DIVERGENCE" # Potential LONG22 return None7.5 Multi-Timeframe Confirmation
1"""2Multi-Timeframe — Align signals across timeframes for confidence.34Higher timeframe: Determine trend direction (bias)5Lower timeframe: Find entry timing (execution)67Example: 8 Daily chart → EMA 50 > EMA 200 → Bullish bias9 4H chart → RSI crosses above 30 → Execute long entry10"""11def multi_tf_strategy(daily_bars, hourly_bars, index):12 # Determine daily bias13 daily_ema_fast = ema(daily_bars, 50)14 daily_ema_slow = ema(daily_bars, 200)15 bias = "bullish" if daily_ema_fast > daily_ema_slow else "bearish"16 17 # Find entry on lower timeframe18 hourly_rsi = rsi(hourly_bars, 14)19 20 if bias == "bullish" and hourly_rsi < 30:21 return "LONG_ENTRY" # Oversold in an uptrend22 elif bias == "bearish" and hourly_rsi > 70:23 return "SHORT_ENTRY" # Overbought in a downtrend24 return None8. Performance Metrics
After , evaluate your strategy with these :
| Metric | Formula | Good Value | What It Tells You |
|---|---|---|---|
| Wins / Total × 100 | > 50% | How often are profitable | |
| Gross Profit / Gross Loss | > 1.5 | Dollars won per dollar lost | |
| Peak-to-trough equity % | < 20% | Worst-case capital decline | |
| Mean Return / | > 1.0 | Risk-adjusted performance | |
| Sortino Ratio | Mean Return / Downside | > 1.5 | Downside risk-adjusted performance |
| Calmar Ratio | Annual Return / | > 1.0 | Return relative to worst decline |
| (WR × AvgWin) - ((1-WR) × AvgLoss) | > 0 | ||
| Avg Trade | Sum of durations / | Varies | How long positions are |
| Recovery Factor | Net Profit / | > 3.0 | How quickly losses are recovered |
1def calculate_performance(trades: list, initial_equity: float) -> dict:2 """Compute comprehensive performance metrics."""3 if not trades:4 return {"total_trades": 0, "win_rate": 0}5 6 wins = [t for t in trades if t["pnl"] > 0]7 losses = [t for t in trades if t["pnl"] <= 0]8 9 total_pnl = sum(t["pnl"] for t in trades)10 gross_profit = sum(t["pnl"] for t in wins) if wins else 011 gross_loss = abs(sum(t["pnl"] for t in losses)) if losses else 012 13 # Track equity curve for drawdown14 equity = initial_equity15 peak = equity16 max_dd = 017 equity_curve = []18 19 for trade in trades:20 equity += trade["pnl"]21 equity_curve.append(equity)22 peak = max(peak, equity)23 dd = (peak - equity) / peak * 10024 max_dd = max(max_dd, dd)25 26 return {27 "total_trades": len(trades),28 "win_rate": len(wins) / len(trades) * 100,29 "profit_factor": gross_profit / gross_loss if gross_loss > 0 else float("inf"),30 "total_pnl": total_pnl,31 "max_drawdown": max_dd,32 "avg_win": gross_profit / len(wins) if wins else 0,33 "avg_loss": gross_loss / len(losses) if losses else 0,34 "return_pct": (equity - initial_equity) / initial_equity * 100,35 "largest_win": max((t["pnl"] for t in wins), default=0),36 "largest_loss": min((t["pnl"] for t in losses), default=0),37 "expectancy": total_pnl / len(trades),38 }9. Complete Strategy Template
1"""2Complete Strategy Template — Quatick IDE3Copy this template and modify the entry/exit logic.4"""5import numpy as np67METADATA = {8 "id": "my-strategy",9 "name": "My Strategy",10 "version": "1.0.0",11 "description": "Description of what this strategy does",12}1314DEFAULT_PARAMS = {15 "fast_period": 9,16 "slow_period": 21,17 "rsi_period": 14,18 "risk_percent": 2.0,19 "stop_loss_atr": 1.5,20 "take_profit_atr": 3.0,21 "initial_equity": 100000,22 "allow_shorts": True,23 "cooldown_bars": 3,24}252627# === Technical Indicators ===2829def sma(values, period):30 """Simple Moving Average."""31 if len(values) < period:32 return float("nan")33 return float(np.mean(values[-period:]))3435def ema(values, period):36 """Exponential Moving Average."""37 if len(values) < period:38 return float("nan")39 k = 2 / (period + 1)40 result = np.mean(values[:period])41 for v in values[period:]:42 result = v * k + result * (1 - k)43 return float(result)4445def atr(bars, period=14):46 """Average True Range."""47 if len(bars) < period + 1:48 return float("nan")49 trs = []50 for i in range(-period, 0):51 h, l, pc = bars[i]["high"], bars[i]["low"], bars[i-1]["close"]52 trs.append(max(h - l, abs(h - pc), abs(l - pc)))53 return float(np.mean(trs))5455def rsi(values, period=14):56 """Relative Strength Index."""57 if len(values) < period + 1:58 return float("nan")59 changes = np.diff(values[-period - 1:])60 gains = np.where(changes > 0, changes, 0)61 losses = np.where(changes < 0, -changes, 0)62 avg_gain = np.mean(gains)63 avg_loss = np.mean(losses)64 if avg_loss == 0:65 return 100.066 rs = avg_gain / avg_loss67 return float(100 - (100 / (1 + rs)))686970# === Main Strategy Logic ===7172def run(bars: list[dict], params: dict | None = None) -> dict:73 """Run the strategy on historical bar data."""74 p = {**DEFAULT_PARAMS, **(params or {})}75 closes = np.array([b["close"] for b in bars], dtype=float)76 77 signals = []78 equity = p["initial_equity"]79 position = None80 trades = []81 last_trade_bar = -p["cooldown_bars"]82 peak_equity = equity83 max_drawdown = 084 85 for i in range(1, len(bars)):86 bar = bars[i]87 88 # === Calculate Indicators ===89 fast_ma = sma(closes[:i+1], p["fast_period"])90 slow_ma = sma(closes[:i+1], p["slow_period"])91 prev_fast = sma(closes[:i], p["fast_period"])92 prev_slow = sma(closes[:i], p["slow_period"])93 current_rsi = rsi(closes[:i+1], p["rsi_period"])94 current_atr = atr(bars[:i+1], 14)95 96 if any(np.isnan(x) for x in [fast_ma, slow_ma, prev_fast, prev_slow]):97 continue98 99 # === Entry Logic ===100 if position is None and (i - last_trade_bar) >= p["cooldown_bars"]:101 102 # LONG: Golden cross + RSI not overbought103 golden_cross = prev_fast <= prev_slow and fast_ma > slow_ma104 if golden_cross and (np.isnan(current_rsi) or current_rsi < 70):105 stop = bar["close"] - p["stop_loss_atr"] * current_atr106 tp = bar["close"] + p["take_profit_atr"] * current_atr107 risk = bar["close"] - stop108 qty = int(equity * (p["risk_percent"]/100) / risk) if risk > 0 else 0109 110 if qty > 0:111 position = {112 "side": "long", "entry": bar["close"],113 "time": bar["time"], "qty": qty,114 "stop": stop, "take_profit": tp,115 }116 signals.append({117 "time": bar["time"], "type": "LONG_ENTRY",118 "price": bar["close"], "reason": "Golden Cross",119 })120 121 # SHORT: Death cross + RSI not oversold122 death_cross = prev_fast >= prev_slow and fast_ma < slow_ma123 if death_cross and p["allow_shorts"] and (np.isnan(current_rsi) or current_rsi > 30):124 stop = bar["close"] + p["stop_loss_atr"] * current_atr125 tp = bar["close"] - p["take_profit_atr"] * current_atr126 risk = stop - bar["close"]127 qty = int(equity * (p["risk_percent"]/100) / risk) if risk > 0 else 0128 129 if qty > 0:130 position = {131 "side": "short", "entry": bar["close"],132 "time": bar["time"], "qty": qty,133 "stop": stop, "take_profit": tp,134 }135 signals.append({136 "time": bar["time"], "type": "SHORT_ENTRY",137 "price": bar["close"], "reason": "Death Cross",138 })139 140 # === Exit Logic ===141 if position:142 price = bar["close"]143 exit_reason = None144 145 if position["side"] == "long":146 if price <= position["stop"]:147 exit_reason = "Stop Loss"148 elif price >= position["take_profit"]:149 exit_reason = "Take Profit"150 elif prev_fast >= prev_slow and fast_ma < slow_ma:151 exit_reason = "Signal Exit (Death Cross)"152 else:153 if price >= position["stop"]:154 exit_reason = "Stop Loss"155 elif price <= position["take_profit"]:156 exit_reason = "Take Profit"157 elif prev_fast <= prev_slow and fast_ma > slow_ma:158 exit_reason = "Signal Exit (Golden Cross)"159 160 if exit_reason:161 pnl = (price - position["entry"]) * position["qty"]162 if position["side"] == "short":163 pnl = -pnl164 165 equity += pnl166 trades.append({167 "entry_time": position["time"],168 "exit_time": bar["time"],169 "side": position["side"],170 "entry_price": position["entry"],171 "exit_price": price,172 "qty": position["qty"],173 "pnl": pnl,174 "reason": exit_reason,175 })176 signals.append({177 "time": bar["time"],178 "type": f"{position['side'].upper()}_EXIT",179 "price": price,180 "reason": exit_reason,181 })182 position = None183 last_trade_bar = i184 185 # Track drawdown186 peak_equity = max(peak_equity, equity)187 dd = (peak_equity - equity) / peak_equity * 100188 max_drawdown = max(max_drawdown, dd)189 190 # === Calculate Performance ===191 wins = [t for t in trades if t["pnl"] > 0]192 return {193 "signals": signals,194 "total_trades": len(trades),195 "win_rate": len(wins) / len(trades) * 100 if trades else 0,196 "profit_factor": (197 sum(t["pnl"] for t in wins) /198 abs(sum(t["pnl"] for t in trades if t["pnl"] <= 0))199 if any(t["pnl"] <= 0 for t in trades) else float("inf")200 ) if wins else 0,201 "total_pnl": sum(t["pnl"] for t in trades),202 "max_drawdown": max_drawdown,203 "final_equity": equity,204 "return_pct": (equity - p["initial_equity"]) / p["initial_equity"] * 100,205 }206207208if __name__ == "__main__":209 sample = [210 {"time": i, "open": 100 + np.sin(i/5) * 10,211 "high": 105 + np.sin(i/5) * 10,212 "low": 95 + np.sin(i/5) * 10,213 "close": 100 + np.sin(i/5) * 10, "volume": 1000}214 for i in range(200)215 ]216 result = run(sample)217 print(f"Trades: {result['total_trades']}")218 print(f"Win Rate: {result['win_rate']:.1f}%")219 print(f"Total PnL: {result['total_pnl']:.2f}")220 print(f"Return: {result['return_pct']:.2f}%")10. Debugging Tips
- Use print() statements to trace signal generation — output appears in Console tab
- Start with a simple strategy and add incrementally
- Verify indicator calculations manually against known-good sources ()
- Check for : ensure no is used in decisions
- Watch for survivorship bias: test on that includes delisted securities
- Log trade reasons to understand why each trade was taken
- performance across different parameter values to assess robustness
- If is suspiciously high (>80%), you likely have a bug or
Related articles
Related to Writing Strategies in the IDE
- Step 6 — Exit & Risk
Automation / Strategy Builder / Wizard Steps
Exit conditions, stop loss, take profit, staged partial exits and portfolio-level limits. The step that decides whether a losing trade costs you a percent…
- Strategy DSL — Execution Model
Coding / Strategy DSL
What the engine does on every bar: protective exits, exit signals, entries, layers and re-entries, sizing and costs, and where every statistic in the result…
- DCA Take Profit
Automation / Predefined Strategies / DCA Bot
Target = average × (1 ± TP). Moves toward the market as safety orders fill, so a small bounce closes the cycle.
- Grid Range
Charts / Drawing Tools / Measurement
Design a grid trading ladder directly on the chart. Drag out the price range the grid should trade and the tool derives every rung price, order size,…
- Arrow Marker
Charts / Drawing Tools / Annotation
A directional arrow pointing at a specific chart element. Arrow markers are used to indicate entry and exit points, highlight bars of interest, and draw…
- ATR Channel
Charts / Drawing Tools / Channels
A least-squares trend line with boundaries a multiple of average true range away on each side. A volatility channel over a range you select — it answers how…
- Built-in Indicators & Overlays
Coding / IDE / Reference
Quatick ships with a full library of pre-built indicators and overlays. You can use these out of the box, or study their implementations as reference for…
- DCA Modes
Automation / Predefined Strategies / DCA Bot
How safety-order sizes grow with depth — and how cycles open.
- DCA Stop Loss
Automation / Predefined Strategies / DCA Bot
When armed, the engine checks every tick: once price moves the stop distance against the cycle's average entry , it cancels the resting safety orders and…