Spaces:
Running
2️⃣ Prompt for DeepSite (supports Crypto + Forex)
Browse files👉 Copy everything inside this box and paste it into DeepSite:
You are a senior full-stack AI engineer.
Build a production-ready project called **CryptoSignal-Sleuth** that works for **both crypto and forex**, not just Bitcoin.
Tech stack:
- Backend: FastAPI (Python 3)
- Frontend: React + TypeScript + Vite + TailwindCSS
- Models: Hugging Face (vision, OCR, sentiment, LLM reasoning)
The app will be deployed on **Hugging Face Spaces**.
====================================================
## 1. FEATURES
The app must do all of this:
1. Let the user **upload a screenshot** of a trading chart (crypto or forex):
- Run **vision + OCR** on the image.
- Extract basic info: trend (up/down/range), key levels, patterns if visible.
- Pass these cues into an LLM to generate a trading **signal**.
2. Let the user **analyze a market live** by sending:
```json
{
"symbol": "BTCUSDT" or "EURUSD",
"timeframe": "5m" or "15m" or "1h",
"asset_type": "crypto" or "forex"
}
Fetch OHLCV data.
Run technical indicators (EMA, RSI, MACD, ATR, support/resistance).
Optionally include sentiment.
Use an LLM to synthesize a final signal.
Provide a clean REST API + webhook so I can connect:
n8n
Discord bot
Telegram bot
====================================================
2. MARKETS: CRYPTO + FOREX
The app MUST support:
2.1 Crypto
Examples: BTCUSDT, ETHUSDT, SOLUSDT
Data source: Binance public klines (no key).
Implement a helper:
fetch_binance_klines(symbol: str, timeframe: str, limit: int = 200)
2.2 Forex
Examples: EURUSD, GBPUSD, USDJPY, XAUUSD, NAS100
Data source: use yfinance (or similar free source).
Implement helpers:
normalize_forex_symbol("EURUSD") -> "EURUSD=X"
fetch_yfinance_fx(symbol: str, timeframe: str, limit: int = 200)
2.3 Common wrapper
Create in backend/timeseries_analysis.py:
fetch_ohlcv(symbol: str, timeframe: str, asset_type: str = "crypto", limit: int = 200)
Logic:
If asset_type == "crypto" → call fetch_binance_klines.
If asset_type == "forex" → normalize symbol and call fetch_yfinance_fx.
Apply the same technical analysis (EMA, RSI, MACD, ATR, support/resistance, trend, momentum) to both crypto and forex.
====================================================
3. SIGNAL SCHEMA (VERY IMPORTANT)
All signals (image or market) MUST follow this JSON:
{
"direction": "long|short|neutral",
"entry_zone": [0.0, 0.0],
"stop_loss": 0.0,
"take_profit_levels": [0.0, 0.0, 0.0],
"timeframe_inferred": "1m|5m|15m|1h|4h|1d",
"confidence": 0,
"time_horizon": "intra-day|swing|position",
"explanation": "string",
"meta": {
"sources": [],
"sentiment": {}
}
}
Use Pydantic models so FastAPI enforces this structure.
====================================================
4. PROJECT STRUCTURE
Use this exact file structure:
app.py
backend/
config.py
main.py
models_registry.py
signal_engine.py
image_analysis.py
timeseries_analysis.py
sentiment_analysis.py
frontend/
index.html
package.json
tsconfig.json
vite.config.ts
postcss.config.js
tailwind.config.js
src/
index.css
main.tsx
App.tsx
lib/api.ts
pages/
Dashboard.tsx
Backtest.tsx
Settings.tsx
Docs.tsx
components/
Navbar.tsx
Sidebar.tsx
ChartUploadCard.tsx
PairSelector.tsx
SignalPanel.tsx
HistoryTable.tsx
ModelSettingsPanel.tsx
requirements.txt
Fill ALL of these files with real, working code.
====================================================
5. BACKEND DETAILS
5.1 Config
backend/config.py:
Pydantic settings class that loads:
HF_TOKEN
USE_INFERENCE_API (default "1")
INFERENCE_LLM_MODEL (default "Qwen/Qwen2.5-7B-Instruct")
LOCAL_LLM_MODEL (default "google/flan-t5-base")
FRONTEND_BASE_PATH (default "/")
API_BASE_URL (default "/")
WEBHOOK_API_KEY (optional)
5.2 Models Registry
backend/models_registry.py:
Functions using Hugging Face Inference API (with HF_TOKEN):
analyze_chart_image(image_bytes) -> dict
run_ocr(image_bytes) -> dict
analyze_sentiment(texts: list[str]) -> dict
llm_reason(prompt: str) -> str
5.3 Image Analysis
backend/image_analysis.py:
extract_chart_features(image_bytes) -> dict:
Use vision + OCR to infer:
trend: up/down/range
key levels
any visible pattern labels
possible timeframe if visible
5.4 Time-Series Analysis
backend/timeseries_analysis.py:
Implement:
fetch_ohlcv (crypto + forex wrapper as described above).
compute_technicals(ohlcv) -> dict:
EMA fast/slow
RSI
MACD
ATR
support/resistance
trend and momentum labels.
5.5 Sentiment Analysis
backend/sentiment_analysis.py:
Fetch recent crypto/forex headlines (e.g. with RSS or generic feed).
Run sentiment model.
Return {"bullish": x, "bearish": y, "neutral": z, "summary": "..."}
This is stored in meta["sentiment"].
5.6 Signal Engine
backend/signal_engine.py:
Implement:
generate_signal_from_image(image_bytes) -> dict
generate_signal_from_market(symbol: str, timeframe: str, asset_type: str, ohlcv, technicals) -> dict
Build a prompt for the LLM that includes:
asset_type (crypto or forex)
symbol
timeframe
recent price action
trend & momentum
volatility
sentiment summary
image cues (if using screenshot)
Ask the LLM to output a decision that maps cleanly into the Signal schema above.
5.7 Main FastAPI App
backend/main.py routes:
GET /api/health → { "status": "ok" }
GET /api/models → shows which HF models are active.
POST /api/analyze/screenshot
multipart/form-data, field name: file.
returns Signal JSON.
POST /api/analyze/market
JSON body:
{ "symbol": "EURUSD", "timeframe": "5m", "asset_type": "forex" }
returns Signal JSON.
POST /api/webhook/signal
optional header X-API-KEY == WEBHOOK_API_KEY.
accepts any valid Signal JSON and echoes it (for n8n integration).
5.8 app.py
Create FastAPI app.
Include backend router at /api.
Serve frontend static files from frontend/dist at /.
Use FRONTEND_BASE_PATH and API_BASE_URL if needed.
====================================================
6. FRONTEND (React + TypeScript + Vite + Tailwind)
Implement the frontend with a dark trading dashboard look.
6.1 API Client
frontend/src/lib/api.ts:
Base URL: import.meta.env.VITE_API_BASE_URL || "/api"
Functions:
uploadChart(file: File): Promise<SignalResponse>
getMarketSignal(symbol: string, timeframe: string, asset_type: "crypto"|"forex"): Promise<SignalResponse>
getModels()
postWebhookSignal(payload: any)
Define SignalResponse TypeScript interface using the Signal schema.
6.2 Components / Pages
Navbar + Sidebar layout.
Dashboard page:
ChartUploadCard → upload screenshot, show returned signal in SignalPanel.
PairSelector:
Input/select for symbol (BTCUSDT, ETHUSDT, EURUSD, GBPUSD, XAUUSD, etc.)
Select for timeframe.
Dropdown for asset_type ("crypto" or "forex").
Button → calls getMarketSignal.
SignalPanel displays the SignalResponse nicely (direction, entry zone, SL, TP1–3, confidence, explanation, sentiment).
HistoryTable → shows a list of signals from the current session.
Backtest:
Simple placeholder explaining that backtesting is coming later.
Settings:
Show data from /api/models.
Show current base URLs and some info text.
Docs:
Show example request/response for:
/api/analyze/market
/api/analyze/screenshot
/api/webhook/signal
====================================================
7. REQUIREMENTS + ENV
requirements.txt should include at least:
fastapi
uvicorn[standard]
python-multipart
pydantic
httpx or requests
numpy
pandas (if needed)
ta (or manual indicator math)
yfinance
feedparser (or similar)
huggingface-hub or transformers (if needed)
jinja2 (if needed)
Also output a .env.example with:
HF_TOKEN=
USE_INFERENCE_API=1
INFERENCE_LLM_MODEL=Qwen/Qwen2.5-7B-Instruct
LOCAL_LLM_MODEL=google/flan-t5-base
FRONTEND_BASE_PATH=/
API_BASE_URL=/api
WEBHOOK_API_KEY=
====================================================
8. SUMMARY
At the very end of your answer, add a short summary:
How to install and run locally:
pip install -r requirements.txt
cd frontend && npm install && npm run build
uvicorn app:app --host 0.0.0.0 --port 7860
How to deploy on Hugging Face Spaces with app.py as the entrypoint.
Generate the COMPLETE codebase with no TODOs or placeholders.
---
If you want next, I can help you with the **exact JSON body** to use in n8n, or with a **Discord bot command** like `/signal EURUSD 5m forex` that calls your new API.
::contentReference[oaicite:0]{index=0}
- .env.example +1 -3
- app.py +1 -3
- backend/config.py +1 -1
- backend/main.py +8 -9
- backend/models_registry.py +1 -1
- backend/signal_engine.py +8 -4
- backend/timeseries_analysis.py +5 -3
- requirements.txt +1 -3
|
@@ -11,6 +11,4 @@ API_BASE_URL=/api
|
|
| 11 |
|
| 12 |
# Security
|
| 13 |
WEBHOOK_API_KEY=your_secure_api_key_here
|
| 14 |
-
```
|
| 15 |
-
|
| 16 |
-
6. For the frontend, let's create a basic setup:
|
|
|
|
| 11 |
|
| 12 |
# Security
|
| 13 |
WEBHOOK_API_KEY=your_secure_api_key_here
|
| 14 |
+
```
|
|
|
|
|
|
|
@@ -22,6 +22,4 @@ app.mount(
|
|
| 22 |
if __name__ == "__main__":
|
| 23 |
import uvicorn
|
| 24 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|
| 25 |
-
```
|
| 26 |
-
|
| 27 |
-
4. Let's create the requirements.txt:
|
|
|
|
| 22 |
if __name__ == "__main__":
|
| 23 |
import uvicorn
|
| 24 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|
| 25 |
+
```
|
|
|
|
|
|
|
@@ -8,7 +8,7 @@ class Settings(BaseSettings):
|
|
| 8 |
INFERENCE_LLM_MODEL: str = "Qwen/Qwen2.5-7B-Instruct"
|
| 9 |
LOCAL_LLM_MODEL: str = "google/flan-t5-base"
|
| 10 |
FRONTEND_BASE_PATH: str = "/"
|
| 11 |
-
API_BASE_URL: str = "/"
|
| 12 |
WEBHOOK_API_KEY: Optional[str] = None
|
| 13 |
|
| 14 |
class Config:
|
|
|
|
| 8 |
INFERENCE_LLM_MODEL: str = "Qwen/Qwen2.5-7B-Instruct"
|
| 9 |
LOCAL_LLM_MODEL: str = "google/flan-t5-base"
|
| 10 |
FRONTEND_BASE_PATH: str = "/"
|
| 11 |
+
API_BASE_URL: str = "/api"
|
| 12 |
WEBHOOK_API_KEY: Optional[str] = None
|
| 13 |
|
| 14 |
class Config:
|
|
@@ -5,9 +5,11 @@ from fastapi.security import APIKeyHeader
|
|
| 5 |
from typing import Optional
|
| 6 |
import io
|
| 7 |
from .config import get_settings
|
| 8 |
-
from .signal_engine import generate_signal_from_image
|
| 9 |
-
from .timeseries_analysis import
|
|
|
|
| 10 |
from .models_registry import model_registry
|
|
|
|
| 11 |
|
| 12 |
app = FastAPI(title="CryptoSignal Sleuth Pro API")
|
| 13 |
|
|
@@ -46,7 +48,7 @@ async def market_analysis(
|
|
| 46 |
timeframe: str = "1h"
|
| 47 |
):
|
| 48 |
try:
|
| 49 |
-
ohlcv = await
|
| 50 |
technicals = compute_technicals(ohlcv)
|
| 51 |
sentiment = await get_crypto_sentiment(symbol[:3])
|
| 52 |
|
|
@@ -66,6 +68,7 @@ async def webhook_handler(
|
|
| 66 |
):
|
| 67 |
# Process webhook payload here
|
| 68 |
return {"status": "received", "data": payload}
|
|
|
|
| 69 |
@app.get("/api/health")
|
| 70 |
async def health_check():
|
| 71 |
return {"status": "ok"}
|
|
@@ -94,7 +97,6 @@ async def analyze_screenshot(
|
|
| 94 |
image_bytes = await file.read()
|
| 95 |
signal = await generate_signal_from_image(image_bytes, symbol or "BTCUSDT")
|
| 96 |
return signal
|
| 97 |
-
from pydantic import BaseModel
|
| 98 |
|
| 99 |
class MarketRequest(BaseModel):
|
| 100 |
symbol: str
|
|
@@ -117,7 +119,7 @@ async def analyze_market(request: MarketRequest):
|
|
| 117 |
ohlcv=ohlcv,
|
| 118 |
technicals=technicals
|
| 119 |
)
|
| 120 |
-
except Exception as e:
|
| 121 |
raise HTTPException(500, detail=str(e))
|
| 122 |
|
| 123 |
@app.post("/api/webhook/signal")
|
|
@@ -132,7 +134,4 @@ async def webhook_signal(
|
|
| 132 |
# Log the signal
|
| 133 |
print(f"Received signal: {payload}")
|
| 134 |
return {"status": "received", "signal": payload}
|
| 135 |
-
```
|
| 136 |
-
___METADATA_START___
|
| 137 |
-
{"repoId":"Alexo19/cryptosignal-sleuth-pro","isNew":false,"userName":"Alexo19"}
|
| 138 |
-
___METADATA_END___
|
|
|
|
| 5 |
from typing import Optional
|
| 6 |
import io
|
| 7 |
from .config import get_settings
|
| 8 |
+
from .signal_engine import generate_signal_from_image, generate_signal_from_market
|
| 9 |
+
from .timeseries_analysis import fetch_ohlcv, compute_technicals
|
| 10 |
+
from .sentiment_analysis import get_crypto_sentiment
|
| 11 |
from .models_registry import model_registry
|
| 12 |
+
from pydantic import BaseModel
|
| 13 |
|
| 14 |
app = FastAPI(title="CryptoSignal Sleuth Pro API")
|
| 15 |
|
|
|
|
| 48 |
timeframe: str = "1h"
|
| 49 |
):
|
| 50 |
try:
|
| 51 |
+
ohlcv = await fetch_ohlcv(symbol, timeframe)
|
| 52 |
technicals = compute_technicals(ohlcv)
|
| 53 |
sentiment = await get_crypto_sentiment(symbol[:3])
|
| 54 |
|
|
|
|
| 68 |
):
|
| 69 |
# Process webhook payload here
|
| 70 |
return {"status": "received", "data": payload}
|
| 71 |
+
|
| 72 |
@app.get("/api/health")
|
| 73 |
async def health_check():
|
| 74 |
return {"status": "ok"}
|
|
|
|
| 97 |
image_bytes = await file.read()
|
| 98 |
signal = await generate_signal_from_image(image_bytes, symbol or "BTCUSDT")
|
| 99 |
return signal
|
|
|
|
| 100 |
|
| 101 |
class MarketRequest(BaseModel):
|
| 102 |
symbol: str
|
|
|
|
| 119 |
ohlcv=ohlcv,
|
| 120 |
technicals=technicals
|
| 121 |
)
|
| 122 |
+
except Exception as e:
|
| 123 |
raise HTTPException(500, detail=str(e))
|
| 124 |
|
| 125 |
@app.post("/api/webhook/signal")
|
|
|
|
| 134 |
# Log the signal
|
| 135 |
print(f"Received signal: {payload}")
|
| 136 |
return {"status": "received", "signal": payload}
|
| 137 |
+
```
|
|
|
|
|
|
|
|
|
|
@@ -28,7 +28,7 @@ class ModelRegistry:
|
|
| 28 |
|
| 29 |
async def run_ocr(self, image_bytes: bytes) -> Dict[str, Any]:
|
| 30 |
if should_use_inference_api():
|
| 31 |
-
response =
|
| 32 |
return {"text": response}
|
| 33 |
else:
|
| 34 |
# Simple fallback
|
|
|
|
| 28 |
|
| 29 |
async def run_ocr(self, image_bytes: bytes) -> Dict[str, Any]:
|
| 30 |
if should_use_inference_api():
|
| 31 |
+
response = self.client.image_to_text(image=image_bytes)
|
| 32 |
return {"text": response}
|
| 33 |
else:
|
| 34 |
# Simple fallback
|
|
@@ -2,7 +2,7 @@
|
|
| 2 |
from typing import Dict, Any
|
| 3 |
import json
|
| 4 |
from .image_analysis import extract_chart_features
|
| 5 |
-
from .timeseries_analysis import
|
| 6 |
from .sentiment_analysis import get_crypto_sentiment
|
| 7 |
from .models_registry import model_registry
|
| 8 |
|
|
@@ -12,7 +12,7 @@ async def generate_signal_from_image(image_bytes: bytes, symbol: str = "BTCUSDT"
|
|
| 12 |
|
| 13 |
# Get technical analysis
|
| 14 |
timeframe = chart_features.get("timeframe", "1h")
|
| 15 |
-
ohlcv = await
|
| 16 |
technicals = compute_technicals(ohlcv)
|
| 17 |
|
| 18 |
# Get sentiment analysis
|
|
@@ -64,6 +64,7 @@ Provide your response in this exact JSON format:
|
|
| 64 |
"sentiment": {{}}
|
| 65 |
}}
|
| 66 |
}}"""
|
|
|
|
| 67 |
async def generate_signal_from_market(
|
| 68 |
symbol: str,
|
| 69 |
timeframe: str,
|
|
@@ -84,11 +85,13 @@ async def generate_signal_from_market(
|
|
| 84 |
|
| 85 |
# Prepare LLM prompt
|
| 86 |
prompt = build_market_prompt(technicals, sentiment, symbol, timeframe, asset_type)
|
| 87 |
-
|
|
|
|
| 88 |
llm_response = await model_registry.llm_reason(prompt)
|
| 89 |
|
| 90 |
# Parse response into structured format
|
| 91 |
return parse_llm_response(llm_response, {}, technicals, sentiment)
|
|
|
|
| 92 |
def build_market_prompt(
|
| 93 |
technicals: Dict,
|
| 94 |
sentiment: Dict,
|
|
@@ -98,6 +101,7 @@ def build_market_prompt(
|
|
| 98 |
) -> str:
|
| 99 |
asset_type_str = "cryptocurrency" if asset_type == "crypto" else "forex"
|
| 100 |
return f"""Analyze this {asset_type_str} trading situation and provide a professional trading signal in JSON format:
|
|
|
|
| 101 |
Technical Indicators ({timeframe} timeframe):
|
| 102 |
- Trend: {technicals['trend']}
|
| 103 |
- Momentum: {technicals['momentum']}
|
|
@@ -129,7 +133,7 @@ Provide your response in this exact JSON format:
|
|
| 129 |
}}"""
|
| 130 |
|
| 131 |
def parse_llm_response(response: str, chart_features: Dict, technicals: Dict, sentiment: Dict) -> Dict[str, Any]:
|
| 132 |
-
try:
|
| 133 |
data = json.loads(response.strip())
|
| 134 |
if not isinstance(data, dict):
|
| 135 |
raise ValueError("Invalid response format")
|
|
|
|
| 2 |
from typing import Dict, Any
|
| 3 |
import json
|
| 4 |
from .image_analysis import extract_chart_features
|
| 5 |
+
from .timeseries_analysis import fetch_ohlcv, compute_technicals
|
| 6 |
from .sentiment_analysis import get_crypto_sentiment
|
| 7 |
from .models_registry import model_registry
|
| 8 |
|
|
|
|
| 12 |
|
| 13 |
# Get technical analysis
|
| 14 |
timeframe = chart_features.get("timeframe", "1h")
|
| 15 |
+
ohlcv = await fetch_ohlcv(symbol, timeframe)
|
| 16 |
technicals = compute_technicals(ohlcv)
|
| 17 |
|
| 18 |
# Get sentiment analysis
|
|
|
|
| 64 |
"sentiment": {{}}
|
| 65 |
}}
|
| 66 |
}}"""
|
| 67 |
+
|
| 68 |
async def generate_signal_from_market(
|
| 69 |
symbol: str,
|
| 70 |
timeframe: str,
|
|
|
|
| 85 |
|
| 86 |
# Prepare LLM prompt
|
| 87 |
prompt = build_market_prompt(technicals, sentiment, symbol, timeframe, asset_type)
|
| 88 |
+
|
| 89 |
+
# Get LLM reasoning
|
| 90 |
llm_response = await model_registry.llm_reason(prompt)
|
| 91 |
|
| 92 |
# Parse response into structured format
|
| 93 |
return parse_llm_response(llm_response, {}, technicals, sentiment)
|
| 94 |
+
|
| 95 |
def build_market_prompt(
|
| 96 |
technicals: Dict,
|
| 97 |
sentiment: Dict,
|
|
|
|
| 101 |
) -> str:
|
| 102 |
asset_type_str = "cryptocurrency" if asset_type == "crypto" else "forex"
|
| 103 |
return f"""Analyze this {asset_type_str} trading situation and provide a professional trading signal in JSON format:
|
| 104 |
+
|
| 105 |
Technical Indicators ({timeframe} timeframe):
|
| 106 |
- Trend: {technicals['trend']}
|
| 107 |
- Momentum: {technicals['momentum']}
|
|
|
|
| 133 |
}}"""
|
| 134 |
|
| 135 |
def parse_llm_response(response: str, chart_features: Dict, technicals: Dict, sentiment: Dict) -> Dict[str, Any]:
|
| 136 |
+
try:
|
| 137 |
data = json.loads(response.strip())
|
| 138 |
if not isinstance(data, dict):
|
| 139 |
raise ValueError("Invalid response format")
|
|
@@ -22,7 +22,8 @@ async def fetch_binance_klines(symbol: str, timeframe: str, limit: int = 200) ->
|
|
| 22 |
response = requests.get(BINANCE_API_URL, params=params)
|
| 23 |
response.raise_for_status()
|
| 24 |
return response.json()
|
| 25 |
-
|
|
|
|
| 26 |
"""Fetch forex data using yfinance"""
|
| 27 |
tf_map = {
|
| 28 |
'1m': '1m', '5m': '5m', '15m': '15m',
|
|
@@ -30,7 +31,7 @@ def fetch_yfinance_fx(symbol: str, timeframe: str, limit: int = 200) -> List[Lis
|
|
| 30 |
'1D': '1d', '1W': '1wk', '1M': '1mo',
|
| 31 |
'1d': '1d', '4h': '4h' # Add common aliases
|
| 32 |
}
|
| 33 |
-
interval = tf_map.get(timeframe, '1h')
|
| 34 |
|
| 35 |
ticker = normalize_forex_symbol(symbol)
|
| 36 |
data = yf.Ticker(ticker)
|
|
@@ -56,9 +57,10 @@ async def fetch_ohlcv(
|
|
| 56 |
if asset_type == "crypto":
|
| 57 |
return await fetch_binance_klines(symbol, timeframe, limit)
|
| 58 |
elif asset_type == "forex":
|
| 59 |
-
return fetch_yfinance_fx(symbol, timeframe, limit)
|
| 60 |
else:
|
| 61 |
raise ValueError("Unsupported asset_type. Must be 'crypto' or 'forex'")
|
|
|
|
| 62 |
def compute_technicals(ohlcv: List[List[Any]]) -> Dict[str, Any]:
|
| 63 |
# Convert to pandas DataFrame
|
| 64 |
df = pd.DataFrame(ohlcv, columns=["timestamp", "open", "high", "low", "close", "volume"])
|
|
|
|
| 22 |
response = requests.get(BINANCE_API_URL, params=params)
|
| 23 |
response.raise_for_status()
|
| 24 |
return response.json()
|
| 25 |
+
|
| 26 |
+
async def fetch_yfinance_fx(symbol: str, timeframe: str, limit: int = 200) -> List[List[Any]]:
|
| 27 |
"""Fetch forex data using yfinance"""
|
| 28 |
tf_map = {
|
| 29 |
'1m': '1m', '5m': '5m', '15m': '15m',
|
|
|
|
| 31 |
'1D': '1d', '1W': '1wk', '1M': '1mo',
|
| 32 |
'1d': '1d', '4h': '4h' # Add common aliases
|
| 33 |
}
|
| 34 |
+
interval = tf_map.get(timeframe, '1h')
|
| 35 |
|
| 36 |
ticker = normalize_forex_symbol(symbol)
|
| 37 |
data = yf.Ticker(ticker)
|
|
|
|
| 57 |
if asset_type == "crypto":
|
| 58 |
return await fetch_binance_klines(symbol, timeframe, limit)
|
| 59 |
elif asset_type == "forex":
|
| 60 |
+
return await fetch_yfinance_fx(symbol, timeframe, limit)
|
| 61 |
else:
|
| 62 |
raise ValueError("Unsupported asset_type. Must be 'crypto' or 'forex'")
|
| 63 |
+
|
| 64 |
def compute_technicals(ohlcv: List[List[Any]]) -> Dict[str, Any]:
|
| 65 |
# Convert to pandas DataFrame
|
| 66 |
df = pd.DataFrame(ohlcv, columns=["timestamp", "open", "high", "low", "close", "volume"])
|
|
@@ -11,6 +11,4 @@ huggingface-hub==0.15.1
|
|
| 11 |
transformers==4.29.2
|
| 12 |
python-dotenv==1.0.0
|
| 13 |
yfinance==0.2.28
|
| 14 |
-
```
|
| 15 |
-
|
| 16 |
-
5. Let's create the .env.example file:
|
|
|
|
| 11 |
transformers==4.29.2
|
| 12 |
python-dotenv==1.0.0
|
| 13 |
yfinance==0.2.28
|
| 14 |
+
```
|
|
|
|
|
|