custom-indicator
Create a custom technical indicator using vectorized NumPy on top of openalgo's Rust-core ta primitives. Generates production-grade, O(n) indicator functions with charting and benchmarking.
How do I install this agent skill?
npx skills add https://github.com/marketcalls/openalgo-indicator-skills --skill custom-indicatorIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill provides guidelines and templates for creating custom technical financial indicators using NumPy and openalgo. No security issues were identified during analysis.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
- Runlayerpass
1 file scanned · No issues
- ZeroLeakspass
Score: 93/100 · 2 sections analyzed
What does this agent skill do?
Create a custom technical indicator by composing openalgo's Rust-core ta primitives with vectorized NumPy.
Arguments
$0= indicator name (e.g., zscore, squeeze, vwap-bands, custom-rsi, mean-reversion). Required.
If no arguments, ask the user what indicator they want to build.
Instructions
- Read the indicator-expert rules, especially:
rules/custom-indicators.md— NumPy + ta-primitive patterns and templatesrules/performance.md— Rust core performance, O(n) guarantees, benchmarkingrules/indicator-catalog.md— Check if indicator already exists in openalgo.ta
- Check first: If the indicator already exists in
openalgo.ta(100+ indicators), tell the user and show the existing API - Create
custom_indicators/{indicator_name}/directory (on-demand) - Create
{indicator_name}.pywith:
File Structure
"""
{Indicator Name} — Custom Indicator
Description: {what it measures}
Category: {trend/momentum/volatility/volume/oscillator}
"""
import numpy as np
import pandas as pd
from openalgo import ta
# --- Core Computation (vectorized NumPy on ta primitives) ---
def _compute_{name}(arr: np.ndarray, period: int) -> np.ndarray:
"""Vectorized core computation built on Rust-core primitives."""
n = len(arr)
result = np.full(n, np.nan)
# Compose from ta primitives (they run in Rust):
# mean = ta.sma(arr, period); std = ta.stdev(arr, period); ...
# then combine with NumPy array math (np.where, masks)
return result
# --- Public API ---
def {name}(data, period=20):
"""
{Indicator Name}
Args:
data: Close prices (numpy array, pandas Series, or list)
period: Lookback period (default: 20)
Returns:
Same type as input with indicator values
"""
if isinstance(data, pd.Series):
idx = data.index
result = _compute_{name}(data.values.astype(np.float64), period)
return pd.Series(result, index=idx, name="{Name}({period})")
arr = np.asarray(data, dtype=np.float64)
return _compute_{name}(arr, period)
- Create
chart.pyfor visualization:
"""Chart the custom indicator with Plotly."""
import os
from pathlib import Path
from datetime import datetime, timedelta
from dotenv import find_dotenv, load_dotenv
from openalgo import api, ta
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from {indicator_name} import {name}
# ... fetch data, compute indicator, create chart ...
- Create
benchmark.pyfor performance testing (no warmup needed — the Rust core runs at full speed from the first call):
"""Benchmark the custom indicator."""
import numpy as np
import time
from {indicator_name} import {name}
for size in [10_000, 100_000, 500_000]:
data = np.random.randn(size).cumsum() + 1000
t0 = time.perf_counter()
_ = {name}(data, 20)
elapsed = (time.perf_counter() - t0) * 1000
print(f"{size:>10,} bars: {elapsed:>8.2f}ms")
NumPy Rules (CRITICAL)
MUST DO
- Compose from
taprimitives wherever possible — they run in the Rust core np.full(n, np.nan)to initialize output arrays- Vectorize with array expressions,
np.where, and boolean masks - Guard divisions:
np.errstate(invalid="ignore", divide="ignore")plus a safe denominator mask - Respect NaN warm-up periods from the primitives (mask on
~np.isnan(...)) - Float64 for all numeric arrays
- O(n) algorithms only
MUST NOT
- Never reimplement an indicator that already exists in
openalgo.ta - Never write per-bar Python loops over large arrays — vectorize instead
- Never divide without masking zero/NaN denominators
- If the indicator is genuinely path-dependent (sequential state no primitive covers), check whether
ta.ema/ Wilder-style primitives already provide the recursion first; a plain Python loop is a last resort — keep it O(n) and document the trade-off
Available Building Blocks
Public ta methods that run in the Rust core:
from openalgo import ta
# Rolling math: ta.sma, ta.ema, ta.wma, ta.stdev, ta.highest, ta.lowest
# Price action: ta.true_range, ta.atr, ta.change, ta.roc
# Bands/channels: ta.bbands, ta.keltner, ta.donchian
# Signals: ta.crossover, ta.crossunder, ta.exrem, ta.rising, ta.falling
Common Custom Indicator Patterns
| Pattern | Implementation |
|---|---|
| Z-Score | (value - rolling_mean) / rolling_stdev |
| Squeeze | Bollinger inside Keltner channel |
| VWAP Bands | VWAP + N * rolling stdev of (close - vwap) |
| Momentum Score | Weighted sum of RSI + MACD + ADX conditions |
| Mean Reversion | Distance from SMA as % + threshold |
| Range Filter | ATR-based dynamic filter on close |
| Trend Strength | ADX + directional movement composite |
Example Usage
/custom-indicator zscore
/custom-indicator squeeze-momentum
/custom-indicator vwap-bands
/custom-indicator range-filter
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/marketcalls/openalgo-indicator-skills/custom-indicator">View custom-indicator on skillZs</a>