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}