Skip to content

Commit c87ecf8

Browse files
committed
Adding and updating docs for feature selection option.
1 parent f98051d commit c87ecf8

4 files changed

Lines changed: 135 additions & 3 deletions

File tree

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,47 @@ Whether you are backtesting, researching, or building production trading systems
4343

4444
---
4545

46+
## Feature Selection
47+
48+
This crate supports feature selection, allowing you to include only the parts you need:
49+
50+
### Types of Features
51+
- **indicators**: Only include technical indicator calculations
52+
- **strategies**: Only include trading strategies
53+
- **all**: Include both indicators and strategies (default)
54+
55+
### How to Choose Features in Cargo.toml
56+
57+
You can enable features in your `Cargo.toml` like this:
58+
59+
```toml
60+
[dependencies]
61+
ta-lib-in-rust = { version = "x.y.z", features = ["strategies"] } # Only strategies
62+
ta-lib-in-rust = { version = "x.y.z", features = ["indicators"] } # Only indicators
63+
ta-lib-in-rust = { version = "x.y.z", features = ["all"] } # Everything (default)
64+
```
65+
66+
If you omit the `features` key, the default is `all`.
67+
68+
### Programmatic Feature Selection
69+
70+
You can also select features at runtime using the `FeatureSelection` enum and `select_features` function:
71+
72+
```rust
73+
use ta_lib_in_rust::{FeatureSelection, select_features};
74+
let mut df = ...; // your Polars DataFrame
75+
// To add only indicators:
76+
let result = select_features(&mut df, FeatureSelection::Indicators);
77+
// To run a strategy:
78+
let result = select_features(&mut df, FeatureSelection::Strategy { strategy_name: "daily_1", params: None });
79+
// To do both:
80+
let result = select_features(&mut df, FeatureSelection::All { strategy_name: "daily_1", params: None });
81+
```
82+
83+
See the crate documentation for more details and examples.
84+
85+
---
86+
4687
## Installation
4788

4889
Add to your `Cargo.toml`:

examples/compare_minute_indicator_strategies.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@ use polars::prelude::*;
22
use ta_lib_in_rust::strategy::minute::{
33
multi_indicator_minute_1, multi_indicator_minute_2, multi_indicator_minute_3,
44
};
5+
use ta_lib_in_rust::{FeatureSelection, select_features};
6+
use std::env;
57

68
fn main() -> Result<(), PolarsError> {
9+
// Parse CLI arguments for feature selection
10+
let args: Vec<String> = env::args().collect();
11+
let feature_mode = args.get(1).map(|s| s.as_str()).unwrap_or("all");
12+
let strategy_name = args.get(2).map(|s| s.as_str()).unwrap_or("minute_1");
13+
714
println!("Loading minute OHLCV data...");
815
let df = CsvReadOptions::default()
916
.with_has_header(true)
@@ -15,7 +22,7 @@ fn main() -> Result<(), PolarsError> {
1522
println!("{}", df.head(Some(5)));
1623

1724
// Create a DataFrame with lowercase column names expected by the indicators
18-
let df = df
25+
let mut df = df
1926
.lazy()
2027
.select([
2128
col("Symbol").alias("symbol"),
@@ -29,6 +36,15 @@ fn main() -> Result<(), PolarsError> {
2936
])
3037
.collect()?;
3138

39+
// Feature selection logic
40+
let selection = match feature_mode {
41+
"indicators" => FeatureSelection::Indicators,
42+
"strategy" => FeatureSelection::Strategy { strategy_name, params: None },
43+
_ => FeatureSelection::All { strategy_name, params: None },
44+
};
45+
let result_df = select_features(&mut df, selection)?;
46+
println!("\nResulting DataFrame ({feature_mode}):\n{result_df}");
47+
3248
// Initialize strategies with default parameters
3349
let params1 = multi_indicator_minute_1::StrategyParams::default();
3450
let params2 = multi_indicator_minute_2::StrategyParams::default();

examples/compare_multi_indicator_strategies.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,15 @@ use ta_lib_in_rust::strategy::daily::multi_indicator_daily_1;
33
use ta_lib_in_rust::strategy::daily::multi_indicator_daily_2;
44
use ta_lib_in_rust::strategy::daily::multi_indicator_daily_3;
55
use ta_lib_in_rust::strategy::daily::multi_indicator_daily_4;
6+
use ta_lib_in_rust::{FeatureSelection, select_features};
7+
use std::env;
68

79
fn main() -> Result<(), PolarsError> {
10+
// Parse CLI arguments for feature selection
11+
let args: Vec<String> = env::args().collect();
12+
let feature_mode = args.get(1).map(|s| s.as_str()).unwrap_or("all");
13+
let strategy_name = args.get(2).map(|s| s.as_str()).unwrap_or("daily_1");
14+
815
// Define the tickers to analyze
916
let tickers = vec!["AAPL", "GOOGL", "MSFT"];
1017

@@ -29,7 +36,7 @@ fn main() -> Result<(), PolarsError> {
2936
println!("==============================================================");
3037

3138
// Load ticker's daily OHLCV data
32-
let file_path = format!("examples/{}_daily_ohlcv.csv", ticker);
39+
let file_path = format!("examples/csv/{}_daily_ohlcv.csv", ticker);
3340

3441
// Read CSV file with Polars using CsvReadOptions
3542
let df = CsvReadOptions::default()
@@ -45,7 +52,7 @@ fn main() -> Result<(), PolarsError> {
4552
}
4653

4754
// Create a new DataFrame with renamed columns and proper types
48-
let df = df
55+
let mut df = df
4956
.lazy()
5057
.select([
5158
col("Open").alias("open"),
@@ -56,6 +63,15 @@ fn main() -> Result<(), PolarsError> {
5663
])
5764
.collect()?;
5865

66+
// Feature selection logic
67+
let selection = match feature_mode {
68+
"indicators" => FeatureSelection::Indicators,
69+
"strategy" => FeatureSelection::Strategy { strategy_name, params: None },
70+
_ => FeatureSelection::All { strategy_name, params: None },
71+
};
72+
let result_df = select_features(&mut df, selection)?;
73+
println!("\nResulting DataFrame for {ticker} ({feature_mode}):\n{result_df}");
74+
5975
println!("--------------------------------------------------------------");
6076
println!(
6177
"COMPARATIVE ANALYSIS OF MULTI-INDICATOR TRADING STRATEGIES FOR {}",

src/lib.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,70 @@ pub mod util;
264264
pub use indicators::*;
265265
pub use strategy::*;
266266

267+
use polars::prelude::Series;
268+
use polars::prelude::NamedFrom;
269+
267270
// This is a placeholder function - should be removed before final release
268271
pub fn add(left: u64, right: u64) -> u64 {
269272
left + right
270273
}
271274

275+
/// Enum for selecting which features to compute or expose
276+
pub enum FeatureSelection<'a> {
277+
Indicators,
278+
Strategy {
279+
/// Name of the strategy module (e.g., "daily_1", "minute_2", etc.)
280+
strategy_name: &'a str,
281+
/// Optional parameters for the strategy (if needed)
282+
params: Option<Box<dyn std::any::Any>>,
283+
},
284+
All {
285+
/// Name of the strategy module
286+
strategy_name: &'a str,
287+
/// Optional parameters for the strategy
288+
params: Option<Box<dyn std::any::Any>>,
289+
},
290+
}
291+
292+
/// Central function to select features (indicators, strategy, or all)
293+
pub fn select_features(
294+
df: &mut polars::prelude::DataFrame,
295+
selection: FeatureSelection,
296+
) -> polars::prelude::PolarsResult<polars::prelude::DataFrame> {
297+
match selection {
298+
FeatureSelection::Indicators => {
299+
indicators::add_technical_indicators(df)
300+
}
301+
FeatureSelection::Strategy { strategy_name, params } => {
302+
// Example: match on strategy_name and call the appropriate strategy
303+
match strategy_name {
304+
"daily_1" => {
305+
use crate::strategy::daily::multi_indicator_daily_1;
306+
let params = params
307+
.and_then(|p| p.downcast::<multi_indicator_daily_1::StrategyParams>().ok())
308+
.map(|b| *b)
309+
.unwrap_or_else(multi_indicator_daily_1::StrategyParams::default);
310+
let signals = multi_indicator_daily_1::run_strategy(df, &params)
311+
.map_err(|e| polars::prelude::PolarsError::ComputeError(format!("Strategy error: {e}").into()))?;
312+
// Return a DataFrame with signals (user can extract more as needed)
313+
let mut result = df.clone();
314+
result.with_column(Series::new("buy_signals".into(), &signals.buy_signals[..]))?;
315+
result.with_column(Series::new("sell_signals".into(), &signals.sell_signals[..]))?;
316+
Ok(result)
317+
}
318+
// Add more strategies as needed
319+
_ => Err(polars::prelude::PolarsError::ComputeError("Unknown strategy name".into())),
320+
}
321+
}
322+
FeatureSelection::All { strategy_name, params } => {
323+
// Add indicators first
324+
let mut df_with_ind = indicators::add_technical_indicators(df)?;
325+
// Then run strategy
326+
select_features(&mut df_with_ind, FeatureSelection::Strategy { strategy_name, params })
327+
}
328+
}
329+
}
330+
272331
#[cfg(test)]
273332
mod tests {
274333
use super::*;

0 commit comments

Comments
 (0)