P
PipsGrowth

SciPy Library

Scientific computing library with powerful tools for trading: detect support/resistance with find_peaks(), smooth noisy data, find market cycles with FFT, and optimize strategy parameters.

Difficulty: Intermediate
Category: Pattern Recognition

Installation

# Usually pre-installed with NumPy
$ pip install scipy

Key Modules for Trading

scipy.signal

Peak detection, filtering, and signal processing for price data

scipy.stats

Statistical distributions, hypothesis testing, and correlation analysis

scipy.optimize

Parameter optimization for trading strategies

scipy.interpolate

Fill gaps in price data and smooth noisy signals

scipy.fft

Fourier analysis to detect market cycles and periodicity

scipy.cluster

Hierarchical clustering for market regime detection

Code Examples

Installation

Install SciPy

Python
pip install scipy
# Verify
python -c "import scipy; print(scipy.__version__)"

Support & Resistance Detection

Use find_peaks() to automatically detect support and resistance levels

Python
from scipy.signal import find_peaks
import numpy as np
import yfinance as yf
df = yf.download("EURUSD=X", period="6mo")
close = df['Close'].values.flatten()
high = df['High'].values.flatten()
low = df['Low'].values.flatten()
# Find resistance levels (peaks in price)
resistance_idx, resistance_props = find_peaks(
high, distance=10, prominence=0.001
)
# Find support levels (peaks in inverted price)
support_idx, support_props = find_peaks(
-low, distance=10, prominence=0.001
)
print("Resistance Levels:")
for idx in resistance_idx[-5:]:
print(f" {df.index[idx].date()}: {high[idx]:.5f}")
print("\nSupport Levels:")
for idx in support_idx[-5:]:
print(f" {df.index[idx].date()}: {low[idx]:.5f}")

Double Top/Bottom Detection

Identify double top and double bottom chart patterns

Python
from scipy.signal import find_peaks, argrelextrema
import numpy as np
import yfinance as yf
df = yf.download("EURUSD=X", period="1y")
close = df['Close'].values.flatten()
# Find local maxima (potential double tops)
peaks, _ = find_peaks(close, distance=15, prominence=0.0005)
# Check for double tops (two peaks at similar levels)
tolerance = 0.001 # 0.1% tolerance
double_tops = []
for i in range(len(peaks) - 1):
for j in range(i + 1, len(peaks)):
if abs(close[peaks[i]] - close[peaks[j]]) / close[peaks[i]] < tolerance:
if 10 < (peaks[j] - peaks[i]) < 60: # 10-60 bars apart
double_tops.append((peaks[i], peaks[j]))
print(f"Found {len(double_tops)} potential double tops:")
for top1, top2 in double_tops[:5]:
print(f" Peak 1: {df.index[top1].date()} ({close[top1]:.5f})")
print(f" Peak 2: {df.index[top2].date()} ({close[top2]:.5f})")

Noise Filtering with Savitzky-Golay

Smooth price data to reveal underlying trends

Python
from scipy.signal import savgol_filter
import numpy as np
import yfinance as yf
df = yf.download("EURUSD=X", period="3mo")
close = df['Close'].values.flatten()
# Apply Savitzky-Golay filter (preserves peaks better than moving average)
smooth_price = savgol_filter(close, window_length=21, polyorder=3)
# Calculate trend direction
trend = np.gradient(smooth_price)
print(f"Current price: {close[-1]:.5f}")
print(f"Smoothed price: {smooth_price[-1]:.5f}")
print(f"Trend direction: {'UP' if trend[-1] > 0 else 'DOWN'}")
print(f"Trend strength: {abs(trend[-1]):.6f}")

Market Cycle Detection with FFT

Find dominant cycles in price data using Fourier analysis

Python
from scipy.fft import fft, fftfreq
import numpy as np
import yfinance as yf
df = yf.download("EURUSD=X", period="2y")
close = df['Close'].values.flatten()
# Detrend the data
detrended = close - np.polyval(np.polyfit(range(len(close)), close, 1), range(len(close)))
# Apply FFT
N = len(detrended)
yf_vals = fft(detrended)
xf = fftfreq(N, d=1) # d=1 for daily frequency
# Get dominant frequencies (ignore DC component)
magnitudes = np.abs(yf_vals[1:N//2])
frequencies = xf[1:N//2]
periods = 1 / frequencies
# Top 5 dominant cycles
top_indices = np.argsort(magnitudes)[-5:][::-1]
print("Dominant Market Cycles:")
for idx in top_indices:
print(f" Period: {periods[idx]:.0f} days, "
f"Strength: {magnitudes[idx]:.4f}")

Strategy Parameter Optimization

Use scipy.optimize to find optimal indicator parameters

Python
from scipy.optimize import minimize
import numpy as np
import yfinance as yf
df = yf.download("EURUSD=X", period="2y")
close = df['Close'].values.flatten()
def strategy_return(params):
fast = int(params[0])
slow = int(params[1])
if fast >= slow or fast < 2 or slow > 200:
return 0 # Invalid parameters
# Simple SMA crossover backtest
sma_fast = np.convolve(close, np.ones(fast)/fast, mode='valid')
sma_slow = np.convolve(close, np.ones(slow)/slow, mode='valid')
# Align lengths
min_len = min(len(sma_fast), len(sma_slow))
sma_fast = sma_fast[-min_len:]
sma_slow = sma_slow[-min_len:]
aligned_close = close[-min_len:]
# Calculate returns
signals = np.where(sma_fast > sma_slow, 1, -1)
returns = np.diff(aligned_close) / aligned_close[:-1]
strategy_returns = signals[:-1] * returns
return -np.sum(strategy_returns) # Negative for minimization
# Optimize
result = minimize(strategy_return, x0=[10, 50],
method='Nelder-Mead',
bounds=[(2, 50), (20, 200)])
print(f"Optimal Fast SMA: {int(result.x[0])}")
print(f"Optimal Slow SMA: {int(result.x[1])}")
print(f"Total Return: {-result.fun:.4%}")

Best Practices

find_peaks() for S/R

Use prominence and distance parameters to filter meaningful support/resistance levels

Savgol Filter

Savitzky-Golay filter preserves peaks better than simple moving averages

Avoid Overfitting

When optimizing parameters, always validate on out-of-sample data

FFT Limitations

Market cycles are not perfectly periodic — use FFT as a guide, not a trading signal

PipsGrowth - Expert Broker Reviews, Trading Strategies & Tools