Skip to main content

str.tostring Pine Script: A Concise Guide for Traders

· 10 min read
Pineify Team
Pine Script and AI trading workflow research team

Ever stared at your TradingView chart and wondered why your indicator shows numbers like "1.23456789" instead of something readable? I've been there. After months of fixing ugly labels on my AAPL and SPY setups, I found str.tostring() is the one function that turns raw Pine Script data into text you can actually read.

str.tostring() is a built-in Pine Script function that converts numbers, booleans, and other values into formatted strings for chart labels, tables, and tooltips. It's the difference between displaying "1.23456789" and "1.2346" — small change, huge impact when you're scanning charts fast.

When You Need str.tostring()

Here's the scenario: you're building a custom indicator, and you want to show the current price, RSI value, or a signal condition right on the chart. Raw numbers look like garbage — too many decimals, no formatting, hard to parse. That's where str.tostring() comes in. It takes your data and gives you back a clean string you can drop into a label or table.

You don't need to write manual rounding logic or string concatenation tricks. One function call handles it. If you're new to writing these scripts, you can automate Pine Script coding with AI to generate boilerplate faster.

Step 1: Start with the Basic Conversion

The simplest form is just passing a value:

str.tostring(value)

Pass a number, get a string back. Works with floats, integers, and booleans. Why start here? Because you need to confirm the function works before you add formatting. I've seen too many traders jump straight to format patterns and then wonder why their labels show "NaN".

What can go wrong: if you pass na, str.tostring(na) returns "na" — not an error, but not helpful either. Always check for na before converting.

Step 2: Pick the Right Format for Your Data

This is where str.tostring() really shines. The second argument controls output format, and picking the wrong one is the most common mistake I see.

str.tostring(value, format)

The format parameter gives you full control over how your data appears on screen.

The Best Pine Script Generator

format.mintick — For Prices

format.mintick rounds to the symbol's minimum tick size. On EUR/USD that's four decimals. On ES futures it's two. I use this for every price label on my charts because it adjusts automatically per symbol — no manual precision guessing.

format.percent — For Percentages

Instead of showing "0.0523" and making you do mental math, format.percent outputs "5.23%". I rely on this for daily return displays and volatility indicators. You don't need to calculate the percentage sign yourself.

format.volume — For Large Numbers

Volume numbers get huge fast. On a high-volume day for QQQ, you might see "89,234,567". format.volume turns that into "89.23M". Your labels stay compact and your chart stays clean.

Custom Patterns — Full Control

Need exactly three decimal places for a custom oscillator? Use "#.000". Want two decimal places with leading zeros for a score display? Try "00.00". I prefer custom patterns when I'm building indicators that other traders will use, because it guarantees consistent formatting regardless of the symbol.

What can go wrong: if your pattern expects more precision than the data provides, you'll get trailing zeros. If it expects less, you'll get rounding. Test with known values first.

Step 3: Build a Real Price Label Display

Here's a script I run on my own charts. It gathers price, volume, and change into one clean label:

//@version=5
indicator("Advanced Price Display", overlay=true)

// Get current market data
current_price = close
volume_data = volume
price_change = (close - close[1]) / close[1]

// Format different types of data
clean_price = str.tostring(current_price, format.mintick)
readable_volume = str.tostring(volume_data, format.volume)
percentage_change = str.tostring(price_change, format.percent)

// Create a comprehensive label
label_text = "Price: " + clean_price + "\nVolume: " + readable_volume + "\nChange: " + percentage_change

// Display on chart
if barstate.islast
label.new(bar_index, current_price, text=label_text,
style=label.style_label_left, size=size.normal,
color=color.white, textcolor=color.black)

Why this works: by formatting each data type separately with its appropriate format, you get a label that's consistent with what traders expect. Price shows tick precision, volume abbreviates, and percentages include the % sign. I've been using this pattern for about a year, and it's cut down the time I spend squinting at raw numbers.

I haven't tested this on forex pairs with five-digit pricing — if you run into precision issues there, let me know. Different brokers sometimes use different tick sizes for the same pair.

Step 4: Handle Conditions and Booleans

Boolean values convert to "true" or "false" strings automatically. This is useful for showing whether a condition is active without writing extra logic:

bullish_condition = close > ta.sma(close, 20)
condition_text = "Bullish Trend: " + str.tostring(bullish_condition)

When the condition is true, your label reads "Bullish Trend: true". When false, "Bullish Trend: false". I prefer this over custom if/else text for one reason: it's harder to introduce bugs. You're showing the actual condition state, not a hardcoded string that can fall out of sync.

Common Gotchas

I've debugged these issues more times than I'd like to admit:

"Function Not Found" Error You need Pine Script v4 or newer. If your script starts with //@version=3, str.tostring() won't exist. Bump to //@version=5 and you're set.

Performance Calling str.tostring() on every bar in a loop will slow rendering. I only convert on the last bar or inside a display block. Keep calculations numeric, convert only when you need to show the result.

Unexpected Rounding Pine Script follows standard rounding rules. If format.mintick rounds differently than you expected, test with a small sample first. I got burned by this on a EUR/JPY strategy where the tick size caught me off guard — the pair uses three decimal places for certain brokers.

Advanced: Multi-Value Dashboard Labels

Once you're comfortable with the basics, you can combine formats into a single dashboard label:

//@version=5
indicator("Trading Dashboard", overlay=true)

// Calculate multiple indicators
rsi_value = ta.rsi(close, 14)
macd_line = ta.macd(close, 12, 26, 9)
bb_upper = ta.bb(close, 20, 2)

// Format all values appropriately
rsi_text = "RSI: " + str.tostring(rsi_value, "#.##")
macd_text = "MACD: " + str.tostring(macd_line, "#.####")
bb_text = "BB Upper: " + str.tostring(bb_upper, format.mintick)

// Combine into a comprehensive display
dashboard_text = rsi_text + "\n" + macd_text + "\n" + bb_text

// Display the dashboard
if barstate.islast
label.new(bar_index + 5, high, text=dashboard_text,
style=label.style_label_left, size=size.large,
color=color.blue, textcolor=color.white)

Each indicator gets its own format: RSI with two decimals, MACD with four, Bollinger Bands with tick precision. This keeps the dashboard readable even with multiple data types.

If you're working with Pine Script v6, the str.tostring() API hasn't changed — it works the same across versions.

For more on building complete indicator systems, check the Pine Script resources on Pineify for examples of how formatting fits into real trading workflows. Clean labels mean you spot signal issues faster.

What is str.tostring() in Pine Script?

It's a built-in Pine Script function that converts numbers, booleans, and other values into readable strings. You pass it a value and optionally a format string, and it returns text you can use in labels, tables, or tooltips.

Which Pine Script version introduced str.tostring()?

Version 4. If your script uses //@version=5 or //@version=4, you can call it. I'd use v5 for access to the latest syntax and features.

How do I display price values with the correct tick precision?

Pass format.mintick as the second argument: str.tostring(close, format.mintick). Pine Script reads the symbol's tick size automatically, so EUR/USD gets four decimals and ES futures get two. No manual precision guessing required.

Can str.tostring() convert boolean values to text?

Yes. str.tostring(close > ta.sma(close, 20)) returns "true" or "false". I use this for condition labels — it's simpler than writing if/else blocks and less likely to hide bugs.

What custom number format patterns does str.tostring() support?

You can pass patterns like "#.##" (two optional decimals), "#.000" (exactly three), or "00.00" (leading zeros). These follow Java DecimalFormat conventions. Test with sample values first — rounding behavior isn't always obvious.

How do I show large volume numbers in a compact format?

Use format.volume. str.tostring(volume, format.volume) turns 1,234,567 into "1.23M" and 1,234,567,890 into "1.23B". Keeps labels clean without losing the scale.

Does calling str.tostring() on every bar affect performance?

It can. If you're converting on every historical bar inside a loop, chart rendering will slow down. Keep your calculations numeric and only call str.tostring() on the last bar or inside the display block. I learned this the hard way on a multi-indicator strategy.