-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathstock_mr.rs
More file actions
167 lines (139 loc) · 5.26 KB
/
stock_mr.rs
File metadata and controls
167 lines (139 loc) · 5.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Example: Mean Reversion Strategy with simplified approach
// This example demonstrates a simple mean reversion strategy
use polars::prelude::*;
fn main() -> Result<(), PolarsError> {
println!("Mean Reversion Strategy Example");
println!("===============================\n");
// Create sample dates - 20 days
let mut dates = Vec::with_capacity(20);
for day in 1..=20 {
dates.push(format!("2023-01-{:02}", day));
}
// Create sample price data with mean-reverting pattern
let mut closes = Vec::with_capacity(20);
let mut price = 100.0;
// First 10 days - uptrend
for _ in 0..10 {
price += 2.0 + (rand() * 0.5);
closes.push(price);
}
// Next 10 days - downtrend (mean reversion)
for _ in 10..20 {
price -= 1.5 + (rand() * 0.5);
closes.push(price);
}
// Create DataFrame
let mut df = DataFrame::new(
dates.len(),
vec![
Series::new("date".into(), dates).into(),
Series::new("close".into(), closes.clone()).into(),
],
)?;
println!("Price data created with {} days", df.height());
// Calculate SMA (simple moving average)
let window_size = 5;
let mut sma_vals = vec![f64::NAN; closes.len()];
for i in window_size - 1..closes.len() {
let mut sum = 0.0;
for j in 0..window_size {
sum += closes[i - j];
}
sma_vals[i] = sum / window_size as f64;
}
// Calculate Bollinger Bands
let mut upper_band = vec![f64::NAN; closes.len()];
let mut lower_band = vec![f64::NAN; closes.len()];
for i in window_size - 1..closes.len() {
let sma = sma_vals[i];
let mut sum_sq_dev = 0.0;
for j in 0..window_size {
let dev = closes[i - j] - sma;
sum_sq_dev += dev * dev;
}
let std_dev = (sum_sq_dev / window_size as f64).sqrt();
upper_band[i] = sma + 2.0 * std_dev;
lower_band[i] = sma - 2.0 * std_dev;
}
// Calculate RSI
let rsi_period = 5;
let mut gains = vec![0.0; closes.len()];
let mut losses = vec![0.0; closes.len()];
let mut rsi_vals = vec![f64::NAN; closes.len()];
for i in 1..closes.len() {
let change = closes[i] - closes[i - 1];
if change > 0.0 {
gains[i] = change;
} else {
losses[i] = -change;
}
}
for i in rsi_period..closes.len() {
let avg_gain: f64 = gains[i - rsi_period + 1..=i].iter().sum::<f64>() / rsi_period as f64;
let avg_loss: f64 = losses[i - rsi_period + 1..=i].iter().sum::<f64>() / rsi_period as f64;
if avg_loss == 0.0 {
rsi_vals[i] = 100.0;
} else {
let rs = avg_gain / avg_loss;
rsi_vals[i] = 100.0 - (100.0 / (1.0 + rs));
}
}
// Calculate Z-score
let mut z_scores = vec![f64::NAN; closes.len()];
for i in 0..closes.len() {
if !sma_vals[i].is_nan() && !upper_band[i].is_nan() && !lower_band[i].is_nan() {
let band_width = upper_band[i] - lower_band[i];
if band_width > 0.0 {
z_scores[i] = (closes[i] - sma_vals[i]) / (band_width / 2.0);
}
}
}
// Generate trading signals
let mut signals = vec![0; closes.len()];
for i in 0..closes.len() {
if !z_scores[i].is_nan() && !rsi_vals[i].is_nan() {
if z_scores[i] <= -1.5 && rsi_vals[i] <= 30.0 {
signals[i] = 1; // Buy signal
} else if z_scores[i] >= 1.5 && rsi_vals[i] >= 70.0 {
signals[i] = -1; // Sell signal
}
}
}
// Add calculated columns to DataFrame
df.with_column(Column::new("sma".into(), sma_vals))?;
df.with_column(Column::new("upper_band".into(), upper_band))?;
df.with_column(Column::new("lower_band".into(), lower_band))?;
df.with_column(Column::new("rsi".into(), rsi_vals))?;
df.with_column(Column::new("z_score".into(), z_scores))?;
// Clone signals before adding to DataFrame
let signals_copy = signals.clone();
df.with_column(Column::new("signal".into(), signals))?;
// Print results
println!("\nMean Reversion Analysis:");
println!("{}", df);
// Count signals
let buy_signals = signals_copy.iter().filter(|&&s| s == 1).count();
let sell_signals = signals_copy.iter().filter(|&&s| s == -1).count();
println!(
"\nTrading signals found: {} buy, {} sell",
buy_signals, sell_signals
);
// Show strategy explanation
println!("\nMean Reversion Strategy Explanation:");
println!("1. Z-Score measures how far a price has deviated from its mean");
println!(" in terms of standard deviations.");
println!("2. Strong buy signals occur when Z-Score < -1.5 (oversold) and RSI < 30.");
println!("3. Strong sell signals occur when Z-Score > 1.5 (overbought) and RSI > 70.");
println!("4. Bollinger Bands help visualize the mean and standard deviation boundaries.");
println!("5. This strategy aims to profit from price movements returning to the mean.");
Ok(())
}
// Simple random number generator
fn rand() -> f64 {
use std::time::{SystemTime, UNIX_EPOCH};
let t = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as f64;
(t.sin() + 1.0) / 2.0
}