Ctrl+K
import React, { useMemo, useState } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Badge } from "@/components/ui/badge"; import { Label } from "@/components/ui/label"; import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts"; import { Download, ExternalLink, LineChart as LineChartIcon, TrendingUp, Zap, Settings2, Search, Copy } from "lucide-react"; // ------------------------------------------------------------ // TradeSignals — واجهة توصيات تداول احترافية (نسخة ديمو أمامية فقط) // ------------------------------------------------------------ // ملاحظات مهمة: // 1) هذا مشروع واجهة فقط (Front-end) — لا ينفّذ صفقات حقيقية ولا يتصل بأي منصة بدون إعداداتك أنت. // 2) لا توجد أرباح مضمونة؛ المؤشرات هنا للأغراض التعليمية. استخدم إدارة مخاطر مناسبة. // 3) يمكنك لاحقًا وصل البيانات الحقيقية عبر مزودي الأسعار (مثل Finnhub/Alpaca/Binance API) بسهولة. // ------------------------------------------------------------ const MARKETS = { Forex: ["EUR/USD", "GBP/USD", "USD/JPY", "XAU/USD"], Crypto: ["BTC/USDT", "ETH/USDT", "BNB/USDT", "SOL/USDT"], Stocks: ["AAPL", "TSLA", "MSFT", "NVDA"], Indices: ["US500", "NAS100", "DAX40", "FTSE100"], }; const TIMEFRAMES = ["1m", "5m", "15m", "1h", "4h", "1d"]; const STRATEGIES = [ { key: "ma", label: "تقاطع المتوسطات (MA)" }, { key: "rsi", label: "قوة نسبية (RSI)" }, { key: "macd", label: "MACD" }, { key: "breakout", label: "اختراق مناطق" }, ]; const PLATFORMS = [ { key: "etoro", name: "eToro", url: "https://www.etoro.com" }, { key: "ibkr", name: "Interactive Brokers", url: "https://www.interactivebrokers.com" }, { key: "mt5", name: "MetaTrader 5", url: "https://www.metatrader5.com" }, { key: "binance", name: "Binance", url: "https://www.binance.com" }, { key: "olymp", name: "Olymp Trade", url: "https://olymptrade.com" }, ]; // توليد بيانات أسعار وهمية لعرض الرسم والمؤشرات function generateSeries(len = 120, start = 100) { let value = start; const data = []; for (let i = 0; i < len; i++) { const drift = (Math.random() - 0.5) * 2; // -1..1 value = Math.max(1, value + drift); data.push({ i, price: +value.toFixed(2) }); } return data; } function sma(arr, period) { const out = []; for (let i = 0; i < arr.length; i++) { if (i + 1 < period) { out.push(null); continue; } const slice = arr.slice(i + 1 - period, i + 1); out.push(slice.reduce((a, b) => a + b, 0) / period); } return out; } function rsi(prices, period = 14) { let gains = 0, losses = 0; const rsis = prices.map(() => null); for (let i = 1; i < prices.length; i++) { const change = prices[i] - prices[i - 1]; gains += Math.max(change, 0); losses += Math.max(-change, 0); if (i >= period) { // إزالة أقدم const oldChange = prices[i - period + 1] - prices[i - period]; gains -= Math.max(oldChange, 0); losses -= Math.max(-oldChange, 0); const rs = losses === 0 ? 100 : gains / losses; rsis[i] = 100 - 100 / (1 + rs); } } return rsis; } function strategySignal({ prices, fast = 9, slow = 21, rsiPeriod = 14 }) { // MA Cross + RSI تأكيد بسيط const fastMA = sma(prices, fast); const slowMA = sma(prices, slow); const r = rsi(prices, rsiPeriod); const i = prices.length - 1; const crossUp = fastMA[i - 1] != null && slowMA[i - 1] != null && fastMA[i - 1] <= slowMA[i - 1] && fastMA[i] > slowMA[i]; const crossDown = fastMA[i - 1] != null && slowMA[i - 1] != null && fastMA[i - 1] >= slowMA[i - 1] && fastMA[i] < slowMA[i]; const lastRSI = r[i] ?? 50; if (crossUp && lastRSI > 50) return { side: "BUY", confidence: Math.min(95, Math.round(lastRSI)) }; if (crossDown && lastRSI < 50) return { side: "SELL", confidence: Math.min(95, Math.round(100 - lastRSI)) }; return { side: "NEUTRAL", confidence: 50 }; } // ====== مولد توصية فورية (Entry/SL/TP وحجم صفقة) ====== function atrLike(prices, period = 14) { // تقريب ATR بسيط باختلافات مطلقة متحركة const diffs = []; function toCSV(rows) { const header = ["التاريخ", "الأصل", "الفريم", "الاستراتيجية", "الإشارة", "الثقة%", "السعر"]; const body = rows.map(r => [r.date, r.symbol, r.timeframe, r.strategy, r.signal.side, r.signal.confidence, r.price]); const csv = [header, ...body].map(r => r.join(",")).join("\n"); return csv; } export default function TradeSignalsApp() { const [market, setMarket] = useState("Forex"); const [symbol, setSymbol] = useState(MARKETS["Forex"][0]); const [timeframe, setTimeframe] = useState("15m"); const [strategy, setStrategy] = useState("ma"); const [platform, setPlatform] = useState("etoro"); const [dark, setDark] = useState(true); const [filter, setFilter] = useState(""); const [history, setHistory] = useState([]); const series = useMemo(() => generateSeries(150, 100), [market, symbol, timeframe]); const prices = series.map(d => d.price); const signal = useMemo(() => strategySignal({ prices }), [prices, strategy]); const filteredSymbols = useMemo(() => { return MARKETS[market].filter(s => s.toLowerCase().includes(filter.toLowerCase())); }, [market, filter]); const platformUrl = useMemo(() => PLATFORMS.find(p => p.key === platform)?.url ?? "#", [platform]); const recordNow = () => { const row = { date: new Date().toLocaleString(), symbol, timeframe, strategy: STRATEGIES.find(s => s.key === strategy)?.label ?? strategy, signal, price: prices[prices.length - 1], }; setHistory(h => [row, ...h].slice(0, 50)); }; const downloadCSV = () => { const csv = toCSV(history); const blob = new Blob(["\uFEFF" + csv], { type: "text/csv;charset=utf-8;" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `signals_${Date.now()}.csv`; a.click(); URL.revokeObjectURL(url); }; return ( <div className={`${dark ? "dark" : ""} w-full min-h-screen`}> <div className="mx-auto max-w-7xl p-4 md:p-8"> <header className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 mb-6"> <div className="flex items-center gap-3"> <LineChartIcon className="w-8 h-8" /> <div> <h1 className="text-2xl md:text-3xl font-bold">TradeSignals — موقع توصيات</h1> <p className="text-sm opacity-70">نسخة واجهة تجريبية، تعليمية وسهلة — اختر السوق والفريم والمنصة واضغط أزرارك الفورية.</p> </div> </div> <div className="flex items-center gap-4"> <div className="flex items-center gap-2"> <Label className="text-sm">وضع ليلي</Label> <Switch checked={dark} onCheckedChange={setDark} /> </div> <a href={platformUrl} target="_blank" rel="noreferrer"> <Button variant="default" className="gap-2"><ExternalLink className="w-4 h-4" /> فتح المنصة المختارة</Button> </a> </div> </header> <div className="grid grid-cols-1 lg:grid-cols-4 gap-6"> {/* لوحة الإعدادات */} <Card className="lg:col-span-1 shadow-md rounded-2xl"> <CardHeader> <CardTitle className="text-lg">الإعدادات</CardTitle> </CardHeader> <CardContent className="space-y-4"> <div className="space-y-2"> <Label>السوق</Label> <Select value={market} onValueChange={(v) => { setMarket(v); setSymbol(MARKETS[v][0]); }}> <SelectTrigger><SelectValue placeholder="اختر السوق" /></SelectTrigger> <SelectContent> {Object.keys(MARKETS).map(k => ( <SelectItem key={k} value={k}>{k}</SelectItem> ))} </SelectContent> </Select> </div> <div className="space-y-2"> <Label>الأصل/الرمز</Label> <div className="flex gap-2"> <div className="relative grow"> <Input placeholder="ابحث عن الرمز" value={filter} onChange={e => setFilter(e.target.value)} className="pl-8" /> <Search className="w-4 h-4 absolute left-2 top-1/2 -translate-y-1/2 opacity-60" /> </div> <Select value={symbol} onValueChange={setSymbol}> <SelectTrigger className="min-w-[140px]"><SelectValue placeholder="اختر الرمز" /></SelectTrigger> <SelectContent> {filteredSymbols.map(s => ( <SelectItem key={s} value={s}>{s}</SelectItem> ))} </SelectContent> </Select> </div> </div> <div className="space-y-2"> <Label>الفريم الزمني</Label> <Tabs value={timeframe} onValueChange={setTimeframe} className="w-full"> <TabsList className="grid grid-cols-3"> {TIMEFRAMES.slice(0,3).map(tf => ( <TabsTrigger key={tf} value={tf}>{tf}</TabsTrigger> ))} </TabsList> <TabsList className="grid grid-cols-3 mt-2"> {TIMEFRAMES.slice(3).map(tf => ( <TabsTrigger key={tf} value={tf}>{tf}</TabsTrigger> ))} </TabsList> </Tabs> </div> <div className="space-y-2"> <Label>الاستراتيجية</Label> <Select value={strategy} onValueChange={setStrategy}> <SelectTrigger><SelectValue placeholder="اختر الاستراتيجية" /></SelectTrigger> <SelectContent> {STRATEGIES.map(s => ( <SelectItem key={s.key} value={s.key}>{s.label}</SelectItem> ))} </SelectContent> </Select> </div> <div className="space-y-2"> <Label>المنصة</Label> <Select value={platform} onValueChange={setPlatform}> <SelectTrigger><SelectValue placeholder="اختر المنصة" /></SelectTrigger> <SelectContent> {PLATFORMS.map(p => ( <SelectItem key={p.key} value={p.key}>{p.name}</SelectItem> ))} </SelectContent> </Select> </div> <div className="space-y-2 pt-2"> <Label>أزرار فورية</Label> <div className="grid grid-cols-3 gap-2"> <Button className="w-full" onClick={recordNow}><Zap className="w-4 h-4 mr-1" /> سجل إشارة</Button> <Button variant="secondary" className="w-full"><TrendingUp className="w-4 h-4 mr-1" /> شراء (تجريبي)</Button> <Button variant="destructive" className="w-full">بيع (تجريبي)</Button> </div> </div> </CardContent> </Card> {/* الرسم + البطاقات */} <div className="lg:col-span-3 space-y-6"> <Card className="shadow-md rounded-2xl"> <CardHeader className="flex flex-row items-center justify-between"> <CardTitle className="text-lg flex items-center gap-2"> <LineChartIcon className="w-5 h-5" /> {symbol} — {timeframe} </CardTitle> <div className="flex items-center gap-2"> <Badge variant={signal.side === "BUY" ? "default" : signal.side === "SELL" ? "destructive" : "secondary"}> {signal.side === "BUY" ? "إشارة شراء" : signal.side === "SELL" ? "إشارة بيع" : "بدون إشارة"} </Badge> <Badge variant="outline">ثقة: {signal.confidence}%</Badge> </div> </CardHeader> <CardContent className="h-72 md:h-96"> <ResponsiveContainer width="100%" height="100%"> <LineChart data={series} margin={{ top: 10, right: 20, left: 0, bottom: 10 }}> <XAxis dataKey="i" hide /> <YAxis domain={["auto", "auto"]} width={40} /> <Tooltip formatter={(v) => v} labelFormatter={(l) => `شمعة #${l}`} /> <Line type="monotone" dataKey="price" strokeWidth={2} dot={false} /> </LineChart> </ResponsiveContainer> <p className="text-xs opacity-70 mt-2">* بيانات سعرية وهمية للعرض فقط.</p> </CardContent> </Card> <Card className="shadow-md rounded-2xl"> <CardHeader className="flex flex-row items-center justify-between"> <CardTitle className="text-lg">آخر التوصيات المسجّلة</CardTitle> <div className="flex items-center gap-2"> <Button variant="secondary" className="gap-2" onClick={() => navigator.clipboard.writeText(JSON.stringify(history, null, 2))}><Copy className="w-4 h-4" /> نسخ JSON</Button> <Button className="gap-2" onClick={downloadCSV}><Download className="w-4 h-4" /> تنزيل CSV</Button> </div> </CardHeader> <CardContent> {history.length === 0 ? ( <div className="text-sm opacity-70">لا توجد سجلات بعد. اضغط "سجل إشارة" لحفظ الإشارة الحالية في الجدول.</div> ) : ( <div className="overflow-x-auto"> <table className="w-full text-sm"> <thead className="text-left"> <tr className="border-b"> <th className="py-2 pr-2">التاريخ</th> <th className="py-2 pr-2">الرمز</th> <th className="py-2 pr-2">الفريم</th> <th className="py-2 pr-2">الإستراتيجية</th> <th className="py-2 pr-2">الإشارة</th> <th className="py-2 pr-2">الثقة</th> <th className="py-2 pr-2">السعر</th> <th className="py-2 pr-2">إجراءات</th> </tr> </thead> <tbody> {history.map((row, idx) => ( <tr key={idx} className="border-b hover:bg-muted/40"> <td className="py-2 pr-2 whitespace-nowrap">{row.date}</td> <td className="py-2 pr-2">{row.symbol}</td> <td className="py-2 pr-2">{row.timeframe}</td> <td className="py-2 pr-2">{row.strategy}</td> <td className="py-2 pr-2">{row.signal.side}</td> <td className="py-2 pr-2">{row.signal.confidence}%</td> <td className="py-2 pr-2">{row.price}</td> <td className="py-2 pr-2"> <div className="flex gap-2"> <a href={platformUrl} target="_blank" rel="noreferrer"> <Button size="sm" className="gap-1"><ExternalLink className="w-3 h-3" /> فتح بالمنصة</Button> </a> <Button size="sm" variant="secondary">شراء (تجريبي)</Button> <Button size="sm" variant="destructive">بيع (تجريبي)</Button> </div> </td> </tr> ))} </tbody> </table> </div> )} </CardContent> </Card> <Card className="shadow-md rounded-2xl"> <CardHeader> <CardTitle className="text-lg flex items-center gap-2"><Settings2 className="w-5 h-5" /> دمج بيانات وأسعار حقيقية (اختياري لاحقًا)</CardTitle> </CardHeader> <CardContent className="text-sm space-y-2 opacity-90"> <p>لجعل الموقع حيًّا، استخدم مزود بيانات مثل <b>Finnhub</b> أو <b>Alpaca</b> للأسهم والفوركس، أو <b>Binance API</b> للعملات الرقمية. أنشئ Route في الخلفية (Node/Express) يستدعي الأسعار ويغذي الواجهة.</p> <pre className="p-3 bg-muted rounded-xl overflow-x-auto"><code>{`// مثال واجهة REST خلفية GET /api/quotes?symbol=AAPL GET /api/candles?symbol=BTCUSDT&tf=15m `}</code></pre> <p>احترم شروط استخدام أي منصة؛ لا تنفّذ صفقات تلقائية بدون تفويض صريح وواجهات برمجية رسمية.</p> </CardContent> </Card> </div> </div> <footer className="mt-8 text-xs opacity-70 text-center"> </footer> </div> </div> ); } - Initial Deployment
24ded29 verified