vexity 0.0.3

Tiny scripting language for hacking on abstractions of financial markets.
Documentation
use crate::interpreter::{Func, VarValue};
use std::sync::Arc;

// These are built in functions apart of the Vexity standard library.

pub fn sma() -> Func {
    Func::Builtin(Arc::new(Box::new(|args: Vec<VarValue>| {
        if args.len() != 1 {
            panic!("`sma` expects 1 argument: a numeric array (data window)");
        }

        let data_window: Vec<f64> = match &args[0] {
            VarValue::Array(vals) => vals.iter().map(to_f64_or_panic).collect(),
            other => panic!("`sma`: expected array as first argument, got {other:?}"),
        };

        let sma = quant_mathema::smoothing::sma(&data_window);
        VarValue::Float(sma)
    })))
}
pub fn sma_batch() -> Func {
    Func::Builtin(Arc::new(Box::new(|args: Vec<VarValue>| {
        if args.len() != 2 {
            panic!("`sma_batch` expects 2 arguments: a numeric array, and a window length");
        }

        let data: Vec<f64> = match &args[0] {
            VarValue::Array(vals) => vals.iter().map(to_f64_or_panic).collect(),
            other => panic!("`sma_batch`: expected array as first argument, got {other:?}"),
        };

        let window_len = match &args[1] {
            VarValue::Int(n) => *n as usize,
            other => panic!("`sma_batch`: expected int as second argument, got {other:?}"),
        };

        let series = quant_mathema::smoothing::sma_series(&data, window_len);
        VarValue::Array(series.into_iter().map(VarValue::Float).collect())
    })))
}

pub fn ema() -> Func {
    Func::Builtin(Arc::new(Box::new(|args: Vec<VarValue>| {
        if args.len() != 1 {
            panic!("`ema` expects 1 argument: a numeric array (data window)");
        }

        let data_window = match &args[0] {
            VarValue::Array(vals) => vals.iter().map(to_f64_or_panic).collect::<Vec<f64>>(),
            other => panic!("`ema`: expected array as first argument, got {other:?}"),
        };

        let ema = quant_mathema::smoothing::ema(&data_window);
        VarValue::Float(ema)
    })))
}
pub fn ema_batch() -> Func {
    Func::Builtin(Arc::new(Box::new(|args: Vec<VarValue>| {
        if args.len() != 2 {
            panic!("`ema_batch` expects 2 arguments: an array and a window length");
        }

        let series = match &args[0] {
            VarValue::Array(vals) => vals.iter().map(to_f64_or_panic).collect::<Vec<f64>>(),
            other => panic!("`ema_batch`: expected array as first argument, got {other:?}"),
        };

        let window_len = match &args[1] {
            VarValue::Int(n) => *n as usize,
            other => panic!("`ema_batch`: expected int as second argument, got {other:?}"),
        };

        let ema_series = quant_mathema::smoothing::ema_series(&series, window_len);
        VarValue::Array(ema_series.into_iter().map(VarValue::Float).collect())
    })))
}

pub fn vwma() -> Func {
    Func::Builtin(Arc::new(Box::new(|args: Vec<VarValue>| {
        if args.len() != 3 {
            panic!("`vwma` expects 2 arguments: price array, volume array");
        }

        let price_window: Vec<f64> = match &args[0] {
            VarValue::Array(vals) => vals.iter().map(to_f64_or_panic).collect(),
            other => panic!("`vwma`: expected array as first argument, got {other:?}"),
        };

        let volume_window: Vec<u32> = match &args[1] {
            VarValue::Array(vals) => vals
                .iter()
                .map(|val| match val {
                    VarValue::Int(x) => *x as u32,
                    other => panic!(
                        "`vwma`: expected array of integers as second argument, got {other:?}",
                    ),
                })
                .collect(),
            other => panic!("`vwma`: expected array as second argument, got {other:?}"),
        };

        let vwma = quant_mathema::smoothing::vwma(&price_window, &volume_window);
        VarValue::Float(vwma)
    })))
}
pub fn vwma_batch() -> Func {
    Func::Builtin(Arc::new(Box::new(|args: Vec<VarValue>| {
        if args.len() != 3 {
            panic!("`vwma_batch` expects 3 arguments: prices array, volumes array, window length");
        }

        let prices: Vec<f64> = match &args[0] {
            VarValue::Array(vals) => vals.iter().map(to_f64_or_panic).collect(),
            other => panic!("`vwma_batch`: expected array as first argument, got {other:?}"),
        };

        let volumes: Vec<u32> = match &args[1] {
            VarValue::Array(vals) => vals
                .iter()
                .map(|val| match val {
                    VarValue::Int(x) => *x as u32,
                    other => panic!(
                        "`vwma_batch`: expected array of integers as second argument, got {other:?}",
                    ),
                })
                .collect(),
            other => panic!("`vwma_batch`: expected array as second argument, got {other:?}"),
        };

        let window_len = match &args[2] {
            VarValue::Int(n) => *n as usize,
            other => panic!("`vwma_batch`: expected int as third argument, got {other:?}"),
        };

        let series = quant_mathema::smoothing::vwma_series(&prices, &volumes, window_len);
        VarValue::Array(series.into_iter().map(VarValue::Float).collect())
    })))
}

pub fn lsma() -> Func {
    Func::Builtin(Arc::new(Box::new(|args: Vec<VarValue>| {
        if args.len() != 1 {
            panic!("`lsma` expects 1 argument: a numeric array (data window)");
        }

        let data_window: Vec<f64> = match &args[0] {
            VarValue::Array(vals) => vals.iter().map(to_f64_or_panic).collect(),
            other => panic!("`lsma`: expected array as first argument, got {other:?}"),
        };

        if let Ok(lsma) = quant_mathema::smoothing::lsma(&data_window) {
            VarValue::Float(lsma)
        } else {
            // TODO: Implement `VarValue::Null`
            VarValue::Float(0.0)
        }
    })))
}
pub fn lsma_batch() -> Func {
    Func::Builtin(Arc::new(Box::new(|args: Vec<VarValue>| {
        if args.len() != 2 {
            panic!("`lsma_batch` expects 2 arguments: a numeric array, and a window length");
        }

        let data: Vec<f64> = match &args[0] {
            VarValue::Array(vals) => vals.iter().map(to_f64_or_panic).collect(),
            other => panic!("`lsma_batch`: expected array as first argument, got {other:?}"),
        };

        let window_len = match &args[1] {
            VarValue::Int(n) => *n as usize,
            other => panic!("`lsma_batch`: expected int as second argument, got {other:?}"),
        };

        if let Ok(series) = quant_mathema::smoothing::lsma_series(&data, window_len) {
            VarValue::Array(series.into_iter().map(VarValue::Float).collect())
        } else {
            VarValue::Array(Vec::new())
        }
    })))
}

fn to_f64_or_panic(v: &VarValue) -> f64 {
    match v {
        VarValue::Float(f) => *f,
        VarValue::Int(i) => *i as f64,
        other => panic!("expected numeric array element, got {other:?}"),
    }
}