Indicator · Pine Script v6 · On the chart

TrendHorizon Suite

by Corbanistan

This indicator combines three trend tools on the price chart.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code354
Settings22inputs
LicenceMPL-2.0
Last update9 months ago27 Jan 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

354 lines of Pine Script v6 from TrendHorizon_Suite.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

TrendHorizon_Suite.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Corbanistan

//@version=6
indicator("TrendHorizon Suite", shorttitle="THS", overlay=true, max_lines_count=500, max_labels_count=500, max_bars_back=5000)

// ══════════════════════════════════════════════════════════════════════════════
// 1. INPUTS
// ══════════════════════════════════════════════════════════════════════════════

// --- Smart Signals ---
string GRP_SIGNALS = "Smart Signals"
bool   i_showCandleColors = input.bool(true, "Show Candle Colors", group=GRP_SIGNALS, tooltip="Color candles based on trend direction")
bool   i_showSignalLabels = input.bool(true, "Show Signal Labels", group=GRP_SIGNALS, tooltip="Display signal arrows on chart")
bool   i_strongOnly       = input.bool(false, "Show Only Strong Signals", group=GRP_SIGNALS, tooltip="Hides regular signals, only shows momentum-backed strong signals")
bool   i_detectRanging    = input.bool(true, "Detect Ranging Candles (Gray)", group=GRP_SIGNALS, tooltip="Highlights low momentum / consolidation periods")
int    i_timeHorizon      = input.int(3, "Time Horizon", minval=1, maxval=7, group=GRP_SIGNALS, tooltip="Sensitivity: 1=Scalp/Fast, 7=Swing/Slow")

// --- Fair Value Trail ---
string GRP_FVT = "Fair Value Trail"
bool   i_enableFVT        = input.bool(true, "Enable Fair Value Trail", group=GRP_FVT)
string i_fvtHorizon       = input.string("Short Term", "Fair Value Trail Horizon", options=["Short Term", "Long Term"], group=GRP_FVT, tooltip="Short Term hugs price closely, Long Term identifies major trends")
float  i_fvtOpacity       = input.float(85, "Cloud Opacity (%)", minval=50, maxval=95, group=GRP_FVT)
float  i_fvtDeviation     = input.float(1.5, "Deviation (ATR Multiplier)", minval=0.5, maxval=5.0, step=0.1, group=GRP_FVT, tooltip="Width of the FVT channel - adjust for different asset volatility")
bool   i_fvtCloudFill     = input.bool(true, "Fill Cloud", group=GRP_FVT, tooltip="When off, only band lines are shown")
bool   i_showFVTSignals   = input.bool(false, "Show FVT Entry Signals", group=GRP_FVT, tooltip="Displays triangles when price pulls back to the cloud")
int    i_fvtSignalCooldown = input.int(5, "Signal Cooldown (bars)", minval=1, maxval=20, group=GRP_FVT, tooltip="Minimum bars between FVT signals")

// --- Trend Spine ---
string GRP_SPINE = "Trend Spine"
bool   i_enableSpine      = input.bool(true, "Enable Trend Spine", group=GRP_SPINE)
string i_spineHorizon     = input.string("Short Term", "Trend Spine Horizon", options=["Short Term", "Long Term"], group=GRP_SPINE, tooltip="Short Term reacts quickly, Long Term confirms established trends")

// --- Dashboard ---
string GRP_DASH = "Dashboard"
bool   i_showDashboard    = input.bool(true, "Show Dashboard", group=GRP_DASH)
string i_dashLocation     = input.string("Bottom Right", "Dashboard Location", options=["Top Left", "Top Right", "Bottom Left", "Bottom Right"], group=GRP_DASH)
int    i_dashLookback     = input.int(100, "Avg Duration Lookback (bars)", minval=20, maxval=500, group=GRP_DASH, tooltip="Number of bars to calculate average signal duration")

// --- Colors (User Customization) ---
string GRP_COLORS = "Colors"
color  i_bullishColor     = input.color(#00c853, "Bullish Color", group=GRP_COLORS)
color  i_bearishColor     = input.color(#ff1744, "Bearish Color", group=GRP_COLORS)
color  i_rangingColor     = input.color(color.gray, "Ranging/Neutral Color", group=GRP_COLORS)
color  i_fvtBullColor     = input.color(#2196f3, "FVT Bullish Color", group=GRP_COLORS)
color  i_fvtBearColor     = input.color(#ff5252, "FVT Bearish Color", group=GRP_COLORS)

// ══════════════════════════════════════════════════════════════════════════════
// 2. HELPER FUNCTIONS
// ══════════════════════════════════════════════════════════════════════════════

// Time Horizon (1-7) → lookback periods: 10, 15, 22, 33, 50, 75, 100
// Using switch for simple int output
simple int signalLen = switch i_timeHorizon
    1 => 10
    2 => 15
    3 => 22
    4 => 33
    5 => 50
    6 => 75
    => 100

// FVT/Spine horizon lengths (simple int)
simple int fvtLen = i_fvtHorizon == "Short Term" ? 14 : 28
simple int spineLen = i_spineHorizon == "Short Term" ? 14 : 28

// Hull Moving Average - smooth and responsive
// Pre-calculate lengths as simple int
simple int hmaHalfLen = math.max(1, math.round(signalLen / 2))
simple int hmaSqrtLen = math.max(1, math.round(math.sqrt(signalLen)))
float hmaWma1 = ta.wma(close, hmaHalfLen)
float hmaWma2 = ta.wma(close, signalLen)
float hma = ta.wma(2 * hmaWma1 - hmaWma2, hmaSqrtLen)
float hmaPrev = hma[1]

// ══════════════════════════════════════════════════════════════════════════════
// 3. SMART SIGNALS ENGINE
// ══════════════════════════════════════════════════════════════════════════════

// --- Trend Detection (Hull MA + ATR) ---
float hmaSlope = hma - hmaPrev

// ATR for volatility measurement
float atr = ta.atr(signalLen)
float atrNorm = atr / close * 100  // Normalized ATR as percentage

// Trend state based on HMA slope
bool isBullish = hmaSlope > 0
bool isBearish = hmaSlope < 0

// --- Ranging/Consolidation Detection ---
// Use built-in ta.dmi() for optimized ADX calculation
[diPlus, diMinus, adx] = ta.dmi(signalLen, signalLen)

// Ranging when ADX is low (weak trend)
float adxThreshold = 20
bool isRanging = i_detectRanging and adx < adxThreshold

// --- Hysteresis Filter (1-bar confirmation after range exit) ---
var int barsSinceRange = 0
barsSinceRange := isRanging ? 0 : barsSinceRange + 1
bool confirmedTrend = barsSinceRange >= 2  // Require 1 bar after range ends

// --- Signal Generation ---
// Track previous trend state
var int trendState = 0  // -1 = bearish, 0 = neutral, 1 = bullish
int prevTrendState = trendState

// Update trend state
if isBullish and not isRanging
    trendState := 1
else if isBearish and not isRanging
    trendState := -1
else if isRanging
    trendState := 0

// Detect trend changes (signals) - require confirmation after range
bool bullishSignal = trendState == 1 and prevTrendState != 1 and confirmedTrend
bool bearishSignal = trendState == -1 and prevTrendState != -1 and confirmedTrend

// --- Strong Signal Detection (Momentum Acceleration) ---
float roc = ta.roc(close, hmaHalfLen)
float rocThreshold = atrNorm * 0.5  // Dynamic threshold based on volatility

bool strongBullish = bullishSignal and roc > rocThreshold
bool strongBearish = bearishSignal and roc < -rocThreshold

// Downgrade to regular signal if not strong
bool regularBullish = bullishSignal and not strongBullish
bool regularBearish = bearishSignal and not strongBearish

// --- Signal Duration Tracking ---
var int signalBarCount = 0
var int bullishSignalCount = 0
var int bearishSignalCount = 0

// Count bars since last signal change
signalBarCount := trendState == prevTrendState ? signalBarCount + 1 : 1

// Track durations for averaging (within lookback window)
if bullishSignal
    bullishSignalCount := bullishSignalCount + 1
if bearishSignal  
    bearishSignalCount := bearishSignalCount + 1

// --- Plot Signals ---
if i_showSignalLabels
    if strongBullish
        label.new(bar_index, low, "▲+", style=label.style_label_up, color=color.new(i_bullishColor, 0), textcolor=color.white, size=size.small)
    else if regularBullish and not i_strongOnly
        label.new(bar_index, low, "▲", style=label.style_label_up, color=color.new(i_bullishColor, 0), textcolor=color.white, size=size.small)
    
    if strongBearish
        label.new(bar_index, high, "▼-", style=label.style_label_down, color=color.new(i_bearishColor, 0), textcolor=color.white, size=size.small)
    else if regularBearish and not i_strongOnly
        label.new(bar_index, high, "▼", style=label.style_label_down, color=color.new(i_bearishColor, 0), textcolor=color.white, size=size.small)

// --- Candle Coloring ---
color candleColor = isRanging ? i_rangingColor : (isBullish ? i_bullishColor : i_bearishColor)
barcolor(i_showCandleColors ? candleColor : na)

// ══════════════════════════════════════════════════════════════════════════════
// 4. FAIR VALUE TRAIL (FVT)
// ══════════════════════════════════════════════════════════════════════════════

// --- VWMA-based Core Calculation ---
float fvtVwma = ta.vwma(hlc3, fvtLen)
float fvtAtr = ta.atr(fvtLen)

// --- Stepped/Trailing Logic ---
// The trail holds its level until price breaks through
var float fvtUpper = na
var float fvtLower = na
var int fvtTrend = 0  // 1 = bullish, -1 = bearish

float potentialUpper = fvtVwma + fvtAtr * i_fvtDeviation
float potentialLower = fvtVwma - fvtAtr * i_fvtDeviation

// Initialize on first bar
if na(fvtUpper)
    fvtUpper := potentialUpper
    fvtLower := potentialLower
    fvtTrend := close > fvtVwma ? 1 : -1

// Update logic with stepping behavior
if fvtTrend == 1
    // In bullish trend, lower band can only move up (trailing stop behavior)
    fvtLower := math.max(fvtLower, potentialLower)
    fvtUpper := potentialUpper
    // Flip to bearish if price closes below lower band
    if close < fvtLower
        fvtTrend := -1
        fvtUpper := potentialUpper
        fvtLower := potentialLower
else
    // In bearish trend, upper band can only move down
    fvtUpper := math.min(fvtUpper, potentialUpper)
    fvtLower := potentialLower
    // Flip to bullish if price closes above upper band
    if close > fvtUpper
        fvtTrend := 1
        fvtUpper := potentialUpper
        fvtLower := potentialLower

// --- FVT Visualization ---
color fvtCloudBullColor = color.new(i_fvtBullColor, i_fvtOpacity)
color fvtCloudBearColor = color.new(i_fvtBearColor, i_fvtOpacity)
color fvtCloudColor = fvtTrend == 1 ? fvtCloudBullColor : fvtCloudBearColor

fvtUpperPlot = plot(i_enableFVT ? fvtUpper : na, "FVT Upper", color=fvtTrend == 1 ? color.new(i_fvtBullColor, 50) : color.new(i_fvtBearColor, 50), linewidth=1)
fvtLowerPlot = plot(i_enableFVT ? fvtLower : na, "FVT Lower", color=fvtTrend == 1 ? color.new(i_fvtBullColor, 50) : color.new(i_fvtBearColor, 50), linewidth=1)
fill(fvtUpperPlot, fvtLowerPlot, color=(i_enableFVT and i_fvtCloudFill) ? fvtCloudColor : na, title="FVT Cloud")

// --- Optimal Entry Signals (Pullback Detection with Cooldown) ---
bool fvtBuyCondition = fvtTrend == 1 and low <= fvtUpper and low >= fvtLower and close > fvtLower
bool fvtSellCondition = fvtTrend == -1 and high >= fvtLower and high <= fvtUpper and close < fvtUpper

// Apply cooldown to prevent signal spam
int barsSinceLastFvtBuy = ta.barssince(fvtBuyCondition[1])
int barsSinceLastFvtSell = ta.barssince(fvtSellCondition[1])
bool fvtBuySignal = fvtBuyCondition and (na(barsSinceLastFvtBuy) or barsSinceLastFvtBuy >= i_fvtSignalCooldown)
bool fvtSellSignal = fvtSellCondition and (na(barsSinceLastFvtSell) or barsSinceLastFvtSell >= i_fvtSignalCooldown)

plotshape(i_enableFVT and i_showFVTSignals and fvtBuySignal, "FVT Buy", shape.triangleup, location.belowbar, color.new(i_fvtBullColor, 0), size=size.small)
plotshape(i_enableFVT and i_showFVTSignals and fvtSellSignal, "FVT Sell", shape.triangledown, location.abovebar, color.new(i_fvtBearColor, 0), size=size.small)


// ══════════════════════════════════════════════════════════════════════════════
// 5. TREND SPINE
// ══════════════════════════════════════════════════════════════════════════════

// SuperTrend-based calculation
float spineAtr = ta.atr(spineLen)
float spineMultiplier = 2.0
float spineHl2 = hl2

float spineUpperBand = spineHl2 + spineMultiplier * spineAtr
float spineLowerBand = spineHl2 - spineMultiplier * spineAtr

// Stepped SuperTrend logic
var float spineFinalUpper = na
var float spineFinalLower = na
var int spineTrend = 0  // 1 = bullish, -1 = bearish, 0 = neutral/consolidation

// Initialize
if na(spineFinalUpper)
    spineFinalUpper := spineUpperBand
    spineFinalLower := spineLowerBand

// Upper band can only move down (resistance)
spineFinalUpper := close[1] < spineFinalUpper ? math.min(spineFinalUpper, spineUpperBand) : spineUpperBand

// Lower band can only move up (support)
spineFinalLower := close[1] > spineFinalLower ? math.max(spineFinalLower, spineLowerBand) : spineLowerBand

// Determine trend
int prevSpineTrend = spineTrend
if close > spineFinalUpper
    spineTrend := 1
else if close < spineFinalLower
    spineTrend := -1

// Detect neutral/consolidation using ADX
bool spineNeutral = adx < (adxThreshold - 5)  // Slightly lower threshold for Spine

// The Spine line value
float spineValue = spineTrend == 1 ? spineFinalLower : spineFinalUpper

// --- Spine Visualization ---
color spineColor = spineNeutral ? i_rangingColor : (spineTrend == 1 ? i_bullishColor : i_bearishColor)
plot(i_enableSpine ? spineValue : na, "Trend Spine", color=spineColor, linewidth=3, style=plot.style_linebr)

// ══════════════════════════════════════════════════════════════════════════════
// 6. STATUS DASHBOARD
// ══════════════════════════════════════════════════════════════════════════════

// Calculate average signal durations
var array<int> bullishDurations = array.new_int(0)
var array<int> bearishDurations = array.new_int(0)
var int lastSignalDuration = 0

// Track signal durations
if bullishSignal and lastSignalDuration > 0
    array.push(bearishDurations, lastSignalDuration)
    // Keep array size manageable
    if array.size(bearishDurations) > 50
        array.shift(bearishDurations)
    lastSignalDuration := 0
else if bearishSignal and lastSignalDuration > 0
    array.push(bullishDurations, lastSignalDuration)
    if array.size(bullishDurations) > 50
        array.shift(bullishDurations)
    lastSignalDuration := 0

lastSignalDuration := lastSignalDuration + 1

// Calculate averages
float avgBullishDuration = array.size(bullishDurations) > 0 ? array.avg(bullishDurations) : 0
float avgBearishDuration = array.size(bearishDurations) > 0 ? array.avg(bearishDurations) : 0
float avgDuration = (avgBullishDuration + avgBearishDuration) / 2

// Dashboard position and table creation
var table dashboard = table.new(
    i_dashLocation == "Top Left" ? position.top_left : 
    i_dashLocation == "Top Right" ? position.top_right : 
    i_dashLocation == "Bottom Left" ? position.bottom_left : 
    position.bottom_right,
    4, 4, bgcolor=color.new(#1a1a2e, 10), border_width=1, border_color=color.new(#ffffff, 80))

if i_showDashboard and barstate.islast
    // Header row
    table.cell(dashboard, 0, 0, "Component", text_color=color.white, text_size=size.small, bgcolor=color.new(#2d2d44, 0))
    table.cell(dashboard, 1, 0, "Signal", text_color=color.white, text_size=size.small, bgcolor=color.new(#2d2d44, 0))
    table.cell(dashboard, 2, 0, "Avg Duration", text_color=color.white, text_size=size.small, bgcolor=color.new(#2d2d44, 0))
    table.cell(dashboard, 3, 0, "Current", text_color=color.white, text_size=size.small, bgcolor=color.new(#2d2d44, 0))
    
    // Smart Signals row
    string ssSignal = isRanging ? "—" : (trendState == 1 ? "▲" : "▼")
    color ssColor = isRanging ? i_rangingColor : (trendState == 1 ? i_bullishColor : i_bearishColor)
    table.cell(dashboard, 0, 1, "Smart Signals", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 1, 1, ssSignal, text_color=ssColor, text_size=size.small)
    table.cell(dashboard, 2, 1, str.tostring(avgDuration, "#.#") + " bars", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 3, 1, str.tostring(signalBarCount) + " bars", text_color=color.white, text_size=size.small)
    
    // Fair Value Trail row
    string fvtSignal = fvtTrend == 1 ? "Bullish" : "Bearish"
    color fvtSigColor = fvtTrend == 1 ? i_fvtBullColor : i_fvtBearColor
    table.cell(dashboard, 0, 2, "Fair Value Trail", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 1, 2, fvtSignal, text_color=fvtSigColor, text_size=size.small)
    table.cell(dashboard, 2, 2, "—", text_color=i_rangingColor, text_size=size.small)
    table.cell(dashboard, 3, 2, "—", text_color=i_rangingColor, text_size=size.small)
    
    // Trend Spine row
    string spineSignal = spineNeutral ? "Neutral" : (spineTrend == 1 ? "Bullish" : "Bearish")
    color spineSigColor = spineNeutral ? i_rangingColor : (spineTrend == 1 ? i_bullishColor : i_bearishColor)
    table.cell(dashboard, 0, 3, "Trend Spine", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 1, 3, spineSignal, text_color=spineSigColor, text_size=size.small)
    table.cell(dashboard, 2, 3, "—", text_color=i_rangingColor, text_size=size.small)
    table.cell(dashboard, 3, 3, "—", text_color=i_rangingColor, text_size=size.small)

// ══════════════════════════════════════════════════════════════════════════════
// 7. ALERTS
// ══════════════════════════════════════════════════════════════════════════════

alertcondition(regularBullish, "Bullish Signal", "THS: Bullish trend signal detected")
alertcondition(regularBearish, "Bearish Signal", "THS: Bearish trend signal detected")
alertcondition(strongBullish, "Strong Bullish Signal", "THS: Strong bullish signal with momentum acceleration")
alertcondition(strongBearish, "Strong Bearish Signal", "THS: Strong bearish signal with momentum acceleration")
alertcondition(bullishSignal, "Any Bullish Signal", "THS: Any bullish signal (regular or strong)")
alertcondition(bearishSignal, "Any Bearish Signal", "THS: Any bearish signal (regular or strong)")
alertcondition(fvtBuySignal, "FVT Buy Entry", "THS: Optimal buy entry - pullback to Fair Value Trail")
alertcondition(fvtSellSignal, "FVT Sell Entry", "THS: Optimal sell entry - pullback to Fair Value Trail")
alertcondition(spineTrend == 1 and prevSpineTrend != 1, "Trend Spine Bullish", "THS: Trend Spine turned bullish")
alertcondition(spineTrend == -1 and prevSpineTrend != -1, "Trend Spine Bearish", "THS: Trend Spine turned bearish")

Overview

Smart signals colour candles and place labels by trend direction, with a time horizon setting from fast to slow and an option to show only strong signals. A fair value trail draws an ATR-based cloud that can mark pullback entries. A trend spine follows a filtered trend line. A dashboard table reports trend status and average signal duration, and ranging candles are shown in grey.

Details

Published by

Corbanistan on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Trend, Volatility & bands, Screeners & dashboards

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Show Candle Colorsbooltruei_showCandleColors
Show Signal Labelsbooltruei_showSignalLabels
Show Only Strong Signalsboolfalsei_strongOnly
Detect Ranging Candles (Gray)booltruei_detectRanging
Time Horizonint3i_timeHorizon
Enable Fair Value Trailbooltruei_enableFVT
Fair Value Trail HorizonstringShort Termi_fvtHorizon
Cloud Opacity (%)float85i_fvtOpacity
Deviation (ATR Multiplier)float1.5i_fvtDeviation
Fill Cloudbooltruei_fvtCloudFill
Show FVT Entry Signalsboolfalsei_showFVTSignals
Signal Cooldown (bars)int5i_fvtSignalCooldown
Show all 22 settings
SettingTypeDefaultVariable
Enable Trend Spinebooltruei_enableSpine
Trend Spine HorizonstringShort Termi_spineHorizon
Show Dashboardbooltruei_showDashboard
Dashboard LocationstringBottom Righti_dashLocation
Avg Duration Lookback (bars)int100i_dashLookback
Bullish Colorcolor#00c853i_bullishColor
Bearish Colorcolor#ff1744i_bearishColor
Ranging/Neutral Colorcolorcolor.grayi_rangingColor
FVT Bullish Colorcolor#2196f3i_fvtBullColor
FVT Bearish Colorcolor#ff5252i_fvtBearColor
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

92 stars

This indicator classifies the market into gamma regime types (negative gamma trend, positive gamma mean-reversion, or transition) by calculating a composite score based on daily ATR ratios, session VWAP crossover frequencies, and opening range displacement. It

Indicator v5 Volatility & bandsTrend
Updated 2 months ago · Custom licence GitHub repository of Moon Dev GEX Regime & Trap
46 stars

This indicator uses a Hidden Markov Model (HMM) framework combined with an adaptive dual moving average engine and Directional Movement Index (DMI) features to identify market regimes (bullish, bearish, or ranging/chop). It calculates regime probabilities on e

Indicator v5 AI / machine learningTrend
Updated 7 weeks ago · None stated GitHub repository of HMM Enhanced: Regime Probability
43 stars

This indicator calculates standardized trend strength and historical volatility to estimate position sizes. It computes the differences between three user-defined EMAs standardized by price using MACD logic, while historical volatility is derived from the stan

Indicator v5 TrendVolatility & bands
Updated 1 year ago · None stated GitHub repository of Trend Strength & Volatility v2

This overlay indicator plots a SuperTrend-style band on the price chart using ATR length, a selectable source, and a base factor. Optional settings enable an adaptive factor driven by rate of change, a volatility filter, K-means clustering, and an LSTM layer w

Indicator v6 TrendVolatility & bands
Updated 4 months ago · MIT GitHub repository of ML SuperTrend (Ultimate) - Auto-Optimized AI with LSTM

Adaptive Fair Value Cloud calculates a volatility-adaptive fair value channel around a selectable centerline, such as KAMA, EMA, linear regression, or VWMA. It blends ATR and mean deviation with volatility percentiles and trend sensors, including ADX, Efficien

Indicator v6 Volatility & bandsTrend
Updated 2 days ago · MIT GitHub repository of Adaptive Fair Value Cloud [WavesUnchained]

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers