Mathematics Methods for Quant finance
Modeling Market Structure, Regimes & Uncertainty.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from arch import arch_model
from scipy import stats, signal, optimize, fft
from sklearn.preprocessing import RobustScaler, StandardScaler
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, VotingClassifier, IsolationForest, RandomForestRegressor
from sklearn.model_selection import train_test_split, TimeSeriesSplit, RandomizedSearchCV
from sklearn.metrics import classification_report, confusion_matrix, precision_score, f1_score, roc_auc_score
from sklearn.svm import SVC
from sklearn.neural_network import MLPClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.calibration import CalibratedClassifierCV
from sklearn.decomposition import PCA
from xgboost import XGBClassifier
from lightgbm import LGBMClassifier
from pykalman import KalmanFilter
import pulp
import mplfinance as mpf
import matplotlib.gridspec as gridspec
from matplotlib.patches import FancyBboxPatch, Rectangle, Circle
import warnings
import ta
from datetime import datetime, timedelta
from scipy.signal import savgol_filter
from statsmodels.tsa.stattools import adfuller
import hashlib
import pickle
import plotly.graph_objects as go
from plotly.subplots import make_subplots
warnings.filterwarnings('ignore')
plt.style.use('dark_background')
DARK_BG = '#0D1117'
FIGURE_FACE = '#0D1117'
AXES_FACE = '#161B22'
TEXT_COLOR = '#E6EDF3'
GRID_COLOR = '#30363D'
LINE_COLORS = {
'price': '#79C0FF',
'signal': '#D2A8FF',
'buy': '#3FB950',
'sell': '#F85149',
'confidence': '#FFA657',
'rsi': '#FF7B72',
'volatility': '#A5D6FF',
'volume': '#8B949E',
'strategy': '#56D4C4',
'fourier': '#FFAB70',
'hyperbolic': '#A371F7',
'complex': '#F778BA',
'quantum': '#58A6FF',
'entropy': '#E3B341'
}
plt.rcParams.update({
'figure.facecolor': FIGURE_FACE,
'axes.facecolor': AXES_FACE,
'axes.edgecolor': GRID_COLOR,
'axes.labelcolor': TEXT_COLOR,
'xtick.color': TEXT_COLOR,
'ytick.color': TEXT_COLOR,
'text.color': TEXT_COLOR,
'legend.facecolor': AXES_FACE,
'legend.edgecolor': GRID_COLOR,
'grid.color': GRID_COLOR,
'grid.alpha': 0.3,
'font.size': 11,
'axes.titlesize': 14,
'axes.labelsize': 12,
'legend.fontsize': 10
})
class AdvancedTrigonometryGeometry:
def fourier_cycle_analysis(price_series, num_components=5):
n = len(price_series)
if n < 10:
return np.zeros_like(price_series), np.array([0]), np.array([0])
t = np.arange(n)
window = np.hanning(n)
price_windowed = price_series * window
fft_vals = fft.fft(price_windowed)
frequencies = fft.fftfreq(n)
magnitudes = np.abs(fft_vals)
positive_freq_indices = np.where((frequencies > 0) & (frequencies < 0.5))[0]
if len(positive_freq_indices) == 0:
return np.zeros_like(price_series), np.array([0]), np.array([0])
num_components = min(num_components, len(positive_freq_indices))
dominant_indices = positive_freq_indices[np.argsort(magnitudes[positive_freq_indices])[-num_components:]]
reconstructed = np.zeros(n, dtype=complex)
for idx in dominant_indices:
reconstructed += fft_vals[idx] * np.exp(2j * np.pi * frequencies[idx] * t)
reconstructed_real = np.real(reconstructed)
if len(reconstructed_real) > len(price_series):
reconstructed_real = reconstructed_real[:len(price_series)]
elif len(reconstructed_real) < len(price_series):
reconstructed_real = np.pad(reconstructed_real, (0, len(price_series) - len(reconstructed_real)))
return reconstructed_real, frequencies[dominant_indices], magnitudes[dominant_indices]
def harmonic_pattern_detection(highs, lows, tolerance=0.1):
patterns = []
n = len(highs)
if n < 5:
return patterns
for i in range(n-4):
try:
X, A, B, C, D = i, i+1, i+2, i+3, i+4
if not (highs[A] > highs[X] and lows[B] < lows[A] and highs[C] > highs[B] and lows[D] < lows[C]):
continue
XA = highs[A] - lows[X]
AB = highs[B] - lows[A]
BC = highs[B] - lows[C]
CD = highs[D] - lows[C]
if XA <= 0 or AB <= 0 or BC <= 0 or CD <= 0:
continue
AB_ratio = AB / XA
BC_ratio = BC / AB
CD_ratio = CD / BC
bullish_conditions = [
(0.618 - tolerance <= AB_ratio <= 0.618 + tolerance and 0.382 - tolerance <= BC_ratio <= 0.886 + tolerance and 1.272 - tolerance <= CD_ratio <= 1.618 + tolerance, 'Gartley_Bullish'),
(0.786 - tolerance <= AB_ratio <= 0.786 + tolerance and 0.382 - tolerance <= BC_ratio <= 0.886 + tolerance and 1.618 - tolerance <= CD_ratio <= 2.618 + tolerance, 'Butterfly_Bullish'),
(0.382 - tolerance <= AB_ratio <= 0.5 + tolerance and 0.382 - tolerance <= BC_ratio <= 0.886 + tolerance and 1.618 - tolerance <= CD_ratio <= 2.618 + tolerance, 'Bat_Bullish'),
(0.382 - tolerance <= AB_ratio <= 0.618 + tolerance and 0.382 - tolerance <= BC_ratio <= 0.886 + tolerance and 2.618 - tolerance <= CD_ratio <= 3.618 + tolerance, 'Crab_Bullish')
]
for condition, pattern_name in bullish_conditions:
if condition:
patterns.append((pattern_name, i, AB_ratio, BC_ratio, CD_ratio))
break
except (IndexError, ZeroDivisionError, ValueError):
continue
return patterns
def spherical_coordinates_mapping(price, volume, time):
price = np.nan_to_num(price, nan=np.nanmedian(price))
volume = np.nan_to_num(volume, nan=np.nanmedian(volume))
time = np.nan_to_num(time, nan=np.nanmedian(time))
price_norm = (price - np.nanmedian(price)) / (np.nanstd(price) + 1e-8)
volume_norm = (volume - np.nanmedian(volume)) / (np.nanstd(volume) + 1e-8)
time_norm = (time - np.nanmedian(time)) / (np.nanstd(time) + 1e-8)
price_norm = np.clip(price_norm, -3, 3)
volume_norm = np.clip(volume_norm, -3, 3)
time_norm = np.clip(time_norm, -3, 3)
r = np.sqrt(price_norm**2 + volume_norm**2 + time_norm**2)
theta = np.arccos(np.clip(price_norm / (r + 1e-8), -1, 1))
phi = np.arctan2(volume_norm, time_norm + 1e-8)
return r, theta, phi
def hyperbolic_geometry_momentum(price_series, period=20):
if len(price_series) < period + 1:
return np.zeros_like(price_series)
price_series_clean = np.nan_to_num(price_series, nan=0.0)
shifted_prices = np.roll(price_series_clean, 1)
shifted_prices[0] = price_series_clean[0]
log_returns = np.log(price_series_clean / shifted_prices)
log_returns[0] = 0
def poincare_distance(x, y):
x = np.clip(x, -0.999, 0.999)
y = np.clip(y, -0.999, 0.999)
numerator = 2 * ((x - y)**2)
denominator = (1 - x**2) * (1 - y**2) + 1e-8
return np.arccosh(1 + numerator / denominator)
price_min = np.nanpercentile(price_series_clean, 5)
price_max = np.nanpercentile(price_series_clean, 95)
if price_max == price_min:
normalized = np.zeros_like(price_series_clean)
else:
normalized = 2 * (price_series_clean - price_min) / (price_max - price_min) - 1
normalized = np.clip(normalized, -0.99, 0.99)
hyperbolic_mom = np.zeros_like(price_series_clean)
for i in range(period, len(price_series_clean)):
try:
dist_current = poincare_distance(normalized[i], normalized[i-1])
dist_period = poincare_distance(normalized[i], normalized[i-period])
momentum = (dist_current * 0.3 + dist_period * 0.7) * np.sign(price_series_clean[i] - price_series_clean[i-period])
hyperbolic_mom[i] = momentum
except (ValueError, FloatingPointError):
hyperbolic_mom[i] = hyperbolic_mom[i-1] * 0.9 if i > 0 else 0
hyperbolic_mom_series = pd.Series(hyperbolic_mom)
hyperbolic_mom_smoothed = hyperbolic_mom_series.rolling(window=5, min_periods=1, center=False).mean().values
return hyperbolic_mom_smoothed
def wavelet_transform_analysis(price_series, scales=None):
if scales is None:
scales = np.arange(1, min(50, len(price_series)//4))
n = len(price_series)
if n < 10:
return np.array([])
def morlet_wavelet(t, scale):
if scale <= 0:
scale = 1
w0 = 6
return np.pi**(-0.25) * np.exp(1j * w0 * t / scale) * np.exp(-0.5 * (t / scale)**2)
wavelet_coeffs = np.zeros((len(scales), n), dtype=complex)
for i, scale in enumerate(scales):
if scale <= 0 or 8 * scale * 2 > n:
continue
try:
wavelet_length = min(int(8 * scale), n//2)
t = np.arange(-wavelet_length, wavelet_length)
wavelet = morlet_wavelet(t, scale)
wavelet = wavelet / (np.sqrt(scale) + 1e-8)
price_series_clean = np.nan_to_num(price_series, nan=0.0)
conv = np.convolve(price_series_clean, wavelet, mode='same')
if len(conv) == n:
wavelet_coeffs[i, :] = conv
elif len(conv) > n:
wavelet_coeffs[i, :] = conv[:n]
else:
wavelet_coeffs[i, :len(conv)] = conv
except (ValueError, FloatingPointError, MemoryError):
continue
return np.abs(wavelet_coeffs)
def topological_data_analysis(price_series, window_size=50):
from scipy.spatial.distance import pdist, squareform
n = len(price_series)
window_size = min(window_size, n//2)
if n < window_size:
return np.array([0]), np.array([[0]])
n_windows = n - window_size + 1
embedding = np.zeros((n_windows, window_size))
for i in range(n_windows):
window_data = price_series[i:i+window_size]
window_data_clean = np.nan_to_num(window_data, nan=0.0)
if np.std(window_data_clean) > 0:
embedding[i, :] = (window_data_clean - np.mean(window_data_clean)) / np.std(window_data_clean)
else:
embedding[i, :] = window_data_clean
try:
dist_matrix = squareform(pdist(embedding, metric='chebyshev'))
persistence = np.zeros(n_windows - 1)
for i in range(n_windows - 1):
persistence[i] = np.sqrt(np.mean((embedding[i+1] - embedding[i])**2))
except (ValueError, np.linalg.LinAlgError, MemoryError):
persistence = np.zeros(max(1, n_windows - 1))
dist_matrix = np.eye(max(1, n_windows))
return persistence, dist_matrix
Run to view results