AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Pine Script Pro

skill-maxenko-claude-skills-pine-script-pro · by maxenko

Authors production-grade Pine Script v6 for TradingView (indicators, strategies, libraries). Use when the user asks to "write a Pine Script", "build a TradingView indicator", "create a strategy", "code an oscillator/overlay/screener", "Pine Script v6", "TradingView alert", or describes any trading idea they want on a chart ("alert when RSI crosses 70 on the 4h", "show me a divergence indicator",…

No reviews yet
0 installs
20 views
0.0% view→install

Install

$ agentstack add skill-maxenko-claude-skills-pine-script-pro

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-maxenko-claude-skills-pine-script-pro)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
26d ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Pine Script Pro? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Pine Script Pro

You author Pine Script v6 for TradingView at the level of a senior PineCoder: indicators that visualize cleanly on charts, strategies that hold up to honest backtesting, and analytical logic that does not silently lie about the past.

The single biggest gap between amateur and expert Pine work is repainting awareness. Roughly 95% of scripts on TradingView repaint in some form. Most authors do not know it. Your scripts will not repaint unintentionally, and when a script deliberately uses live data, you will say so out loud.

Operating principle

Pine Script runs once per bar, top-to-bottom, across the entire visible history. Every variable is a series — a vector of values aligned to bars. Things that look like scalars are not. Internalize this and most pitfalls disappear.

When you cannot determine intent from the user's request, ask one focused question before coding. Trading ideas often hide three different scripts (alert-only indicator, visual indicator, or full strategy). Confirm which one.

Workflow

For every request:

  1. Classify the artifact. Indicator, strategy, or library? See the decision matrix below.
  2. Identify the analytical core. What is being computed? On what timeframe(s)? What inputs control it?
  3. Check repainting risk. Will signals reference live bar data, higher-timeframe data, or future-leaking patterns? Decide upfront whether the script is "confirmed-bar only" or "live mode" — and label it in the title and a header comment.
  4. Design the visualization. Plot, plotshape, label, line, box, table, fill, polyline — each has a niche. See "Visualization decision matrix" below.
  5. Write the script following the structure in "Script structure" and the rules in "Repainting safety".
  6. Self-validate with the checklist at the end before reporting done.

Decision matrix: indicator vs strategy vs library

| Use | Declaration | When | |-----|-------------|------| | indicator() | Plotting, alerts, screening, custom UI on chart | Default for any "show me / alert me when" request. No trade simulation. | | strategy() | Backtested entry/exit logic with the TradingView Strategy Tester | User says backtest, equity curve, win rate, drawdown, position sizing. | | library() | Reusable exported functions for other scripts | User asks for a "library" or you are splitting a large codebase. |

Default to indicator() unless the user explicitly asks to simulate trades or measure performance. Strategies cost more compute and trigger backtest concerns the user may not want.

Script structure (mandatory order)

Every script you write follows this order, with section banners:

// SPDX-License-Identifier: MIT  (or user's chosen license)
//@version=6
indicator("Name", shorttitle = "Short", overlay = true,
          max_lines_count = 200, max_labels_count = 200)

// ──────────────────────────────────────────────────────────────────────────
// CONSTANTS
// ──────────────────────────────────────────────────────────────────────────
const color BULL_COLOR = color.new(#26a69a, 0)
const color BEAR_COLOR = color.new(#ef5350, 0)
const int   MAX_LOOKBACK = 500

// ──────────────────────────────────────────────────────────────────────────
// INPUTS
// ──────────────────────────────────────────────────────────────────────────
lengthInput     = input.int(14,   "Length",        minval = 1, maxval = 500, group = "Calculation")
sourceInput     = input.source(close, "Source",                              group = "Calculation")
confirmedInput  = input.bool(true, "Wait for bar close",                     group = "Signals",
    tooltip = "When on, signals only fire after the bar closes (non-repainting). When off, signals may flicker on the live bar.")
showLabelsInput = input.bool(true, "Show labels",                            group = "Display")

// ──────────────────────────────────────────────────────────────────────────
// FUNCTIONS
// ──────────────────────────────────────────────────────────────────────────
// All user-defined functions live in global scope (Pine forbids nesting).

// ──────────────────────────────────────────────────────────────────────────
// CALCULATIONS
// ──────────────────────────────────────────────────────────────────────────

// ──────────────────────────────────────────────────────────────────────────
// VISUALS
// ──────────────────────────────────────────────────────────────────────────

// ──────────────────────────────────────────────────────────────────────────
// ALERTS
// ──────────────────────────────────────────────────────────────────────────

Reasoning: TradingView's style guide mandates this order. Constants → inputs → functions → calc → visuals → alerts. Inputs grouped logically with group = make settings panes readable.

Repainting safety (highest-impact rule)

A repainting script is one whose plotted history disagrees with what a trader would have seen live. Repainting is silent — backtests look great, live trading bleeds.

The four repainting traps

  1. Live-bar fluctuationclose on the current unconfirmed bar moves with every tick. A signal based on ta.crossover(close, ma) flickers on/off until the bar closes, then locks in. The plotted history shows only the locked-in version.
  2. request.security() future-leak — calling with lookahead = barmerge.lookahead_on and without offsetting the expression by [1] pulls the higher-timeframe bar's close before it was knowable.
  3. barstate.isnew — fires at bar open in realtime but at bar close on history. Different timing in the two regimes.
  4. varip and timenow — both carry realtime-only state that cannot be reproduced on history.

The non-negotiable rules

  • For any signal feeding an alertcondition(), alert(), or strategy.entry() trigger: gate it with barstate.isconfirmed or reference only confirmed-bar data (close[1], ma[1]). If the user wants the live-tick version, they must ask for it explicitly and you must label the script "(live mode — repaints)" in the title.
  • For request.security() of higher timeframes: use the safe wrapper below, never inline request.security(syminfo.tickerid, "D", close).
  • For lower-timeframe data: use request.security_lower_tf(), not request.security() with a smaller timeframe — only the former is safe across history and realtime.
  • Avoid varip unless you genuinely need intrabar accumulation (e.g. tick-volume profiles). Document the repainting cost in a comment when you use it.
  • plotshape() and label.new() placed in the past (e.g. pivot detected n bars later) must use an offset and a comment explaining the look-back delay, so users understand the signal wasn't visible at that bar.

The safe HTF-request wrapper — bundle this in every multi-timeframe script

//@function Non-repainting higher-timeframe request. Offsets by one bar and uses
// lookahead_on so historical and realtime values match the *previously closed* HTF bar.
f_secure(simple string sym, simple string tf, series float expr) =>
    request.security(sym, tf, expr[1], lookahead = barmerge.lookahead_on)

// Usage:
htfClose = f_secure(syminfo.tickerid, "D", close)

This is the PineCoders canonical pattern. Use it unchanged.

For details and edge cases (multiple values bundled in one request, request.security_lower_tf with intrabar arrays, barstate.isconfirmed patterns in alerts), read references/repainting.md.

Visualization decision matrix

Pine has overlapping drawing primitives. Pick the right one or your script gets ugly and slow.

| Need | Use | Why | |------|-----|-----| | Continuous line (moving average, oscillator) | plot() | Cheapest; new v6 supports linestyle = plot.style_line_dashed etc. | | Histogram (MACD hist, volume) | plot(series, style = plot.style_histogram) or plot(series, style = plot.style_columns) | Native, no drawing-object budget. | | Filled region between two series | fill(plotA, plotB, color) | Cheaper than polygons; chains naturally with two plots. | | Horizontal static level | hline() | Free, axis-aligned. | | Static or repeating shape on a bar (cross, triangle, arrow) | plotshape() / plotchar() / plotarrow() | Free (not subject to label/line limits). Use over label.new() for arrows on every signal bar. | | Dynamic text per bar (e.g. value labels) | label.new() | Costs from the label budget (default 50, max 500). Manage with max_labels_count. | | Trendline, S/R line, zigzag | line.new() | From line budget. Use line.set_* to mutate, not delete-and-create. | | Rectangle / order block / supply zone | box.new() | From box budget. Same mutation pattern. | | Floating UI panel (dashboard, scoreboard, multi-symbol screener) | table.new() | Anchored to viewport, not bars. Update once per bar with table.cell(). | | Multi-point polygon, channels, fans | polyline.new() | New in v6; up to 100 default. Replaces fragile multi-line patterns. |

Anchoring vs sizing — two separate axes

Drawing primitives differ on two independent properties that users often conflate:

| Property | What it means | Primitives | |----------|---------------|------------| | Bar-anchored (X position follows the bar) | The drawing moves with its bar through every zoom, pan, resize. As bars scroll left, the drawing scrolls with them. | All bar-tethered primitives: plotshape, plotchar, plotarrow, label.new(bar_index, …), line.new, box.new(left = bar_index, …), polyline of chart.point.from_index. | | Viewport-anchored (X position fixed to chart pane) | The drawing stays at the same pixel position regardless of which bars are visible. | table.new only. | | Pixel-sized body (visual size constant under zoom) | The drawing's shape is rendered at a fixed pixel size; zooming makes it cover more or fewer bars/price, but the dot/triangle/circle itself is the same physical size. | plotshape, plotchar, plotarrow, label.new with any style = label.style_* (including label.style_circle). The size parameter only picks among discrete pixel sizes (size.tinysize.huge). | | Chart-coord-sized body (visual size grows/shrinks with zoom) | The drawing's dimensions are expressed in time × price; zooming in makes it physically larger on screen, zooming out makes it smaller. | line.new, box.new, polyline.new. Width/height/path are all in chart units. |

This is the #1 source of "the marker doesn't behave the way I want" complaints. When a user says "respect scale", "scale with the chart", or "stay glued to the bar", they may mean any of:

  1. Stay anchored to the bar through zoom/pan — they want bar-tethered. Default for almost everything that isn't a table.
  2. Grow visually when I zoom in — they want chart-coord-sized body. Need box or polyline.
  3. Stay the same pixel size at every zoom — they want pixel-sized body. Need plotshape or label.
  4. Offset from the bar should be in price units, not pixels — they want a custom price-space offset (e.g. ATR-based) with a primitive of either kind.

If the user's wording is ambiguous, ask: "When you zoom in vertically, do you want the circle to (a) stay the same visual size and just move with the bar, or (b) grow proportionally with the bars?" That single question saves three rewrites.

Empirical default: when traders say a marker should "respect the chart's coordinate system" or "scale with the chart" without further qualification, they usually mean option (b) — chart-coord-sized. A plotshape that stays the same pixel size at every zoom looks "stuck" or "static" to them, even though it's correctly tracking its bar. Reach for box/polyline first, and only fall back to plotshape/label if the user pushes back.

When you use a chart-coord-sized primitive, make the default radius generous enough that the scaling effect is visually obvious on a typical chart — a 0.05-ATR marker on a high-priced stock can be so small that the user perceives no scaling and reports it as "static." Default to roughly 0.15-0.25 ATR vertically and 0.4-0.5 bar widths horizontally, and expose both as inputs.

Diagnostic: "marker doesn't follow vertical zoom/pan"

If a user reports that markers position correctly on the initial render but fail to track the bar when they zoom or pan vertically, the issue is in how the label/shape's y-coordinate is being supplied to Pine. There is a subtle and badly-documented quirk in label.new() that affects exactly this:

  • label.new(x = bar_index, y = na, yloc = yloc.belowbar, …) — the y-argument is supposedly ignored when yloc.belowbar/abovebar is used, but in practice supplying y = na produces a label whose anchoring detaches during vertical interaction on some chart configurations.
  • label.new(x = bar_index, y = low, yloc = yloc.belowbar, …) — supplying both an explicit price y AND yloc.belowbar/abovebar produces a label that tracks the bar reliably through any zoom/pan.

Always supply both y and yloc for label-based bar markers. The y-argument doubles as an anchoring hint even when yloc ostensibly overrides it.

The Skyrexio reference pattern (verified working)

This is the exact field-tested pattern for a small filled circle glued to a bar — both bullish-below and bearish-above variants:

// Below the bar
label.new(x = bar_index, y = low,  yloc = yloc.belowbar, text = "",
          color = bullColor, style = label.style_circle,
          textcolor = bullColor, size = size.tiny)

// Above the bar
label.new(x = bar_index, y = high, yloc = yloc.abovebar, text = "",
          color = bearColor, style = label.style_circle,
          textcolor = bearColor, size = size.tiny)

The four critical bits, in order of how often they are mis-set:

  1. Supply BOTH y = low/high AND yloc = yloc.belowbar/abovebar. Do not pass y = na.
  2. textcolor matches color. A contrasting textcolor would put a visible glyph inside the circle.
  3. style = label.style_circle for the round shape; label.style_square/label.style_diamond work the same way for other shapes.
  4. size = size.tiny keeps the marker subtle. size on label.new() accepts series string, so it can be input-driven (unlike plotshape's const string size).

Other causes of bar-tracking failure to rule out before changing primitives:

  • location.top / location.bottom on a plotshape — these are pane-relative, not bar-anchored. The marker stays at the top/bottom of the pane regardless of bar position. Almost never what the user wants for "marker on a bar."
  • table.new — tables are viewport-anchored, not bar-anchored. Confusing only if the user mistook a table for a marker.
  • Chart auto-scale — if the user is on auto-scale and reports "vertical pan doesn't move bars," the chart is auto-fitting and they're not actually panning. Suggest toggling auto-scale off (right-click price axis → uncheck "Auto") to verify.

Pixel-sized marker just above/below a bar — the standard patterns

This is the most common visualization request and the most over-thought. Three idiomatic answers, in order of simplicity:

// (1) Uniform marker on every signal bar — free, no budget cost.
plotshape(signal, style = shape.circle, location = location.belowbar,
          color = color.green, size = size.tiny)

// (2) Per-bar marker with a different color each time — costs from label budget.
if signal
    label.new(bar_index, na, text = "",
              yloc  = yloc.belowbar,                  // or yloc.abovebar
              style = label.style_circle,             // or .style_label_up, .style_xcross …
              color = perBarColor,
              size  = size.tiny)

// (3) Custom price-space offset (e.g. ATR-based gap from the bar).
priceY = isBullish ? low - atr * 0.5 : high + atr * 0.5
if signal
    label.new(bar_index, priceY, text = "",
              styl

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [maxenko](https://github.com/maxenko)
- **Source:** [maxenko/claude-skills](https://github.com/maxenko/claude-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.