Forex-AI-Analyst / src /vision /pattern_detector.py
leovean's picture
Upload folder using huggingface_hub
d82ffcc verified
Raw History Blame Contribute Delete
4.48 kB
import cv2
import numpy as np
import os
from ultralytics import YOLO
from typing import List, Dict, Any
from PIL import Image
from huggingface_hub import hf_hub_download
class PatternDetector:
def __init__(self, model_path: str = "foduucom/stockmarket-pattern-detection-yolov8"):
if "/" in model_path and not os.path.exists(model_path):
print(f"⬇️ Downloading model from Hugging Face: {model_path}")
try:
local_model_path = hf_hub_download(
repo_id=model_path,
filename="model.pt",
cache_dir="./models"
)
print(f"✅ Model downloaded to: {local_model_path}")
except Exception as e:
print(f"❌ Failed to download model: {e}")
print("⚠️ Falling back to default YOLOv8n model.")
local_model_path = "yolov8n.pt"
else:
local_model_path = model_path
self.model = YOLO(local_model_path)
self.supported_patterns = [
'Head and shoulders bottom', 'Head and shoulders top',
'M_Head', 'W_Bottom', 'Triangle', 'Flag', 'Pennant',
'Double Top', 'Double Bottom', 'Cup and Handle'
]
def detect_patterns(self, image: np.ndarray, conf_threshold: float = 0.3) -> List[Dict[str, Any]]:
if isinstance(image, Image.Image):
image = np.array(image)
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
results = self.model(image, conf=conf_threshold)
detections = []
for result in results:
boxes = result.boxes
if boxes is not None:
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
conf = float(box.conf[0])
cls_id = int(box.cls[0])
pattern_name = self.model.names[cls_id]
detections.append({
'pattern': pattern_name,
'confidence': conf,
'bbox': (int(x1), int(y1), int(x2), int(y2)),
'center': ((x1 + x2) / 2, (y1 + y2) / 2)
})
return detections
def analyze_pattern_sentiment(self, detections: List[Dict]) -> Dict[str, Any]:
bullish_patterns = ['W_Bottom', 'Double Bottom', 'Cup and Handle', 'Head and shoulders bottom']
bearish_patterns = ['M_Head', 'Double Top', 'Head and shoulders top']
bullish_score = 0
bearish_score = 0
for det in detections:
pattern = det['pattern']
conf = det['confidence']
if pattern in bullish_patterns:
bullish_score += conf
elif pattern in bearish_patterns:
bearish_score += conf
total = bullish_score + bearish_score
sentiment = "NEUTRAL"
strength = 0.0
if total > 0:
if bullish_score > bearish_score:
sentiment = "BULLISH"
strength = bullish_score / total
else:
sentiment = "BEARISH"
strength = bearish_score / total
return {
'sentiment': sentiment,
'strength': strength,
'bullish_score': bullish_score,
'bearish_score': bearish_score,
'patterns_found': [d['pattern'] for d in detections],
'highest_confidence_pattern': max(detections, key=lambda x: x['confidence']) if detections else None
}
def draw_detections(self, image: np.ndarray, detections: List[Dict]) -> np.ndarray:
img_copy = image.copy()
for det in detections:
x1, y1, x2, y2 = det['bbox']
label = f"{det['pattern']}: {det['confidence']:.2f}"
bullish_patterns = ['W_Bottom', 'Double Bottom', 'Cup and Handle']
bearish_patterns = ['M_Head', 'Double Top']
if det['pattern'] in bullish_patterns:
color = (0, 255, 0)
elif det['pattern'] in bearish_patterns:
color = (0, 0, 255)
else:
color = (255, 255, 0)
cv2.rectangle(img_copy, (x1, y1), (x2, y2), color, 2)
cv2.putText(img_copy, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
return img_copy