openpine_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 openpine_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)]
23pub struct ExecutionLimits {
24    /// Maximum total loop iterations allowed per bar.
25    ///
26    /// All `for`, `while`, and `for..in` loops share this budget within a
27    /// single bar execution. When the limit is reached the VM raises a
28    /// runtime error.
29    ///
30    /// Set to [`u64::MAX`] to effectively disable the limit.
31    pub max_loop_iterations_per_bar: u64,
32
33    /// Maximum nesting depth for `request.security()` calls.
34    ///
35    /// The main chart has depth 0; each nested `request.security()` expression
36    /// increments the depth by one. A depth limit of 3 means a security
37    /// expression can itself call `request.security()` up to two more levels.
38    ///
39    /// Set to `0` to completely disable `request.security()`.
40    pub max_security_depth: usize,
41
42    /// Maximum number of unique `(symbol, timeframe)` streams for
43    /// `request.security()` calls.
44    ///
45    /// Multiple call sites that request the same `(symbol, timeframe)` pair
46    /// share a single stream and count as one toward this limit.
47    ///
48    /// Set to `0` to completely disable `request.security()`.
49    pub max_security_calls: usize,
50}
51
52impl ExecutionLimits {
53    /// Sets the maximum number of loop iterations allowed per bar.
54    #[must_use]
55    pub const fn with_max_loop_iterations_per_bar(mut self, value: u64) -> Self {
56        self.max_loop_iterations_per_bar = value;
57        self
58    }
59
60    /// Sets the maximum `request.security()` nesting depth.
61    #[must_use]
62    pub const fn with_max_security_depth(mut self, value: usize) -> Self {
63        self.max_security_depth = value;
64        self
65    }
66
67    /// Sets the maximum number of unique `(symbol, timeframe)` streams.
68    #[must_use]
69    pub const fn with_max_security_calls(mut self, value: usize) -> Self {
70        self.max_security_calls = value;
71        self
72    }
73}
74
75impl Default for ExecutionLimits {
76    fn default() -> Self {
77        Self {
78            max_loop_iterations_per_bar: 500_000,
79            max_security_depth: 3,
80            max_security_calls: 40,
81        }
82    }
83}