From a3359b62d4d6c46e775ce6741419e3c68776b51b Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 2 May 2025 20:58:13 +0200 Subject: [PATCH] feat: validate annotation-type before returning this avoids breaking the whole chart due to annotations. --- docs/strategy-callbacks.md | 3 ++- freqtrade/ft_types/plot_annotation_type.py | 22 +++++++++++++--------- freqtrade/strategy/interface.py | 8 ++++++-- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index e8ebd6280..74bf432cb 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -1172,11 +1172,12 @@ class AwesomeStrategy(IStrategy): ``` +Entries will be validated, and won't be passed to the UI if they don't correspond to the expected schema and will log an error if they don't. + !!! Warning "Many annotations" Using too many annotations can cause the UI to hang, especially when plotting large amounts of historic data. Use the annotation feature with care. - ### Plot annotations example ![FreqUI - plot Annotations](assets/frequi-chart-annotations-dark.png#only-dark) diff --git a/freqtrade/ft_types/plot_annotation_type.py b/freqtrade/ft_types/plot_annotation_type.py index 3aee7e319..b132d9922 100644 --- a/freqtrade/ft_types/plot_annotation_type.py +++ b/freqtrade/ft_types/plot_annotation_type.py @@ -1,14 +1,18 @@ from datetime import datetime from typing import Literal -from pydantic import BaseModel +from pydantic import TypeAdapter +from typing_extensions import Required, TypedDict -class AnnotationType(BaseModel): - type: Literal["area"] - start: None | str | datetime = None - end: None | str | datetime = None - y_start: None | float = None - y_end: None | float = None - color: None | str = None - label: None | str = None +class AnnotationType(TypedDict, total=False): + type: Required[Literal["area"]] + start: str | datetime + end: str | datetime + y_start: float + y_end: float + color: str + label: str + + +AnnotationTypePA = TypeAdapter(AnnotationType) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 6d5adb204..761f95f61 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -1812,12 +1812,16 @@ class IStrategy(ABC, HyperStrategyMixin): start_date=dataframe.iloc[0]["date"].to_pydatetime(), end_date=dataframe.iloc[-1]["date"].to_pydatetime(), ) - annotations_new = [] + + from freqtrade.ft_types.plot_annotation_type import AnnotationTypePA + + annotations_new: list[AnnotationType] = [] for annotation in annotations: if isinstance(annotation, dict): # Convert to AnnotationType try: - annotations_new.append(AnnotationType(**annotation)) + AnnotationTypePA.validate_python(annotation) + annotations_new.append(annotation) except ValidationError as e: logger.error(f"Invalid annotation data: {annotation}. Error: {e}") else: