
This guide teaches SEIOO as a progressive journey, from first expressions to multi-layer signal architectures that behave like leading confirmation engines rather than noisy lagging triggers.
SEIOO is a formula system that lets you express market logic as readable, composable expressions over:
Assets (crypto, forex, stocks, futures, macro)
OHLCV fields (close, open, high, low, volume, etc.)
Indicators (RSI, MACD, ADX, ATR, MFI, VWAP, etc.)
Transform functions (sma, ema, roc, zscore, logdiff, diff, etc.)
Outputs for visualization and composition (card, chart, table)
Widget references so you can build systems, not single scripts
Its purpose is to make it fast to build robust trading logic without the overhead of a general purpose language, while still enabling sophisticated multi-signal systems through composition.
SEIOO vs Pine Script
Pine is a full scripting language with control flow, loops, and stateful series semantics.
SEIOO is expression-first and intentionally constrained: you compose functions and series rather than writing programs.
SEIOO is built around reuse across widgets via @WIDGET_ID.output, enabling modular signal graphs.
SEIOO vs Python (backtrader, vectorbt, pandas, etc.)
Python is unlimited but heavy: data plumbing, lookahead mistakes, state complexity, and deployment work.
SEIOO is opinionated toward common market transformations, fast indicator composition, normalized scoring, and dashboard-native outputs.
SEIOO gives you a consistent semantic model: time series in, time series out, with clear “latest value” behavior when a scalar is required.
Think of SEIOO as a signal graph builder.
Everything is a series unless forced to be a scalar. Many functions return a time series. When used in a “latest value” context (like a card), you get the most recent computed value.
Indicators are self-contained, transforms are explicit-input.
Indicator usage: X:BTCUSD.rsi, X:BTCUSD.macd, X:BTCUSD.adx
Transform usage: sma(X:BTCUSD.close, 20), ema(X:BTCUSD.rsi, 14), normalize(X:BTCUSD.volume) This is a key design boundary: indicators “know what they consume”, transforms require you to supply the input series.
Composites are first-class.composite() and weighted() auto-normalize to a 0–100 score, turning messy heterogeneous inputs into a single decision-friendly signal surface.
Output DSL is built in. You do not just compute signals, you render them: card(), chart.*(), table.rows().cols().color()
Systems over scripts via widget referencing.@WIDGET.output is how you build modular pipelines: one widget produces a time series, another normalizes it, another builds a composite, another gates execution.
Assets use consistent prefixes:
X:BTCUSD.close
C:EURUSD.close
AAPL.close
GoldFutures.close
VIX.closeCrypto is X:, forex is C:, stocks are bare tickers, macro can be symbols like VIX or PascalCase like CoreCpi.
X:BTCUSD.rsiThis returns RSI (0–100).
Render it as a value card:
card(X:BTCUSD.rsi)card() is an output widget that displays the latest value.
A moving average requires an explicit series input:
sma(X:BTCUSD.close, 50)Now compare price vs SMA:
X:BTCUSD.close > sma(X:BTCUSD.close, 50)Comparison operators return 1 or 0.
Oversold mean reversion “setup flag”:
X:BTCUSD.rsi < 30Add a trend quality filter:
X:BTCUSD.rsi < 30 AND X:BTCUSD.adx > 20SEIOO logical operators are AND and OR.
A boolean is brittle. A score is tradable.
composite(X:BTCUSD.rsi, X:BTCUSD.macd, X:BTCUSD.adx)composite() auto-normalizes to 0–100. This is one of the most important building blocks for robust signal design.
chart.line(X:BTCUSD.rsi)
chart.area(X:BTCUSD.close)
chart.area.log(X:BTCUSD.close)Log scale is a built-in chart modifier.
At intermediate level, the big shift is this:
You stop asking “is condition true” and start asking “is the market in the right context to trust the condition”.
Example: oversold + trend strength + participation.
X:BTCUSD.rsi < 30AND X:BTCUSD.adx > 25AND X:BTCUSD.volume_surge > 150This is a classic confirmation pattern explicitly shown in the reference.
Interpretation:
RSI provides location (stretch)
ADX provides regime quality (trend or impulse environment)
Volume surge provides validity (participation)
Instead of raw momentum, use smoothed ROC:
ema(roc(X:BTCUSD.close, 10), 5)Or ROC of a smoothed price:
roc(ema(X:BTCUSD.close, 14), 10)These patterns reduce noise without turning your logic into a lagging crossover-only strategy.
A mean reversion pressure gauge:
(X:BTCUSD.close - sma(X:BTCUSD.close, 50)) / std(X:BTCUSD.close, 50)This behaves like a z-style distance metric, but stays fully interpretable.
A table is not a toy. It is how you prevent single-asset tunnel vision.
table.rows(X:BTCUSD, X:ETHUSD, X:SOLUSD).cols(
close_price,
price_change,
"Tech Score": composite(rsi, macd, adx, atr),
"BTC Corr": correlation(close, X:BTCUSD.close),
"Momentum": weighted(2:rsi, 1:macd, 1:adx)
).color("Tech Score")This is exactly the kind of view that supports discretionary trading and automated portfolio logic.
You can reference other widgets:
@WIDGET.value for a computed value
@WIDGET.signal for a 0–10 signal
@WIDGET.timeseries for chaining series operations
Example: normalize another widget’s output:
normalize(@ABC123-C.timeseries)Example: rolling correlation between two widget-generated series:
rollingCorrelation(@INFLATION_SCORE.timeseries, @CRYPTO_INDEX.timeseries, 30)Widget referencing is how SEIOO becomes a full signal architecture language.
Advanced SEIOO is about building multi-axis evidence:
Price structure axis (trend, mean deviation)
Momentum axis (rate, acceleration)
Volatility axis (risk regime)
Participation axis (volume flows)
Cross-asset axis (correlations, relative strength)
Composite axis (normalized scoring)
Create interpretable sub-scores:
Trend quality score
composite(
X:BTCUSD.adx,
(ema(X:BTCUSD.close, 12) - ema(X:BTCUSD.close, 26)) / X:BTCUSD.close * 100
)The second term is a trend magnitude normalized by price, shown as a trend-following example in the reference.
Risk regime score
normalize(zscore(X:BTCUSD.atr))ATR as volatility proxy, standardized then normalized for comparability.
Participation score
normalize(X:BTCUSD.volume_usd)Now combine them:
weighted(
0.45:composite(X:BTCUSD.rsi, X:BTCUSD.macd),
0.35:normalize(zscore(X:BTCUSD.atr)),
0.20:normalize(X:BTCUSD.volume_usd)
)This produces a single 0–100 score that is dynamically influenced by multiple market dimensions.
A common failure mode is trading BTC in isolation. Use correlation regime:
rollingCorrelation(X:BTCUSD.close, X:ETHUSD.close, 30)Then use it as a filter conceptually:
High correlation means crypto beta is unified, momentum signals may follow through.
Falling correlation can imply dispersion, idiosyncratic moves, or rotation, requiring different playbooks.
Rolling correlation is first-class in SEIOO.
SEIOO will not magically predict the future. But you can design signals that behave more leading by using:
Momentum change (diff of smoothed momentum)
Volatility compression / expansion
Mean deviation + momentum deceleration
Example: deceleration proxy using ROC change:
diff(ema(roc(X:BTCUSD.close, 10), 5))If momentum is weakening while price is extended, you are closer to a turning zone than with lagging crossovers.
Build a crypto index:
weighted(0.50:X:BTCUSD.close,0.25:X:ETHUSD.close,0.10:X:SOLUSD.close,0.05:X:XRPUSD.close,0.05:X:ADAUSD.close,0.05:X:DOGEUSD.close)Then compare an asset to the index:
X:BTCUSD.close / avg(X:BTCUSD.close, X:ETHUSD.close, X:SOLUSD.close)These ratio constructs are explicitly supported and extremely useful for relative strength systems.
A “winning” signal is rarely one condition. It is an architecture.
A strong setup usually has:
Context gate (regime, risk, market state)
Trigger (timing)
Validation (participation, cross-asset support)
Risk awareness (avoid high chaos regimes, avoid weak follow-through states)
In SEIOO, you implement this by mixing:
Booleans (hard gates)
Scores (soft evidence)
Composites (normalized multi-factor confidence)
Here is a pattern you can adapt for both dashboards and automation:
Step A: Define sub-scores
# Stretch / locationnormalize(100 - X:BTCUSD.rsi)
# Trend qualitynormalize(X:BTCUSD.adx)
# Participationnormalize(X:BTCUSD.volume_surge)
# Risk regime (lower is better, invert via 100 - normalize)100 - normalize(zscore(X:BTCUSD.atr))Step B: Combine into a confidence score
weighted(
0.35:normalize(100 - X:BTCUSD.rsi),
0.25:normalize(X:BTCUSD.adx),
0.20:normalize(X:BTCUSD.volume_surge),
0.20:(100 - normalize(zscore(X:BTCUSD.atr)))
)This yields a 0–100 “long attractiveness” score.
Step C: Add a hard safety gate For example, require participation:
X:BTCUSD.volume_surge > 120 AND
weighted(
0.35:normalize(100 - X:BTCUSD.rsi),
0.25:normalize(X:BTCUSD.adx),
0.20:normalize(X:BTCUSD.volume_surge),
0.20:(100 - normalize(zscore(X:BTCUSD.atr)))
) > 65SEIOO comparisons return 1 or 0, so this can become a strict signal if you want.
Use a table to scan multiple assets with the same architecture:
table.rows(X:BTCUSD, X:ETHUSD, X:SOLUSD, X:XRPUSD).cols(
close_price,
price_change,
"Long Score": weighted(
0.35:normalize(100 - rsi),
0.25:normalize(adx),
0.20:normalize(volume_surge),
0.20:(100 - normalize(zscore(atr)))
)
).color("Long Score")This is where SEIOO shines: strategy logic and trader interface are the same artifact.
Connect exchange and SEIOO can output:
A boolean signal (0/1)
A confidence score (0–100)
Supporting context series for logging and audits
Use card() to show the final “go/no-go” and score:
card(
weighted(
0.35:normalize(100 - X:BTCUSD.rsi),
0.25:normalize(X:BTCUSD.adx),
0.20:normalize(X:BTCUSD.volume_surge),
0.20:(100 - normalize(zscore(X:BTCUSD.atr)))
)
)Bad: “Add RSI, MACD, ADX because they are popular.”
Good: “Each component has a job.”
Location (stretch): RSI, mean deviation
Trend quality: ADX, trend magnitude
Participation: volume surge, volume USD
Risk: ATR, rolling std
Cross-asset: correlation, relative strength ratios
SEIOO’s composite() and weighted() exist specifically to make this systems approach practical.
Booleans are fine as safety gates, but scores let you:
Tune thresholds
Rank opportunities
Combine signals without “all-or-nothing” failure
If you combine raw RSI (0–100) with MACD (unbounded) without normalization, your composite will be distorted. Use:
composite() or weighted() because they auto-normalize
Or use normalize() / zscore() explicitly when building custom pipelines
Smooth the noisy parts (returns, momentum, ROC), not the entire decision chain. Examples already supported:
ema(roc(close, 12), 9)
roc(ema(close, 14), 10)
Mistake: treating X:BTCUSD.sma as a generic moving average. In SEIOO, SMA is fundamentally a transform requiring an input series: sma(X:BTCUSD.close, 50).
Mistake: stacking derivatives until NaNs dominate. Repeated derivatives like logdiff(logdiff(x)) can produce NaNs; use diff(logdiff(x)) for smoother second derivative behavior.
Mistake: mixing scales without normalization. If you do avg(rsi, macd, adx) you have a scale problem. Prefer composite() or normalize each input.
A clean SEIOO style looks like:
Define subcomponents as separate widgets (or separate expressions)
Chain with @WIDGET.timeseries
Combine with weighted()
Render with card(), chart.*(), and table.*()
This makes your logic:
Auditable
Reusable
Easy to optimize without rewriting everything
If you want to master SEIOO quickly, build in this order:
Single asset cards: RSI, close, ATR
One transform: SMA or EMA of close
One boolean: price above SMA
One composite score: composite(rsi, macd, adx)
One table scanner: multiple assets, add a score column
One chained system: produce a score in one widget, normalize or correlate it in another
One “trade architecture”: context gate + confidence score + visualization
Everything above is already supported by the SEIOO syntax and function set.