Skip to main content

nautilus_trading/examples/strategies/hurst_vpin_directional/
config.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Configuration for the Hurst/VPIN directional strategy.
17
18use nautilus_model::{
19    data::BarType,
20    identifiers::{InstrumentId, StrategyId},
21    types::Quantity,
22};
23
24use crate::strategy::StrategyConfig;
25
26/// Configuration for the Hurst/VPIN directional strategy.
27///
28/// Combines a rescaled-range Hurst regime filter on dollar bars with a
29/// VPIN-derived informed-flow signal, and gates entry timing on the
30/// live quote stream.
31#[derive(Debug, Clone)]
32#[cfg_attr(
33    feature = "python",
34    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.trading", from_py_object)
35)]
36pub struct HurstVpinDirectionalConfig {
37    /// Base strategy configuration.
38    pub base: StrategyConfig,
39    /// Instrument to subscribe to and trade.
40    pub instrument_id: InstrumentId,
41    /// Dollar bar type (value aggregation sourced from trades).
42    pub bar_type: BarType,
43    /// Order quantity for each entry.
44    pub trade_size: Quantity,
45    /// Rolling window of dollar bar returns used to estimate the Hurst exponent.
46    pub hurst_window: usize,
47    /// Lag set used for rescaled range regression.
48    pub hurst_lags: Vec<usize>,
49    /// Hurst threshold for entering a position (trending regime).
50    pub hurst_enter: f64,
51    /// Hurst threshold for exiting an open position (regime decay).
52    pub hurst_exit: f64,
53    /// Number of completed volume buckets averaged for VPIN.
54    pub vpin_window: usize,
55    /// Minimum VPIN value required to treat a bucket imbalance as informed flow.
56    pub vpin_threshold: f64,
57    /// Maximum time (seconds) a position is held before forced flatten.
58    pub max_holding_secs: u64,
59}
60
61impl HurstVpinDirectionalConfig {
62    /// Creates a new [`HurstVpinDirectionalConfig`] with required fields and sensible defaults.
63    #[must_use]
64    pub fn new(instrument_id: InstrumentId, bar_type: BarType, trade_size: Quantity) -> Self {
65        Self {
66            base: StrategyConfig {
67                strategy_id: Some(StrategyId::from("HURST_VPIN-001")),
68                order_id_tag: Some("001".to_string()),
69                ..Default::default()
70            },
71            instrument_id,
72            bar_type,
73            trade_size,
74            hurst_window: 128,
75            hurst_lags: vec![4, 8, 16, 32],
76            hurst_enter: 0.55,
77            hurst_exit: 0.50,
78            vpin_window: 50,
79            vpin_threshold: 0.30,
80            max_holding_secs: 3600,
81        }
82    }
83
84    #[must_use]
85    pub fn with_hurst_window(mut self, window: usize) -> Self {
86        self.hurst_window = window;
87        self
88    }
89
90    #[must_use]
91    pub fn with_hurst_lags(mut self, lags: Vec<usize>) -> Self {
92        self.hurst_lags = lags;
93        self
94    }
95
96    #[must_use]
97    pub fn with_hurst_enter(mut self, threshold: f64) -> Self {
98        self.hurst_enter = threshold;
99        self
100    }
101
102    #[must_use]
103    pub fn with_hurst_exit(mut self, threshold: f64) -> Self {
104        self.hurst_exit = threshold;
105        self
106    }
107
108    #[must_use]
109    pub fn with_vpin_window(mut self, window: usize) -> Self {
110        self.vpin_window = window;
111        self
112    }
113
114    #[must_use]
115    pub fn with_vpin_threshold(mut self, threshold: f64) -> Self {
116        self.vpin_threshold = threshold;
117        self
118    }
119
120    #[must_use]
121    pub fn with_max_holding_secs(mut self, secs: u64) -> Self {
122        self.max_holding_secs = secs;
123        self
124    }
125
126    #[must_use]
127    pub fn with_strategy_id(mut self, strategy_id: StrategyId) -> Self {
128        self.base.strategy_id = Some(strategy_id);
129        self
130    }
131
132    #[must_use]
133    pub fn with_order_id_tag(mut self, tag: String) -> Self {
134        self.base.order_id_tag = Some(tag);
135        self
136    }
137}
138
139#[cfg(feature = "python")]
140#[pyo3::pymethods]
141impl HurstVpinDirectionalConfig {
142    #[new]
143    #[pyo3(signature = (
144        instrument_id,
145        bar_type,
146        trade_size,
147        strategy_id=None,
148        order_id_tag=None,
149        hurst_window=128,
150        hurst_lags=vec![4, 8, 16, 32],
151        hurst_enter=0.55,
152        hurst_exit=0.50,
153        vpin_window=50,
154        vpin_threshold=0.30,
155        max_holding_secs=3600,
156    ))]
157    #[expect(clippy::too_many_arguments)]
158    fn py_new(
159        instrument_id: InstrumentId,
160        bar_type: BarType,
161        trade_size: Quantity,
162        strategy_id: Option<StrategyId>,
163        order_id_tag: Option<String>,
164        hurst_window: usize,
165        hurst_lags: Vec<usize>,
166        hurst_enter: f64,
167        hurst_exit: f64,
168        vpin_window: usize,
169        vpin_threshold: f64,
170        max_holding_secs: u64,
171    ) -> Self {
172        let mut config = Self::new(instrument_id, bar_type, trade_size)
173            .with_hurst_window(hurst_window)
174            .with_hurst_lags(hurst_lags)
175            .with_hurst_enter(hurst_enter)
176            .with_hurst_exit(hurst_exit)
177            .with_vpin_window(vpin_window)
178            .with_vpin_threshold(vpin_threshold)
179            .with_max_holding_secs(max_holding_secs);
180
181        if let Some(id) = strategy_id {
182            config.base.strategy_id = Some(id);
183        }
184
185        if let Some(tag) = order_id_tag {
186            config.base.order_id_tag = Some(tag);
187        }
188
189        config
190    }
191
192    #[getter]
193    fn instrument_id(&self) -> InstrumentId {
194        self.instrument_id
195    }
196
197    #[getter]
198    fn bar_type(&self) -> BarType {
199        self.bar_type
200    }
201
202    #[getter]
203    fn trade_size(&self) -> Quantity {
204        self.trade_size
205    }
206
207    #[getter]
208    fn hurst_window(&self) -> usize {
209        self.hurst_window
210    }
211
212    #[getter]
213    fn hurst_lags(&self) -> Vec<usize> {
214        self.hurst_lags.clone()
215    }
216
217    #[getter]
218    fn hurst_enter(&self) -> f64 {
219        self.hurst_enter
220    }
221
222    #[getter]
223    fn hurst_exit(&self) -> f64 {
224        self.hurst_exit
225    }
226
227    #[getter]
228    fn vpin_window(&self) -> usize {
229        self.vpin_window
230    }
231
232    #[getter]
233    fn vpin_threshold(&self) -> f64 {
234        self.vpin_threshold
235    }
236
237    #[getter]
238    fn max_holding_secs(&self) -> u64 {
239        self.max_holding_secs
240    }
241}