Build Your First Pine Strategy: MA Crossover Walkthrough

Ship your first strategy() with an MA Golden/Death Cross via Strategy Builder — then Editor and Strategy Tester.

📖 6 min read

📝 1,060 words

🏷️ Pine Script and TradingView

Share this article:

The smallest strategy that is not lying to you

One rule in the form maps to the specific lines it produces in the generated file.rule in the formRSI(14) crosses 30generated lines
One rule, the lines it owns. The generator is rule-based, so the same form always produces the same lines.

Most first-strategy tutorials start with strategy("My Strategy") because it is short. That version reports profits from a market that charges nothing to trade, and the habit is hard to unlearn. Four extra arguments fix it.

pine
//@version=6
strategy("First strategy", overlay = true,
    initial_capital = 10000.0,
    commission_type = strategy.commission.percent,
    commission_value = 0.1,
    slippage = 3,
    default_qty_type = strategy.fixed,
    default_qty_value = 1)

fastLen = input.int(20, "Fast MA length", minval = 1)
slowLen = input.int(50, "Slow MA length", minval = 1)

fast = ta.sma(close, fastLen)
slow = ta.sma(close, slowLen)

if ta.crossover(fast, slow)
    strategy.entry("Long", strategy.long)

if ta.crossunder(fast, slow)
    strategy.close("Long")

plot(fast, "Fast", color = color.blue)
plot(slow, "Slow", color = color.orange)

That is a complete, honest strategy. Everything below is why each line is the way it is.

Why the indicator calls sit outside the if

fast and slow are computed at the top level, not inside the conditions. This is not style. In v6 the and and or operators evaluate lazily [2], so a ta.* call placed after a condition that is false gets skipped on that bar — and an indicator that did not run on every bar no longer has a continuous history.

Hoisting them is the fix, and it is the single most common defect in AI-generated and copied Pine. The detail is in the v6 changes that do not error.

Why the lengths are input.int and not variables

ta.sma() wants its length as a simple int, and anything computed from price would be series, which is stronger in the qualifier hierarchy and therefore rejected [3]. input.int() produces an input int, which is weaker and accepted. This is the error that confuses everybody once — why Pine is hard explains what the engine actually needs.

minval = 1 is not decoration either: a length of zero or a negative length is meaningless, and constraining it in the declaration is cheaper than handling it in the logic.

Why strategy.fixed and not percent of equity

strategy.fixed trades a constant size, so the equity curve does not compound [1]. strategy.percent_of_equity compounds, which makes a mediocre edge look dramatic over a long backtest.

For a first strategy you are asking "does this idea work", not "what would this have grown to". Fixed size answers the first question and is the harder test to pass.

Why strategy.close() instead of an exit price

The exit here is the opposite signal, not a stop or a target. That keeps the first version to one decision — when to be in — rather than three. Stops and targets bring in the emulator's rule about which level a bar hit first, and that is worth understanding before you depend on it: risk management.


Six decisions you just made without knowing

A working first strategy answers about six questions you were never asked. They are all defaults, they all change your results, and none of them is visible in the code you wrote.

QuestionWhat was decided for youWhere
How many positions may be open at once?onepyramiding defaults to 1 [1]the declaration
Is margin enforced?yes since v6 — margin_long and margin_short default to 100 [2]the declaration
When does the order actually fill?on the next bar's open, not the signal bar's closethe emulator
How much history is tested?as much as your account plan allows [4]your subscription
What timeframe is this strategy for?whatever chart you happened to attach it tothe chart
Which instrument is the slippage calibrated for?the one you tested on — a tick is a different size elsewherenothing records it

The two with money attached

pyramiding defaults to 1 [1]. So when your entry condition fires again while a position is open, nothing happens. That is usually what you want, and it is also the answer to "why did my second entry not appear" — the condition was fine; the declaration silently declined.

Margin is now enforced. Since v6, margin_long and margin_short default to 100, and a strategy that needs more money than is available does not open the entry [2]. A strategy ported from v5 will therefore take fewer trades — no error, just a shorter trade list. The v6 default is the more honest of the two, and worth keeping.

The one that is not a default at all

Nothing in Pine records which instrument or timeframe a strategy was built for. The chart decides, and the chart can be changed after the fact by anyone.

This is why slippage = 3 is a claim about one instrument rather than a setting: slippage is counted in ticks, and a tick is not the same size on two symbols. Move the script to another market and that number quietly means something else — the commonest reason a sound strategy stops working, and the one least often discussed: why copied scripts fail.

Write the intended instrument and timeframe in a comment at the top. It costs one line and it is the only place that information can live.

What to change first, once it runs

In this order, because each one can make the next irrelevant:

  1. Costs, if you have not already — everything downstream is measured through them
  2. The date range, to something you did not pick because it looked good
  3. A second instrument, which tests whether you built a strategy or fitted one market
  4. The parameters, last — and if you try several, the count of how many is part of your result: why that matters

Most people do these in exactly the opposite order, which is why most first strategies look excellent and do not survive contact with a second symbol.



Tactix AI — Studio vs Guide

Tactix AI is AlfaTactix’s product assistant brand (open Tactix AI).

  • Tactix Studio turns a one-sentence strategy description into a draft across Timeframe, Signals, Filters, and Risk in the visual Strategy Builder. You review and edit every field before Code Generator writes MQL5 or Pine Script.
  • Tactix Guide explains the step you are on — what to fill, what a control means, or how to phrase a rule — without dumping untested source code.

That is form-first automation: the LLM never replaces Code Generator, and you keep plan limits and real-time validation.

Frequently Asked Questions

Use an MA Golden/Death Cross in Strategy Builder, export Pine v6, paste via Editor, run the Tester. Full UI detail: MA playbook.

No — build without coding. Optional conceptual snippet only; builder is source of truth.

H1 EURUSD, one TF, Period 10-style MA family (see playbook). Risk: Pine risk.

UNSUPPORTED on Basic — MA playbook.