use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq)]
pub struct Color {
pub r: u8, pub g: u8, pub b: u8, pub t: u8, }
impl Color {
pub fn new(r: u8, g: u8, b: u8, t: u8) -> Self {
Color { r, g, b, t }
}
}
#[derive(Clone, Debug)]
pub struct Label {
pub x: f64,
pub y: f64,
pub text: String,
pub xloc: String,
pub yloc: String,
pub color: Option<Color>,
pub style: String,
pub textcolor: Option<Color>,
pub size: String,
pub textalign: String,
pub tooltip: Option<String>,
pub text_font_family: String,
}
#[derive(Clone, Debug)]
pub struct FillObject {
pub id1: Option<usize>,
pub id2: Option<usize>,
pub color: Option<Color>,
pub title: String,
}
#[derive(Clone, Debug, Default)]
pub struct GlobalContext {
pub bgcolor: Option<Color>,
pub barcolor: Option<Color>,
}
#[derive(Clone, Debug, Default)]
pub struct AlertCondition {
pub title: String,
pub message: String,
}
#[derive(Clone, Debug, Default)]
pub struct Indicator {
pub title: String,
pub shorttitle: String,
pub overlay: bool,
pub format: String,
pub precision: Option<i64>,
pub timeframe: String,
}
#[derive(Clone, Debug)]
pub struct LineObject {
pub x1: f64,
pub y1: f64,
pub x2: f64,
pub y2: f64,
pub xloc: String,
pub extend: String,
pub color: Option<Color>,
pub style: String,
pub width: f64,
}
#[derive(Clone, Debug, Default)]
pub struct TableCell {
pub text: String,
pub text_color: Option<Color>,
pub bgcolor: Option<Color>,
pub text_size: String,
pub text_halign: String,
pub text_valign: String,
}
#[derive(Clone, Debug)]
pub struct Table {
pub position: String,
pub columns: usize,
pub rows: usize,
pub bgcolor: Option<Color>,
pub cells: HashMap<(usize, usize), TableCell>,
}
#[derive(Clone, Debug)]
pub struct PineBox {
pub left: f64,
pub top: f64,
pub right: f64,
pub bottom: f64,
pub border_color: Option<Color>,
pub border_width: f64,
pub border_style: String,
pub extend: String,
pub xloc: String,
pub bgcolor: Option<Color>,
pub text: String,
pub text_size: f64,
pub text_color: Option<Color>,
pub text_halign: String,
pub text_valign: String,
pub text_wrap: String,
pub text_font_family: String,
}
#[derive(Clone, Debug, Default)]
pub struct Plot {
pub series: f64,
pub title: String,
pub color: Option<Color>,
pub linewidth: f64,
pub style: String,
pub trackprice: bool,
pub histbase: f64,
pub offset: f64,
pub join: bool,
pub editable: bool,
pub show_last: Option<f64>,
pub display: String,
pub format: Option<String>,
pub precision: Option<f64>,
pub force_overlay: bool,
pub linestyle: String,
}
#[derive(Clone, Debug)]
pub struct Plotarrow {
pub series: f64,
pub title: String,
pub colorup: Option<Color>,
pub colordown: Option<Color>,
pub offset: f64,
pub minheight: f64,
pub maxheight: f64,
pub editable: bool,
pub show_last: Option<f64>,
pub display: String,
pub format: Option<String>,
pub precision: Option<f64>,
pub force_overlay: bool,
}
#[derive(Clone, Debug)]
pub struct Plotbar {
pub open: f64,
pub high: f64,
pub low: f64,
pub close: f64,
pub title: String,
pub color: Option<Color>,
pub editable: bool,
pub show_last: Option<f64>,
pub display: String,
pub format: Option<String>,
pub precision: Option<f64>,
pub force_overlay: bool,
}
#[derive(Clone, Debug)]
pub struct Plotcandle {
pub open: f64,
pub high: f64,
pub low: f64,
pub close: f64,
pub title: String,
pub color: Option<Color>,
pub wickcolor: Option<Color>,
pub editable: bool,
pub show_last: Option<f64>,
pub bordercolor: Option<Color>,
pub display: String,
pub format: Option<String>,
pub precision: Option<f64>,
pub force_overlay: bool,
}
#[derive(Clone, Debug)]
pub struct Plotchar {
pub series: f64,
pub title: String,
pub char: String,
pub location: String,
pub color: Option<Color>,
pub offset: f64,
pub text: String,
pub textcolor: Option<Color>,
pub editable: bool,
pub size: String,
pub show_last: Option<f64>,
pub display: String,
pub format: Option<String>,
pub precision: Option<f64>,
pub force_overlay: bool,
}
#[derive(Clone, Debug)]
pub struct Plotshape {
pub series: f64,
pub title: String,
pub style: String,
pub location: String,
pub color: Option<Color>,
pub offset: f64,
pub text: String,
pub textcolor: Option<Color>,
pub editable: bool,
pub size: String,
pub show_last: Option<f64>,
pub display: String,
pub format: Option<String>,
pub precision: Option<f64>,
pub force_overlay: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogLevel {
Info,
Warning,
Error,
}
#[derive(Debug, Clone)]
pub struct LogEntry {
pub level: LogLevel,
pub message: String,
}
#[derive(Debug, Clone, PartialEq)]
pub enum InputValue {
Int(i64),
Float(f64),
Bool(bool),
Str(String),
Color(Color),
}
#[derive(Debug, Clone)]
pub struct Input {
pub kind: String,
pub title: String,
pub group: String,
pub default: InputValue,
}
pub trait PineOutput: Default + Clone + std::fmt::Debug + 'static {
fn clear(&mut self);
}
pub trait LogOutput: PineOutput {
fn add_log(&mut self, level: LogLevel, message: String);
fn get_logs(&self) -> &[LogEntry];
}
#[macro_export]
macro_rules! impl_output_traits_delegate {
($type:ty, $field:ident) => {
impl $crate::LogOutput for $type {
fn add_log(&mut self, level: $crate::LogLevel, message: String) {
self.$field.add_log(level, message)
}
fn get_logs(&self) -> &[$crate::LogEntry] {
self.$field.get_logs()
}
}
impl $crate::PlotOutput for $type {
fn add_plot(&mut self, plot: $crate::Plot) {
self.$field.add_plot(plot)
}
fn plots(&self) -> &[$crate::Plot] {
self.$field.plots()
}
fn add_plotarrow(&mut self, arrow: $crate::Plotarrow) {
self.$field.add_plotarrow(arrow)
}
fn plotarrows(&self) -> &[$crate::Plotarrow] {
self.$field.plotarrows()
}
fn add_plotbar(&mut self, bar: $crate::Plotbar) {
self.$field.add_plotbar(bar)
}
fn plotbars(&self) -> &[$crate::Plotbar] {
self.$field.plotbars()
}
fn add_plotcandle(&mut self, candle: $crate::Plotcandle) {
self.$field.add_plotcandle(candle)
}
fn plotcandles(&self) -> &[$crate::Plotcandle] {
self.$field.plotcandles()
}
fn add_plotchar(&mut self, char: $crate::Plotchar) {
self.$field.add_plotchar(char)
}
fn plotchars(&self) -> &[$crate::Plotchar] {
self.$field.plotchars()
}
fn add_plotshape(&mut self, shape: $crate::Plotshape) {
self.$field.add_plotshape(shape)
}
fn plotshapes(&self) -> &[$crate::Plotshape] {
self.$field.plotshapes()
}
}
impl $crate::LabelOutput for $type {
fn add_label(&mut self, label: $crate::Label) -> usize {
self.$field.add_label(label)
}
fn get_label(&self, id: usize) -> Option<&$crate::Label> {
self.$field.get_label(id)
}
fn get_label_mut(&mut self, id: usize) -> Option<&mut $crate::Label> {
self.$field.get_label_mut(id)
}
fn delete_label(&mut self, id: usize) -> bool {
self.$field.delete_label(id)
}
}
impl $crate::BoxOutput for $type {
fn add_box(&mut self, box_obj: $crate::PineBox) -> usize {
self.$field.add_box(box_obj)
}
fn get_box(&self, id: usize) -> Option<&$crate::PineBox> {
self.$field.get_box(id)
}
fn get_box_mut(&mut self, id: usize) -> Option<&mut $crate::PineBox> {
self.$field.get_box_mut(id)
}
fn delete_box(&mut self, id: usize) -> bool {
self.$field.delete_box(id)
}
}
impl $crate::InputOutput for $type {
fn add_input(&mut self, input: $crate::Input) {
self.$field.add_input(input)
}
fn inputs(&self) -> &[$crate::Input] {
self.$field.inputs()
}
}
impl $crate::IndicatorOutput for $type {
fn set_indicator(&mut self, indicator: $crate::Indicator) {
self.$field.set_indicator(indicator)
}
fn indicator(&self) -> Option<&$crate::Indicator> {
self.$field.indicator()
}
}
impl $crate::AlertConditionOutput for $type {
fn add_alertcondition(&mut self, alert: $crate::AlertCondition) {
self.$field.add_alertcondition(alert)
}
fn alertconditions(&self) -> &[$crate::AlertCondition] {
self.$field.alertconditions()
}
}
impl $crate::FillOutput for $type {
fn add_fill(&mut self, fill: $crate::FillObject) {
self.$field.add_fill(fill)
}
fn fills(&self) -> &[$crate::FillObject] {
self.$field.fills()
}
}
impl $crate::GlobalOutput for $type {
fn set_bgcolor(&mut self, color: Option<$crate::Color>) {
self.$field.set_bgcolor(color)
}
fn set_barcolor(&mut self, color: Option<$crate::Color>) {
self.$field.set_barcolor(color)
}
fn global_context(&self) -> &$crate::GlobalContext {
self.$field.global_context()
}
}
impl $crate::LineOutput for $type {
fn add_line(&mut self, line: $crate::LineObject) -> usize {
self.$field.add_line(line)
}
fn get_line(&self, id: usize) -> Option<&$crate::LineObject> {
self.$field.get_line(id)
}
fn get_line_mut(&mut self, id: usize) -> Option<&mut $crate::LineObject> {
self.$field.get_line_mut(id)
}
fn delete_line(&mut self, id: usize) -> bool {
self.$field.delete_line(id)
}
}
impl $crate::TableOutput for $type {
fn add_table(&mut self, table: $crate::Table) -> usize {
self.$field.add_table(table)
}
fn get_table(&self, id: usize) -> Option<&$crate::Table> {
self.$field.get_table(id)
}
fn get_table_mut(&mut self, id: usize) -> Option<&mut $crate::Table> {
self.$field.get_table_mut(id)
}
fn delete_table(&mut self, id: usize) -> bool {
self.$field.delete_table(id)
}
}
};
}
pub trait PlotOutput: PineOutput {
fn add_plot(&mut self, plot: Plot);
fn plots(&self) -> &[Plot];
fn add_plotarrow(&mut self, arrow: Plotarrow);
fn plotarrows(&self) -> &[Plotarrow];
fn add_plotbar(&mut self, bar: Plotbar);
fn plotbars(&self) -> &[Plotbar];
fn add_plotcandle(&mut self, candle: Plotcandle);
fn plotcandles(&self) -> &[Plotcandle];
fn add_plotchar(&mut self, char: Plotchar);
fn plotchars(&self) -> &[Plotchar];
fn add_plotshape(&mut self, shape: Plotshape);
fn plotshapes(&self) -> &[Plotshape];
}
pub trait LabelOutput: PineOutput {
fn add_label(&mut self, label: Label) -> usize;
fn get_label(&self, id: usize) -> Option<&Label>;
fn get_label_mut(&mut self, id: usize) -> Option<&mut Label>;
fn delete_label(&mut self, id: usize) -> bool;
}
pub trait BoxOutput: PineOutput {
fn add_box(&mut self, box_obj: PineBox) -> usize;
fn get_box(&self, id: usize) -> Option<&PineBox>;
fn get_box_mut(&mut self, id: usize) -> Option<&mut PineBox>;
fn delete_box(&mut self, id: usize) -> bool;
}
pub trait TableOutput: PineOutput {
fn add_table(&mut self, table: Table) -> usize;
fn get_table(&self, id: usize) -> Option<&Table>;
fn get_table_mut(&mut self, id: usize) -> Option<&mut Table>;
fn delete_table(&mut self, id: usize) -> bool;
}
pub trait LineOutput: PineOutput {
fn add_line(&mut self, line: LineObject) -> usize;
fn get_line(&self, id: usize) -> Option<&LineObject>;
fn get_line_mut(&mut self, id: usize) -> Option<&mut LineObject>;
fn delete_line(&mut self, id: usize) -> bool;
}
pub trait FillOutput: PineOutput {
fn add_fill(&mut self, fill: FillObject);
fn fills(&self) -> &[FillObject];
}
pub trait GlobalOutput: PineOutput {
fn set_bgcolor(&mut self, color: Option<Color>);
fn set_barcolor(&mut self, color: Option<Color>);
fn global_context(&self) -> &GlobalContext;
}
pub trait AlertConditionOutput: PineOutput {
fn add_alertcondition(&mut self, alert: AlertCondition);
fn alertconditions(&self) -> &[AlertCondition];
}
pub trait IndicatorOutput: PineOutput {
fn set_indicator(&mut self, indicator: Indicator);
fn indicator(&self) -> Option<&Indicator>;
}
pub trait InputOutput: PineOutput {
fn add_input(&mut self, input: Input);
fn inputs(&self) -> &[Input];
}
#[derive(Default, Clone, Debug)]
pub struct DefaultPineOutput {
labels: HashMap<usize, Label>,
next_label_id: usize,
boxes: HashMap<usize, PineBox>,
next_box_id: usize,
lines: HashMap<usize, LineObject>,
next_line_id: usize,
tables: HashMap<usize, Table>,
next_table_id: usize,
plots: Vec<Plot>,
plotarrows: Vec<Plotarrow>,
plotbars: Vec<Plotbar>,
plotcandles: Vec<Plotcandle>,
plotchars: Vec<Plotchar>,
plotshapes: Vec<Plotshape>,
logs: Vec<LogEntry>,
inputs: Vec<Input>,
indicator: Option<Indicator>,
globals: GlobalContext,
alertconditions: Vec<AlertCondition>,
fills: Vec<FillObject>,
}
impl PineOutput for DefaultPineOutput {
fn clear(&mut self) {
self.labels.clear();
self.boxes.clear();
self.plots.clear();
self.plotarrows.clear();
self.plotbars.clear();
self.plotcandles.clear();
self.plotchars.clear();
self.plotshapes.clear();
self.logs.clear();
self.inputs.clear();
self.indicator = None;
self.globals = GlobalContext::default();
self.alertconditions.clear();
self.fills.clear();
self.lines.clear();
self.tables.clear();
self.next_label_id = 0;
self.next_box_id = 0;
self.next_line_id = 0;
self.next_table_id = 0;
}
}
impl LogOutput for DefaultPineOutput {
fn add_log(&mut self, level: LogLevel, message: String) {
self.logs.push(LogEntry { level, message });
}
fn get_logs(&self) -> &[LogEntry] {
&self.logs
}
}
impl PlotOutput for DefaultPineOutput {
fn add_plot(&mut self, plot: Plot) {
self.plots.push(plot);
}
fn plots(&self) -> &[Plot] {
&self.plots
}
fn add_plotarrow(&mut self, arrow: Plotarrow) {
self.plotarrows.push(arrow);
}
fn plotarrows(&self) -> &[Plotarrow] {
&self.plotarrows
}
fn add_plotbar(&mut self, bar: Plotbar) {
self.plotbars.push(bar);
}
fn plotbars(&self) -> &[Plotbar] {
&self.plotbars
}
fn add_plotcandle(&mut self, candle: Plotcandle) {
self.plotcandles.push(candle);
}
fn plotcandles(&self) -> &[Plotcandle] {
&self.plotcandles
}
fn add_plotchar(&mut self, char: Plotchar) {
self.plotchars.push(char);
}
fn plotchars(&self) -> &[Plotchar] {
&self.plotchars
}
fn add_plotshape(&mut self, shape: Plotshape) {
self.plotshapes.push(shape);
}
fn plotshapes(&self) -> &[Plotshape] {
&self.plotshapes
}
}
impl LabelOutput for DefaultPineOutput {
fn add_label(&mut self, label: Label) -> usize {
let id = self.next_label_id;
self.next_label_id += 1;
self.labels.insert(id, label);
id
}
fn get_label(&self, id: usize) -> Option<&Label> {
self.labels.get(&id)
}
fn get_label_mut(&mut self, id: usize) -> Option<&mut Label> {
self.labels.get_mut(&id)
}
fn delete_label(&mut self, id: usize) -> bool {
self.labels.remove(&id).is_some()
}
}
impl BoxOutput for DefaultPineOutput {
fn add_box(&mut self, box_obj: PineBox) -> usize {
let id = self.next_box_id;
self.next_box_id += 1;
self.boxes.insert(id, box_obj);
id
}
fn get_box(&self, id: usize) -> Option<&PineBox> {
self.boxes.get(&id)
}
fn get_box_mut(&mut self, id: usize) -> Option<&mut PineBox> {
self.boxes.get_mut(&id)
}
fn delete_box(&mut self, id: usize) -> bool {
self.boxes.remove(&id).is_some()
}
}
impl LineOutput for DefaultPineOutput {
fn add_line(&mut self, line: LineObject) -> usize {
let id = self.next_line_id;
self.next_line_id += 1;
self.lines.insert(id, line);
id
}
fn get_line(&self, id: usize) -> Option<&LineObject> {
self.lines.get(&id)
}
fn get_line_mut(&mut self, id: usize) -> Option<&mut LineObject> {
self.lines.get_mut(&id)
}
fn delete_line(&mut self, id: usize) -> bool {
self.lines.remove(&id).is_some()
}
}
impl TableOutput for DefaultPineOutput {
fn add_table(&mut self, table: Table) -> usize {
let id = self.next_table_id;
self.next_table_id += 1;
self.tables.insert(id, table);
id
}
fn get_table(&self, id: usize) -> Option<&Table> {
self.tables.get(&id)
}
fn get_table_mut(&mut self, id: usize) -> Option<&mut Table> {
self.tables.get_mut(&id)
}
fn delete_table(&mut self, id: usize) -> bool {
self.tables.remove(&id).is_some()
}
}
impl InputOutput for DefaultPineOutput {
fn add_input(&mut self, input: Input) {
self.inputs.push(input);
}
fn inputs(&self) -> &[Input] {
&self.inputs
}
}
impl IndicatorOutput for DefaultPineOutput {
fn set_indicator(&mut self, indicator: Indicator) {
self.indicator = Some(indicator);
}
fn indicator(&self) -> Option<&Indicator> {
self.indicator.as_ref()
}
}
impl AlertConditionOutput for DefaultPineOutput {
fn add_alertcondition(&mut self, alert: AlertCondition) {
self.alertconditions.push(alert);
}
fn alertconditions(&self) -> &[AlertCondition] {
&self.alertconditions
}
}
impl FillOutput for DefaultPineOutput {
fn add_fill(&mut self, fill: FillObject) {
self.fills.push(fill);
}
fn fills(&self) -> &[FillObject] {
&self.fills
}
}
impl GlobalOutput for DefaultPineOutput {
fn set_bgcolor(&mut self, color: Option<Color>) {
self.globals.bgcolor = color;
}
fn set_barcolor(&mut self, color: Option<Color>) {
self.globals.barcolor = color;
}
fn global_context(&self) -> &GlobalContext {
&self.globals
}
}