From 484d183c30e2ad886ce4160af8fa5e138cbe5e2e Mon Sep 17 00:00:00 2001 From: Alana E Date: Mon, 18 Nov 2024 17:21:08 -0800 Subject: [PATCH] Reapply "fix ui/ux" This reverts commit c53f203dbc8af99eaf58aaf6dc584340da937a4f. --- index.html | 210 +++++++++++++++++++++++--------------------- realtime.js | 2 +- script.js | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++ styles.css | 83 ++++++++++++++++++ 4 files changed, 440 insertions(+), 103 deletions(-) create mode 100644 styles.css diff --git a/index.html b/index.html index 12cd122..7f29a29 100644 --- a/index.html +++ b/index.html @@ -3,119 +3,125 @@ - Crypto Analyzer + Crypto Portfolio Tracker + + - + - -
-

Crypto Analyzer

+ +
+

Crypto Portfolio Tracker

-
- - + +
+
+
+ + +
+ +
-
+ +
+
+
+
+

Your Portfolio

+
+
+
+ + + + + + + + + + + + +
AssetHoldingsPriceValue24h ChangeActions
+
+ +
+
+
+
-
-
- + + +
-
-

Current Metrics

- - - - - - - - - -
MetricValue
+ + + + diff --git a/realtime.js b/realtime.js index 2c18d0b..79b7677 100644 --- a/realtime.js +++ b/realtime.js @@ -1,4 +1,4 @@ -const CRYPTOCOMPARE_API_KEY = 'YOUR_API_KEY_HERE'; // Get free API key from CryptoCompare +const CRYPTOCOMPARE_API_KEY = '1a9286290d0a05d143066a71a3ab030390bf7cc27e8dfa671b11833ea929c7fb'; // Get free API key from CryptoCompare const MAX_DATA_POINTS = 100; let chart = null; let priceData = []; diff --git a/script.js b/script.js index 98691ee..2a41bc6 100644 --- a/script.js +++ b/script.js @@ -1,8 +1,13 @@ const COINGECKO_API_BASE = 'https://api.coingecko.com/api/v3'; let priceChart = null; +<<<<<<< let cryptoData = null; +======= +let portfolio = JSON.parse(localStorage.getItem('portfolio')) || {}; +>>>>>>> +<<<<<<< async function getCoinId(symbol) { const response = await fetch(`${COINGECKO_API_BASE}/search?query=${symbol}`); if (!response.ok) throw new Error('Failed to search coin'); @@ -12,16 +17,63 @@ async function getCoinId(symbol) { return coin.id; } +======= +// Technical Analysis Parameters +const RSI_PERIOD = 14; +const RSI_OVERBOUGHT = 70; +const RSI_OVERSOLD = 30; +const FAST_PERIOD = 12; +const SLOW_PERIOD = 26; +const SIGNAL_PERIOD = 9; +const MA_FAST = 20; +const MA_SLOW = 50; + +>>>>>>> async function analyzeCrypto() { const symbol = document.getElementById('cryptoSymbol').value.toLowerCase().trim(); +<<<<<<< if (!symbol) { showError('Please enter a crypto symbol'); return; +======= + const errorMessage = document.getElementById('errorMessage'); + const dataContainer = document.getElementById('dataContainer'); + + try { + errorMessage.style.display = 'none'; + const response = await fetch(`${COINCAP_API_BASE}/assets/${symbol}`); + + if (!response.ok) { + throw new Error(`Failed to fetch data. Status: ${response.status}`); + } + + const data = await response.json(); + + if (!data.data) { + throw new Error('Invalid cryptocurrency symbol'); + } + + dataContainer.style.display = 'flex'; + updateUI(data.data); + } catch (error) { + dataContainer.style.display = 'none'; + errorMessage.textContent = error.message; + errorMessage.style.display = 'block'; + if (priceChart) { + priceChart.destroy(); + priceChart = null; + } +>>>>>>> } try { +<<<<<<< showError(''); document.getElementById('dataContainer').classList.remove('active'); +======= + const now = new Date(); + const start = new Date(now - 7 * 24 * 60 * 60 * 1000); // 7 days of data for better analysis +>>>>>>> const coinId = await getCoinId(symbol); const response = await fetch(`${COINGECKO_API_BASE}/coins/${coinId}/market_chart?vs_currency=usd&days=1&interval=minutely`); @@ -35,11 +87,17 @@ async function analyzeCrypto() { if (!data || !data.prices || data.prices.length === 0) { throw new Error('No price data available'); } +<<<<<<< cryptoData = data; document.getElementById('dataContainer').classList.add('active'); updateChart(data); +======= + + updateChart(data.data); + generateTradingSignal(data.data); +>>>>>>> } catch (error) { showError(error.message); console.error('Error:', error); @@ -100,6 +158,7 @@ function updateChart(historyData) { }); } +<<<<<<< function showError(message) { const errorElement = document.getElementById('errorMessage'); if (message) { @@ -111,6 +170,170 @@ function showError(message) { } // Event Listeners +======= +function generateTradingSignal(history) { + const prices = history.map(price => parseFloat(price.priceUsd)); + const signalIndicator = document.getElementById('signalIndicator'); + const signalReason = document.getElementById('signalReason'); + + // Calculate technical indicators + const rsi = calculateRSI(prices); + const macd = calculateMACD(prices); + const movingAverages = calculateMovingAverages(prices); + + // Generate trading signal based on multiple indicators + let signal = 'HOLD'; + let reasons = []; + + // RSI Analysis + if (rsi < RSI_OVERSOLD) { + signal = 'BUY'; + reasons.push(`RSI (${rsi.toFixed(2)}) indicates oversold conditions`); + } else if (rsi > RSI_OVERBOUGHT) { + signal = 'SELL'; + reasons.push(`RSI (${rsi.toFixed(2)}) indicates overbought conditions`); + } + + // MACD Analysis + if (macd.histogram > 0 && macd.histogram > macd.signal) { + if (signal !== 'SELL') signal = 'BUY'; + reasons.push('MACD shows bullish momentum'); + } else if (macd.histogram < 0 && macd.histogram < macd.signal) { + if (signal !== 'BUY') signal = 'SELL'; + reasons.push('MACD shows bearish momentum'); + } + + // Moving Averages Analysis + if (movingAverages.fast > movingAverages.slow) { + if (signal !== 'SELL') signal = 'BUY'; + reasons.push('Fast MA crossed above Slow MA'); + } else if (movingAverages.fast < movingAverages.slow) { + if (signal !== 'BUY') signal = 'SELL'; + reasons.push('Fast MA crossed below Slow MA'); + } + + // Update UI with signal + signalIndicator.innerHTML = ` + + ${signal} + + `; + signalReason.innerHTML = reasons.join('
'); +} + +function calculateRSI(prices) { + const gains = []; + const losses = []; + + for (let i = 1; i < prices.length; i++) { + const difference = prices[i] - prices[i - 1]; + if (difference >= 0) { + gains.push(difference); + losses.push(0); + } else { + gains.push(0); + losses.push(Math.abs(difference)); + } + } + + const avgGain = gains.slice(-RSI_PERIOD).reduce((a, b) => a + b) / RSI_PERIOD; + const avgLoss = losses.slice(-RSI_PERIOD).reduce((a, b) => a + b) / RSI_PERIOD; + + const rs = avgGain / avgLoss; + return 100 - (100 / (1 + rs)); +} + +function calculateMACD(prices) { + const emaFast = calculateEMA(prices, FAST_PERIOD); + const emaSlow = calculateEMA(prices, SLOW_PERIOD); + const macdLine = emaFast - emaSlow; + const signalLine = calculateEMA([macdLine], SIGNAL_PERIOD); + + return { + macd: macdLine, + signal: signalLine, + histogram: macdLine - signalLine + }; +} + +function calculateEMA(prices, period) { + const multiplier = 2 / (period + 1); + let ema = prices[0]; + + for (let i = 1; i < prices.length; i++) { + ema = (prices[i] - ema) * multiplier + ema; + } + + return ema; +} + +function calculateMovingAverages(prices) { + const fastMA = prices.slice(-MA_FAST).reduce((a, b) => a + b) / MA_FAST; + const slowMA = prices.slice(-MA_SLOW).reduce((a, b) => a + b) / MA_SLOW; + + return { + fast: fastMA, + slow: slowMA + }; +} + +function updatePortfolio() { + const portfolioTableBody = document.getElementById('portfolioTableBody'); + portfolioTableBody.innerHTML = ''; + + Object.entries(portfolio).forEach(async ([symbol, amount]) => { + try { + const response = await fetch(`${COINCAP_API_BASE}/assets/${symbol}`); + const data = await response.json(); + + if (data.data) { + const value = amount * parseFloat(data.data.priceUsd); + const change = parseFloat(data.data.changePercent24Hr); + + portfolioTableBody.innerHTML += ` + + ${symbol.toUpperCase()} + ${amount} + $${parseFloat(data.data.priceUsd).toFixed(2)} + $${value.toFixed(2)} + ${change.toFixed(2)}% + + + + + `; + } + } catch (error) { + console.error(`Error fetching data for ${symbol}:`, error); + } + }); +} + +function addAsset() { + const symbol = document.getElementById('assetSymbol').value.toLowerCase().trim(); + const amount = parseFloat(document.getElementById('assetAmount').value); + + if (symbol && amount > 0) { + portfolio[symbol] = (portfolio[symbol] || 0) + amount; + localStorage.setItem('portfolio', JSON.stringify(portfolio)); + updatePortfolio(); + + // Close modal + const modal = bootstrap.Modal.getInstance(document.getElementById('addAssetModal')); + modal.hide(); + + // Clear form + document.getElementById('addAssetForm').reset(); + } +} + +function removeAsset(symbol) { + delete portfolio[symbol]; + localStorage.setItem('portfolio', JSON.stringify(portfolio)); + updatePortfolio(); +} + +>>>>>>> document.addEventListener('DOMContentLoaded', () => { document.getElementById('cryptoSymbol').addEventListener('keypress', function(event) { if (event.key === 'Enter') { @@ -118,4 +341,29 @@ document.addEventListener('DOMContentLoaded', () => { analyzeCrypto(); } }); +<<<<<<< + +======= + + document.getElementById('analyzeBtn').addEventListener('click', (event) => { + event.preventDefault(); + analyzeCrypto(); + }); + + document.getElementById('saveAssetBtn').addEventListener('click', (event) => { + event.preventDefault(); + addAsset(); + }); + + // Initialize portfolio + updatePortfolio(); +>>>>>>> }); + +<<<<<<< + +======= +window.analyzeCrypto = analyzeCrypto; +window.removeAsset = removeAsset; + +>>>>>>> \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..eb4b54a --- /dev/null +++ b/styles.css @@ -0,0 +1,83 @@ +body { + background-color: #1a1a1a !important; + color: #ffffff !important; +} + +.card { + background-color: #2a2a2a !important; + border-color: #3a3a3a !important; +} + +.table { + color: #ffffff !important; +} + +.positive { + color: #00ff00 !important; +} + +.negative { + color: #ff0000 !important; +} + +#dataContainer { + display: none; +} + +#dataContainer.active { + display: flex; +} + +.input-group .form-control { + background-color: #2a2a2a; + border-color: #3a3a3a; + color: #ffffff; +} + +.input-group .form-control:focus { + background-color: #2a2a2a; + border-color: #4a4a4a; + color: #ffffff; + box-shadow: none; +} + +.modal-content { + background-color: #2a2a2a !important; + color: #ffffff !important; +} + +.form-control { + background-color: #1a1a1a !important; + border-color: #3a3a3a !important; + color: #ffffff !important; +} + +.form-control:focus { + background-color: #1a1a1a !important; + border-color: #4a4a4a !important; + color: #ffffff !important; + box-shadow: none !important; +} + +#signalIndicator .badge { + font-size: 1.2em; + padding: 10px 20px; +} + +.badge.bg-success { + background-color: #198754 !important; +} + +.badge.bg-danger { + background-color: #dc3545 !important; +} + +.badge.bg-warning { + background-color: #ffc107 !important; + color: #000 !important; +} + +.table-responsive { + max-height: 400px; + overflow-y: auto; +}