use pine_builtin_macro::BuiltinFunction;
use pine_core::{PineVersion, SymInfo, Timeframe};
use pine_interpreter::{
AlertConditionOutput, BoxOutput, FillOutput, GlobalOutput, IndicatorOutput, InputOutput,
LabelOutput, LineOutput, LogOutput, PineOutput, PlotOutput, TableOutput,
};
use pine_interpreter::{Interpreter, RuntimeError, Value};
use std::collections::HashMap;
pub use pine_core::Bar;
pub use pine_interpreter::BuiltinFn;
pub use pine_interpreter::DefaultPineOutput;
pub use pine_interpreter::EvaluatedArg;
pub use pine_interpreter::LogLevel;
mod alertcondition;
mod array;
mod barstate;
mod r#box;
mod color;
mod constants;
mod currency;
mod fill;
mod globals;
mod indicator;
mod input;
mod label;
mod line;
mod log;
mod math;
mod matrix;
mod plot;
mod request;
mod str;
mod strategy;
mod syminfo;
mod ta;
mod table;
mod time;
mod timeframe;
#[derive(BuiltinFunction)]
#[builtin(name = "na")]
struct Na<O: PineOutput> {
value: Value<O>,
}
impl<O: PineOutput> Na<O> {
fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
Ok(Value::Bool(matches!(self.value, Value::Na)))
}
}
#[derive(BuiltinFunction)]
#[builtin(name = "bool")]
struct Bool<O: PineOutput> {
x: Value<O>,
}
impl<O: PineOutput> Bool<O> {
fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
match &self.x {
Value::Bool(b) => Ok(Value::Bool(*b)),
Value::Int(n) => Ok(Value::Bool(*n != 0)),
Value::Number(n) => Ok(Value::Bool(*n != 0.0)),
Value::Na => Ok(Value::Bool(false)),
_ => Ok(Value::Bool(true)),
}
}
}
#[derive(BuiltinFunction)]
#[builtin(name = "int")]
struct Int<O: PineOutput> {
x: Value<O>,
}
impl<O: PineOutput> Int<O> {
fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
match &self.x {
Value::Int(n) => Ok(Value::Int(*n)),
Value::Number(n) => Ok(Value::Int(n.trunc() as i64)),
Value::Bool(b) => Ok(Value::Int(if *b { 1 } else { 0 })),
Value::Na => Ok(Value::Na),
_ => Err(RuntimeError::TypeError(format!(
"Cannot convert {:?} to int",
self.x
))),
}
}
}
#[derive(BuiltinFunction)]
#[builtin(name = "float")]
struct Float<O: PineOutput> {
x: Value<O>,
}
impl<O: PineOutput> Float<O> {
fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
match &self.x {
Value::Int(n) => Ok(Value::Number(*n as f64)),
Value::Number(n) => Ok(Value::Number(*n)),
Value::Bool(b) => Ok(Value::Number(if *b { 1.0 } else { 0.0 })),
Value::Na => Ok(Value::Na),
_ => Err(RuntimeError::TypeError(format!(
"Cannot convert {:?} to float",
self.x
))),
}
}
}
#[derive(BuiltinFunction)]
#[builtin(name = "nz")]
struct Nz<O: PineOutput> {
source: Value<O>,
#[arg(default = Value::Number(0.0))]
replacement: Value<O>,
}
impl<O: PineOutput> Nz<O> {
fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
match &self.source {
Value::Na => {
match &self.replacement {
Value::Number(_) => Ok(self.replacement.clone()),
_ => Ok(Value::Number(0.0)),
}
}
_ => Ok(self.source.clone()),
}
}
}
#[derive(BuiltinFunction)]
#[builtin(name = "fixnan")]
struct Fixnan<O: PineOutput> {
source: Value<O>,
}
impl<O: PineOutput> Fixnan<O> {
fn execute(&self, _ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
match &self.source {
Value::Na => {
Ok(Value::Number(0.0))
}
Value::Number(n) if n.is_nan() => Ok(Value::Number(0.0)),
_ => Ok(self.source.clone()),
}
}
}
pub fn register_namespace_objects<
O: PineOutput
+ LogOutput
+ PlotOutput
+ LabelOutput
+ BoxOutput
+ InputOutput
+ LineOutput
+ TableOutput
+ IndicatorOutput
+ GlobalOutput
+ AlertConditionOutput
+ FillOutput,
>(
version: PineVersion,
syminfo: Option<SymInfo>,
timeframe: Option<Timeframe>,
) -> HashMap<String, Value<O>> {
let mut namespaces = HashMap::new();
namespaces.insert(
"syminfo".to_string(),
syminfo::create_syminfo(syminfo.unwrap_or_default()),
);
namespaces.insert(
"timeframe".to_string(),
timeframe::register(timeframe.unwrap_or_default()),
);
namespaces.insert("array".to_string(), array::register());
namespaces.insert("box".to_string(), r#box::register());
namespaces.insert("color".to_string(), color::register());
namespaces.insert("currency".to_string(), currency::register());
for (name, value) in input::register(version) {
namespaces.insert(name, value);
}
namespaces.insert("label".to_string(), label::register());
for (name, value) in line::register(version) {
namespaces.insert(name, value);
}
namespaces.insert("table".to_string(), table::register());
for (name, value) in indicator::register(version) {
namespaces.insert(name, value);
}
namespaces.insert("request".to_string(), request::register());
namespaces.insert("strategy".to_string(), strategy::register(version));
namespaces.insert("alertcondition".to_string(), alertcondition::register());
namespaces.insert("fill".to_string(), fill::register());
for (name, value) in globals::register() {
namespaces.insert(name, value);
}
namespaces.insert("size".to_string(), constants::size::register());
namespaces.insert("shape".to_string(), constants::shape::register());
namespaces.insert("location".to_string(), constants::location::register());
namespaces.insert("position".to_string(), constants::position::register());
namespaces.insert("display".to_string(), constants::display::register());
namespaces.insert("format".to_string(), constants::format::register());
namespaces.insert("order".to_string(), constants::order::register());
namespaces.insert("text".to_string(), constants::text::register());
namespaces.insert("xloc".to_string(), constants::xloc::register());
namespaces.insert("extend".to_string(), constants::extend::register());
namespaces.insert("barmerge".to_string(), constants::barmerge::register());
namespaces.insert("log".to_string(), log::register());
for (name, func) in math::register(version) {
namespaces.insert(name, func);
}
namespaces.insert("matrix".to_string(), matrix::register());
for (name, func) in str::register(version) {
namespaces.insert(name, func);
}
for (name, func) in ta::register(version) {
namespaces.insert(name, func);
}
namespaces.insert("na".to_string(), Na::<O>::builtin_value());
namespaces.insert("bool".to_string(), Bool::<O>::builtin_value());
namespaces.insert("int".to_string(), Int::<O>::builtin_value());
namespaces.insert("float".to_string(), Float::<O>::builtin_value());
namespaces.insert("nz".to_string(), Nz::<O>::builtin_value());
namespaces.insert("fixnan".to_string(), Fixnan::<O>::builtin_value());
for (name, func) in time::register_time_functions() {
namespaces.insert(name, func);
}
for (name, func) in plot::register_plot_functions() {
namespaces.insert(name, func);
}
namespaces
}
pub fn register_per_bar<O: PineOutput>(bar: &Bar) -> Vec<(String, Value<O>)> {
vec![
("barstate".to_string(), barstate::register(bar)),
("time".to_string(), time::register_bar_time(bar)),
("timenow".to_string(), time::register_timenow()),
]
}
#[cfg(test)]
mod tests {
use super::*;
use pine_interpreter::{EvaluatedArg, FunctionCallArgs};
#[test]
fn test_na() {
let mut ctx = Interpreter::<DefaultPineOutput>::new();
let args = vec![EvaluatedArg::Positional(Value::Na)];
let result = Na::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Bool(true));
let args = vec![EvaluatedArg::Positional(Value::Number(42.0))];
let result = Na::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Bool(false));
let args = vec![EvaluatedArg::Positional(Value::String("hello".to_string()))];
let result = Na::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Bool(false));
let args = vec![EvaluatedArg::Positional(Value::Bool(true))];
let result = Na::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_bool() {
let mut ctx = Interpreter::<DefaultPineOutput>::new();
let args = vec![EvaluatedArg::Positional(Value::Number(5.0))];
let result = Bool::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Bool(true));
let args = vec![EvaluatedArg::Positional(Value::Number(0.0))];
let result = Bool::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Bool(false));
let args = vec![EvaluatedArg::Positional(Value::Na)];
let result = Bool::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_int() {
let mut ctx = Interpreter::<DefaultPineOutput>::new();
let args = vec![EvaluatedArg::Positional(Value::Number(5.7))];
let result = Int::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Number(5.0));
let args = vec![EvaluatedArg::Positional(Value::Number(-5.7))];
let result = Int::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Number(-5.0));
let args = vec![EvaluatedArg::Positional(Value::Bool(true))];
let result = Int::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Number(1.0));
let args = vec![EvaluatedArg::Positional(Value::Na)];
let result = Int::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Na);
}
#[test]
fn test_float() {
let mut ctx = Interpreter::<DefaultPineOutput>::new();
let args = vec![EvaluatedArg::Positional(Value::Number(5.0))];
let result = Float::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Number(5.0));
let args = vec![EvaluatedArg::Positional(Value::Bool(true))];
let result = Float::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Number(1.0));
let args = vec![EvaluatedArg::Positional(Value::Na)];
let result = Float::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Na);
}
#[test]
fn test_nz() {
let mut ctx = Interpreter::<DefaultPineOutput>::new();
let args = vec![EvaluatedArg::Positional(Value::Na)];
let result = Nz::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Number(0.0));
let args = vec![
EvaluatedArg::Positional(Value::Na),
EvaluatedArg::Positional(Value::Number(42.0)),
];
let result = Nz::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Number(42.0));
let args = vec![EvaluatedArg::Positional(Value::Number(5.0))];
let result = Nz::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Number(5.0));
}
#[test]
fn test_fixnan() {
let mut ctx = Interpreter::<DefaultPineOutput>::new();
let args = vec![EvaluatedArg::Positional(Value::Na)];
let result = Fixnan::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Number(0.0));
let args = vec![EvaluatedArg::Positional(Value::Number(5.0))];
let result = Fixnan::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Number(5.0));
let args = vec![EvaluatedArg::Positional(Value::Number(f64::NAN))];
let result = Fixnan::builtin_fn(&mut ctx, FunctionCallArgs::without_types(args)).unwrap();
assert_eq!(result, Value::Number(0.0));
}
}