Convert Pine Script to Python: Series, Signals and Validation
Converting Pine Script to Python requires reproducing the original calculation and its timing. Translating function names is only one part: the two implementations must also use the same bars, missing-value rules, indicator initialization and order assumptions.
Python can support a separate research or execution system. Broker access still depends on an available API, account permissions and the broker's supported order types. Data licenses, infrastructure and operating costs also remain relevant.
Define the conversion target
Start with one of three deliverables: indicator values, signal timestamps or a trading system. They require different evidence. A matching moving average does not establish matching trades, and a matching historical trade list does not establish reliable live execution.
Record the Pine version, symbol and exchange, timeframe, session, adjustment settings and all inputs. Save the original script and a reference export. Include warm-up data before the comparison period. Two feeds with the same ticker label can still differ in sessions, corporate-action adjustments or individual bars.
For a strategy, also record sizing, pyramiding, commissions, slippage, order types and calculation settings. TradingView's broker emulator has specific execution assumptions; reproduce them deliberately rather than treating a Python library's defaults as interchangeable. See TradingView's strategy documentation.
Map history, windows and missing values separately
With rows ordered from oldest to newest, Pine's previous-bar reference corresponds to a one-row lag in pandas. Use shift(1) without a freq argument when you want the preceding observed row. A missing calendar date does not itself create a new row. Supplying a frequency changes index-label behavior and is a different operation. See pandas shift.
A rolling average is a window calculation, not a replacement for every history reference. A ten-row window over complete numeric observations can represent a ten-bar simple moving average. Set min_periods explicitly. Missing observations require a separate decision: pandas counts valid values inside the selected window, which should not be assumed to reproduce every Pine function's treatment of na. See pandas rolling.
Replacing a missing value with zero, carrying the preceding value forward and dropping a row are three different changes. A numeric nz replacement with zero can often be represented by fillna(0); it is not a general instruction to forward-fill prices or remove rows. A nonzero or series replacement requires corresponding logic. Check the original expression before choosing a policy. See pandas fillna.
A reproducible SMA crossover calculation
The following Python example uses invented closes and explicit two- and three-row averages. It computes signals only; it downloads no market data and submits no orders. Install pandas in your own isolated Python environment before running it.
import pandas as pd
close = pd.Series([3., 2., 1., 2., 3., 2., 1.], name="close")
fast = close.rolling(2, min_periods=2).mean()
slow = close.rolling(3, min_periods=3).mean()
ready = fast.notna() & slow.notna() & fast.shift(1).notna() & slow.shift(1).notna()
cross_up = ready & (fast > slow) & (fast.shift(1) <= slow.shift(1))
cross_down = ready & (fast < slow) & (fast.shift(1) >= slow.shift(1))
result = pd.DataFrame({"close": close, "fast": fast, "slow": slow,
"cross_up": cross_up, "cross_down": cross_down})
assert result.index[result["cross_up"]].tolist() == [4]
assert result.index[result["cross_down"]].tolist() == [6]
print(result.to_string(index=True))
This example was executed during the October 2026 revision and both assertions passed. It is a Python calculation check, not a completed comparison against a TradingView export. The expected events are at row indices 4 and 6, with zero-based indexing. The equality cases in the previous-row comparisons matter; replacing them with strict inequalities changes crossover behavior.
The example's ready mask prevents early incomplete averages from creating signals. For a real dataset, validate timestamps, ordering, duplicates and missing closes before applying it. Do not silently fill gaps merely to make the assertions or comparison pass.
Decide where a library helps
A parser, a numerical indicator library and a backtesting engine solve different problems. PyneScript's repository describes Pine parsing and AST handling and includes examples. Read the supported features of the exact version before depending on it for execution. An AST shows program structure; it does not by itself prove equivalence with TradingView's runtime.
For a numerical library, compare the particular function you need: input source, length, smoothing recurrence, seed, warm-up and missing-value behavior. RSI, EMA and standard deviation are especially sensitive to choices hidden behind similar names. A package being widely used or offering many indicators is not evidence that its default output matches your Pine script.
Pin dependency versions in the research environment. Keep a small fixture dataset and expected outputs so a later upgrade can be checked. No general conversion-success percentage or preferred library ranking is established here.
Preserve execution semantics
Pine runs under a bar-by-bar execution model, with additional behavior on realtime updates. A vectorized Python calculation over finished bars does not automatically reproduce intrabar updates, persistent state or rollback. Stateful rules may require an explicit loop or event-driven engine. See the Pine execution model.
Multi-timeframe code needs its own alignment specification: which higher-timeframe value was available at each lower-timeframe timestamp, whether it was confirmed, and how gaps are represented. Joining a completed daily value onto earlier intraday rows can introduce future information. Read the source script's requested context before implementing the join.
Version matters too. The ta namespace belongs to the v5 migration; v4 functions were not already universally namespaced that way. Consult the v5 migration guide when identifying older calls. Version conversion and Python translation should be separate, reviewable steps.
Compare the first disagreement
Use a staged comparison:
- Match timestamps and raw OHLCV values before indicators.
- Compare intermediate values after the agreed warm-up period.
- Compare signal timestamps and direction exactly.
- Compare order creation time, order type, size and cancellation behavior.
- Compare fills, fees and positions under the same execution assumptions.
Choose a numerical tolerance appropriate to the calculation and instrument, then inspect signals near decision boundaries even when numeric differences fall within it. A universal percentage tolerance can hide a trade-changing error. Diagnose the earliest disagreement before inspecting total profit: aggregate returns can look similar despite different trades.
If you use Pineify to explain or revise the source strategy, retain the original version and review every generated change. Generating or compiling Pine code is not proof of Python equivalence. Pine Script strategy examples can help identify the source's order logic before building a separate Python implementation.
Does converting an indicator also create a Python trading bot?
No. Indicator calculations, signal rules, simulated execution and live broker integration are separate components. A port needs explicit position tracking, order handling and failure recovery before it can serve as an execution system.
Should I use forward fill for every Pine na value?
No. Forward fill carries an earlier observation into the current row. That can change the original rule. Reproduce the replacement expression, and retain missing values where the source calculation requires them.
How can I tell whether a conversion matches?
Compare identical input bars, intermediate calculations, signals and then orders. Preserve the first mismatching row and its preceding state. A syntax check, similar chart or similar total return is insufficient evidence.

