Skip to main content

example/
example.rs

1// Finalytics — Rust Examples
2//
3// Installation
4// ────────────
5// Add to your Cargo.toml:
6//
7//   [dependencies]
8//   finalytics = "*"
9//
10// Or run:
11//
12//   cargo add finalytics
13//
14// Full docs: https://docs.rs/finalytics/
15//
16// Run this example (from the repo root)
17// ───────────────────────────────────────
18//   bash examples/example.sh rust
19//
20// (The script copies this file to rust/examples/ and runs:
21//   cd rust && cargo run --example example)
22
23use finalytics::prelude::*;
24use std::error::Error;
25
26#[tokio::main]
27async fn main() -> Result<(), Box<dyn Error>> {
28    screener().await?;
29    ticker().await?;
30    tickers().await?;
31    portfolio_optimization_oos().await?;
32    portfolio_optimization_constraints().await?;
33    portfolio_allocation_rebalancing_dca().await?;
34    custom_data().await?;
35    Ok(())
36}
37
38// ── 1. Screener — Large-Cap NASDAQ Technology Stocks with ROE >= 15% ────────
39
40async fn screener() -> Result<(), Box<dyn Error>> {
41    println!("=== 1. Screener ===");
42
43    let screener = Screener::builder()
44        .quote_type(QuoteType::Equity)
45        .add_filter(ScreenerFilter::EqStr(
46            ScreenerMetric::Equity(EquityScreener::Exchange),
47            Exchange::NASDAQ.as_ref(),
48        ))
49        .add_filter(ScreenerFilter::EqStr(
50            ScreenerMetric::Equity(EquityScreener::Sector),
51            Sector::Technology.as_ref(),
52        ))
53        .add_filter(ScreenerFilter::Gte(
54            ScreenerMetric::Equity(EquityScreener::MarketCapIntraday),
55            10_000_000_000.0,
56        ))
57        .add_filter(ScreenerFilter::Gte(
58            ScreenerMetric::Equity(EquityScreener::ReturnOnEquity),
59            0.15,
60        ))
61        .sort_by(
62            ScreenerMetric::Equity(EquityScreener::MarketCapIntraday),
63            true,
64        )
65        .size(10)
66        .build()
67        .await?;
68
69    screener.overview().show()?;
70    screener.metrics().await?.show()?;
71
72    Ok(())
73}
74
75// ── 2. Ticker — Single security analysis with all report types ───────────────
76
77async fn ticker() -> Result<(), Box<dyn Error>> {
78    println!("=== 2. Ticker ===");
79
80    let ticker = Ticker::builder()
81        .ticker("AAPL")
82        .start_date("2023-01-01")
83        .end_date("2024-12-31")
84        .interval(Interval::OneDay)
85        .benchmark_symbol("^GSPC")
86        .confidence_level(0.95)
87        .risk_free_rate(0.02)
88        .build();
89
90    ticker.report(Some(ReportType::Performance)).await?.show()?;
91    ticker.report(Some(ReportType::Financials)).await?.show()?;
92    ticker.report(Some(ReportType::Options)).await?.show()?;
93    ticker.report(Some(ReportType::News)).await?.show()?;
94
95    Ok(())
96}
97
98// ── 3. Tickers — Multiple securities analysis ────────────────────────────────
99
100async fn tickers() -> Result<(), Box<dyn Error>> {
101    println!("=== 3. Tickers ===");
102
103    let tickers = Tickers::builder()
104        .tickers(vec!["NVDA", "GOOG", "AAPL", "MSFT", "BTC-USD"])
105        .start_date("2023-01-01")
106        .end_date("2024-12-31")
107        .interval(Interval::OneDay)
108        .benchmark_symbol("^GSPC")
109        .confidence_level(0.95)
110        .risk_free_rate(0.02)
111        .build();
112
113    tickers
114        .report(Some(ReportType::Performance))
115        .await?
116        .show()?;
117
118    Ok(())
119}
120
121// ── 4. Portfolio — Optimization with Out-of-Sample Evaluation ───────────────
122
123// Optimize on 2023-2024 data (in-sample)
124async fn portfolio_optimization_oos() -> Result<(), Box<dyn Error>> {
125    println!("=== 4. Portfolio — Optimization with Out-of-Sample Evaluation ===");
126
127    let mut portfolio = Portfolio::builder()
128        .ticker_symbols(vec!["NVDA", "GOOG", "AAPL", "MSFT", "BTC-USD"])
129        .benchmark_symbol("^GSPC")
130        .start_date("2023-01-01")
131        .end_date("2024-12-31")
132        .interval(Interval::OneDay)
133        .confidence_level(0.95)
134        .risk_free_rate(0.02)
135        .objective_function(ObjectiveFunction::MaxSharpe)
136        .build()
137        .await?;
138
139    portfolio.optimize()?;
140    portfolio
141        .report(Some(ReportType::Optimization))
142        .await?
143        .show()?;
144
145    // Update to 2025 data for out-of-sample evaluation
146    portfolio.update_dates("2025-01-01", "2026-01-01").await?;
147    portfolio.performance_stats()?;
148    portfolio
149        .report(Some(ReportType::Performance))
150        .await?
151        .show()?;
152
153    Ok(())
154}
155
156// ── 5. Portfolio — Optimization with Weight & Categorical Constraints ─────────
157
158async fn portfolio_optimization_constraints() -> Result<(), Box<dyn Error>> {
159    println!("=== 5. Portfolio — Optimization with Weight & Categorical Constraints ===");
160
161    let constraints = Constraints {
162        // Per-asset (lower_bound, upper_bound) in the same order as ticker_symbols.
163        asset_weights: Some(vec![
164            (0.05, 0.40), // AAPL
165            (0.05, 0.40), // MSFT
166            (0.05, 0.40), // NVDA
167            (0.05, 0.30), // JPM
168            (0.05, 0.20), // XOM
169            (0.05, 0.25), // BTC-USD
170        ]),
171        categorical_weights: Some(vec![
172            // ── Sector constraint ──────────────────────────────────────
173            CategoricalWeights {
174                name: "Sector".to_string(),
175                category_per_symbol: vec![
176                    "Tech".to_string(),    // AAPL
177                    "Tech".to_string(),    // MSFT
178                    "Tech".to_string(),    // NVDA
179                    "Finance".to_string(), // JPM
180                    "Energy".to_string(),  // XOM
181                    "Crypto".to_string(),  // BTC-USD
182                ],
183                weight_per_category: vec![
184                    ("Tech".to_string(),    0.30, 0.60),
185                    ("Finance".to_string(), 0.05, 0.30),
186                    ("Energy".to_string(),  0.05, 0.20),
187                    ("Crypto".to_string(),  0.05, 0.25),
188                ],
189            },
190            // ── Asset-class constraint ─────────────────────────────────
191            CategoricalWeights {
192                name: "Asset Class".to_string(),
193                category_per_symbol: vec![
194                    "Equity".to_string(), // AAPL
195                    "Equity".to_string(), // MSFT
196                    "Equity".to_string(), // NVDA
197                    "Equity".to_string(), // JPM
198                    "Equity".to_string(), // XOM
199                    "Crypto".to_string(), // BTC-USD
200                ],
201                weight_per_category: vec![
202                    ("Equity".to_string(), 0.70, 0.95),
203                    ("Crypto".to_string(), 0.05, 0.30),
204                ],
205            },
206        ]),
207    };
208
209    let mut portfolio = Portfolio::builder()
210        .ticker_symbols(vec!["AAPL", "MSFT", "NVDA", "JPM", "XOM", "BTC-USD"])
211        .benchmark_symbol("^GSPC")
212        .start_date("2023-01-01")
213        .end_date("2024-12-31")
214        .interval(Interval::OneDay)
215        .confidence_level(0.95)
216        .risk_free_rate(0.02)
217        .objective_function(ObjectiveFunction::MaxSharpe)
218        .constraints(Some(constraints))
219        .build()
220        .await?;
221
222    portfolio.optimize()?;
223    portfolio
224        .report(Some(ReportType::Optimization))
225        .await?
226        .show()?;
227
228    Ok(())
229}
230
231// ── 6. Portfolio — Explicit Allocation with Rebalancing and DCA ─────────────
232
233async fn portfolio_allocation_rebalancing_dca() -> Result<(), Box<dyn Error>> {
234    println!("=== 6. Portfolio — Explicit Allocation with Rebalancing and DCA ===");
235
236    let mut portfolio_alloc = Portfolio::builder()
237        .ticker_symbols(vec!["AAPL", "MSFT", "NVDA", "BTC-USD"])
238        .benchmark_symbol("^GSPC")
239        .start_date("2023-01-01")
240        .end_date("2024-12-31")
241        .interval(Interval::OneDay)
242        .confidence_level(0.95)
243        .risk_free_rate(0.02)
244        .weights(vec![25_000.0, 25_000.0, 25_000.0, 25_000.0])
245        .rebalance_strategy(Some(RebalanceStrategy::Calendar(
246            ScheduleFrequency::Quarterly,
247        )))
248        .scheduled_cash_flows(Some(vec![ScheduledCashFlow {
249            amount: 2_000.0,
250            frequency: ScheduleFrequency::Monthly,
251            start_date: None,
252            end_date: None,
253            allocation: CashFlowAllocation::ProRata,
254        }]))
255        .build()
256        .await?;
257
258    portfolio_alloc.performance_stats()?;
259    portfolio_alloc
260        .report(Some(ReportType::Performance))
261        .await?
262        .show()?;
263
264    Ok(())
265}
266
267// ── 7. Custom Data (KLINE) — Load CSV data and use with Ticker, Tickers, Portfolio ──
268
269async fn custom_data() -> Result<(), Box<dyn Error>> {
270    println!("=== 7. Custom Data (KLINE) ===");
271
272    // Load data from CSV files
273    let aapl = KLINE::from_csv("AAPL", "examples/datasets/aapl.csv")?;
274    let msft = KLINE::from_csv("MSFT", "examples/datasets/msft.csv")?;
275    let nvda = KLINE::from_csv("NVDA", "examples/datasets/nvda.csv")?;
276    let goog = KLINE::from_csv("GOOG", "examples/datasets/goog.csv")?;
277    let btcusd = KLINE::from_csv("BTC-USD", "examples/datasets/btcusd.csv")?;
278    let gspc = KLINE::from_csv("^GSPC", "examples/datasets/gspc.csv")?;
279
280    // Single Ticker from custom data
281    println!("--- Custom Ticker ---");
282    let custom_ticker = Ticker::builder()
283        .ticker("AAPL")
284        .benchmark_symbol("^GSPC")
285        .confidence_level(0.95)
286        .risk_free_rate(0.02)
287        .ticker_data(Some(aapl.clone()))
288        .benchmark_data(Some(gspc.clone()))
289        .build();
290
291    custom_ticker
292        .report(Some(ReportType::Performance))
293        .await?
294        .show()?;
295
296    // Multiple Tickers from custom data
297    println!("--- Custom Tickers ---");
298    let custom_tickers = Tickers::builder()
299        .tickers(vec!["NVDA", "GOOG", "AAPL", "MSFT", "BTC-USD"])
300        .benchmark_symbol("^GSPC")
301        .confidence_level(0.95)
302        .risk_free_rate(0.02)
303        .tickers_data(Some(vec![
304            nvda.clone(),
305            goog.clone(),
306            aapl.clone(),
307            msft.clone(),
308            btcusd.clone(),
309        ]))
310        .benchmark_data(Some(gspc.clone()))
311        .build();
312
313    custom_tickers
314        .report(Some(ReportType::Performance))
315        .await?
316        .show()?;
317
318    // Portfolio optimization from custom data
319    println!("--- Custom Portfolio ---");
320    let mut custom_portfolio = Portfolio::builder()
321        .ticker_symbols(vec!["NVDA", "GOOG", "AAPL", "MSFT", "BTC-USD"])
322        .benchmark_symbol("^GSPC")
323        .confidence_level(0.95)
324        .risk_free_rate(0.02)
325        .objective_function(ObjectiveFunction::MaxSharpe)
326        .tickers_data(Some(vec![nvda, goog, aapl, msft, btcusd]))
327        .benchmark_data(Some(gspc))
328        .build()
329        .await?;
330
331    custom_portfolio.optimize()?;
332    custom_portfolio
333        .report(Some(ReportType::Optimization))
334        .await?
335        .show()?;
336
337    Ok(())
338}