Skip to main content

pine_builtins/
lib.rs

1use pine_builtin_macro::BuiltinFunction;
2use pine_core::{PineVersion, SymInfo, Timeframe};
3use pine_interpreter::{
4    AlertConditionOutput, BoxOutput, FillOutput, GlobalOutput, IndicatorOutput, InputOutput,
5    LabelOutput, LineOutput, LogOutput, PineOutput, PlotOutput, TableOutput,
6};
7use pine_interpreter::{Interpreter, RuntimeError, Value};
8use std::collections::HashMap;
9
10// Re-export for convenience
11pub use pine_core::Bar;
12pub use pine_interpreter::BuiltinFn;
13pub use pine_interpreter::DefaultPineOutput;
14pub use pine_interpreter::EvaluatedArg;
15pub use pine_interpreter::LogLevel;
16
17// Namespace modules
18mod alertcondition;
19mod array;
20mod barstate;
21mod r#box;
22mod color;
23mod constants;
24mod currency;
25mod fill;
26mod globals;
27mod indicator;
28mod input;
29mod label;
30mod line;
31mod log;
32mod math;
33mod matrix;
34mod plot;
35mod request;
36mod str;
37mod strategy;
38mod syminfo;
39mod ta;
40mod table;
41mod time;
42mod timeframe;
43
44// Global utility functions - defined first so they can be referenced in register function
45
46/// na(value) - Returns true if the value is na, false otherwise
47#[derive(BuiltinFunction)]
48#[builtin(name = "na")]
49struct Na<O: PineOutput> {
50    value: Value<O>,
51}
52
53impl<O: PineOutput> Na<O> {
54    fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
55        Ok(Value::Bool(matches!(self.value, Value::Na)))
56    }
57}
58
59/// bool(x) - Converts value to bool
60#[derive(BuiltinFunction)]
61#[builtin(name = "bool")]
62struct Bool<O: PineOutput> {
63    x: Value<O>,
64}
65
66impl<O: PineOutput> Bool<O> {
67    fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
68        match &self.x {
69            Value::Bool(b) => Ok(Value::Bool(*b)),
70            Value::Int(n) => Ok(Value::Bool(*n != 0)),
71            Value::Number(n) => Ok(Value::Bool(*n != 0.0)),
72            Value::Na => Ok(Value::Bool(false)),
73            _ => Ok(Value::Bool(true)),
74        }
75    }
76}
77
78/// int(x) - Converts value to int (truncates float)
79#[derive(BuiltinFunction)]
80#[builtin(name = "int")]
81struct Int<O: PineOutput> {
82    x: Value<O>,
83}
84
85impl<O: PineOutput> Int<O> {
86    fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
87        match &self.x {
88            Value::Int(n) => Ok(Value::Int(*n)),
89            Value::Number(n) => Ok(Value::Int(n.trunc() as i64)),
90            Value::Bool(b) => Ok(Value::Int(if *b { 1 } else { 0 })),
91            Value::Na => Ok(Value::Na),
92            _ => Err(RuntimeError::TypeError(format!(
93                "Cannot convert {:?} to int",
94                self.x
95            ))),
96        }
97    }
98}
99
100/// float(x) - Converts value to float
101#[derive(BuiltinFunction)]
102#[builtin(name = "float")]
103struct Float<O: PineOutput> {
104    x: Value<O>,
105}
106
107impl<O: PineOutput> Float<O> {
108    fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
109        match &self.x {
110            Value::Int(n) => Ok(Value::Number(*n as f64)),
111            Value::Number(n) => Ok(Value::Number(*n)),
112            Value::Bool(b) => Ok(Value::Number(if *b { 1.0 } else { 0.0 })),
113            Value::Na => Ok(Value::Na),
114            _ => Err(RuntimeError::TypeError(format!(
115                "Cannot convert {:?} to float",
116                self.x
117            ))),
118        }
119    }
120}
121
122/// nz(source, replacement) - Replaces na values with default or replacement value
123#[derive(BuiltinFunction)]
124#[builtin(name = "nz")]
125struct Nz<O: PineOutput> {
126    source: Value<O>,
127    #[arg(default = Value::Number(0.0))]
128    replacement: Value<O>,
129}
130
131impl<O: PineOutput> Nz<O> {
132    fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
133        match &self.source {
134            Value::Na => {
135                // If replacement is not provided (default), use type-specific defaults
136                match &self.replacement {
137                    Value::Number(_) => Ok(self.replacement.clone()),
138                    _ => Ok(Value::Number(0.0)),
139                }
140            }
141            _ => Ok(self.source.clone()),
142        }
143    }
144}
145
146/// fixnan(source) - Replaces NaN values with previous nearest non-NaN value
147#[derive(BuiltinFunction)]
148#[builtin(name = "fixnan")]
149struct Fixnan<O: PineOutput> {
150    source: Value<O>,
151}
152
153impl<O: PineOutput> Fixnan<O> {
154    fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
155        // This is a simplified implementation
156        // A full implementation would need to track previous values across bar evaluations
157        match &self.source {
158            Value::Na => {
159                // Try to get the last non-na value from context
160                // For now, just return 0.0 as a placeholder
161                Ok(Value::Number(0.0))
162            }
163            Value::Number(n) if n.is_nan() => Ok(Value::Number(0.0)),
164            _ => Ok(self.source.clone()),
165        }
166    }
167}
168
169/// Register all builtin namespaces as objects and global functions
170/// Returns namespace objects to be loaded as variables (e.g., "array", "str", "ta")
171/// and global builtin functions (e.g., "na")
172/// Each member stores the builtin function pointer as Value::BuiltinFunction
173///
174/// This uses DefaultPineOutput for now. Full generic support will be added when the
175/// BuiltinFunction macro is updated to support generic output types.
176pub fn register_namespace_objects<
177    O: PineOutput
178        + LogOutput
179        + PlotOutput
180        + LabelOutput
181        + BoxOutput
182        + InputOutput
183        + LineOutput
184        + TableOutput
185        + IndicatorOutput
186        + GlobalOutput
187        + AlertConditionOutput
188        + FillOutput,
189>(
190    version: PineVersion,
191    syminfo: Option<SymInfo>,
192    timeframe: Option<Timeframe>,
193) -> HashMap<String, Value<O>> {
194    let mut namespaces = HashMap::new();
195
196    // `syminfo` and `timeframe` are always present in Pine, so an absent one
197    // falls back to defaults.
198    namespaces.insert(
199        "syminfo".to_string(),
200        syminfo::create_syminfo(syminfo.unwrap_or_default()),
201    );
202    namespaces.insert(
203        "timeframe".to_string(),
204        timeframe::register(timeframe.unwrap_or_default()),
205    );
206
207    // Register namespace objects
208    namespaces.insert("array".to_string(), array::register());
209    namespaces.insert("box".to_string(), r#box::register());
210    namespaces.insert("color".to_string(), color::register());
211    namespaces.insert("currency".to_string(), currency::register());
212    for (name, value) in input::register(version) {
213        namespaces.insert(name, value);
214    }
215    namespaces.insert("label".to_string(), label::register());
216    for (name, value) in line::register(version) {
217        namespaces.insert(name, value);
218    }
219    namespaces.insert("table".to_string(), table::register());
220    for (name, value) in indicator::register(version) {
221        namespaces.insert(name, value);
222    }
223    namespaces.insert("request".to_string(), request::register());
224    namespaces.insert("strategy".to_string(), strategy::register(version));
225    namespaces.insert("alertcondition".to_string(), alertcondition::register());
226    namespaces.insert("fill".to_string(), fill::register());
227    for (name, value) in globals::register() {
228        namespaces.insert(name, value);
229    }
230
231    // Constant-only namespaces (string tags used as arguments elsewhere).
232    namespaces.insert("size".to_string(), constants::size::register());
233    namespaces.insert("shape".to_string(), constants::shape::register());
234    namespaces.insert("location".to_string(), constants::location::register());
235    namespaces.insert("position".to_string(), constants::position::register());
236    namespaces.insert("display".to_string(), constants::display::register());
237    namespaces.insert("format".to_string(), constants::format::register());
238    namespaces.insert("order".to_string(), constants::order::register());
239    namespaces.insert("text".to_string(), constants::text::register());
240    namespaces.insert("xloc".to_string(), constants::xloc::register());
241    namespaces.insert("extend".to_string(), constants::extend::register());
242    namespaces.insert("barmerge".to_string(), constants::barmerge::register());
243    namespaces.insert("log".to_string(), log::register());
244    for (name, func) in math::register(version) {
245        namespaces.insert(name, func);
246    }
247    namespaces.insert("matrix".to_string(), matrix::register());
248    for (name, func) in str::register(version) {
249        namespaces.insert(name, func);
250    }
251    for (name, func) in ta::register(version) {
252        namespaces.insert(name, func);
253    }
254
255    // Register global builtin functions
256    namespaces.insert("na".to_string(), Na::<O>::builtin_value());
257    namespaces.insert("bool".to_string(), Bool::<O>::builtin_value());
258    namespaces.insert("int".to_string(), Int::<O>::builtin_value());
259    namespaces.insert("float".to_string(), Float::<O>::builtin_value());
260    namespaces.insert("nz".to_string(), Nz::<O>::builtin_value());
261    namespaces.insert("fixnan".to_string(), Fixnan::<O>::builtin_value());
262
263    // Register time/date functions
264    for (name, func) in time::register_time_functions() {
265        namespaces.insert(name, func);
266    }
267
268    // Register plot functions
269    for (name, func) in plot::register_plot_functions() {
270        namespaces.insert(name, func);
271    }
272
273    namespaces
274}
275
276/// Per-bar variables, rebuilt for each [`Bar`] and registered before it executes.
277///
278/// The compile-time counterpart is [`register_namespace_objects`]; this holds the
279/// values that change every bar.
280pub fn register_per_bar<O: PineOutput>(bar: &Bar) -> Vec<(String, Value<O>)> {
281    vec![
282        ("barstate".to_string(), barstate::register(bar)),
283        ("time".to_string(), time::register_bar_time(bar)),
284        ("timenow".to_string(), time::register_timenow()),
285    ]
286}
287
288#[cfg(test)]
289mod tests {
290    use super::*;
291    use pine_interpreter::{EvaluatedArg, FunctionCallArgs};
292
293    #[test]
294    fn test_na() {
295        let mut ctx = Interpreter::<DefaultPineOutput>::new();
296
297        // Test with na value
298        let args = vec![EvaluatedArg::Positional(Value::Na)];
299        let result = Na::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
300        assert_eq!(result, Value::Bool(true));
301
302        // Test with number
303        let args = vec![EvaluatedArg::Positional(Value::Number(42.0))];
304        let result = Na::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
305        assert_eq!(result, Value::Bool(false));
306
307        // Test with string
308        let args = vec![EvaluatedArg::Positional(Value::String("hello".to_string()))];
309        let result = Na::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
310        assert_eq!(result, Value::Bool(false));
311
312        // Test with bool
313        let args = vec![EvaluatedArg::Positional(Value::Bool(true))];
314        let result = Na::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
315        assert_eq!(result, Value::Bool(false));
316    }
317
318    #[test]
319    fn test_bool() {
320        let mut ctx = Interpreter::<DefaultPineOutput>::new();
321
322        // Test number to bool
323        let args = vec![EvaluatedArg::Positional(Value::Number(5.0))];
324        let result = Bool::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
325        assert_eq!(result, Value::Bool(true));
326
327        let args = vec![EvaluatedArg::Positional(Value::Number(0.0))];
328        let result = Bool::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
329        assert_eq!(result, Value::Bool(false));
330
331        // Test na to bool
332        let args = vec![EvaluatedArg::Positional(Value::Na)];
333        let result = Bool::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
334        assert_eq!(result, Value::Bool(false));
335    }
336
337    #[test]
338    fn test_int() {
339        let mut ctx = Interpreter::<DefaultPineOutput>::new();
340
341        // Test float to int (truncate)
342        let args = vec![EvaluatedArg::Positional(Value::Number(5.7))];
343        let result = Int::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
344        assert_eq!(result, Value::Number(5.0));
345
346        let args = vec![EvaluatedArg::Positional(Value::Number(-5.7))];
347        let result = Int::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
348        assert_eq!(result, Value::Number(-5.0));
349
350        // Test bool to int
351        let args = vec![EvaluatedArg::Positional(Value::Bool(true))];
352        let result = Int::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
353        assert_eq!(result, Value::Number(1.0));
354
355        // Test na to int
356        let args = vec![EvaluatedArg::Positional(Value::Na)];
357        let result = Int::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
358        assert_eq!(result, Value::Na);
359    }
360
361    #[test]
362    fn test_float() {
363        let mut ctx = Interpreter::<DefaultPineOutput>::new();
364
365        // Test number to float
366        let args = vec![EvaluatedArg::Positional(Value::Number(5.0))];
367        let result = Float::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
368        assert_eq!(result, Value::Number(5.0));
369
370        // Test bool to float
371        let args = vec![EvaluatedArg::Positional(Value::Bool(true))];
372        let result = Float::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
373        assert_eq!(result, Value::Number(1.0));
374
375        // Test na to float
376        let args = vec![EvaluatedArg::Positional(Value::Na)];
377        let result = Float::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
378        assert_eq!(result, Value::Na);
379    }
380
381    #[test]
382    fn test_nz() {
383        let mut ctx = Interpreter::<DefaultPineOutput>::new();
384
385        // Test na value without replacement (should return 0.0)
386        let args = vec![EvaluatedArg::Positional(Value::Na)];
387        let result = Nz::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
388        assert_eq!(result, Value::Number(0.0));
389
390        // Test na value with replacement
391        let args = vec![
392            EvaluatedArg::Positional(Value::Na),
393            EvaluatedArg::Positional(Value::Number(42.0)),
394        ];
395        let result = Nz::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
396        assert_eq!(result, Value::Number(42.0));
397
398        // Test non-na value (should return source)
399        let args = vec![EvaluatedArg::Positional(Value::Number(5.0))];
400        let result = Nz::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
401        assert_eq!(result, Value::Number(5.0));
402    }
403
404    #[test]
405    fn test_fixnan() {
406        let mut ctx = Interpreter::<DefaultPineOutput>::new();
407
408        // Test na value
409        let args = vec![EvaluatedArg::Positional(Value::Na)];
410        let result = Fixnan::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
411        assert_eq!(result, Value::Number(0.0));
412
413        // Test normal value
414        let args = vec![EvaluatedArg::Positional(Value::Number(5.0))];
415        let result = Fixnan::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
416        assert_eq!(result, Value::Number(5.0));
417
418        // Test NaN value
419        let args = vec![EvaluatedArg::Positional(Value::Number(f64::NAN))];
420        let result = Fixnan::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
421        assert_eq!(result, Value::Number(0.0));
422    }
423}