Skip to main content

navi_vm/
execution_limits.rs

1use serde::{Deserialize, Serialize};
2
3/// Configures runtime execution limits for the VM.
4///
5/// These limits protect against runaway scripts (e.g. infinite loops) that
6/// could otherwise hang the host process. All limits are enforced per bar
7/// (each call to [`Instance::run()`](crate::Instance::run) resets
8/// the counters).
9///
10/// # Examples
11///
12/// ```
13/// use navi_vm::ExecutionLimits;
14///
15/// // Use defaults (500 000 loop iterations per bar)
16/// let limits = ExecutionLimits::default();
17/// assert_eq!(limits.max_loop_iterations_per_bar, 500_000);
18///
19/// // Custom limits
20/// let limits = ExecutionLimits::default().with_max_loop_iterations_per_bar(1_000_000);
21/// ```
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(rename_all = "camelCase")]
24pub struct ExecutionLimits {
25    /// Maximum total loop iterations allowed per bar.
26    ///
27    /// All `for`, `while`, and `for..in` loops share this budget within a
28    /// single bar execution. When the limit is reached the VM raises a
29    /// runtime error.
30    ///
31    /// Set to [`u64::MAX`] to effectively disable the limit.
32    pub max_loop_iterations_per_bar: u64,
33
34    /// Maximum nesting depth for `request.security()` calls.
35    ///
36    /// The main chart has depth 0; each nested `request.security()` expression
37    /// increments the depth by one. A depth limit of 3 means a security
38    /// expression can itself call `request.security()` up to two more levels.
39    ///
40    /// Set to `0` to completely disable `request.security()`.
41    pub max_security_depth: usize,
42
43    /// Maximum number of unique `(symbol, timeframe)` streams for
44    /// `request.security()` calls.
45    ///
46    /// Multiple call sites that request the same `(symbol, timeframe)` pair
47    /// share a single stream and count as one toward this limit.
48    ///
49    /// Set to `0` to completely disable `request.security()`.
50    pub max_security_calls: usize,
51
52    /// Maximum number of historical bars retained in memory for series
53    /// variables and candlestick data.
54    ///
55    /// When the number of bars exceeds this limit, the oldest bars are
56    /// discarded from the front of the series. History references like
57    /// `close[N]` where `N >= max_bars_back` will return `na`.
58    pub max_bars_back: usize,
59}
60
61impl ExecutionLimits {
62    /// Sets the maximum number of loop iterations allowed per bar.
63    #[must_use]
64    pub const fn with_max_loop_iterations_per_bar(mut self, value: u64) -> Self {
65        self.max_loop_iterations_per_bar = value;
66        self
67    }
68
69    /// Sets the maximum `request.security()` nesting depth.
70    #[must_use]
71    pub const fn with_max_security_depth(mut self, value: usize) -> Self {
72        self.max_security_depth = value;
73        self
74    }
75
76    /// Sets the maximum number of unique `(symbol, timeframe)` streams.
77    #[must_use]
78    pub const fn with_max_security_calls(mut self, value: usize) -> Self {
79        self.max_security_calls = value;
80        self
81    }
82
83    /// Sets the maximum number of historical bars retained in memory.
84    #[must_use]
85    pub const fn with_max_bars_back(mut self, value: usize) -> Self {
86        self.max_bars_back = value;
87        self
88    }
89}
90
91impl Default for ExecutionLimits {
92    fn default() -> Self {
93        Self {
94            max_loop_iterations_per_bar: 500_000,
95            max_security_depth: 3,
96            max_security_calls: 40,
97            max_bars_back: 1000,
98        }
99    }
100}