pine-developer

Writes production-quality Pine Script v6 code following TradingView guidelines and best practices. Use when implementing indicators, strategies, or any Pine…

INSTALLATION
npx skills add https://github.com/traderspost/pinescript-agents --skill pine-developer
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

Pine Script Developer

Specialized in writing production-quality Pine Script v6 code for TradingView.

⚠️ CRITICAL: Pine Script Syntax Rules

BEFORE writing ANY multi-line Pine Script code, remember:

  • TERNARY OPERATORS (? :) - MUST stay on ONE line or use intermediate variables
  • Line continuation - ALL continuation lines must be indented MORE than the starting line
  • Common error: "end of line without line continuation" - caused by improper line breaks
// ❌ NEVER DO THIS:

text = condition ? "value1" :

       "value2"

// ✅ ALWAYS DO THIS:

text = condition ? "value1" : "value2"

See the "Line Wrapping Rules" section below for complete rules.

## Documentation Access

Primary documentation references:

- `/docs/pinescript-v6/quick-reference/syntax-basics.md` - Core syntax and structure

- `/docs/pinescript-v6/reference-tables/function-index.md` - Complete function reference

- `/docs/pinescript-v6/core-concepts/execution-model.md` - Understanding Pine Script execution

- `/docs/pinescript-v6/core-concepts/repainting.md` - Avoiding repainting issues

- `/docs/pinescript-v6/quick-reference/limitations.md` - Platform limits and workarounds

Load these docs as needed based on the task at hand.

## Project File Management

- When starting a new project, work with the file that has been renamed from blank.pine

- Always save work to `/projects/[project-name].pine`

- Never create new files unless specifically needed for multi-file projects

- Update the file header with accurate project information

## Core Expertise

### Pine Script v6 Mastery

- Complete understanding of Pine Script v6 syntax

- All built-in functions and their proper usage

- Variable scoping and namespaces

- Series vs simple values

- Request functions (request.security, request.security_lower_tf)

### TradingView Environment

- Platform limitations (500 bars, 500 plots, 64 drawings, etc.)

- Execution model and calculation stages

- Real-time vs historical bar states

- Alert system capabilities and constraints

- Library development standards

### Code Quality Standards

- Clean, readable code structure

- Proper error handling for na values

- Efficient calculations to minimize load time

- Appropriate use of var/varip for persistence

- Proper type declarations

## CRITICAL: Line Wrapping Rules

Pine Script has STRICT line continuation rules that MUST be followed:

1. **Indentation Rule**: Lines MUST be indented more than the first line

2. **Break at operators/commas**: Split AFTER operators or commas, not before

3. **Function arguments**: Each continuation must be indented

4. **No explicit continuation character** in Pine Script v6

### SYSTEMATIC CHECK - Review ALL of these:

- [ ] `indicator()` or `strategy()` declarations at the top

- [ ] All `plot()`, `plotshape()`, `plotchar()` functions

- [ ] All `if` statements with multiple conditions

- [ ] All variable assignments with long expressions

- [ ] All `strategy.entry()`, `strategy.exit()` calls

- [ ] All `alertcondition()` calls

- [ ] All `table.cell()` calls

- [ ] All `label.new()` and `box.new()` calls

- [ ] Any line longer than 80 characters

### CRITICAL: Ternary Operators MUST Stay on One Line

// WRONG - Will cause "end of line without line continuation" error

text = condition ?

"true value" :

"false value"

// CORRECT - Entire ternary on one line

text = condition ? "true value" : "false value"

// CORRECT - For long ternaries, assign intermediate variables

trueText = str.format("Long true value with {0}", param)

falseText = str.format("Long false value with {0}", other)

text = condition ? trueText : falseText


### CORRECT Line Wrapping:

// CORRECT - indented continuation

longCondition = ta.crossover(ema50, ema200) and

rsi < 30 and

volume > ta.sma(volume, 20)

// CORRECT - function arguments

plot(series,

title="My Plot",

color=color.blue,

linewidth=2)

// CORRECT - long calculations

result = (high - low) / 2 +

(close - open) * 1.5 +

volume / 1000000


### INCORRECT Line Wrapping (WILL CAUSE ERRORS):

// WRONG - same indentation

longCondition = ta.crossover(ema50, ema200) and

rsi < 30 and

volume > ta.sma(volume, 20)

// WRONG - not indented enough

plot(series,

title="My Plot",

color=color.blue)


## Script Structure Template

//@version=6

indicator(title="", shorttitle="", overlay=true)

// ============================================================================

// INPUTS

// ============================================================================

[Group inputs logically]

// ============================================================================

// CALCULATIONS

// ============================================================================

[Core calculations]

// ============================================================================

// CONDITIONS

// ============================================================================

[Logic conditions]

// ============================================================================

// PLOTS

// ============================================================================

[Visual outputs]

// ============================================================================

// ALERTS

// ============================================================================

[Alert conditions]


## CRITICAL: Plot Scope Restriction

**NEVER use plot() inside local scopes** - This causes "Cannot use 'plot' in local scope" error

// ❌ WRONG - These will ALL fail:

if condition

plot(value) // ERROR!

for i = 0 to 10

plot(close[i]) // ERROR!

myFunc() =>

plot(close) // ERROR!

// ✅ CORRECT - Use these patterns instead:

plot(condition ? value : na) // Conditional plotting

plot(value, color=condition ? color.blue : color.new(color.blue, 100)) // Conditional styling

// For dynamic drawing in local scopes, use:

if condition

line.new(...) // OK

label.new(...) // OK

box.new(...) // OK


## Best Practices

### Avoid Repainting

- Use barstate.isconfirmed for signals

- Proper request.security() with lookahead=barmerge.lookahead_off

- Document any intentional repainting

### Performance Optimization

- Minimize security() calls

- Cache repeated calculations

- Use switch instead of multiple ifs

- Optimize array operations

### User Experience

- Logical input grouping with group= parameter

- Helpful tooltips for complex inputs

- Sensible default values

- Clear input labels

### Error Handling

- Check for na values before operations

- Handle edge cases (first bars, division by zero)

- Graceful degradation when data unavailable

## TradingView Constraints

### Limits to Remember

- Maximum 500 bars historical reference

- Maximum 500 plot/hline/fill outputs

- Maximum 64 drawing objects (label/line/box/table)

- Maximum 40 security() calls

- Maximum 100KB compiled script size

- Tables: max 100 cells

- Arrays: max 100,000 elements

### Platform Quirks

- bar_index starts at 0

- na propagation in calculations

- Historical vs real-time calculation differences

- Strategy calculations on bar close (unless calc_on_every_tick)

- Alert firing conditions and timing

## Code Review Checklist

-  Version declaration (//@version=6)

-  Proper title and overlay setting

-  Inputs have tooltips and groups

-  No repainting issues

-  na values handled

-  Efficient calculations

-  Clear variable names

-  Comments for complex logic

-  Proper plot styling

-  Alert conditions if needed

## Example: Moving Average Cross Strategy

//@version=6

strategy("MA Cross Strategy", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=10)

// Inputs

fastLength = input.int(50, "Fast MA Length", minval=1, group="Moving Averages")

slowLength = input.int(200, "Slow MA Length", minval=1, group="Moving Averages")

maType = input.string("EMA", "MA Type", options=["SMA", "EMA", "WMA"], group="Moving Averages")

// Calculations

ma(source, length, type) =>

switch type

"SMA" => ta.sma(source, length)

"EMA" => ta.ema(source, length)

"WMA" => ta.wma(source, length)

fastMA = ma(close, fastLength, maType)

slowMA = ma(close, slowLength, maType)

// Conditions

longCondition = ta.crossover(fastMA, slowMA)

shortCondition = ta.crossunder(fastMA, slowMA)

// Strategy

if longCondition

strategy.entry("Long", strategy.long)

if shortCondition

strategy.close("Long")

// Plots

plot(fastMA, "Fast MA", color.blue, 2)

plot(slowMA, "Slow MA", color.red, 2)

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card