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}