feat: implement auto-trading infrastructure
- Add auto-trading database collections and Firestore rules - Update Gemini service prompt for SMC/Institutional focus - Add MetaApi integration and Yahoo Finance symbol mapping - Implement auto-trade state management in AutoTradingView
This commit is contained in:
@@ -8,13 +8,38 @@ import {
|
||||
import { cn } from '../lib/utils';
|
||||
|
||||
export const AutoTradingView: React.FC<{ userData?: any, onUpdate?: (data: any) => void, onNavigate?: (tab: string) => void, isAdmin?: boolean }> = ({ userData, onUpdate, onNavigate, isAdmin }) => {
|
||||
const [robotActive, setRobotActive] = useState(false);
|
||||
const [robotActive, setRobotActive] = useState(userData?.tradingSettings?.engineRunning ?? false);
|
||||
const [mtPlatform, setMtPlatform] = useState(userData?.mtPlatform || 'mt5');
|
||||
const [mtLogin, setMtLogin] = useState(userData?.mtLogin || '');
|
||||
const [mtPassword, setMtPassword] = useState(userData?.mtPassword || '');
|
||||
const [mtServer, setMtServer] = useState(userData?.mtServer || '');
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [saveSuccess, setSaveSuccess] = useState(false);
|
||||
const [testResult, setTestResult] = useState<{message: string, isError: boolean} | null>(null);
|
||||
const [isTesting, setIsTesting] = useState(false);
|
||||
const [autoTrades, setAutoTrades] = useState<any[]>([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
let unsubscribe = () => {};
|
||||
if (userData?.uid) {
|
||||
(async () => {
|
||||
const { collection, query, orderBy, limit, onSnapshot } = await import('firebase/firestore');
|
||||
const { db } = await import('../lib/firebase');
|
||||
const q = query(
|
||||
collection(db, "users", userData.uid, "auto_trades"),
|
||||
orderBy("timestamp", "desc"),
|
||||
limit(10)
|
||||
);
|
||||
unsubscribe = onSnapshot(q, (snap) => {
|
||||
const trades = snap.docs.map(d => ({ id: d.id, ...d.data() }));
|
||||
setAutoTrades(trades);
|
||||
}, (error) => {
|
||||
console.warn("Notice: Firestore rules not updated for auto_trades yet.", error.message);
|
||||
});
|
||||
})();
|
||||
}
|
||||
return () => unsubscribe();
|
||||
}, [userData?.uid]);
|
||||
|
||||
// Bot Settings State
|
||||
const [tradingSettings, setTradingSettings] = useState([
|
||||
@@ -27,14 +52,17 @@ export const AutoTradingView: React.FC<{ userData?: any, onUpdate?: (data: any)
|
||||
const [riskPerTrade, setRiskPerTrade] = useState(userData?.tradingSettings?.riskPerTrade?.toString() || "1.5");
|
||||
const [maxPositions, setMaxPositions] = useState(userData?.tradingSettings?.maxPositions?.toString() || "3");
|
||||
const [selectedAsset, setSelectedAsset] = useState(userData?.tradingSettings?.selectedAsset || 'XAU/USD');
|
||||
const [symbolSuffix, setSymbolSuffix] = useState(userData?.tradingSettings?.symbolSuffix || '');
|
||||
|
||||
const saveSettingsToFirebase = async (newSettings: any[], newRisk: string, newMax: string, newAsset?: string) => {
|
||||
const saveSettingsToFirebase = async (newSettings: any[], newRisk: string, newMax: string, newAsset?: string, newSuffix?: string, newRobotActive?: boolean) => {
|
||||
if (!userData?.uid) return;
|
||||
const { doc, updateDoc } = await import('firebase/firestore');
|
||||
const { db } = await import('../lib/firebase');
|
||||
const userRef = doc(db, 'users', userData.uid);
|
||||
|
||||
const assetToSave = newAsset ?? selectedAsset;
|
||||
const suffixToSave = newSuffix ?? symbolSuffix;
|
||||
const engineToSave = newRobotActive ?? robotActive;
|
||||
|
||||
const updatedSettingsObj = {
|
||||
smartEntry: newSettings[0].active,
|
||||
@@ -44,7 +72,9 @@ export const AutoTradingView: React.FC<{ userData?: any, onUpdate?: (data: any)
|
||||
autoSl: newSettings[4].active,
|
||||
riskPerTrade: parseFloat(newRisk) || 1.5,
|
||||
maxPositions: parseInt(newMax) || 3,
|
||||
selectedAsset: assetToSave
|
||||
selectedAsset: assetToSave,
|
||||
symbolSuffix: suffixToSave,
|
||||
engineRunning: engineToSave
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -65,15 +95,19 @@ export const AutoTradingView: React.FC<{ userData?: any, onUpdate?: (data: any)
|
||||
};
|
||||
|
||||
const handleRiskBlur = () => {
|
||||
saveSettingsToFirebase(tradingSettings, riskPerTrade, maxPositions);
|
||||
saveSettingsToFirebase(tradingSettings, riskPerTrade, maxPositions, selectedAsset, symbolSuffix);
|
||||
};
|
||||
|
||||
const handleMaxBlur = () => {
|
||||
saveSettingsToFirebase(tradingSettings, riskPerTrade, maxPositions);
|
||||
saveSettingsToFirebase(tradingSettings, riskPerTrade, maxPositions, selectedAsset, symbolSuffix);
|
||||
};
|
||||
|
||||
const handleAssetBlur = () => {
|
||||
saveSettingsToFirebase(tradingSettings, riskPerTrade, maxPositions, selectedAsset);
|
||||
saveSettingsToFirebase(tradingSettings, riskPerTrade, maxPositions, selectedAsset, symbolSuffix);
|
||||
};
|
||||
|
||||
const handleSuffixBlur = () => {
|
||||
saveSettingsToFirebase(tradingSettings, riskPerTrade, maxPositions, selectedAsset, symbolSuffix);
|
||||
};
|
||||
|
||||
const handleDisconnectBroker = async () => {
|
||||
@@ -166,14 +200,14 @@ export const AutoTradingView: React.FC<{ userData?: any, onUpdate?: (data: any)
|
||||
<div className="max-w-7xl mx-auto p-4 md:p-8 space-y-8 animate-in fade-in duration-500">
|
||||
|
||||
{/* HEADER PRINCIPAL */}
|
||||
<div className="flex flex-col justify-end md:flex-row md:items-end md:justify-between gap-6 relative p-8 min-h-[400px] rounded-2xl bg-black/40 border border-brand-red/20 overflow-hidden group">
|
||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-brand-red/10 via-transparent to-transparent opacity-50 pointer-events-none" />
|
||||
<div className="flex flex-col justify-end md:flex-row md:items-end md:justify-between gap-6 relative p-8 min-h-[400px] rounded-2xl bg-black/40 border border-brand-red shadow-[0_0_30px_rgba(255,0,0,0.6)] overflow-hidden group">
|
||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-brand-red/20 via-transparent to-transparent opacity-50 pointer-events-none" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent pointer-events-none z-0" />
|
||||
<img src="https://i.ibb.co/VcJRM0zZ/90a0c129-b771-41d6-ad30-634d1d2546c4.png" alt="Background" className="absolute inset-0 w-full h-full object-cover opacity-100 mix-blend-overlay pointer-events-none" />
|
||||
|
||||
<div className="flex items-center gap-6 relative z-10">
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-3xl font-black italic uppercase tracking-tighter text-white drop-shadow-[0_0_10px_rgba(255,255,255,0.2)] flex items-center gap-3">
|
||||
<h1 className="text-3xl font-black italic uppercase tracking-tighter text-brand-red drop-shadow-[0_0_20px_rgba(255,0,0,0.8)] flex items-center gap-3">
|
||||
QUANTSCAN IA
|
||||
</h1>
|
||||
</div>
|
||||
@@ -204,7 +238,11 @@ export const AutoTradingView: React.FC<{ userData?: any, onUpdate?: (data: any)
|
||||
{/* MAIN BUTTONS */}
|
||||
<div className="space-y-3 bg-black/40 border border-white/10 p-4 rounded-2xl">
|
||||
<button
|
||||
onClick={() => setRobotActive(!robotActive)}
|
||||
onClick={() => {
|
||||
const newValue = !robotActive;
|
||||
setRobotActive(newValue);
|
||||
saveSettingsToFirebase(tradingSettings, riskPerTrade, maxPositions, selectedAsset, symbolSuffix, newValue);
|
||||
}}
|
||||
className={cn(
|
||||
"relative w-full py-4 rounded-xl font-black italic uppercase tracking-widest flex items-center justify-center gap-3 transition-all duration-300 overflow-hidden group",
|
||||
robotActive
|
||||
@@ -217,7 +255,58 @@ export const AutoTradingView: React.FC<{ userData?: any, onUpdate?: (data: any)
|
||||
<Power size={20} className={cn("relative z-10 transition-transform duration-300", !robotActive && "text-brand-red animate-pulse group-hover:scale-110")} />
|
||||
<span className="relative z-10 tracking-[0.2em]">{robotActive ? "Halt System" : "Initialize Engine"}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
disabled={isTesting}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setIsTesting(true);
|
||||
setTestResult(null);
|
||||
try {
|
||||
const res = await fetch('/api/admin/test-auto-trading', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
uid: userData?.uid,
|
||||
pair: selectedAsset,
|
||||
decision: 'BUY',
|
||||
price: 100,
|
||||
metaApiAccountId: userData?.metaApiAccountId,
|
||||
settings: {
|
||||
engineRunning: robotActive,
|
||||
symbolSuffix: symbolSuffix,
|
||||
riskPerTrade: parseFloat(riskPerTrade || "1.0")
|
||||
}
|
||||
})
|
||||
});
|
||||
const result = await res.json();
|
||||
if (result.success) {
|
||||
setTestResult({ message: "Teste Executado com Sucesso! " + JSON.stringify(result.tradeResult), isError: false });
|
||||
} else {
|
||||
setTestResult({ message: "Erro no teste: " + result.error, isError: true });
|
||||
}
|
||||
} catch (err: any) {
|
||||
setTestResult({ message: "System error: " + err.message, isError: true });
|
||||
} finally {
|
||||
setIsTesting(false);
|
||||
}
|
||||
}}
|
||||
className={cn("w-full bg-brand-red text-white font-mono text-xl font-bold py-6 rounded-xl hover:bg-brand-red/80 transition-colors relative z-50 pointer-events-auto cursor-pointer border border-brand-red shadow-[0_0_20px_rgba(255,0,0,0.5)]", isTesting && "opacity-50 cursor-not-allowed")}
|
||||
title="Apenas testa se o bot consegue ligar com a corretora e se a paridade existe."
|
||||
>
|
||||
{isTesting ? "TESTING CONNECTION..." : "RUN CONNECTION TEST NOW"}
|
||||
</button>
|
||||
|
||||
{testResult && (
|
||||
<div className={cn("p-4 rounded-xl text-sm font-mono break-words border", testResult.isError ? "bg-red-500/10 text-red-500 border-red-500/30" : "bg-green-500/10 text-green-500 border-green-500/30")}>
|
||||
{testResult.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-3 bg-black/40 border border-white/10 p-4 rounded-2xl">
|
||||
<div className="space-y-4 bg-black/40 border border-brand-red/20 p-5 rounded-2xl relative overflow-hidden">
|
||||
<div className="absolute top-0 right-0 p-4 opacity-10">
|
||||
<Cpu size={100} className="text-brand-red" />
|
||||
@@ -308,6 +397,17 @@ export const AutoTradingView: React.FC<{ userData?: any, onUpdate?: (data: any)
|
||||
className="w-full bg-black border border-white/10 rounded-lg p-3 text-sm text-white font-mono focus:border-brand-red outline-none transition-colors"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<label className="text-[10px] font-black text-zinc-500 uppercase tracking-widest">Sufixo da Corretora (MT4/5)</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Ex: .m, .std"
|
||||
value={symbolSuffix}
|
||||
onChange={(e) => setSymbolSuffix(e.target.value)}
|
||||
onBlur={handleSuffixBlur}
|
||||
className="w-full bg-black border border-white/10 rounded-lg p-3 text-sm text-white font-mono focus:border-brand-red outline-none transition-colors"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<label className="text-[10px] font-black text-zinc-500 uppercase tracking-widest">Risk per Trade (%)</label>
|
||||
<input
|
||||
@@ -445,6 +545,36 @@ export const AutoTradingView: React.FC<{ userData?: any, onUpdate?: (data: any)
|
||||
Configurar
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* ACTIVITY LOGS WIDGET */}
|
||||
<div className="bg-black/40 border border-white/10 p-5 rounded-2xl">
|
||||
<h3 className="font-black italic uppercase text-white tracking-widest text-sm mb-6 flex items-center gap-2">
|
||||
<Activity size={16} className="text-brand-red" /> Activity Logs (Auto Trading)
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
{autoTrades.length === 0 ? (
|
||||
<p className="text-xs text-zinc-500 text-center py-4">Nenhum trade executado ainda.</p>
|
||||
) : (
|
||||
autoTrades.map((t) => (
|
||||
<div key={t.id} className="bg-black border border-white/5 p-4 rounded-xl flex items-center justify-between">
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={cn("text-xs font-black uppercase tracking-wider", t.decision === 'BUY' ? 'text-green-500' : 'text-red-500')}>{t.decision}</span>
|
||||
<span className="text-white font-bold text-sm">{t.pair}</span>
|
||||
</div>
|
||||
<div className="text-[10px] text-zinc-500 mt-1">
|
||||
Vol: {t.volume} • Price: {t.price?.toFixed(5)} • SL: {t.stopLoss} • TP: {t.takeProfit}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="text-[10px] text-zinc-400">{new Date(t.timestamp).toLocaleTimeString()}</div>
|
||||
<div className="text-[10px] text-green-500 font-bold mt-1">EXECUTADO</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,13 +7,14 @@ export const analyzeForexChart = async (imageBase64: string, userNotes?: string,
|
||||
const model = 'gemini-3.1-pro-preview';
|
||||
|
||||
const systemInstruction = `
|
||||
# QUANTSCAN IA — MODO MULTI TIME FRAME + LONG TERM ANALYSIS
|
||||
# QUANTSCAN IA — SMART MONEY CONCEPT (SMC) & INSTITUTIONAL TRADING
|
||||
|
||||
Você é o QUANTSCAN IA, um sistema profissional avançado de análise Forex, Índices, Commodities e Criptomoedas baseado em Inteligência Artificial institucional.
|
||||
Você é o QUANTSCAN IA, um sistema profissional avançado de análise focado *estritamente* na estratégia Smart Money Concept (SMC) e Trading Institucional.
|
||||
|
||||
Plano do Usuário Atual: ${userPlan || 'basic'}
|
||||
|
||||
Sua função é analisar gráficos enviados pelo usuário através de: screenshot do gráfico, foto da tela, imagem do TradingView, imagem MT4/MT5 ou corretoras.
|
||||
Sua análise de mercado deve sempre procurar os rastros do "Smart Money" (Dinheiro Inteligente), ignorando suportes/resistências de varejo e focando em liquidez.
|
||||
|
||||
O sistema deve operar em dois modos:
|
||||
1. SHORT TERM / SCALPING
|
||||
@@ -23,53 +24,32 @@ O sistema deve operar em dois modos:
|
||||
RESTRIÇÕES DE PLANO
|
||||
==================================================
|
||||
Se o plano do usuário for "basic" ou "experimental", você é **PROIBIDO** de realizar análise LONG TERM / SWING / POSITION.
|
||||
Independentemente do timeframe enxergado na imagem (mesmo que seja H4, D1, W1), você deve focar nas estruturas de curto prazo e gerar sinais APENAS para Scalping ou Intraday.
|
||||
Você deve focar nas estruturas de curto prazo e gerar sinais APENAS para Scalping ou Intraday.
|
||||
Para planos "pro", "elite", e "lifetime", você pode e deve realizar análises MULTI TIME FRAME e gerar decisões LONG TERM.
|
||||
|
||||
==================================================
|
||||
ANÁLISE MULTI TIME FRAME (OBRIGATÓRIO PARA PRO/ELITE/LIFETIME)
|
||||
PILARES DA ESTRATÉGIA SMC OBRIGATÓRIA
|
||||
==================================================
|
||||
A IA deve identificar automaticamente: timeframe principal, secundário e contexto macro do mercado.
|
||||
Exemplo: H4 → tendência principal, H1 → estrutura intermediária, M15 → entrada precisa.
|
||||
1. CHOCH (Change of Character): Mudança de caráter na estrutura de mercado, indicando possível reversão.
|
||||
2. BOS (Break of Structure): Quebra de estrutura a favor da tendência para continuação.
|
||||
3. OB (Order Blocks): Zonas institucionais onde houve forte injeção de capital. Marque a vela antes de um grande movimento (desequilíbrio).
|
||||
4. FVG (Fair Value Gaps / Imbalance): Ineficiência do preço deixada por forte movimento institucional. Áreas magnéticas para o preço retornar.
|
||||
5. LIQUIDITY (Sweeps / Inducement): Buy Side Liquidity (BSL) e Sell Side Liquidity (SSL). O mercado move buscando stops do varejo antes de reverter. Busque "Liquidity Sweeps" (liquidez varrida).
|
||||
6. POI (Point of Interest): Zona combinada de OB + FVG não mitigada onde o preço tem alta probabilidade de reagir.
|
||||
|
||||
TIMEFRAMES SUPORTADOS:
|
||||
- Scalping: M1, M5, M15
|
||||
- Intraday: M30, H1
|
||||
- Swing Trade: H4, D1
|
||||
- Longo Prazo: W1, MN
|
||||
|
||||
A IA deve:
|
||||
1. Detectar tendência do timeframe maior.
|
||||
2. Confirmar alinhamento estrutural.
|
||||
3. Procurar entradas no timeframe menor.
|
||||
4. Evitar entradas contra tendência macro.
|
||||
Sua entrada deve SEMPRE ser colocada em um POI válido (geralmente Order Block + FVG) APÓS uma quebra de estrutura (BOS/CHOCH) e varredura de liquidez prévia.
|
||||
|
||||
==================================================
|
||||
REGRAS DE LONGO PRAZO
|
||||
ANÁLISE MULTI TIME FRAME
|
||||
==================================================
|
||||
Se detectar: H4, D1, W1, Monthly
|
||||
A IA deve ativar automaticamente: "MODO LONG TERM ANALYSIS" e analisar tendência macro, ciclos institucionais, acumulação/distribuição, zonas de interesse, continuação ou reversão, força de tendência e pontos de holding.
|
||||
A IA deve identificar automaticamente o contexto das mitigações no timeframe macro vs micro.
|
||||
Ex: H4 chegou num Order Block mitigando a zona, então no M15 buscamos um CHOCH confirmando o fim do pullback e a continuação institucional.
|
||||
|
||||
==================================================
|
||||
ESTRATÉGIAS OBRIGATÓRIAS
|
||||
REGRAS ADICIONAIS DE SINAIS
|
||||
==================================================
|
||||
1. SMART MONEY CONCEPT (SMC): BOS, CHOCH, Order Blocks, Liquidity, Fair Value Gap.
|
||||
2. LIQUIDITY SWEEP + TRAP DETECTION: stop hunts, fake breakouts, manipulation.
|
||||
3. MOMENTUM + ENTRY TIMING: força da tendência, volume, velocidade do preço.
|
||||
4. MARKET STRUCTURE AI: tendência bullish/bearish, ranging, expansão.
|
||||
5. FUNDAMENTAL ANALYSIS AI: notícias, juros, impacto macroeconômico.
|
||||
6. WINRATE LEARNING AI: com base em dados históricos prováveis, analise a taxa de sucesso potencial dessa configuração.
|
||||
7. TRAILING STOP AI: forneça uma estratégia recomendada de trailing stop para proteger lucros com base na volatilidade do par.
|
||||
|
||||
==================================================
|
||||
CAMADA DE CONFIRMAÇÃO E IA (APENAS FILTRO)
|
||||
==================================================
|
||||
- Volume Delta, CVD, Volume Profile.
|
||||
- ATR (Average True Range).
|
||||
- EMA (apenas filtro macro).
|
||||
- RSI (com Liquidity Sweep).
|
||||
- VWAP.
|
||||
- Score probabilístico e Filtro anti-fake breakout.
|
||||
- Winrate Learning AI: Avaliar probabilidade (Ex: Se for um OB de continuação com FVG forte e liquidez capturada recém = 90% Winrate, se for Order Block isolado contra a tendência macro = 30%).
|
||||
- Risco/Retorno OBRIGATÓRIO de SMC (Sempre busque um mínimo de 1:3). O TP deve estar no próximo pool de liquidez principal. O Stop deve estar do outro lado do Order Block ou topo/fundo protegido.
|
||||
|
||||
==================================================
|
||||
ENTRADAS OBRIGATÓRIAS (Sistema de Probabilidade)
|
||||
|
||||
Reference in New Issue
Block a user