Enums for Strategy Settings

Fact checked by
Mike Christensen, CFOA
March 11, 2026
How to use Pine Script v6 enums to create clean dropdown menus for strategy settings with type-safe entry modes, exit rules, and risk profiles.

Bottom Line

  • Enums replace string-based input.string() dropdowns with type-safe named values that prevent invalid selections
  • Create enum types for entry modes exit rules position sizing and risk profiles with descriptive dropdown titles
  • Use switch statements on enum values to route strategy logic cleanly without string comparison
  • Combine enums with the active input parameter to show only relevant settings for each selected mode

Every serious Pine Script strategy needs configurable settings. Entry modes, exit rules, position sizing methods, and risk profiles all benefit from dropdown menus that let users switch between options without editing code. Before Pine Script v6, the standard approach was input.string() with an options list of hardcoded strings1. It worked, but it was fragile. A single typo in a string comparison could silently break your logic, and the compiler had no way to catch it.

Pine Script v6 introduces enums, which solve this problem entirely2. Enums are custom types where every possible value is explicitly declared in advance. When you use an enum with input.enum(), TradingView generates a dropdown menu from the enum's field titles3. When you compare enum values in a switch statement, the compiler guarantees that only valid values are used. This tutorial shows you how to build professional strategy settings using enums for every major configuration category.

The Problem with Strings

Consider a typical v5 strategy that lets users choose between different entry modes. The traditional approach uses input.string() with a list of option strings:

// The old way: string-based settings (fragile)
string entryMode = input.string("EMA Cross", "Entry Mode",
     options = ["EMA Cross", "RSI Reversal", "Breakout"])

// String comparison is error-prone
if entryMode == "EMA cross"  // BUG: lowercase "c" will never match!
    strategy.entry("Long", strategy.long)

The bug in this example is subtle. The options list contains "EMA Cross" with an uppercase C, but the comparison checks for "EMA cross" with a lowercase c. Pine Script string comparisons are case-sensitive4, so this condition will never be true. The script compiles without errors, runs without warnings, and silently fails to enter trades. You might spend hours debugging before noticing the capitalization mismatch.

Enums eliminate this entire class of bugs. Since enum fields are named identifiers rather than strings, the compiler catches any reference to a field that does not exist3. If you type EntryMode.ema_cross instead of EntryMode.emaCross, you get a compile error immediately.

Defining Enum Types

An enum declaration creates a custom type with a fixed set of named fields3. Each field represents one possible value. Optionally, you can assign a string title to each field that appears in the dropdown menu2.

Here are three enums covering the core settings for a configurable strategy:

//@version=6
strategy("Enum Strategy Settings Demo", overlay = true, default_qty_type = strategy.percent_of_equity, default_qty_value = 10)

//@enum         Entry mode selection for the strategy.
//@field ema    Enters on EMA crossover signals.
//@field rsi    Enters on RSI reversal signals.
//@field brk    Enters on price breakout above/below a lookback high/low.
enum EntryMode
    ema = "EMA Crossover"
    rsi = "RSI Reversal"
    brk = "Price Breakout"

//@enum          Exit rule selection for the strategy.
//@field tp_sl   Uses fixed take-profit and stop-loss levels.
//@field trail   Uses a trailing stop based on ATR.
//@field signal  Exits on the opposite entry signal.
enum ExitRule
    tp_sl  = "Take Profit / Stop Loss"
    trail  = "Trailing Stop (ATR)"
    signal = "Signal-Based Exit"

//@enum            Risk profile controlling position sizing.
//@field conserv   Small position size with wide stops.
//@field moderate  Moderate position size with balanced stops.
//@field aggress   Large position size with tight stops.
enum RiskProfile
    conserv  = "Conservative"
    moderate = "Moderate"
    aggress  = "Aggressive"

Each enum is a distinct type. EntryMode, ExitRule, and RiskProfile are independent from each other. You cannot accidentally assign an ExitRule value to an EntryMode variable because the compiler enforces type safety4. This is a fundamental advantage over strings, where any string can be assigned to any string variable regardless of whether it makes sense.

The string titles after the equals sign are what users see in the dropdown menu. The field names before the equals sign are what you use in your code. Choose short, clear field names for your code and descriptive, human-readable titles for the dropdown.

Creating Enum Inputs

The input.enum() function generates a dropdown input from an enum's field titles3. It works similarly to input.string() with options, but returns an enum member instead of a string.

// --- Inputs ---
string GRP_STRATEGY = "Strategy Settings"
EntryMode  entryInput = input.enum(EntryMode.ema,     "Entry Mode",   group = GRP_STRATEGY)
ExitRule   exitInput  = input.enum(ExitRule.tp_sl,     "Exit Rule",    group = GRP_STRATEGY)
RiskProfile riskInput = input.enum(RiskProfile.moderate, "Risk Profile", group = GRP_STRATEGY)

The defval argument must be a member of the enum. Here we default to EMA Crossover entries, Take Profit / Stop Loss exits, and a Moderate risk profile. Users see the descriptive titles in the dropdown and can select any combination of settings.

The group parameter organizes all three inputs under a single "Strategy Settings" heading in the Settings/Inputs tab. This keeps the configuration clean and easy to find.

Conditional Settings with active

Different entry modes need different parameters. An EMA crossover needs fast and slow period lengths. An RSI reversal needs an RSI length and threshold. A breakout needs a lookback period. Showing all parameters at once clutters the input panel with irrelevant fields.

The active parameter solves this. When active is false, the input field is dimmed and the user cannot change its value3. By passing a boolean expression that depends on the entry mode enum, you can show only the inputs relevant to the selected mode.

// --- Entry Mode Parameters ---
string GRP_ENTRY = "Entry Parameters"
bool isEMA = entryInput == EntryMode.ema
bool isRSI = entryInput == EntryMode.rsi
bool isBRK = entryInput == EntryMode.brk

int emaFastInput   = input.int(9,  "Fast EMA Length",     minval = 1, group = GRP_ENTRY, active = isEMA)
int emaSlowInput   = input.int(21, "Slow EMA Length",     minval = 1, group = GRP_ENTRY, active = isEMA)
int rsiLenInput    = input.int(14, "RSI Length",           minval = 2, group = GRP_ENTRY, active = isRSI)
int rsiThreshInput = input.int(30, "RSI Oversold Level",   minval = 1, maxval = 50, group = GRP_ENTRY, active = isRSI)
int brkLookback    = input.int(20, "Breakout Lookback",    minval = 1, group = GRP_ENTRY, active = isBRK)

When the user selects "EMA Crossover," only the fast and slow EMA length fields are active. The RSI and breakout inputs are still visible but dimmed, showing the user what options exist for other modes without cluttering the interface. When the user switches to "RSI Reversal," the EMA fields dim and the RSI fields become active.

This pattern works because the active parameter accepts an input bool value3. Since the enum input returns an input-qualified value, the comparison entryInput == EntryMode.ema produces an input bool, which satisfies the active parameter's type requirement4.

// --- Exit Parameters ---
string GRP_EXIT = "Exit Parameters"
bool isTPSL   = exitInput == ExitRule.tp_sl
bool isTrail  = exitInput == ExitRule.trail

float tpPercInput  = input.float(2.0, "Take Profit %",    minval = 0.1, step = 0.5, group = GRP_EXIT, active = isTPSL)
float slPercInput  = input.float(1.0, "Stop Loss %",      minval = 0.1, step = 0.5, group = GRP_EXIT, active = isTPSL)
float atrMultInput = input.float(2.0, "ATR Trailing Mult", minval = 0.5, step = 0.5, group = GRP_EXIT, active = isTrail)
int atrLenInput    = input.int(14,    "ATR Length",         minval = 1, group = GRP_EXIT, active = isTrail)

The same pattern applies to exit parameters. Take Profit and Stop Loss percentage fields activate only when the TP/SL exit rule is selected. ATR trailing stop fields activate only when the trailing stop rule is selected. The signal-based exit does not need any extra parameters, so no inputs are associated with it.

Routing Logic with switch

With enums, the switch statement becomes the natural way to route strategy logic based on user selections4. Each branch handles one enum member, and the compiler ensures you are matching against valid values.

// --- Indicator Calculations ---
float emaFast = ta.ema(close, emaFastInput)
float emaSlow = ta.ema(close, emaSlowInput)
float rsiVal  = ta.rsi(close, rsiLenInput)
float brkHigh = ta.highest(high, brkLookback)

// --- Entry Logic ---
bool longSignal = switch entryInput
    EntryMode.ema => ta.crossover(emaFast, emaSlow)
    EntryMode.rsi => ta.crossunder(rsiVal, rsiThreshInput) and rsiVal[1] < rsiThreshInput
    EntryMode.brk => close > brkHigh[1]

// --- Risk-Adjusted Position Sizing ---
float qtyPercent = switch riskInput
    RiskProfile.conserv  => 5.0
    RiskProfile.moderate => 10.0
    RiskProfile.aggress  => 20.0

// --- Entry Execution ---
if longSignal and strategy.opentrades == 0
    strategy.entry("Long", strategy.long, qty = strategy.equity * (qtyPercent / 100.0) / close)

The switch statement on entryInput evaluates to a boolean signal based on which entry mode the user selected3. If the user chose "EMA Crossover," only the EMA crossover logic runs. If they chose "RSI Reversal," only the RSI logic runs. No string comparisons, no risk of typos, and no fallthrough bugs.

The risk profile switch assigns a position size percentage based on the selected profile. Conservative uses 5% of equity, moderate uses 10%, and aggressive uses 20%. This value feeds directly into the strategy.entry() quantity calculation.

// --- Exit Logic ---
switch exitInput
    ExitRule.tp_sl =>
        strategy.exit("TP/SL", "Long",
             profit = close * (tpPercInput / 100.0) / syminfo.mintick,
             loss = close * (slPercInput / 100.0) / syminfo.mintick)
    ExitRule.trail =>
        float atrVal = ta.atr(atrLenInput)
        float trailStop = close - atrVal * atrMultInput
        strategy.exit("Trail", "Long", stop = trailStop)
    ExitRule.signal =>
        bool exitSignal = switch entryInput
            EntryMode.ema => ta.crossunder(emaFast, emaSlow)
            EntryMode.rsi => rsiVal > (100 - rsiThreshInput)
            EntryMode.brk => close < ta.lowest(low, brkLookback)[1]
        if exitSignal
            strategy.close("Long")

// --- Plot Entry Indicators ---
plot(isEMA ? emaFast : na, "Fast EMA", color.green)
plot(isEMA ? emaSlow : na, "Slow EMA", color.red)

The exit logic switch handles all three exit rules. The TP/SL branch uses strategy.exit() with profit and loss targets converted to ticks. The trailing stop branch calculates an ATR-based stop level. The signal-based branch uses a nested switch on the entry mode to determine what constitutes an exit signal for each entry type, creating a clean bidirectional system.

Complete Strategy

Here is the full strategy combining all the enum types, conditional inputs, and routing logic.

//@version=6
strategy("Enum Strategy Settings Demo", overlay = true,
     default_qty_type = strategy.percent_of_equity, default_qty_value = 10)

// --- Enum Definitions ---
//@enum Entry mode selection.
enum EntryMode
    ema = "EMA Crossover"
    rsi = "RSI Reversal"
    brk = "Price Breakout"

//@enum Exit rule selection.
enum ExitRule
    tp_sl  = "Take Profit / Stop Loss"
    trail  = "Trailing Stop (ATR)"
    signal = "Signal-Based Exit"

//@enum Risk profile for position sizing.
enum RiskProfile
    conserv  = "Conservative"
    moderate = "Moderate"
    aggress  = "Aggressive"

// --- Strategy Inputs ---
string GRP_STRAT = "Strategy Settings"
EntryMode   entryInput = input.enum(EntryMode.ema,       "Entry Mode",   group = GRP_STRAT)
ExitRule    exitInput  = input.enum(ExitRule.tp_sl,       "Exit Rule",    group = GRP_STRAT)
RiskProfile riskInput  = input.enum(RiskProfile.moderate, "Risk Profile", group = GRP_STRAT)

// --- Conditional Entry Inputs ---
string GRP_ENTRY = "Entry Parameters"
bool isEMA = entryInput == EntryMode.ema
bool isRSI = entryInput == EntryMode.rsi
bool isBRK = entryInput == EntryMode.brk

int emaFastInput   = input.int(9,  "Fast EMA Length",    minval = 1, group = GRP_ENTRY, active = isEMA)
int emaSlowInput   = input.int(21, "Slow EMA Length",    minval = 1, group = GRP_ENTRY, active = isEMA)
int rsiLenInput    = input.int(14, "RSI Length",          minval = 2, group = GRP_ENTRY, active = isRSI)
int rsiThreshInput = input.int(30, "RSI Oversold Level",  minval = 1, maxval = 50, group = GRP_ENTRY, active = isRSI)
int brkLookback    = input.int(20, "Breakout Lookback",   minval = 1, group = GRP_ENTRY, active = isBRK)

// --- Conditional Exit Inputs ---
string GRP_EXIT = "Exit Parameters"
bool isTPSL  = exitInput == ExitRule.tp_sl
bool isTrail = exitInput == ExitRule.trail

float tpPercInput  = input.float(2.0, "Take Profit %",     minval = 0.1, step = 0.5, group = GRP_EXIT, active = isTPSL)
float slPercInput  = input.float(1.0, "Stop Loss %",       minval = 0.1, step = 0.5, group = GRP_EXIT, active = isTPSL)
float atrMultInput = input.float(2.0, "ATR Trailing Mult",  minval = 0.5, step = 0.5, group = GRP_EXIT, active = isTrail)
int atrLenInput    = input.int(14,    "ATR Length",          minval = 1, group = GRP_EXIT, active = isTrail)

// --- Calculations ---
float emaFast = ta.ema(close, emaFastInput)
float emaSlow = ta.ema(close, emaSlowInput)
float rsiVal  = ta.rsi(close, rsiLenInput)
float brkHigh = ta.highest(high, brkLookback)

// --- Entry Signal ---
bool longSignal = switch entryInput
    EntryMode.ema => ta.crossover(emaFast, emaSlow)
    EntryMode.rsi => ta.crossunder(rsiVal, rsiThreshInput) and rsiVal[1] < rsiThreshInput
    EntryMode.brk => close > brkHigh[1]

// --- Position Size ---
float qtyPercent = switch riskInput
    RiskProfile.conserv  => 5.0
    RiskProfile.moderate => 10.0
    RiskProfile.aggress  => 20.0

// --- Entry ---
if longSignal and strategy.opentrades == 0
    strategy.entry("Long", strategy.long, qty = strategy.equity * (qtyPercent / 100.0) / close)

// --- Exit ---
switch exitInput
    ExitRule.tp_sl =>
        strategy.exit("TP/SL", "Long",
             profit = close * (tpPercInput / 100.0) / syminfo.mintick,
             loss = close * (slPercInput / 100.0) / syminfo.mintick)
    ExitRule.trail =>
        float atrVal = ta.atr(atrLenInput)
        float trailStop = close - atrVal * atrMultInput
        strategy.exit("Trail", "Long", stop = trailStop)
    ExitRule.signal =>
        bool exitSignal = switch entryInput
            EntryMode.ema => ta.crossunder(emaFast, emaSlow)
            EntryMode.rsi => rsiVal > (100 - rsiThreshInput)
            EntryMode.brk => close < ta.lowest(low, brkLookback)[1]
        if exitSignal
            strategy.close("Long")

// --- Visual ---
plot(isEMA ? emaFast : na, "Fast EMA", color.green)
plot(isEMA ? emaSlow : na, "Slow EMA", color.red)

Automate Enum-Based Strategies

The real power of enum-based strategy settings shows up when you connect the strategy to live execution. With enums, you can test different configurations quickly in the Strategy Tester, find the best-performing combination of entry mode, exit rule, and risk profile, and then deploy the winning configuration to live markets.

TradersPost makes this deployment seamless. Set up a TradingView alert on your enum-configured strategy, point the webhook to TradersPost, and your strategy executes automatically on your connected broker5. When you want to test a different configuration, just change the enum inputs in TradingView and update the alert. No code changes, no webhook modifications, no downtime.

This workflow turns your Pine Script strategy into a modular trading system where the settings panel replaces hours of code editing. Build once with enums, configure through dropdowns, and let TradersPost handle execution5.

References

1 Pine Script v5 to v6 Migration Guide
2 Pine Script Release Notes
3 Pine Script v6 Language Reference
4 Pine Script User Manual
5 TradersPost - Automated Trading Platform

Ready to automate your trading? Try a free 7-day account:
Try it for free ->