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:
desartstudio95
2026-05-29 11:22:12 -07:00
parent b4df5a2d17
commit 08bb52bf23
12 changed files with 369 additions and 119 deletions
+140 -10
View File
@@ -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>
+18 -38
View File
@@ -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)