CHAT_OUTPUT_BEGIN
COMMAND_ID=GRAM1-SITE-CHARTS-BROWSER-RUNTIME-V28-REDIRECT-AWARE-READ-ONLY-EXACT-DIAG-20260801T103600Z
STATUS=OK
RC=0
HOST=pve01
MODE=verify
COMPONENT=gram1-charts-v28-redirect-diag
REFERENCE_REGISTER_CHECK=OK
REFERENCE_SHA256=5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e
ERROR_REGISTER_CHECK=OK
ERROR_REGISTER_SHA256=24934a2c7fa5c26f6e828583c4aefac7f143b40c0171736ed7571e617d55ab83
COMMAND_SHA256=98af2a3d5e9c35d738d720d709b5d1bc33f6497016ebe05567f476baa93d9c2b
DUPLICATE_FAILED_COMMAND_BLOCKED=false
EXECUTION_STARTED=true
CHANGE_DECLARED=false
RESULT_CONTRACT_VALID=true
RESULT_CONTRACT_STATUS=NOT_APPLICABLE
RESULT_CONTRACT_ERROR=NONE
COMMAND_RC=0
CHANGES_MADE=false
ROLLBACK_STARTED=false
ROLLBACK_RESTORED=null
MUTATION_OUTCOME=NO_MUTATION
SANITIZED=yes
SECRETS_INCLUDED=no
PRIVATE_ADDRESSES_INCLUDED=no
RAW_EVIDENCE_SHA256=b6a56141a8cd4f0df4c91b72560fa95477d9cd1a4f37e624b33d8fbdebbab62a
SANITIZED_OUTPUT_SHA256=d42000ea7905c64d8e19e55149a81d3750ab5f9aa692923a7805001cf247c48b
OUTPUT_BEGIN
HOMELAB_RESULT_CONTRACT={"changes_made":false,"command_id":"GRAM1-SITE-CHARTS-BROWSER-RUNTIME-V28-REDIRECT-AWARE-READ-ONLY-EXACT-DIAG-20260801T103600Z","mutation_outcome":"NO_MUTATION","rollback_restored":null,"rollback_started":false,"status":"OK","version":1}
{"changes_made":false,"command_id":"GRAM1-SITE-CHARTS-BROWSER-RUNTIME-V28-REDIRECT-AWARE-READ-ONLY-EXACT-DIAG-20260801T103600Z","control_plane_error":null,"gitea_changed":false,"mutation_outcome":"NO_MUTATION","pinned_host_key_aliases":["[PRIVATE_IP]"],"previous_capsule_sha256":"568b0311741b93ae8ece3a3346c287f195c3f727ce6d04d4e28bdf87a5a4a88b","previous_command_id":"GRAM1-SITE-CHARTS-LAZY-ACCORDION-V26-ATOMIC-FIX-20260801T093500Z","production_site_changed":false,"rc":0,"remote_result":{"changes_made":false,"chart_libraries":[{"api_symbols":{"CandlestickSeries":1,"ColorType":1,"addCandlestickSeries":0,"addSeries":6,"createChart":2},"bytes":196203,"http_status":200,"sha256":"c0992580867c4912cc9385b3c2728315bcc1a76c7f1087dca908430fccdf31d7","url":"/vendor/lightweight-charts.js","versions":["5.2.0"]}],"command_id":"GRAM1-SITE-CHARTS-BROWSER-RUNTIME-V28-REDIRECT-AWARE-READ-ONLY-EXACT-DIAG-20260801T103600Z","compose_services":["web","worker"],"control_plane_error":null,"diagnosis":["LOCAL_HOME_REDIRECT_STATUS_303","CANDLE_SCHEMA_INVALID:HBAR,POL,AVAX,LINK","PUBLIC_ENDPOINT_SERVES_DIFFERENT_OR_CACHED_HTML:gram1.ru","LEGACY_NO_DATA_FALLBACK_REMAINS_IN_HTML"],"duration_seconds":0.678,"exact_state":{"backup_metadata_sha256":"fee67cb4877e96dca3113a17d1ec2facc8ecacaa97def7d83db86c1a545f1f53","compose_sha256":"6d344e46b3de94ad86131a914df2ff3ff25e9d82f7ce0557cbffa468b8cdc1b8","current_hashes":{"app.py":"ec226952cdb7fb58edbfc0173af47f4d901f3206e15169df12294983731fbc20","templates/index.html":"db9a1605bd37fca7d6ff651921dd3bc0ccac991f2e8f221179c4dddc3b2c3536","worker.py":"4a7b16ece826fe2d938f392db6b0b5ca028dcb6ff02fdff4ab431f114edf0e5c"},"exact_v26_target_installed":true,"v26_target_hashes":{"app.py":"ec226952cdb7fb58edbfc0173af47f4d901f3206e15169df12294983731fbc20","templates/index.html":"db9a1605bd37fca7d6ff651921dd3bc0ccac991f2e8f221179c4dddc3b2c3536","worker.py":"4a7b16ece826fe2d938f392db6b0b5ca028dcb6ff02fdff4ab431f114edf0e5c"}},"gitea_changed":false,"headless_browser":{"available":false,"ports":[8000,5000,8080,3000,80,36645]},"local_runtime":{"api":[{"count":0,"http_bytes":27,"http_sha256":"913db652b5cf6ba54020fa7426c642b69f1c868742feddc69fd4077076d942d9","http_status":401,"issues":[],"lightweight_charts_schema_ok":false,"sample":[],"status":null,"symbol":"HBAR"},{"count":0,"http_bytes":27,"http_sha256":"913db652b5cf6ba54020fa7426c642b69f1c868742feddc69fd4077076d942d9","http_status":401,"issues":[],"lightweight_charts_schema_ok":false,"sample":[],"status":null,"symbol":"POL"},{"count":0,"http_bytes":27,"http_sha256":"913db652b5cf6ba54020fa7426c642b69f1c868742feddc69fd4077076d942d9","http_status":401,"issues":[],"lightweight_charts_schema_ok":false,"sample":[],"status":null,"symbol":"AVAX"},{"count":0,"http_bytes":27,"http_sha256":"913db652b5cf6ba54020fa7426c642b69f1c868742feddc69fd4077076d942d9","http_status":401,"issues":[],"lightweight_charts_schema_ok":false,"sample":[],"status":null,"symbol":"LINK"}],"homepage":{"bytes":32738,"id_contexts":{"chart":[{"context":"y=\"RUB\">RUB</button></div><form method=\"post\" action=\"/logout\"><button class=\"btn ghost\">Выйти</button></form></div></div> <section class=\"grid4\"><div class=\"card\"><span class=\"muted\">Стоимость портфеля</span><strong id=\"total\">—</strong><span id=\"day\" class=\"muted\">—</span></div><div class=\"card\"><span class=\"muted\">Вложено</span><strong id=\"invested\">—</strong></div><div class=\"card\"><span class=\"muted\">Прибыль / убыток</span><strong id=\"pnl\">—</strong><span id=\"pnlp\" class=\"muted\">0%</span></div><div class=\"card\"><span class=\"muted\">Обновление рынка</span><strong id=\"updated\">—</strong><span class=\"muted\">каждые 5 минут</span></div></section> <section id=\"coins\" class=\"coins\"></section><section class=\"layout\"><div class=\"panel\"><div class=\"title\"><div><h2 id=\"chartTitle\">График цены</h2><span class=\"muted\">7 дней</span></div><span id=\"marketStatus\" class=\"status\">Загрузка</span></div><svg id=\"chart\" class=\"chart\" viewBox=\"0 0 900 270\" preserveAspectRatio=\"none\"></svg></div><div class=\"panel sharp-moves-panel\"><div class=\"title\"><h2>Резкие движения</h2></div><div id=\"moves\" class=\"moves\"></div></div></section> <section class=\"panel\" style=\"margin-top:14px\"><div class=\"title\"><div><h2>Позиции портфеля</h2><span class=\"muted\">Количество и средняя цена покупки</span></div></div><div class=\"table\"><table><thead><tr><th>Актив</th><th>Цена</th><th>Капитализация</th><th>Количество</th><th id=\"avgHeader\">Средняя покупка (USD)</th><th>Вложено</th><th>Сейчас</th><th>P/L</th></tr></thead><tbody id=\"rows\"></tbody></table></div></section> <section class=\"alerts\"><div class=\"panel\"><div class=\"title\"><h2>Новое оповещение</h2></div><div class=\"form\"><select id=\"aCoin\"></select><select id=\"aKind\"><option value=\"price_above\">Цена выше</option><option value=\"price_below\">Цена ниже</option><option value=\"cap_above\">Капитализация выше</option><option value=\"cap_bel","match":"<svg id=\"chart\" class=\"chart\" viewBox=\"0 0 900 270\" preserveAspectRatio=\"none\">","offset":5120}],"chartTitle":[{"context":"tch\"><button type=\"button\" class=\"currency-btn\" data-currency=\"USD\">USD</button><button type=\"button\" class=\"currency-btn\" data-currency=\"RUB\">RUB</button></div><form method=\"post\" action=\"/logout\"><button class=\"btn ghost\">Выйти</button></form></div></div> <section class=\"grid4\"><div class=\"card\"><span class=\"muted\">Стоимость портфеля</span><strong id=\"total\">—</strong><span id=\"day\" class=\"muted\">—</span></div><div class=\"card\"><span class=\"muted\">Вложено</span><strong id=\"invested\">—</strong></div><div class=\"card\"><span class=\"muted\">Прибыль / убыток</span><strong id=\"pnl\">—</strong><span id=\"pnlp\" class=\"muted\">0%</span></div><div class=\"card\"><span class=\"muted\">Обновление рынка</span><strong id=\"updated\">—</strong><span class=\"muted\">каждые 5 минут</span></div></section> <section id=\"coins\" class=\"coins\"></section><section class=\"layout\"><div class=\"panel\"><div class=\"title\"><div><h2 id=\"chartTitle\">График цены</h2><span class=\"muted\">7 дней</span></div><span id=\"marketStatus\" class=\"status\">Загрузка</span></div><svg id=\"chart\" class=\"chart\" viewBox=\"0 0 900 270\" preserveAspectRatio=\"none\"></svg></div><div class=\"panel sharp-moves-panel\"><div class=\"title\"><h2>Резкие движения</h2></div><div id=\"moves\" class=\"moves\"></div></div></section> <section class=\"panel\" style=\"margin-top:14px\"><div class=\"title\"><div><h2>Позиции портфеля</h2><span class=\"muted\">Количество и средняя цена покупки</span></div></div><div class=\"table\"><table><thead><tr><th>Актив</th><th>Цена</th><th>Капитализация</th><th>Количество</th><th id=\"avgHeader\">Средняя покупка (USD)</th><th>Вложено</th><th>Сейчас</th><th>P/L</th></tr></thead><tbody id=\"rows\"></tbody></table></div></section> <section class=\"alerts\"><div class=\"panel\"><div class=\"title\"><h2>Новое оповещение</h2></div><div class=\"form\"><select id=\"aCoin\"><","match":"<h2 id=\"chartTitle\">","offset":4985}],"tvCharts":[]},"label":"installed_template_due_local_redirect","markers":{"accordion_style":1,"chart_error_text":1,"details_creator":1,"loading_text":2,"no_data_text":3,"tvCharts":3,"v1_script":0,"v26_script":1},"no_data_contexts":[{"context":"tep=\\\"any\\\" value=\\\"\"+inputPrice(h.avg_buy)+\"\\\"></td><td>\"+money(cost,0)+\"</td><td>\"+money(value,2)+\"</td><td class=\\\"\"+(p>=0?\"good\":\"bad\")+\"\\\">\"+money(p,2)+\"</td></tr>\"}).join(\"\");document.querySelectorAll(\"#rows tr\").forEach((tr,i)=>{let id=ids[i];let td=tr.cells[0];td.classList.add(\"asset-cell\");let img=document.createElement(\"img\");img.className=\"coin-icon table-coin-icon\";img.src=\"/coin-icon/\"+id;img.alt=state.coins[id];img.loading=\"lazy\";td.prepend(img);let symbol=td.querySelector(\"b\");if(symbol)symbol.classList.add(\"asset-symbol\")});totalEl=total;document.getElementById(\"total\").textContent=money(total,0);invested.textContent=money(inv,0);let p=total-inv;pnl.textContent=money(p,0);pnl.className=p>=0?\"good\":\"bad\";pnlp.textContent=(inv?p/inv*100:0).toFixed(2)+\"%\";day.textContent=(daily>=0?\"+\":\"\")+money(daily,2)+\" за 24ч\";updated.textContent=state.market.updated_at?new Date(state.market.updated_at*1000).toLocaleTimeString(\"ru-RU\"):\"нет данных\";marketStatus.textContent=Object.keys(m).length===2?\"Рынок обновлён\":\"Нет данных\";marketStatus.className=\"status \"+(Object.keys(m).length===2?\"good\":\"bad\");bindInputs();draw();renderMoves();renderAlerts();aCoin.innerHTML=ids.map(id=>\"<option value=\\\"\"+id+\"\\\">\"+state.coins[id]+\"</option>\").join(\"\")}function bindInputs(){document.querySelectorAll(\".qty,.avg\").forEach(x=>x.onchange=async()=>{let id=x.dataset.id;let q=document.querySelector(\".qty[data-id=\\\"\"+id+\"\\\"]\").value;let a=document.querySelector(\".avg[data-id=\\\"\"+id+\"\\\"]\").value;let avg=currency===\"RUB\"?Number(a)/fx():Number(a);await fetch(\"/api/holding\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:JSON.stringify({coin_id:id,quantity:Number(q),avg_buy:avg})});load()})}function draw(){let c=coin();chartTitle.textContent=(c.name||selected)+\" · \"+state.coins[selected]+\"/\"+currency;let a=c.sparkline_7d||[];if(a.length<2){chart.innerHTML=\"<text x=\\\"20\\\" y=\\\"40\\\" fill=\\","match":"нет данных","offset":10457},{"context":"d>\"+money(value,2)+\"</td><td class=\\\"\"+(p>=0?\"good\":\"bad\")+\"\\\">\"+money(p,2)+\"</td></tr>\"}).join(\"\");document.querySelectorAll(\"#rows tr\").forEach((tr,i)=>{let id=ids[i];let td=tr.cells[0];td.classList.add(\"asset-cell\");let img=document.createElement(\"img\");img.className=\"coin-icon table-coin-icon\";img.src=\"/coin-icon/\"+id;img.alt=state.coins[id];img.loading=\"lazy\";td.prepend(img);let symbol=td.querySelector(\"b\");if(symbol)symbol.classList.add(\"asset-symbol\")});totalEl=total;document.getElementById(\"total\").textContent=money(total,0);invested.textContent=money(inv,0);let p=total-inv;pnl.textContent=money(p,0);pnl.className=p>=0?\"good\":\"bad\";pnlp.textContent=(inv?p/inv*100:0).toFixed(2)+\"%\";day.textContent=(daily>=0?\"+\":\"\")+money(daily,2)+\" за 24ч\";updated.textContent=state.market.updated_at?new Date(state.market.updated_at*1000).toLocaleTimeString(\"ru-RU\"):\"нет данных\";marketStatus.textContent=Object.keys(m).length===2?\"Рынок обновлён\":\"Нет данных\";marketStatus.className=\"status \"+(Object.keys(m).length===2?\"good\":\"bad\");bindInputs();draw();renderMoves();renderAlerts();aCoin.innerHTML=ids.map(id=>\"<option value=\\\"\"+id+\"\\\">\"+state.coins[id]+\"</option>\").join(\"\")}function bindInputs(){document.querySelectorAll(\".qty,.avg\").forEach(x=>x.onchange=async()=>{let id=x.dataset.id;let q=document.querySelector(\".qty[data-id=\\\"\"+id+\"\\\"]\").value;let a=document.querySelector(\".avg[data-id=\\\"\"+id+\"\\\"]\").value;let avg=currency===\"RUB\"?Number(a)/fx():Number(a);await fetch(\"/api/holding\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:JSON.stringify({coin_id:id,quantity:Number(q),avg_buy:avg})});load()})}function draw(){let c=coin();chartTitle.textContent=(c.name||selected)+\" · \"+state.coins[selected]+\"/\"+currency;let a=c.sparkline_7d||[];if(a.length<2){chart.innerHTML=\"<text x=\\\"20\\\" y=\\\"40\\\" fill=\\\"#8fa5b6\\\">История ещё загружается</text>\";return}let min=Math.min(...a),max=Math","match":"Нет данных","offset":10538},{"context":"ls=[\"HBAR\",\"POL\",\"AVAX\",\"LINK\"]; const charts=new Map(); const candleCache=new Map(); const afterLayout=()=>new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve))); const loadCandles=async symbol=>{ if(candleCache.has(symbol))return candleCache.get(symbol); const task=(async()=>{ const response=await fetch(\"/api/klines/\"+encodeURIComponent(symbol)+\"?limit=1000&interval=1M\",{cache:\"no-store\",credentials:\"same-origin\"}); if(response.status===401){location=\"/login\";throw new Error(\"Требуется повторный вход\")} if(!response.ok)throw new Error(\"HTTP \"+response.status); const data=await response.json(); const candles=Array.isArray(data.candles)?data.candles:(data.result&&Array.isArray(data.result.candles)?data.result.candles:[]); if(data.status&&data.status!==\"OK\")throw new Error(String(data.error||data.message||data.status)); if(!candles.length)throw new Error(\"Нет данных для \"+symbol); return candles; })(); candleCache.set(symbol,task); try{return await task}catch(error){candleCache.delete(symbol);throw error} }; const widthOf=box=>Math.max(320,Math.floor(box.getBoundingClientRect().width||box.clientWidth||900)); const ensureChart=card=>{ if(card.dataset.chartState===\"ready\")return Promise.resolve(charts.get(card.dataset.symbol)); if(card.dataset.chartState===\"loading\"&&card._chartPromise)return card._chartPromise; const symbol=card.dataset.symbol; const box=card.querySelector(\".tv-widget-slot\"); const errorBox=card.querySelector(\".tv-chart-error\"); card.dataset.chartState=\"loading\"; errorBox.textContent=\"Загрузка...\"; card._chartPromise=(async()=>{ try{ const candles=await loadCandles(symbol); await afterLayout(); if(!card.open){card.dataset.chartState=\"idle\";return null} const api=LightweightCharts.createCha","match":"Нет данных","offset":25676}],"scripts":[{"async":false,"body_bytes":7045,"body_sha256":"e6afa17d7b94d9da5036d55bf6b8c623ec036ca05e9dea9c789432631c1b6ca7","contains_v26":false,"defer":false,"id":null,"order":0,"src":null},{"async":false,"body_bytes":4775,"body_sha256":"f30846a0a26517bd6e8303d552aeba19887576c54b4a70678e234a5e1f6e7582","contains_v26":false,"defer":false,"id":"ALERT_CREATE_OVERRIDE_V3","order":1,"src":null},{"async":false,"body_bytes":4605,"body_sha256":"b6fedba59295891e784ab98cef7690efe81553715d35a9cf6a31c30d08efb5a9","contains_v26":false,"defer":false,"id":"ALERT_LIST_RENDERER_V5","order":2,"src":null},{"async":false,"body_bytes":0,"body_sha256":null,"contains_v26":false,"defer":false,"id":null,"order":3,"src":"/vendor/lightweight-charts.js"},{"async":false,"body_bytes":6016,"body_sha256":"084f5447f052e941f4eef6784b1431399a6dd17b28dba277fe310fca903224d9","contains_v26":true,"defer":false,"id":"LOCAL_TRADINGVIEW_CHARTS_V2_LAZY_ACCORDION","order":4,"src":null},{"async":false,"body_bytes":642,"body_sha256":"90a765a83fb8a5a0542dee1abf8c32af0f248aa411aa203848f73758bc3f9b40","contains_v26":false,"defer":false,"id":"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1","order":5,"src":null}],"sha256":"db9a1605bd37fca7d6ff651921dd3bc0ccac991f2e8f221179c4dddc3b2c3536"},"homepage_headers":{"cache-control":null,"content-type":null,"etag":null,"last-modified":null,"location":"/login"},"homepage_redirect":{"location":"/login","status":303},"homepage_source":"installed_template_due_local_redirect","homepage_status":303},"mutation_outcome":"NO_MUTATION","previous_capsule_sha256":"568b0311741b93ae8ece3a3346c287f195c3f727ce6d04d4e28bdf87a5a4a88b","previous_command_id":"GRAM1-SITE-CHARTS-LAZY-ACCORDION-V26-ATOMIC-FIX-20260801T093500Z","production_site_changed":false,"public_hosts":["gram1.ru"],"public_runtime":[{"bytes":1088,"final_url":"https://gram1.ru/login","headers":{"age":null,"cache-control":null,"cf-cache-status":null,"content-type":"text/html; charset=utf-8","etag":null,"last-modified":null,"server":null},"host":"gram1.ru","markers":{"accordion_style":0,"no_data":0,"tvCharts":0,"v26_script":0},"sha256":"66b06909949879d84757d0f96be74eebf1db40f35de22b625ea67e85e1bbdae2","status":200}],"rc":0,"rollback_restored":null,"rollback_started":false,"schema":1,"status":"OK","template":{"bytes":32738,"id_contexts":{"chart":[{"context":"y=\"RUB\">RUB</button></div><form method=\"post\" action=\"/logout\"><button class=\"btn ghost\">Выйти</button></form></div></div> <section class=\"grid4\"><div class=\"card\"><span class=\"muted\">Стоимость портфеля</span><strong id=\"total\">—</strong><span id=\"day\" class=\"muted\">—</span></div><div class=\"card\"><span class=\"muted\">Вложено</span><strong id=\"invested\">—</strong></div><div class=\"card\"><span class=\"muted\">Прибыль / убыток</span><strong id=\"pnl\">—</strong><span id=\"pnlp\" class=\"muted\">0%</span></div><div class=\"card\"><span class=\"muted\">Обновление рынка</span><strong id=\"updated\">—</strong><span class=\"muted\">каждые 5 минут</span></div></section> <section id=\"coins\" class=\"coins\"></section><section class=\"layout\"><div class=\"panel\"><div class=\"title\"><div><h2 id=\"chartTitle\">График цены</h2><span class=\"muted\">7 дней</span></div><span id=\"marketStatus\" class=\"status\">Загрузка</span></div><svg id=\"chart\" class=\"chart\" viewBox=\"0 0 900 270\" preserveAspectRatio=\"none\"></svg></div><div class=\"panel sharp-moves-panel\"><div class=\"title\"><h2>Резкие движения</h2></div><div id=\"moves\" class=\"moves\"></div></div></section> <section class=\"panel\" style=\"margin-top:14px\"><div class=\"title\"><div><h2>Позиции портфеля</h2><span class=\"muted\">Количество и средняя цена покупки</span></div></div><div class=\"table\"><table><thead><tr><th>Актив</th><th>Цена</th><th>Капитализация</th><th>Количество</th><th id=\"avgHeader\">Средняя покупка (USD)</th><th>Вложено</th><th>Сейчас</th><th>P/L</th></tr></thead><tbody id=\"rows\"></tbody></table></div></section> <section class=\"alerts\"><div class=\"panel\"><div class=\"title\"><h2>Новое оповещение</h2></div><div class=\"form\"><select id=\"aCoin\"></select><select id=\"aKind\"><option value=\"price_above\">Цена выше</option><option value=\"price_below\">Цена ниже</option><option value=\"cap_above\">Капитализация выше</option><option value=\"cap_bel","match":"<svg id=\"chart\" class=\"chart\" viewBox=\"0 0 900 270\" preserveAspectRatio=\"none\">","offset":5120}],"chartTitle":[{"context":"tch\"><button type=\"button\" class=\"currency-btn\" data-currency=\"USD\">USD</button><button type=\"button\" class=\"currency-btn\" data-currency=\"RUB\">RUB</button></div><form method=\"post\" action=\"/logout\"><button class=\"btn ghost\">Выйти</button></form></div></div> <section class=\"grid4\"><div class=\"card\"><span class=\"muted\">Стоимость портфеля</span><strong id=\"total\">—</strong><span id=\"day\" class=\"muted\">—</span></div><div class=\"card\"><span class=\"muted\">Вложено</span><strong id=\"invested\">—</strong></div><div class=\"card\"><span class=\"muted\">Прибыль / убыток</span><strong id=\"pnl\">—</strong><span id=\"pnlp\" class=\"muted\">0%</span></div><div class=\"card\"><span class=\"muted\">Обновление рынка</span><strong id=\"updated\">—</strong><span class=\"muted\">каждые 5 минут</span></div></section> <section id=\"coins\" class=\"coins\"></section><section class=\"layout\"><div class=\"panel\"><div class=\"title\"><div><h2 id=\"chartTitle\">График цены</h2><span class=\"muted\">7 дней</span></div><span id=\"marketStatus\" class=\"status\">Загрузка</span></div><svg id=\"chart\" class=\"chart\" viewBox=\"0 0 900 270\" preserveAspectRatio=\"none\"></svg></div><div class=\"panel sharp-moves-panel\"><div class=\"title\"><h2>Резкие движения</h2></div><div id=\"moves\" class=\"moves\"></div></div></section> <section class=\"panel\" style=\"margin-top:14px\"><div class=\"title\"><div><h2>Позиции портфеля</h2><span class=\"muted\">Количество и средняя цена покупки</span></div></div><div class=\"table\"><table><thead><tr><th>Актив</th><th>Цена</th><th>Капитализация</th><th>Количество</th><th id=\"avgHeader\">Средняя покупка (USD)</th><th>Вложено</th><th>Сейчас</th><th>P/L</th></tr></thead><tbody id=\"rows\"></tbody></table></div></section> <section class=\"alerts\"><div class=\"panel\"><div class=\"title\"><h2>Новое оповещение</h2></div><div class=\"form\"><select id=\"aCoin\"><","match":"<h2 id=\"chartTitle\">","offset":4985}],"tvCharts":[]},"label":"production_template","markers":{"accordion_style":1,"chart_error_text":1,"details_creator":1,"loading_text":2,"no_data_text":3,"tvCharts":3,"v1_script":0,"v26_script":1},"no_data_contexts":[{"context":"tep=\\\"any\\\" value=\\\"\"+inputPrice(h.avg_buy)+\"\\\"></td><td>\"+money(cost,0)+\"</td><td>\"+money(value,2)+\"</td><td class=\\\"\"+(p>=0?\"good\":\"bad\")+\"\\\">\"+money(p,2)+\"</td></tr>\"}).join(\"\");document.querySelectorAll(\"#rows tr\").forEach((tr,i)=>{let id=ids[i];let td=tr.cells[0];td.classList.add(\"asset-cell\");let img=document.createElement(\"img\");img.className=\"coin-icon table-coin-icon\";img.src=\"/coin-icon/\"+id;img.alt=state.coins[id];img.loading=\"lazy\";td.prepend(img);let symbol=td.querySelector(\"b\");if(symbol)symbol.classList.add(\"asset-symbol\")});totalEl=total;document.getElementById(\"total\").textContent=money(total,0);invested.textContent=money(inv,0);let p=total-inv;pnl.textContent=money(p,0);pnl.className=p>=0?\"good\":\"bad\";pnlp.textContent=(inv?p/inv*100:0).toFixed(2)+\"%\";day.textContent=(daily>=0?\"+\":\"\")+money(daily,2)+\" за 24ч\";updated.textContent=state.market.updated_at?new Date(state.market.updated_at*1000).toLocaleTimeString(\"ru-RU\"):\"нет данных\";marketStatus.textContent=Object.keys(m).length===2?\"Рынок обновлён\":\"Нет данных\";marketStatus.className=\"status \"+(Object.keys(m).length===2?\"good\":\"bad\");bindInputs();draw();renderMoves();renderAlerts();aCoin.innerHTML=ids.map(id=>\"<option value=\\\"\"+id+\"\\\">\"+state.coins[id]+\"</option>\").join(\"\")}function bindInputs(){document.querySelectorAll(\".qty,.avg\").forEach(x=>x.onchange=async()=>{let id=x.dataset.id;let q=document.querySelector(\".qty[data-id=\\\"\"+id+\"\\\"]\").value;let a=document.querySelector(\".avg[data-id=\\\"\"+id+\"\\\"]\").value;let avg=currency===\"RUB\"?Number(a)/fx():Number(a);await fetch(\"/api/holding\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:JSON.stringify({coin_id:id,quantity:Number(q),avg_buy:avg})});load()})}function draw(){let c=coin();chartTitle.textContent=(c.name||selected)+\" · \"+state.coins[selected]+\"/\"+currency;let a=c.sparkline_7d||[];if(a.length<2){chart.innerHTML=\"<text x=\\\"20\\\" y=\\\"40\\\" fill=\\","match":"нет данных","offset":10457},{"context":"d>\"+money(value,2)+\"</td><td class=\\\"\"+(p>=0?\"good\":\"bad\")+\"\\\">\"+money(p,2)+\"</td></tr>\"}).join(\"\");document.querySelectorAll(\"#rows tr\").forEach((tr,i)=>{let id=ids[i];let td=tr.cells[0];td.classList.add(\"asset-cell\");let img=document.createElement(\"img\");img.className=\"coin-icon table-coin-icon\";img.src=\"/coin-icon/\"+id;img.alt=state.coins[id];img.loading=\"lazy\";td.prepend(img);let symbol=td.querySelector(\"b\");if(symbol)symbol.classList.add(\"asset-symbol\")});totalEl=total;document.getElementById(\"total\").textContent=money(total,0);invested.textContent=money(inv,0);let p=total-inv;pnl.textContent=money(p,0);pnl.className=p>=0?\"good\":\"bad\";pnlp.textContent=(inv?p/inv*100:0).toFixed(2)+\"%\";day.textContent=(daily>=0?\"+\":\"\")+money(daily,2)+\" за 24ч\";updated.textContent=state.market.updated_at?new Date(state.market.updated_at*1000).toLocaleTimeString(\"ru-RU\"):\"нет данных\";marketStatus.textContent=Object.keys(m).length===2?\"Рынок обновлён\":\"Нет данных\";marketStatus.className=\"status \"+(Object.keys(m).length===2?\"good\":\"bad\");bindInputs();draw();renderMoves();renderAlerts();aCoin.innerHTML=ids.map(id=>\"<option value=\\\"\"+id+\"\\\">\"+state.coins[id]+\"</option>\").join(\"\")}function bindInputs(){document.querySelectorAll(\".qty,.avg\").forEach(x=>x.onchange=async()=>{let id=x.dataset.id;let q=document.querySelector(\".qty[data-id=\\\"\"+id+\"\\\"]\").value;let a=document.querySelector(\".avg[data-id=\\\"\"+id+\"\\\"]\").value;let avg=currency===\"RUB\"?Number(a)/fx():Number(a);await fetch(\"/api/holding\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:JSON.stringify({coin_id:id,quantity:Number(q),avg_buy:avg})});load()})}function draw(){let c=coin();chartTitle.textContent=(c.name||selected)+\" · \"+state.coins[selected]+\"/\"+currency;let a=c.sparkline_7d||[];if(a.length<2){chart.innerHTML=\"<text x=\\\"20\\\" y=\\\"40\\\" fill=\\\"#8fa5b6\\\">История ещё загружается</text>\";return}let min=Math.min(...a),max=Math","match":"Нет данных","offset":10538},{"context":"ls=[\"HBAR\",\"POL\",\"AVAX\",\"LINK\"]; const charts=new Map(); const candleCache=new Map(); const afterLayout=()=>new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve))); const loadCandles=async symbol=>{ if(candleCache.has(symbol))return candleCache.get(symbol); const task=(async()=>{ const response=await fetch(\"/api/klines/\"+encodeURIComponent(symbol)+\"?limit=1000&interval=1M\",{cache:\"no-store\",credentials:\"same-origin\"}); if(response.status===401){location=\"/login\";throw new Error(\"Требуется повторный вход\")} if(!response.ok)throw new Error(\"HTTP \"+response.status); const data=await response.json(); const candles=Array.isArray(data.candles)?data.candles:(data.result&&Array.isArray(data.result.candles)?data.result.candles:[]); if(data.status&&data.status!==\"OK\")throw new Error(String(data.error||data.message||data.status)); if(!candles.length)throw new Error(\"Нет данных для \"+symbol); return candles; })(); candleCache.set(symbol,task); try{return await task}catch(error){candleCache.delete(symbol);throw error} }; const widthOf=box=>Math.max(320,Math.floor(box.getBoundingClientRect().width||box.clientWidth||900)); const ensureChart=card=>{ if(card.dataset.chartState===\"ready\")return Promise.resolve(charts.get(card.dataset.symbol)); if(card.dataset.chartState===\"loading\"&&card._chartPromise)return card._chartPromise; const symbol=card.dataset.symbol; const box=card.querySelector(\".tv-widget-slot\"); const errorBox=card.querySelector(\".tv-chart-error\"); card.dataset.chartState=\"loading\"; errorBox.textContent=\"Загрузка...\"; card._chartPromise=(async()=>{ try{ const candles=await loadCandles(symbol); await afterLayout(); if(!card.open){card.dataset.chartState=\"idle\";return null} const api=LightweightCharts.createCha","match":"Нет данных","offset":25676}],"scripts":[{"async":false,"body_bytes":7045,"body_sha256":"e6afa17d7b94d9da5036d55bf6b8c623ec036ca05e9dea9c789432631c1b6ca7","contains_v26":false,"defer":false,"id":null,"order":0,"src":null},{"async":false,"body_bytes":4775,"body_sha256":"f30846a0a26517bd6e8303d552aeba19887576c54b4a70678e234a5e1f6e7582","contains_v26":false,"defer":false,"id":"ALERT_CREATE_OVERRIDE_V3","order":1,"src":null},{"async":false,"body_bytes":4605,"body_sha256":"b6fedba59295891e784ab98cef7690efe81553715d35a9cf6a31c30d08efb5a9","contains_v26":false,"defer":false,"id":"ALERT_LIST_RENDERER_V5","order":2,"src":null},{"async":false,"body_bytes":0,"body_sha256":null,"contains_v26":false,"defer":false,"id":null,"order":3,"src":"/vendor/lightweight-charts.js"},{"async":false,"body_bytes":6016,"body_sha256":"084f5447f052e941f4eef6784b1431399a6dd17b28dba277fe310fca903224d9","contains_v26":true,"defer":false,"id":"LOCAL_TRADINGVIEW_CHARTS_V2_LAZY_ACCORDION","order":4,"src":null},{"async":false,"body_bytes":642,"body_sha256":"90a765a83fb8a5a0542dee1abf8c32af0f248aa411aa203848f73758bc3f9b40","contains_v26":false,"defer":false,"id":"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1","order":5,"src":null}],"sha256":"db9a1605bd37fca7d6ff651921dd3bc0ccac991f2e8f221179c4dddc3b2c3536"}},"rollback_restored":null,"rollback_started":false,"schema":1,"selected_method":"ALIAS_PINNED_AS_DEBIAN","ssh_method_probes":[{"duration_seconds":0.132,"identity_count":null,"identity_sudo_read_ok":true,"name":"ALIAS_PINNED_AS_DEBIAN","rc":0,"stderr":""},{"duration_seconds":0.136,"identity_count":2,"identity_sudo_read_ok":true,"name":"DIRECT_PINNED_AS_DEBIAN_NO_PROXY","rc":0,"stderr":""}],"status":"OK"}

OUTPUT_END
CHAT_OUTPUT_END
