tvsignals-to-tg/app/models.py
Artemii Peretiachenko cdbda8fea3 Replace TradingView polling with a local LTF/FVG scanner that posts Telegram cards and Heryon webhooks.
Per-strategy Heryon accounts, reversal captions with previous-trade path on charts, and Telegram replies chained by ticker.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 20:37:16 +02:00

63 lines
1.7 KiB
Python

from __future__ import annotations
from enum import Enum
from typing import Optional
from pydantic import BaseModel, Field, field_validator
class Action(str, Enum):
LONG = "long"
SHORT = "short"
class SignalPayload(BaseModel):
ticker: str
action: Action
entry_price: str
current_price: str
stop_loss_price: str
take_profit_1_price: str
take_profit_2_price: Optional[str] = None
take_profit_3_price: Optional[str] = None
visual_timeframe: str
signal_sequence: int = Field(ge=1)
signal_time: int = Field(gt=0) # unix seconds of seq==1 bar open (UTC)
is_reversal: bool = False
realized_pnl_pct: Optional[float] = None
prev_side: Optional[str] = None
prev_entry_price: Optional[str] = None
prev_signal_time: Optional[int] = None
@field_validator("action", mode="before")
@classmethod
def normalize_action(cls, value: object) -> object:
if isinstance(value, str):
return value.strip().lower()
return value
@field_validator(
"ticker",
"entry_price",
"current_price",
"stop_loss_price",
"take_profit_1_price",
"visual_timeframe",
mode="before",
)
@classmethod
def strip_strings(cls, value: object) -> object:
if isinstance(value, str):
return value.strip()
return value
@field_validator("take_profit_2_price", "take_profit_3_price", mode="before")
@classmethod
def empty_optional_price(cls, value: object) -> object:
if value is None:
return None
if isinstance(value, str) and not value.strip():
return None
if isinstance(value, str):
return value.strip()
return value