Development of Custom Technical Analysis Indicators
Custom indicator is visualization of your own trading logic that doesn't exist in TradingView or MetaTrader standard set. Could be modification of known indicator, composite from several factors, or principally new metric. Let's break down how to build it properly.
Anatomy of Trading Indicator
Indicator takes OHLCV data, performs calculations and returns series of values for display. Technically—pure function of data.
from dataclasses import dataclass
import pandas as pd
import numpy as np
@dataclass
class IndicatorOutput:
values: pd.Series
signal_line: pd.Series = None
histogram: pd.Series = None
upper_band: pd.Series = None
lower_band: pd.Series = None
signals: pd.Series = None # BUY/SELL markers
Example: Hull Moving Average
HMA reacts faster to trend changes and lags less than EMA. Calculation:
def hull_ma(close: pd.Series, period: int = 20) -> pd.Series:
"""
HMA = WMA(2 * WMA(close, period/2) - WMA(close, period), sqrt(period))
"""
half_period = int(period / 2)
sqrt_period = int(np.sqrt(period))
wma_half = close.ewm(span=half_period, adjust=False).mean()
wma_full = close.ewm(span=period, adjust=False).mean()
raw_hma = 2 * wma_half - wma_full
hma = raw_hma.ewm(span=sqrt_period, adjust=False).mean()
return hma
Example: Composite Momentum Score
Combine several momentum indicators into one normalized score:
def composite_momentum_score(df: pd.DataFrame) -> pd.Series:
"""
Composite score from -100 to +100.
Positive = upward momentum, negative = downward.
"""
# RSI normalized to [-1, 1]
rsi = (df['close'].diff(1).apply(lambda x: max(x, 0)).rolling(14).mean() /
df['close'].diff(1).abs().rolling(14).mean()) * 2 - 1
# Rate of Change normalized
roc_14 = df['close'].pct_change(14)
roc_z = (roc_14 - roc_14.rolling(100).mean()) / roc_14.rolling(100).std()
roc_norm = roc_z.clip(-2, 2) / 2 # normalize to [-1, 1]
# Williams %R normalized
highest_high = df['high'].rolling(14).max()
lowest_low = df['low'].rolling(14).min()
williams_r = ((highest_high - df['close']) / (highest_high - lowest_low) - 0.5) * -2
# Weighted combination
score = (rsi * 0.35 + roc_norm * 0.40 + williams_r * 0.25) * 100
return score.round(1)
Implementation in Pine Script (TradingView)
//@version=5
indicator("Composite Momentum Score", shorttitle="CMS", overlay=false)
rsi_length = input.int(14, "RSI Length")
roc_length = input.int(14, "ROC Length")
norm_window = input.int(100, "Normalization Window")
// RSI normalized
gain = math.max(ta.change(close), 0)
loss = math.abs(math.min(ta.change(close), 0))
avg_gain = ta.rma(gain, rsi_length)
avg_loss = ta.rma(loss, rsi_length)
rs = avg_gain / avg_loss
rsi_norm = (100 / (1 + rs) - 50) / 50 * -1 // to [-1, 1], flipped
// ROC normalized
roc = (close - close[roc_length]) / close[roc_length]
roc_mean = ta.sma(roc, norm_window)
roc_std = ta.stdev(roc, norm_window)
roc_z = (roc - roc_mean) / roc_std
roc_norm = math.max(-1, math.min(1, roc_z / 2))
// Composite Score
score = (rsi_norm * 0.35 + roc_norm * 0.40) * 100
// Visualization
hline(0, color=color.gray, linewidth=1)
hline(50, color=color.new(color.green, 70), linewidth=1)
hline(-50, color=color.new(color.red, 70), linewidth=1)
score_color = score > 0 ? color.new(color.green, 30) : color.new(color.red, 30)
plot(score, "CMS", color=score_color, linewidth=2)
Example: Order Flow Imbalance Indicator
Bid/ask volume imbalance—leading indicator of price movement:
def order_flow_imbalance(df: pd.DataFrame, window: int = 10) -> pd.Series:
"""
Uses OHLCV data as order flow approximation.
More precise—with tick data, but useful even so.
"""
# Approximate buy/sell volume from candle body
candle_range = df['high'] - df['low']
candle_range = candle_range.replace(0, np.nan)
# Portion of volume proportional to close position in range
close_position = (df['close'] - df['low']) / candle_range
buy_vol_approx = df['volume'] * close_position
sell_vol_approx = df['volume'] * (1 - close_position)
# OFI = (buy_vol - sell_vol) / total_vol
ofi = (buy_vol_approx - sell_vol_approx) / df['volume']
ofi_smooth = ofi.rolling(window).mean()
return ofi_smooth * 100 # in percent
Publishing on TradingView
Publish custom indicators to TradingView Pine Script library:
- Write in Pine Script v5
- Add description and usage examples
- Publish as Public Script (free) or Protected (hidden code)
- Users add via indicator search
For commercial indicators—TradingView doesn't provide monetization directly. Alternative: sell through own site (Gumroad, Stripe) with invite-only access.
Quality custom indicator with unique logic and good documentation is an asset. Popular indicators on TradingView collect tens of thousands of followers and serve as powerful channel for attracting clients for trading products.







