File size: 5,259 Bytes
e6021a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
"""
Pine Script Lab Router.

Endpoints for Pine Script generation, template browsing,
validation, backtesting, and code customization.
"""

from __future__ import annotations

import logging
from typing import Any, Dict, List, Optional

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field

from app.dependencies import get_current_user
from app.models.user import User

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/pinescript", tags=["Pine Script Lab"])


# ── Schemas ──────────────────────────────────────────────────────────────

class GenerateRequest(BaseModel):
    description: str = Field(..., min_length=5, max_length=2000, description="Natural language description")
    parameters: Optional[Dict[str, Any]] = None


class TemplateRequest(BaseModel):
    template_id: str = Field(..., min_length=1, max_length=50)
    parameters: Optional[Dict[str, Any]] = None


class ValidateRequest(BaseModel):
    code: str = Field(..., min_length=10, max_length=10000)


class BacktestRequest(BaseModel):
    code: str = Field(..., min_length=10, max_length=10000)
    ticker: str = Field("SPY", min_length=1, max_length=20)
    period: str = Field("3y")
    initial_capital: float = Field(100000, ge=1000)
    commission_pct: float = Field(0.1, ge=0, le=5)


class CustomizeRequest(BaseModel):
    code: str = Field(..., min_length=10, max_length=10000)
    modification: str = Field(..., min_length=5, max_length=2000)


# ── Endpoints ────────────────────────────────────────────────────────────

@router.post("/generate")
async def generate_pinescript(
    data: GenerateRequest,
    user: User = Depends(get_current_user),
):
    """
    Generate Pine Script v5 code from a natural language description.
    Uses LLM (Groq) for intelligent code generation, falling back
    to template matching if LLM is unavailable.
    """
    from app.services.pinescript.generator import generate_from_description

    try:
        result = await generate_from_description(
            description=data.description,
            parameters=data.parameters,
        )
        return result
    except Exception as e:
        logger.error("Pine Script generation failed: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Generation failed")


@router.get("/templates")
async def list_templates(
    user: User = Depends(get_current_user),
):
    """List all available strategy templates."""
    from app.services.pinescript.generator import get_all_templates
    templates = get_all_templates()
    return {"total": len(templates), "templates": templates}


@router.post("/templates/generate")
async def generate_from_template_endpoint(
    data: TemplateRequest,
    user: User = Depends(get_current_user),
):
    """Generate Pine Script from a specific template with optional parameter overrides."""
    from app.services.pinescript.generator import generate_from_template

    try:
        result = generate_from_template(
            template_id=data.template_id,
            parameters=data.parameters,
        )
        return result
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))


@router.post("/validate")
async def validate_pinescript(
    data: ValidateRequest,
    user: User = Depends(get_current_user),
):
    """Validate Pine Script v5 syntax."""
    from app.services.pinescript.validator import validate_pine_script
    return validate_pine_script(data.code)


@router.post("/backtest")
async def backtest_pinescript(
    data: BacktestRequest,
    user: User = Depends(get_current_user),
):
    """
    Run a backtest on Pine Script code using historical data.
    Returns TradingView-style performance metrics, equity curve,
    trade log, and monthly returns.
    """
    from app.services.pinescript.pine_backtester import pine_backtester

    try:
        result = await pine_backtester.backtest(
            code=data.code,
            ticker=data.ticker,
            period=data.period,
            initial_capital=data.initial_capital,
            commission_pct=data.commission_pct,
        )
        return result
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error("Backtest failed: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Backtest failed")


@router.post("/customize")
async def customize_pinescript(
    data: CustomizeRequest,
    user: User = Depends(get_current_user),
):
    """Modify existing Pine Script via LLM-powered customization."""
    from app.services.pinescript.generator import customize_code

    try:
        result = await customize_code(
            existing_code=data.code,
            modification=data.modification,
        )
        return result
    except Exception as e:
        logger.error("Customization failed: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Customization failed")