Skip to content

Commit b526bf6

Browse files
committed
trying to fix the backtesting that are identifying 0 alphas
1 parent 71ed444 commit b526bf6

33 files changed

+7581
-484
lines changed

analysis/quick_rsi_analysis.sh

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#!/bin/bash
2+
# Quick RSI Analysis using Python with system packages
3+
4+
cd /mnt/c/Users/DaviCastroSamora/Documents/SamoraDC/RustAlgorithmTrading
5+
6+
# Create venv if not exists
7+
if [ ! -d "venv" ]; then
8+
echo "Creating virtual environment..."
9+
python3 -m venv venv
10+
fi
11+
12+
# Activate and install dependencies
13+
source venv/bin/activate
14+
pip install --quiet pandas pyarrow numpy 2>&1 | grep -v "Requirement already"
15+
16+
echo "==================== RSI SIGNAL ANALYSIS ===================="
17+
echo ""
18+
19+
# Run quick analysis
20+
python3 << 'EOF'
21+
import pandas as pd
22+
import numpy as np
23+
import json
24+
from pathlib import Path
25+
26+
symbols = ['AAPL', 'MSFT', 'GOOGL']
27+
results = {}
28+
29+
print("LOADING & ANALYZING DATA...\n")
30+
31+
for symbol in symbols:
32+
# Load parquet
33+
df = pd.read_parquet(f'data/historical/{symbol}.parquet')
34+
35+
# Calculate RSI(14)
36+
delta = df['close'].diff()
37+
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
38+
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
39+
rs = gain / loss
40+
rsi = 100 - (100 / (1 + rs))
41+
rsi_valid = rsi.dropna()
42+
43+
# Statistics
44+
stats = {
45+
'min': float(rsi_valid.min()),
46+
'max': float(rsi_valid.max()),
47+
'mean': float(rsi_valid.mean()),
48+
'median': float(rsi_valid.median()),
49+
'std': float(rsi_valid.std()),
50+
'q25': float(rsi_valid.quantile(0.25)),
51+
'q75': float(rsi_valid.quantile(0.75)),
52+
}
53+
54+
# Threshold analysis
55+
oversold_35 = (rsi_valid <= 35).sum()
56+
overbought_65 = (rsi_valid >= 65).sum()
57+
oversold_30 = (rsi_valid <= 30).sum()
58+
overbought_70 = (rsi_valid >= 70).sum()
59+
60+
# Crossings
61+
cross_35_down = ((rsi_valid.shift(1) > 35) & (rsi_valid <= 35)).sum()
62+
cross_65_up = ((rsi_valid.shift(1) < 65) & (rsi_valid >= 65)).sum()
63+
cross_30_down = ((rsi_valid.shift(1) > 30) & (rsi_valid <= 30)).sum()
64+
cross_70_up = ((rsi_valid.shift(1) < 70) & (rsi_valid >= 70)).sum()
65+
66+
results[symbol] = {
67+
'bars': len(rsi_valid),
68+
'rsi_stats': stats,
69+
'current_params_35_65': {
70+
'oversold_bars': int(oversold_35),
71+
'overbought_bars': int(overbought_65),
72+
'oversold_crossings': int(cross_35_down),
73+
'overbought_crossings': int(cross_65_up),
74+
'total_signals': int(cross_35_down + cross_65_up)
75+
},
76+
'industry_params_30_70': {
77+
'oversold_bars': int(oversold_30),
78+
'overbought_bars': int(overbought_70),
79+
'oversold_crossings': int(cross_30_down),
80+
'overbought_crossings': int(cross_70_up),
81+
'total_signals': int(cross_30_down + cross_70_up)
82+
}
83+
}
84+
85+
print(f"{'='*60}")
86+
print(f"{symbol} - RSI(14) Analysis ({len(rsi_valid)} bars)")
87+
print(f"{'='*60}")
88+
print(f"\nRSI Statistics:")
89+
print(f" Min: {stats['min']:6.2f}")
90+
print(f" 25%: {stats['q25']:6.2f}")
91+
print(f" Mean: {stats['mean']:6.2f}")
92+
print(f" Median: {stats['median']:6.2f}")
93+
print(f" 75%: {stats['q75']:6.2f}")
94+
print(f" Max: {stats['max']:6.2f}")
95+
print(f" StdDev: {stats['std']:6.2f}")
96+
97+
print(f"\nCurrent Parameters (35/65):")
98+
print(f" Bars RSI ≤ 35: {oversold_35:3d} ({oversold_35/len(rsi_valid)*100:5.1f}%)")
99+
print(f" Bars RSI ≥ 65: {overbought_65:3d} ({overbought_65/len(rsi_valid)*100:5.1f}%)")
100+
print(f" Oversold crossings: {cross_35_down:3d}")
101+
print(f" Overbought crossings: {cross_65_up:3d}")
102+
print(f" TOTAL SIGNALS: {cross_35_down + cross_65_up:3d}")
103+
104+
print(f"\nIndustry Standard (30/70):")
105+
print(f" Bars RSI ≤ 30: {oversold_30:3d} ({oversold_30/len(rsi_valid)*100:5.1f}%)")
106+
print(f" Bars RSI ≥ 70: {overbought_70:3d} ({overbought_70/len(rsi_valid)*100:5.1f}%)")
107+
print(f" Oversold crossings: {cross_30_down:3d}")
108+
print(f" Overbought crossings: {cross_70_up:3d}")
109+
print(f" TOTAL SIGNALS: {cross_30_down + cross_70_up:3d}")
110+
print()
111+
112+
# Save results
113+
with open('analysis/rsi_quick_analysis.json', 'w') as f:
114+
json.dump(results, f, indent=2)
115+
116+
print(f"\n{'='*60}")
117+
print("DIAGNOSIS & RECOMMENDATIONS")
118+
print(f"{'='*60}\n")
119+
120+
total_signals_35_65 = sum(r['current_params_35_65']['total_signals'] for r in results.values())
121+
total_signals_30_70 = sum(r['industry_params_30_70']['total_signals'] for r in results.values())
122+
avg_rsi = np.mean([r['rsi_stats']['mean'] for r in results.values()])
123+
124+
print(f"Root Cause Analysis:")
125+
print(f" • Total signals with current params (35/65): {total_signals_35_65}")
126+
print(f" • Total signals with industry params (30/70): {total_signals_30_70}")
127+
print(f" • Average RSI across all symbols: {avg_rsi:.2f}")
128+
print()
129+
130+
if total_signals_35_65 == 0:
131+
print("❌ CRITICAL: Current parameters generate ZERO signals!")
132+
print(" RSI thresholds (35/65) are too tight for this market data.")
133+
print()
134+
135+
if avg_rsi > 55:
136+
print("📈 Market Condition: Bullish bias (elevated RSI)")
137+
print(" RSI staying above 50 indicates strong uptrend")
138+
print()
139+
140+
print("Recommendations:")
141+
print(" 1. Switch to industry standard thresholds: 30/70")
142+
print(f" This would generate ~{total_signals_30_70} signals across all symbols")
143+
print()
144+
print(" 2. Consider strategy appropriateness:")
145+
print(" RSI mean-reversion may not suit strong trending markets")
146+
print()
147+
print(" 3. Alternative approaches:")
148+
print(" - Use momentum-following instead of mean-reversion")
149+
print(" - Add trend filter before RSI signals")
150+
print(" - Consider asymmetric thresholds (e.g., 25/75)")
151+
print()
152+
153+
print(f"Analysis saved to: analysis/rsi_quick_analysis.json")
154+
print("="*60)
155+
156+
EOF
157+
158+
deactivate

analysis/rsi_quick_analysis.json

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"AAPL": {
3+
"bars": 237,
4+
"rsi_stats": {
5+
"min": 16.74811195024428,
6+
"max": 96.16295594504986,
7+
"mean": 54.40716932241217,
8+
"median": 55.27981767536083,
9+
"std": 17.885021444132683,
10+
"q25": 40.52339413164155,
11+
"q75": 67.55102040816323
12+
},
13+
"current_params_35_65": {
14+
"oversold_bars": 32,
15+
"overbought_bars": 72,
16+
"oversold_crossings": 7,
17+
"overbought_crossings": 14,
18+
"total_signals": 21
19+
},
20+
"industry_params_30_70": {
21+
"oversold_bars": 21,
22+
"overbought_bars": 50,
23+
"oversold_crossings": 3,
24+
"overbought_crossings": 14,
25+
"total_signals": 17
26+
}
27+
},
28+
"MSFT": {
29+
"bars": 237,
30+
"rsi_stats": {
31+
"min": 17.542343587970933,
32+
"max": 96.03529148983694,
33+
"mean": 55.30377767410985,
34+
"median": 54.45840813883903,
35+
"std": 17.834574287814203,
36+
"q25": 42.70799347471453,
37+
"q75": 68.53301224162145
38+
},
39+
"current_params_35_65": {
40+
"oversold_bars": 37,
41+
"overbought_bars": 80,
42+
"oversold_crossings": 9,
43+
"overbought_crossings": 9,
44+
"total_signals": 18
45+
},
46+
"industry_params_30_70": {
47+
"oversold_bars": 23,
48+
"overbought_bars": 52,
49+
"oversold_crossings": 13,
50+
"overbought_crossings": 9,
51+
"total_signals": 22
52+
}
53+
},
54+
"GOOGL": {
55+
"bars": 237,
56+
"rsi_stats": {
57+
"min": 12.984218077474807,
58+
"max": 93.25864779874207,
59+
"mean": 56.46947586005865,
60+
"median": 56.77535148070595,
61+
"std": 17.081445801290506,
62+
"q25": 44.41212121212122,
63+
"q75": 68.73167871799882
64+
},
65+
"current_params_35_65": {
66+
"oversold_bars": 24,
67+
"overbought_bars": 74,
68+
"oversold_crossings": 4,
69+
"overbought_crossings": 11,
70+
"total_signals": 15
71+
},
72+
"industry_params_30_70": {
73+
"oversold_bars": 17,
74+
"overbought_bars": 52,
75+
"oversold_crossings": 6,
76+
"overbought_crossings": 11,
77+
"total_signals": 17
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)