DEV Community

Propfirmkey
Propfirmkey

Posted on

Time Series Analysis for Traders: Beyond Moving Averages

Moving averages are just the beginning. Here are more sophisticated time series techniques that can improve your trading analysis.

Exponentially Weighted Statistics

Standard moving averages weight all periods equally. Exponential weighting gives more importance to recent data:

import numpy as np
import pandas as pd

def ewm_volatility(prices, span=20):
    returns = np.diff(np.log(prices))
    ewm_var = pd.Series(returns).ewm(span=span).var()
    return np.sqrt(ewm_var * 252)  # Annualized

def ewm_correlation(series1, series2, span=30):
    r1 = np.diff(np.log(series1))
    r2 = np.diff(np.log(series2))
    return pd.Series(r1).ewm(span=span).corr(pd.Series(r2))
Enter fullscreen mode Exit fullscreen mode

Hurst Exponent: Trending or Mean-Reverting?

def hurst_exponent(prices, max_lag=100):
    """
    H > 0.5: trending (momentum strategies work)
    H = 0.5: random walk (no edge)
    H < 0.5: mean-reverting (reversion strategies work)
    """
    lags = range(2, max_lag)
    tau = []

    for lag in lags:
        pp = np.subtract(prices[lag:], prices[:-lag])
        tau.append(np.std(pp))

    log_lags = np.log(lags)
    log_tau = np.log(tau)

    # Linear regression
    coeffs = np.polyfit(log_lags, log_tau, 1)
    return coeffs[0]  # Hurst exponent

# Example
h = hurst_exponent(price_series)
if h > 0.55:
    print("Trending — use momentum strategies")
elif h < 0.45:
    print("Mean-reverting — use reversion strategies")
else:
    print("Random walk — no clear edge")
Enter fullscreen mode Exit fullscreen mode

Regime Detection with Hidden Markov Models

def simple_regime_detection(returns, window=60):
    """
    Detect market regimes using rolling statistics.
    """
    rolling_mean = pd.Series(returns).rolling(window).mean()
    rolling_std = pd.Series(returns).rolling(window).std()

    regimes = []
    for m, s in zip(rolling_mean, rolling_std):
        if np.isnan(m):
            regimes.append('unknown')
        elif m > 0 and s < np.median(rolling_std.dropna()):
            regimes.append('bull_calm')
        elif m > 0 and s >= np.median(rolling_std.dropna()):
            regimes.append('bull_volatile')
        elif m <= 0 and s < np.median(rolling_std.dropna()):
            regimes.append('bear_calm')
        else:
            regimes.append('bear_volatile')

    return regimes
Enter fullscreen mode Exit fullscreen mode

Autocorrelation Analysis

def analyze_autocorrelation(returns, max_lags=20):
    """
    Check if past returns predict future returns.
    Significant autocorrelation = potential edge.
    """
    from statsmodels.stats.diagnostic import acorr_ljungbox

    results = {}

    for lag in range(1, max_lags + 1):
        correlation = np.corrcoef(returns[:-lag], returns[lag:])[0, 1]
        results[lag] = correlation

    # Ljung-Box test for significance
    lb_test = acorr_ljungbox(returns, lags=max_lags)

    return {
        'correlations': results,
        'significant_lags': [i+1 for i, p in enumerate(lb_test['lb_pvalue']) if p < 0.05]
    }
Enter fullscreen mode Exit fullscreen mode

Volatility Forecasting

def garch_like_forecast(returns, alpha=0.1, beta=0.85):
    """
    Simple GARCH(1,1)-like volatility forecast.
    """
    omega = np.var(returns) * (1 - alpha - beta)
    forecasts = [np.var(returns)]

    for r in returns:
        new_var = omega + alpha * r**2 + beta * forecasts[-1]
        forecasts.append(new_var)

    return np.sqrt(np.array(forecasts) * 252)
Enter fullscreen mode Exit fullscreen mode

Seasonal Patterns

def analyze_seasonality(df):
    """
    Find day-of-week and time-of-day patterns.
    """
    df['dow'] = df.index.dayofweek
    df['hour'] = df.index.hour

    # Day of week effect
    dow_returns = df.groupby('dow')['return'].agg(['mean', 'std', 'count'])
    dow_returns['t_stat'] = dow_returns['mean'] / (dow_returns['std'] / np.sqrt(dow_returns['count']))

    # Intraday pattern
    hourly = df.groupby('hour')['return'].agg(['mean', 'std', 'count'])

    return {
        'day_of_week': dow_returns,
        'hourly': hourly,
        'best_day': dow_returns['mean'].idxmax(),
        'worst_day': dow_returns['mean'].idxmin()
    }
Enter fullscreen mode Exit fullscreen mode

Practical Workflow

1. Calculate Hurst exponent → determine strategy type
2. Run regime detection → know current market state
3. Check autocorrelation → find predictive lags
4. Forecast volatility → set position sizes
5. Analyze seasonality → optimize entry timing
Enter fullscreen mode Exit fullscreen mode

These tools give you a quantitative view of market behavior that goes far beyond simple chart patterns. Whether you're refining a strategy or preparing for a trading evaluation, data-driven analysis is the foundation. More on matching your analysis approach to the right trading environment at propfirmkey.com.


Which time series techniques have improved your trading the most?

Top comments (0)