pine-script
$
npx mdskill add HKUDS/Vibe-Trading/pine-scriptTransform Python backtests or natural language into Pine Script v6.
- Converts Python signal_engine.py logic to TradingView Pine Script v6.
- Generates Pine Script v6 code from natural language strategy descriptions.
- Outputs strategy.pine files to artifacts/strategy.pine within the run directory.
- Returns generated code in a code block with TradingView editor usage instructions.
SKILL.md
.github/skills/pine-scriptView on GitHub ↗
---
name: pine-script
description: Convert Python backtest strategies to TradingView Pine Script v6, or generate Pine Script from natural language descriptions.
category: tool
---
## Overview
This skill enables two workflows:
1. **Export**: convert an existing `signal_engine.py` (from a completed backtest run) into a TradingView Pine Script v6 strategy
2. **Generate**: write Pine Script v6 directly from a natural-language strategy description
Output file: `artifacts/strategy.pine` (inside the run directory, or a new run directory).
## Workflow: Export from Backtest
1. `load_skill("pine-script")` — read this conversion guide
2. `read_file("config.json")` — understand instruments, dates, parameters
3. `read_file("code/signal_engine.py")` — understand the Python strategy logic
4. **Translate** the strategy to Pine Script v6 using the mapping table below
5. `write_file("artifacts/strategy.pine")` — save the output
6. Return the Pine Script in a code block with usage instructions
## Workflow: Generate from Description
1. `load_skill("pine-script")` — read this guide
2. Write Pine Script v6 directly based on the user's description
3. `write_file("artifacts/strategy.pine")` — save the output
4. Return the code with usage instructions
## Pine Script v6 Strategy Template
```pinescript
// This strategy was generated by Vibe-Trading
// Paste into TradingView Pine Editor → Add to Chart
//@version=6
strategy("Strategy Name", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100, commission_type=strategy.commission.percent, commission_value=0.1, initial_capital=1000000)
// ============================================================================
// INPUTS
// ============================================================================
// [Group inputs logically with input.int(), input.float(), input.string()]
// ============================================================================
// CALCULATIONS
// ============================================================================
// [Core indicator calculations]
// ============================================================================
// CONDITIONS
// ============================================================================
longCondition = false
shortCondition = false
exitLongCondition = false
exitShortCondition = false
// ============================================================================
// STRATEGY EXECUTION
// ============================================================================
if longCondition
strategy.entry("Long", strategy.long)
if shortCondition
strategy.entry("Short", strategy.short)
if exitLongCondition
strategy.close("Long")
if exitShortCondition
strategy.close("Short")
// ============================================================================
// PLOTS
// ============================================================================
// [Visual overlays: moving averages, bands, signals]
// ============================================================================
// ALERTS
// ============================================================================
alertcondition(longCondition, title="Long Signal", message="Long entry signal triggered")
alertcondition(shortCondition, title="Short Signal", message="Short entry signal triggered")
```
## Python → Pine Script Mapping Table
### Technical Indicators
| Python (pandas/numpy) | Pine Script v6 |
|------------------------|----------------|
| `df['close'].rolling(n).mean()` | `ta.sma(close, n)` |
| `df['close'].ewm(span=n).mean()` | `ta.ema(close, n)` |
| `ta.RSI(df['close'], n)` or manual RSI | `ta.rsi(close, n)` |
| `ta.MACD(df['close'])` | `[macdLine, signalLine, hist] = ta.macd(close, 12, 26, 9)` |
| `df['close'].rolling(n).std()` | `ta.stdev(close, n)` |
| `df['high'].rolling(n).max()` | `ta.highest(high, n)` |
| `df['low'].rolling(n).min()` | `ta.lowest(low, n)` |
| `df['close'].pct_change()` | `(close - close[1]) / close[1]` |
| `df['volume'].rolling(n).mean()` | `ta.sma(volume, n)` |
| `df['close'] > df['close'].shift(1)` | `close > close[1]` |
| Bollinger Bands (manual) | `[mid, upper, lower] = ta.bb(close, length, mult)` |
| ATR (manual or ta-lib) | `ta.atr(length)` |
| ADX | `ta.adx(high, low, close, length)` (returns adx value) |
| Stochastic | `ta.stoch(close, high, low, length, smoothK, smoothD)` |
| CCI | `ta.cci(close, length)` (Pine v6: `ta.cci(close, length)`) |
| Williams %R | `ta.wpr(length)` |
| MFI | `ta.mfi(hlc3, volume, length)` — Pine v6: `ta.mfi(close, length)` |
| OBV | `ta.obv` |
| VWAP | `ta.vwap` |
### Data References
| Python | Pine Script v6 |
|--------|----------------|
| `df['open']` | `open` |
| `df['high']` | `high` |
| `df['low']` | `low` |
| `df['close']` | `close` |
| `df['volume']` | `volume` |
| `df.index` (datetime) | `time` |
| `df['close'].shift(n)` | `close[n]` |
### Signal Logic
| Python Pattern | Pine Script v6 |
|---------------|----------------|
| `(fast > slow) & (fast.shift(1) <= slow.shift(1))` | `ta.crossover(fast, slow)` |
| `(fast < slow) & (fast.shift(1) >= slow.shift(1))` | `ta.crossunder(fast, slow)` |
| `signal.where(condition, 0)` | `condition ? value : 0` |
| `np.where(cond, val_true, val_false)` | `cond ? val_true : val_false` |
| `signal.clip(-1, 1)` | `math.max(-1, math.min(1, signal))` |
| `signal.fillna(0)` | `nz(signal, 0)` |
| `pd.isna(value)` | `na(value)` |
### Position Sizing
| Python Pattern | Pine Script v6 |
|---------------|----------------|
| Equal weight 1/N | `strategy.percent_of_equity` with `default_qty_value = 100/N` |
| Full position on signal=1.0 | `default_qty_type=strategy.percent_of_equity, default_qty_value=100` |
| Half position on signal=0.5 | Use `strategy.entry(..., qty=strategy.equity * 0.5 / close)` |
| Stop-loss | `strategy.exit("Exit", stop=entryPrice * (1 - stopPct))` |
| Take-profit | `strategy.exit("Exit", limit=entryPrice * (1 + tpPct))` |
## Pine Script v6 Syntax Rules
### Critical Rules (common errors)
1. **Version declaration must be first line**: `//@version=6`
2. **Ternary operators MUST stay on one line** or use intermediate variables:
```pinescript
// WRONG — causes "end of line without line continuation"
text = condition ? "value1" :
"value2"
// CORRECT
text = condition ? "value1" : "value2"
```
3. **Line continuation**: continuation lines must be indented MORE than the starting line
4. **No plot() in local scope** (if/for/function):
```pinescript
// WRONG
if condition
plot(value)
// CORRECT
plot(condition ? value : na)
```
5. **var vs regular variables**: use `var` for persistent state across bars, regular assignment recalculates each bar
6. **varip**: persistent even on real-time bar updates (intrabar persistence)
### Script Types
| Type | Declaration | Use Case |
|------|-------------|----------|
| `strategy` | `strategy("Name", ...)` | Backtestable with entry/exit orders |
| `indicator` | `indicator("Name", ...)` | Visual indicators, no backtesting |
| `library` | `library("Name")` | Reusable functions |
**Default output: `strategy`** (matches Vibe-Trading's backtest-first philosophy).
### Avoid Repainting
- Use `barstate.isconfirmed` for signals on confirmed bars only
- Use `request.security()` with `lookahead=barmerge.lookahead_off` (default in v6)
- Do NOT use `security()` on lower timeframes without understanding implications
### Platform Limits
- Max 500 bars lookback for `[]` operator
- Max 500 plot calls
- Max 64 `strategy.entry/exit` calls per bar
- Max 40 `request.security()` calls
## TradingView Symbol Format
When generating Pine Script, map Vibe-Trading codes to TradingView symbols:
| Vibe-Trading Format | TradingView Format | Exchange |
|---------------------|-------------------|----------|
| `000001.SZ` | `SZSE:000001` | Shenzhen |
| `600519.SH` | `SSE:600519` | Shanghai |
| `AAPL.US` | `NASDAQ:AAPL` | US (auto-detect) |
| `700.HK` | `HKEX:0700` | Hong Kong |
| `BTC-USDT` | `OKX:BTCUSDT.P` or `BINANCE:BTCUSDT` | Crypto |
**Note**: Pine Script strategies are chart-independent — the user applies them to whichever symbol they want on TradingView. Include a comment noting the original instrument.
## Usage Instructions (include in response)
After generating the Pine Script, always include:
```
How to use in TradingView:
1. Open TradingView → Pine Editor (bottom panel)
2. Click "Open" → "New blank indicator"
3. Delete all existing code
4. Paste the Pine Script above
5. Click "Add to Chart"
6. The strategy will appear on your chart with entry/exit markers
7. Open "Strategy Tester" tab to see backtest results
```
## Quality Checklist
Before outputting the Pine Script:
- [ ] `//@version=6` is the first line
- [ ] `strategy()` declaration includes commission and initial_capital matching config.json
- [ ] All indicator calculations use Pine built-in `ta.*` functions (not manual reimplementation)
- [ ] Entry/exit conditions match the Python signal logic semantically
- [ ] No `plot()` inside local scopes (if/for/function)
- [ ] Ternary operators are on single lines
- [ ] Inputs use `input.int()` / `input.float()` so users can tune parameters in TradingView UI
- [ ] Alert conditions are included for key signals
- [ ] Comment header notes the original Vibe-Trading run_id and instrument