1var env = "production"; // MUST set Real tab in Leaderboard section to active at init to not get error 2 3function toggleFaq(element) { // This should work as long as the FAQ section have at most 1 active element at start 4 const faqItem = element.parentElement; 5 const isActive = faqItem.classList.contains('active'); 6 7 if (isActive) { 8 faqItem.classList.remove('active'); 9 return; 10 } 11 12 // Close current active item then set chosen element to 'active' 13 const active_el = document.querySelector('.faq-item.active'); 14 if (active_el) 15 active_el.classList.remove('active'); 16 17 faqItem.classList.add('active'); 18} 19 20function convertDateTime(isoString) { 21 // Parse the ISO string to a Date object 22 const date = new Date(isoString); 23 24 // Extract day, month, year 25 const day = String(date.getUTCDate()).padStart(2, '0'); 26 const month = String(date.getUTCMonth() + 1).padStart(2, '0'); // Months are 0-based 27 const year = date.getUTCFullYear(); 28 29 return `${day}/${month}/${year}`; 30} 31 32async function fetchLeaderboard() { // This should work as long as the Leaderboard section have buttons with textContent (1W/1M/3M/6M/1Y) 33 const active_el = document.querySelector('.time-btn.active'); 34 const URL = `https://services.entrade.com.vn/bot-api/leaderboard?env=${env}&time=${active_el.textContent}`; 35 36 const response = await fetch(URL); 37 if (response.ok) { 38 const data = await response.json(); 39 return data; 40 } 41 42 return null; 43} 44 45async function updateLeaderboard(json) { 46 start_date = convertDateTime(json.from.replace('Z', '-07:00')); 47 end_date = convertDateTime(json.lastUpdated.replace('Z', '-07:00')); 48 49 const update_date_tag = document.querySelector(".leaderboard-date"); 50 update_date_tag.textContent = `Từ ${start_date} Äến ${end_date}` 51 52 const leaderboard = document.getElementById("leaderboard-tbody"); 53 leaderboard.replaceChildren(); // Delete current board 54 55 json.data.forEach((item, idx) => { 56 const row = document.createElement("tr"); 57 58 // Process rank 59 const rank = document.createElement("td"); 60 rank.classList.add("rank-cell") 61 if (idx == 0) 62 rank.textContent = "ð¥"; 63 else if (idx == 1) 64 rank.textContent = "ð¥"; 65 else if (idx == 2) 66 rank.textContent = "ð¥"; 67 else 68 rank.textContent = idx + 1; 69 70 row.appendChild(rank); 71 72 // Process name 73 const name = document.createElement("td"); 74 name.classList.add("name-cell") 75 name.innerHTML = `<span>${item.strategyName}</span>`; 76 row.appendChild(name); 77 78 // Process position 79 const position = document.createElement("td"); 80 position.classList.add("position-cell"); 81 82 if (item.botType == "LONG") 83 position.innerHTML = '<span class="position-badge buy"><i class="fas fa-arrow-up"></i>MUA</span>'; 84 else 85 position.innerHTML = '<span class="position-badge sell"><i class="fas fa-arrow-down"></i>BÃN</span>'; 86 87 row.appendChild(position); 88 89 // Process indicator 90 const indicator = document.createElement("td"); 91 indicator.classList.add("indicators-cell"); 92 93 const indicators_arr = item.indicators; 94 for (let i = 0; i < indicators_arr.length; ++i) { 95 const span = document.createElement("span"); 96 span.classList.add("indicator-tag"); 97 if (i < 3) { 98 span.innerText = indicators_arr[i]; 99 indicator.appendChild(span); 100 } 101 else { 102 span.innerText = "..."; 103 indicator.appendChild(span); 104 break; 105 } 106 } 107 row.appendChild(indicator); 108 109 // Process profit 110 const profit = document.createElement("td"); 111 profit.classList.add("profit-cell"); 112 profit.innerText = item.netPoints.toFixed(2); 113 row.appendChild(profit); 114 115 // Include everything 116 leaderboard.appendChild(row); 117 }) 118} 119 120document.querySelectorAll('.time-btn').forEach(el => { 121 el.addEventListener("click", async () => { 122 const active_el = document.querySelector('.time-btn.active'); 123 if (active_el != el) { 124 active_el.classList.remove('active'); 125 el.classList.add('active'); 126 127 const data = await fetchLeaderboard(); 128 if (data) 129 updateLeaderboard(data); 130 } 131 }); 132}); 133 134document.querySelectorAll(".tab-btn").forEach(el => { 135 el.addEventListener("click", async () => { 136 const active_el = document.querySelector('.tab-btn.active'); 137 if (active_el != el) { 138 active_el.classList.remove('active'); 139 el.classList.add('active'); 140 env = (env == "production") ? "papertrade" : "production"; 141 142 const data = await fetchLeaderboard(); 143 if (data) 144 updateLeaderboard(data); 145 } 146 }); 147}); 148 149class AvatarBot { 150 constructor(id, name, profit_rate, tag, feature_img, description="", version=1) { 151 this.id = id; 152 this.name = name; 153 this.profit_rate = profit_rate; 154 this.tag = tag; 155 this.feature_img = feature_img; 156 this.version = version; 157 this.description = description; 158 } 159} 160 161function GenerateBotCard(bot) { 162 return ` 163 <div style="width: 320px; height: 180px; background: white; color: #111; border-radius: 16px; border: 1px solid #ddd; box-shadow: 0 6px 16px rgba(0, 0, 0, 0.5);"> 164 165 <div style="display: flex; justify-content: space-between; margin: 10px; align-items: center"> 166 <div class="bot-name">${bot.name}</div> 167 <div class="tooltip-container" style="width: 16px; height: 16px; color: #0066cc; border: 1px solid #0066cc; border-radius: 50%; text-align: center; line-height: 16px; font-size: 8px; font-weight: 900"> 168 i 169 <div class="tooltip"> 170 ${bot.description.replace(/\n/g, '<br>')} 171 </div> 172 </div> 173 </div> 174 175 <div style="display: flex; gap: 10px; margin: 10px"> 176 <div style="display: flex; width: 100x; flex-shrink: 0; text-align: center; flex-direction: column; justify-content: space-between; align-items: center"> 177 <div>
178 <span style="font-size: 30px; font-weight: bold; color: #00cc00; line-height: 1">${bot.profit_rate}%</span> 179 <span style="font-size: 6px; color: #666; vertical-align: top; line-height: 2">/nÄm</span> 180 </div> 181 182 <div style="width: 100%; background: ${(bot.tag === "Long") ? "#00cc00" : (bot.tag === "Short") ? "#ff6600" : "gold"}; color: white; padding: 6px 18px; border-radius: 999px; font-weight: bold; font-size: 10px">${bot.tag}</div> 183 </div> 184 185 <div style="flex: 1; height: 77px; background: #f8f9fa; border-radius: 6px"> 186 <img src=${bot.feature_img} style="width: 100%; height: 100%; object-fit: fill"> 187 </div> 188 </div> 189 190 <div style="display: flex; justify-content: center"> 191 <button class="copy-bot-btn" 192 onclick="window.open('https://avatar.entrade.com.vn/tao-chien-thuat?strategyId=${bot.id}&strategyVersion=${bot.version}', '_blank')"> 193 + Copy ngay! 194 </button> 195 </div> 196 </div> 197 `; 198} 199 200function renderBots(bot_list, page=1, bot_per_page=8) { 201 const grid = document.querySelector('.bot-avatar-leaderboard .bot-grid'); 202 if (!grid) return; 203 204 const start_index = bot_per_page * (page - 1); 205 const filteredBots = bot_list.slice(start_index, start_index + bot_per_page); 206 207 if (filteredBots.length === 0) { 208 grid.innerHTML = '<div style="padding:40px; font-size: 36px;">Chúng tôi Äang trong quá trình chá»n lá»c các bot có hiá»u quả nhất. Trong thá»i gian Äó, xin vui lòng chá»n cho mình chiến thuáºt khác.</div>'; 209 return; 210 } 211 212 grid.innerHTML = filteredBots 213 .map(bot => GenerateBotCard(bot)) 214 .join(''); 215} 216 217document.querySelectorAll('.bot-avatar-leaderboard .tab').forEach(btn => { 218 btn.addEventListener('click', () => { 219 220 document.querySelectorAll('.bot-avatar-leaderboard .tab').forEach(b => b.classList.remove('active')); 221 btn.classList.add('active'); 222 223 const tag = btn.getAttribute('data-tab'); 224 const filteredBot = (tag === "all") ? bots : bots.filter(bot => bot.tag === tag); 225 226 renderBots(filteredBot); 227 }); 228});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.