Spaces:
Running
Running
ch möchte eine App erstellen die in erster Linie als trading Bot dient. Sie soll Handels Signale die durch pine script Strategien oder Indikatoren auf Tradingview generiert werden, per webhook an meine Exchange wie Bitget, Pionex oder Bybit weiterleiten. Die anbindung zur Exchange läuft über die Exchange Api.
c54a803
verified
| // TradeFlow Bot - Main JavaScript | |
| // Mock Data Generators | |
| const generateMockSignal = (index = 0) => { | |
| const actions = ['BUY', 'SELL']; | |
| const pairs = ['BTC/USDT', 'ETH/USDT', 'SOL/USDT', 'AVAX/USDT', 'MATIC/USDT']; | |
| const strategies = ['TrendMaster v2.1', 'ScalpPro 3.0', 'MoonSignal Alpha', 'GridFlow Pro']; | |
| const statuses = ['executed', 'pending', 'failed']; | |
| return { | |
| id: `sig_${Date.now()}_${index}`, | |
| timestamp: new Date(Date.now() - Math.random() * 3600000), | |
| pair: pairs[Math.floor(Math.random() * pairs.length)], | |
| action: actions[Math.floor(Math.random() * actions.length)], | |
| price: (Math.random() * 50000 + 1000).toFixed(2), | |
| strategy: strategies[Math.floor(Math.random() * strategies.length)], | |
| exchange: ['Bitget', 'Pionex', 'Bybit'][Math.floor(Math.random() * 3)], | |
| status: statuses[Math.floor(Math.random() * statuses.length)], | |
| pnl: (Math.random() * 200 - 50).toFixed(2) | |
| }; | |
| }; | |
| const generateMockTrade = (index = 0) => { | |
| const signal = generateMockSignal(index); | |
| return { | |
| ...signal, | |
| executionTime: new Date(Date.now() - Math.random() * 86400000), | |
| filledPrice: (parseFloat(signal.price) * (1 + (Math.random() - 0.5) * 0.001)).toFixed(2), | |
| filledAmount: (Math.random() * 2 + 0.1).toFixed(4) | |
| }; | |
| }; | |
| // DOM Manipulation Helpers | |
| const formatTime = (date) => { | |
| return new Intl.DateTimeFormat('en-US', { | |
| hour: '2-digit', | |
| minute: '2-digit', | |
| second: '2-digit', | |
| hour12: false | |
| }).format(date); | |
| }; | |
| const formatDate = (date) => { | |
| return new Intl.DateTimeFormat('en-US', { | |
| month: 'short', | |
| day: 'numeric', | |
| hour: '2-digit', | |
| minute: '2-digit' | |
| }).format(date); | |
| }; | |
| const getStatusBadge = (status) => { | |
| const classes = { | |
| 'executed': 'status-connected', | |
| 'pending': 'status-pending', | |
| 'failed': 'status-disconnected' | |
| }; | |
| const icons = { | |
| 'executed': 'check-circle', | |
| 'pending': 'clock', | |
| 'failed': 'x-circle' | |
| }; | |
| return ` | |
| <span class="status-badge ${classes[status]}"> | |
| <i data-feather="${icons[status]}" class="w-3 h-3"></i> | |
| ${status} | |
| </span> | |
| `; | |
| }; | |
| // Signal Feed Manager | |
| class SignalFeedManager { | |
| constructor(containerId) { | |
| this.container = document.getElementById(containerId); | |
| this.signals = []; | |
| this.maxSignals = 10; | |
| } | |
| addSignal(signal) { | |
| this.signals.unshift(signal); | |
| if (this.signals.length > this.maxSignals) { | |
| this.signals.pop(); | |
| } | |
| this.render(); | |
| } | |
| render() { | |
| if (!this.container) return; | |
| const signalsHTML = this.signals.map(signal => ` | |
| <div class="signal-item ${signal.action.toLowerCase()} glass-card p-4 rounded-lg border"> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center gap-4"> | |
| <div class="flex flex-col"> | |
| <span class="text-2xl font-bold ${signal.action === 'BUY' ? 'text-emerald-400' : 'text-red-400'}"> | |
| ${signal.action} | |
| </span> | |
| <span class="text-xs text-slate-400">${signal.pair}</span> | |
| </div> | |
| <div class="flex flex-col"> | |
| <span class="text-lg font-semibold">$${signal.price}</span> | |
| <span class="text-xs text-slate-400">Price</span> | |
| </div> | |
| </div> | |
| <div class="flex flex-col items-end gap-2"> | |
| <span class="text-sm font-medium">${signal.strategy}</span> | |
| <span class="text-xs text-slate-400">${signal.exchange}</span> | |
| <span class="text-xs text-slate-500">${formatTime(signal.timestamp)}</span> | |
| </div> | |
| </div> | |
| <div class="mt-3 flex items-center justify-between"> | |
| ${getStatusBadge(signal.status)} | |
| <span class="text-xs text-slate-500">${signal.pnl ? `Est. PnL: $${signal.pnl}` : ''}</span> | |
| </div> | |
| </div> | |
| `).join(''); | |
| this.container.innerHTML = signalsHTML; | |
| feather.replace(); | |
| } | |
| startLiveFeed() { | |
| // Initial load | |
| for (let i = 0; i < 5; i++) { | |
| setTimeout(() => { | |
| this.addSignal(generateMockSignal(i)); | |
| }, i * 200); | |
| } | |
| // Live updates every 3-7 seconds | |
| const scheduleNext = () => { | |
| const delay = Math.random() * 4000 + 3000; | |
| setTimeout(() => { | |
| this.addSignal(generateMockSignal()); | |
| scheduleNext(); | |
| }, delay); | |
| }; | |
| scheduleNext(); | |
| } | |
| } | |
| // Trades Table Manager | |
| class TradesTableManager { | |
| constructor(containerId) { | |
| this.container = document.getElementById(containerId); | |
| this.trades = []; | |
| this.maxTrades = 8; | |
| } | |
| addTrade(trade) { | |
| this.trades.unshift(trade); | |
| if (this.trades.length > this.maxTrades) { | |
| this.trades.pop(); | |
| } | |
| this.render(); | |
| } | |
| render() { | |
| if (!this.container) return; | |
| const tradesHTML = this.trades.map(trade => ` | |
| <tr class="hover:bg-slate-800/50 transition-colors"> | |
| <td class="py-4">${formatDate(trade.executionTime)}</td> | |
| <td class="py-4 font-mono font-semibold">${trade.pair}</td> | |
| <td class="py-4"> | |
| <span class="font-bold ${trade.action === 'BUY' ? 'text-emerald-400' : 'text-red-400'}"> | |
| ${trade.action} | |
| </span> | |
| </td> | |
| <td class="py-4">${trade.exchange}</td> | |
| <td class="py-4">${getStatusBadge(trade.status)}</td> | |
| <td class="py-4 font-semibold ${parseFloat(trade.pnl) > 0 ? 'text-emerald-400' : 'text-red-400'}"> | |
| ${parseFloat(trade.pnl) > 0 ? '+' : ''}$${trade.pnl} | |
| </td> | |
| </tr> | |
| `).join(''); | |
| this.container.innerHTML = tradesHTML; | |
| feather.replace(); | |
| } | |
| loadInitial() { | |
| for (let i = 0; i < this.maxTrades; i++) { | |
| this.trades.push(generateMockTrade(i)); | |
| } | |
| this.render(); | |
| } | |
| } | |
| // Initialize App | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Initialize signal feed | |
| const signalManager = new SignalFeedManager('signal-feed'); | |
| signalManager.startLiveFeed(); | |
| // Initialize trades table | |
| const tradesManager = new TradesTableManager('trades-table'); | |
| tradesManager.loadInitial(); | |
| // Auto-update stats | |
| const updateStats = () => { | |
| const statsCards = document.querySelectorAll('tf-stats-card'); | |
| statsCards.forEach(card => { | |
| if (card.getAttribute('title') === 'Total PnL') { | |
| const currentValue = parseFloat(card.getAttribute('value').replace(/[$,]/g, '')); | |
| const change = (Math.random() - 0.5) * 100; | |
| const newValue = (currentValue + change).toFixed(2); | |
| card.setAttribute('value', `$${newValue.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`); | |
| } | |
| if (card.getAttribute('title') === 'Total Trades') { | |
| const currentValue = parseInt(card.getAttribute('value')); | |
| if (Math.random() > 0.8) { | |
| card.setAttribute('value', (currentValue + 1).toString()); | |
| } | |
| } | |
| }); | |
| }; | |
| // Update stats every 10 seconds | |
| setInterval(updateStats, 10000); | |
| // Page navigation | |
| window.navigateTo = (page) => { | |
| window.location.href = page; | |
| }; | |
| console.log('🚀 TradeFlow Bot initialized successfully'); | |
| }); |