39 lines
144 KiB
JSON
39 lines
144 KiB
JSON
{
|
||
"schema_version": 1,
|
||
"channel": "homelab-runtime",
|
||
"command_id": "GRAM1-SITE-CHARTS-V38R1-SUDO-PROBE-LINECOUNT-FIX-20260801T124200Z",
|
||
"status": "OK",
|
||
"rc": 0,
|
||
"host": "pve01",
|
||
"mode": "verify",
|
||
"component": "gram1-charts-v38r1-conflict",
|
||
"started_at_utc": "2026-08-01T12:43:26Z",
|
||
"finished_at_utc": "2026-08-01T12:43:27Z",
|
||
"reference_register_checked": true,
|
||
"reference_sha256": "5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e",
|
||
"error_register_checked": true,
|
||
"error_register_sha256": "24934a2c7fa5c26f6e828583c4aefac7f143b40c0171736ed7571e617d55ab83",
|
||
"command_sha256": "7a893a91638a4f1e9b7bc56a5953f15a92f5e83f98db532793a6a4c70ab87132",
|
||
"duplicate_failed_command_blocked": false,
|
||
"block_reason": null,
|
||
"execution_started": true,
|
||
"change_declared": false,
|
||
"result_contract_valid": true,
|
||
"result_contract_status": null,
|
||
"result_contract_error": null,
|
||
"command_rc": 0,
|
||
"changes_made": false,
|
||
"rollback_started": false,
|
||
"rollback_restored": null,
|
||
"mutation_outcome": "NO_MUTATION",
|
||
"sanitized": true,
|
||
"secrets_included": false,
|
||
"private_addresses_included": false,
|
||
"raw_evidence_retained_locally": true,
|
||
"raw_evidence_sha256": "7909460d0d093dc7b3fd4fc26760c31b746c2726817e367c4f54af19d08b1973",
|
||
"sanitized_output_sha256": "1d40dade1f3b2a33564b123ae1f5845ed486fd142c4ffe88f92143c9d8e2a4e9",
|
||
"output_truncated_in_json": false,
|
||
"full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt",
|
||
"output": "HOMELAB_RESULT_CONTRACT={\"changes_made\":false,\"command_id\":\"GRAM1-SITE-CHARTS-V38R1-SUDO-PROBE-LINECOUNT-FIX-20260801T124200Z\",\"mutation_outcome\":\"NO_MUTATION\",\"rollback_restored\":null,\"rollback_started\":false,\"status\":\"OK\",\"version\":1}\n{\"changes_made\":false,\"command_id\":\"GRAM1-SITE-CHARTS-V38R1-SUDO-PROBE-LINECOUNT-FIX-20260801T124200Z\",\"control_plane_error\":null,\"gitea_changed\":false,\"mutation_outcome\":\"NO_MUTATION\",\"pinned_host_key_aliases\":[\"[PRIVATE_IP]\"],\"production_site_changed\":false,\"rc\":0,\"remote_result\":{\"app_analysis\":{\"app_bytes\":32843,\"app_sha256\":\"3b55b78c015173eea66b67afb8fb9371f0d7d2e38a7679d4c433749f4c07301d\",\"chart_matches\":[{\"line\":466,\"snippet\":\"00461: x=left+step*(i+.5);color=\\\"#63e6be\\\" if c>=o else \\\"#ff6b81\\\";yo=y(o);yc=y(c);yh=y(h);yl=y(l);rh=max(1.2,abs(yc-yo));ry=min(yo,yc)\\n00462: parts.append(f'<line x1=\\\"{x:.2f}\\\" y1=\\\"{yh:.2f}\\\" x2=\\\"{x:.2f}\\\" y2=\\\"{yl:.2f}\\\" stroke=\\\"{color}\\\" stroke-width=\\\"1.2\\\"/>');parts.append(f'<rect x=\\\"{x-body/2:.2f}\\\" y=\\\"{ry:.2f}\\\" width=\\\"{body:.2f}\\\" height=\\\"{rh:.2f}\\\" fill=\\\"{color}\\\"/>')\\n00463: parts.append(f'<text x=\\\"{left}\\\" y=\\\"{height-12}\\\" fill=\\\"#8fa5b6\\\" font-size=\\\"13\\\">{_gram1_html.escape(symbol)} / USDT · 1M · {len(rows)} свечей</text></svg>')\\n00464: return ''.join(parts)\\n00465: \\n00466: @app.get(\\\"/api/chart-svg/{symbol}\\\", include_in_schema=False)\\n00467: async def _gram1_chart_svg_endpoint_v4(symbol: str, request: _Gram1Request):\\n00468: if not request.session.get(\\\"auth\\\"):\\n00469: return _Gram1Response(content=\\\"Требуется вход\\\", status_code=401, media_type=\\\"text/plain; charset=utf-8\\\")\\n00470: symbol=symbol.upper()\\n00471: if symbol not in _GRAM1_CHART_SYMBOLS:\",\"token\":\"/api/chart-svg\"},{\"line\":397,\"snippet\":\"00392: require_login(request)\\n00393: with db() as c:\\n00394: c.execute(\\\"DELETE FROM alerts WHERE id=?\\\",(alert_id,)); c.commit()\\n00395: return {\\\"ok\\\":True}\\n00396: \\n00397: # GRAM1_SERVER_CHART_FALLBACK_V4\\n00398: import asyncio as _gram1_asyncio\\n00399: import base64 as _gram1_base64\\n00400: import html as _gram1_html\\n00401: import inspect as _gram1_inspect\\n00402: import json as _gram1_json\",\"token\":\"GRAM1_SERVER_CHART_FALLBACK_V4\"},{\"line\":452,\"snippet\":\"00447: except Exception:\\n00448: continue\\n00449: if not rows: raise RuntimeError(\\\"no valid OHLC rows\\\")\\n00450: return rows[-180:]\\n00451: \\n00452: def _gram1_chart_svg_v4(symbol: str, rows):\\n00453: width,height,left,right,top,bottom=1200,430,72,28,24,42\\n00454: lo=min(r[3] for r in rows);hi=max(r[2] for r in rows);span=max(hi-lo,abs(hi)*1e-9,1e-12)\\n00455: plot_w=width-left-right;plot_h=height-top-bottom;step=plot_w/max(len(rows),1);body=max(2.0,min(10.0,step*0.58))\\n00456: def y(v): return top+(hi-v)/span*plot_h\\n00457: parts=[f'<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 {width} {height}\\\" role=\\\"img\\\" aria-label=\\\"{_gram1_html.escape(symbol)} candle chart\\\" data-symbol=\\\"{_gram1_html.escape(symbol)}\\\" data-candles=\\\"{len(rows)}\\\">', '<rect width=\\\"100%\\\" height=\\\"100%\\\" fill=\\\"#081823\\\"/>']\",\"token\":\"_gram1_chart_svg_v4\"},{\"line\":475,\"snippet\":\"00470: symbol=symbol.upper()\\n00471: if symbol not in _GRAM1_CHART_SYMBOLS:\\n00472: return _Gram1Response(content=\\\"Неизвестная монета\\\", status_code=404, media_type=\\\"text/plain; charset=utf-8\\\")\\n00473: try:\\n00474: rows=await _gram1_chart_data_v4(symbol,request)\\n00475: svg=_gram1_chart_svg_v4(symbol,rows)\\n00476: return _Gram1Response(content=svg,media_type=\\\"image/svg+xml\\\",headers={\\\"Cache-Control\\\":\\\"no-store\\\"})\\n00477: except Exception as exc:\\n00478: msg=_gram1_html.escape(str(exc))\\n00479: svg=f'<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 1200 430\\\" data-symbol=\\\"{symbol}\\\" data-candles=\\\"0\\\"><rect width=\\\"100%\\\" height=\\\"100%\\\" fill=\\\"#081823\\\"/><text x=\\\"30\\\" y=\\\"60\\\" fill=\\\"#ff6b81\\\" font-size=\\\"18\\\">Ошибка серверного графика {symbol}: {msg}</text></svg>'\\n00480: return _Gram1Response(content=svg,status_code=503,media_type=\\\"image/svg+xml\\\",headers={\\\"Cache-Control\\\":\\\"no-store\\\"})\",\"token\":\"_gram1_chart_svg_v4\"},{\"line\":409,\"snippet\":\"00404: from fastapi.responses import Response as _Gram1Response\\n00405: \\n00406: _GRAM1_CHART_JS_B64 = \\\"KCgpPT57CiAgInVzZSBzdHJpY3QiOwogIGNvbnN0IFZFUlNJT049IkdSQU0xX0NIQVJUU19FWFRFUk5BTF9WNCI7CiAgY29uc3Qgc3ltYm9scz1bIkhCQVIiLCJQT0wiLCJBVkFYIiwiTElOSyJdOwogIGNvbnN0IGNoYXJ0cz1uZXcgTWFwKCk7CiAgY29uc3QgY2FuZGxlQ2FjaGU9bmV3IE1hcCgpOwogIGNvbnN0IHNsZWVwPW1zPT5uZXcgUHJvbWlzZShyZXNvbHZlPT5zZXRUaW1lb3V0KHJlc29sdmUsbXMpKTsKICBjb25zdCBhZnRlckxheW91dD0oKT0+bmV3IFByb21pc2UocmVzb2x2ZT0+cmVxdWVzdEFuaW1hdGlvbkZyYW1lKCgpPT5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUocmVzb2x2ZSkpKTsKICBjb25zdCB0aW1lS2V5PXZhbHVlPT57CiAgICBpZih0eXBlb2YgdmFsdWU9PT0ibnVtYmVyIilyZXR1cm4gdmFsdWU7CiAgICBpZih0eXBlb2YgdmFsdWU9PT0ic3RyaW5nIil7CiAgICAgIGNvbnN0IG51bWVyaWM9TnVtYmVyKHZhbHVlKTtpZihOdW1iZXIuaXNGaW5pdGUobnVtZXJpYykpcmV0dXJuIG51bWVyaWM7CiAgICAgIGNvbnN0IHBhcnNlZD1EYXRlLnBhcnNlKHZhbHVlKTtpZihOdW1iZXIuaXNGaW5pdGUocGFyc2VkKSlyZXR1cm4gTWF0aC5mbG9vcihwYXJzZWQvMTAwMCk7CiAgICB9CiAgICBpZih2YWx1ZSYmdHlwZW9mIHZhbHVlPT09Im9iamVjdCImJk51bWJlci5pc0Zpbml0ZShOdW1iZXIodmFsdWUueWVhcikpJiZOdW1iZXIuaXNGaW5pdGUoTnVtYmVyKHZhbHVlLm1vbnRoKSkmJk51bWJlci5pc0Zpbml0ZShOdW1iZXIodmFsdWUuZGF5KSkpcmV0dXJuIERhdGUuVVRDKE51bWJlcih2YWx1ZS55ZWFyKSxOdW1iZXIodmFsdWUubW9udGgpLTEsTnVtYmVyKHZhbHVlLmRheSkpLzEwMDA7CiAgICByZXR1cm4gTmFOOwogIH07CiAgY29uc3Qgbm9ybWFsaXplVGltZT12YWx1ZT0+ewogICAgaWYodHlwZW9mIHZhbHVlPT09InN0cmluZyImJi9eXGQrJC8udGVzdCh2YWx1ZSkpdmFsdWU9TnVtYmVyKHZhbHVlKTsKICAgIGlmKHR5cGVvZiB2YWx1ZT09PSJudW1iZXIiJiZOdW1iZXIuaXNGaW5pdGUodmFsdWUpKXJldHVybiBNYXRoLmZsb29yKHZhbHVlPjFlMTE/dmFsdWUvMTAwMDp2YWx1ZSk7CiAgICBpZih0eXBlb2YgdmFsdWU9PT0ic3RyaW5nIiYmL15cZHs0fS1cZHsyfS1cZHsyfS8udGVzdCh2YWx1ZSkpcmV0dXJuIHZhbHVlLnNsaWNlKDAsMTApOwogICAgaWYodmFsdWUmJnR5cGVvZiB2YWx1ZT09PSJvYmplY3QiJiZOdW1iZXIuaXNGaW5pdGUoTnVtYmVyKHZhbHVlLnllYXIpKSYmTnVtYmVyLmlzRmluaXRlKE51bWJlcih2YWx1ZS5tb250aCkpJiZOdW1iZXIuaXNGaW5pdGUoTnVtYmVyKHZhbHVlLmRheSkpKXJldHVybiB7eWVhcjpOdW1iZXIodmFsdWUueWVhciksbW9udGg6TnVtYmVyKHZhbHVlLm1vbnRoKSxkYXk6TnVtYmVyKHZhbHVlLmRheSl9OwogICAgcmV0dXJuIG51bGw7CiAgfTsKICBjb25zdCBub3JtYWxpemVDYW5kbGVzPXJhdz0+ewogICAgY29uc3Qgcm93cz1bXTsKICAgIGZvcihjb25zdCBpdGVtIG9mIEFycmF5LmlzQXJyYXkocmF3KT9yYXc6W10pewogICAgICBjb25zdCBpc0FycmF5PUFycmF5LmlzQXJyYXkoaXRlbSk7CiAgICAgIGNvbnN0IHRpbWU9bm9ybWFsaXplVGltZShpc0FycmF5P2l0ZW1bMF06KGl0ZW0mJihpdGVtLnRpbWU/P2l0ZW0ub3Blbl90aW1lPz9pdGVtLm9wZW5UaW1lKSkpOwogICAgICBjb25zdCBvcGVuPU51bWJlcihpc0FycmF5P2l0ZW1bMV06aXRlbSYmaXRlbS5vcGVuKSxoaWdoPU51bWJlcihpc0FycmF5P2l0ZW1bMl06aXRlbSYmaXRlbS5oaWdoKSxsb3c9TnVtYmVyKGlzQXJyYXk/aXRlbVszXTppdGVtJiZpdGVtLmxvdyksY2xvc2U9TnVtYmVyKGlzQXJyYXk/aXRlbVs0XTppdGVtJiZpdGVtLmNsb3NlKTsKICAgICAgaWYodGltZT09PW51bGx8fCFbb3BlbixoaWdoLGxvdyxjbG9zZV0uZXZlcnkoTnVtYmVyLmlzRmluaXRlKSljb250aW51ZTsKICAgICAgcm93cy5wdXNoKHt0aW1lLG9wZW4saGlnaCxsb3csY2xvc2V9KTsKICAgIH0KICAgIHJvd3Muc29ydCgoYSxiKT0+dGltZUtleShhLnRpbWUpLXRpbWVLZXkoYi50aW1lKSk7CiAgICBjb25zdCBkZWR1cD1bXTsKICAgIGZvcihjb25zdCByb3cgb2Ygcm93cyl7Y29uc3Qga2V5PVN0cmluZyh0aW1lS2V5KHJvdy50aW1lKSk7aWYoZGVkdXAubGVuZ3RoJiZTdHJpbmcodGltZUtleShkZWR1cFtkZWR1cC5sZW5ndGgtMV0udGltZSkpPT09a2V5KWRlZHVwW2RlZHVwLmxlbmd0aC0xXT1yb3c7ZWxzZSBkZWR1cC5wdXNoKHJvdyl9CiAgICBpZighZGVkdXAubGVuZ3RoKXRocm93IG5ldyBFcnJvcigiQVBJINCy0LXRgNC90YPQuyDQtNCw0L3QvdGL0LUg0LHQtdC3INC60L7RgNGA0LXQutGC0L3Ri9GFIE9ITEMt0YHQstC10YfQtdC5Iik7CiAgICByZXR1cm4gZGVkdXA7CiAgfTsKICBjb25zdCBsb2FkQ2FuZGxlcz1hc3luYyBzeW1ib2w9PnsKICAgIGlmKGNhbmRsZUNhY2hlLmhhcyhzeW1ib2wpKXJldHVybiBjYW5kbGVDYWNoZS5nZXQoc3ltYm9sKTsKICAgIGNvbnN0IHRhc2s9KGFzeW5jKCk9PnsKICAgICAgY29uc3QgcmVzcG9uc2U9YXdhaXQgZmV0Y2goIi9hcGkva2xpbmVzLyIrZW5jb2RlVVJJQ29tcG9uZW50KHN5bWJvbCkrIj9saW1pdD0xMDAwJmludGVydmFsPTFNIix7Y2FjaGU6Im5vLXN0b3JlIixjcmVkZW50aWFsczoic2FtZS1vcmlnaW4iLGhlYWRlcnM6e0FjY2VwdDoiYXBwbGljYXRpb24vanNvbiJ9fSk7CiAgICAgIGlmKHJlc3BvbnNlLnN0YXR1cz09PTQwMXx8cmVzcG9uc2Uuc3RhdHVzPT09NDAzKXtsb2NhdGlvbj0iL2xvZ2luIjt0aHJvdyBuZXcgRXJyb3IoItCi0YDQtdCx0YPQtdGC0YHRjyDQv9C+0LLRgtC+0YDQvdGL0Lkg0LLRhdC+0LQiKX0KICAgICAgaWYoIXJlc3BvbnNlLm9rKXRocm93IG5ldyBFcnJvcigiSFRUUCAiK3Jlc3BvbnNlLnN0YXR1cyk7CiAgICAgIGNvbnN0IGRhdGE9YXdhaXQgcmVzcG9uc2UuanNvbigpOwogICAgICBpZihkYXRhJiZkYXRhLnN0YXR1cyYmZGF0YS5zdGF0dXMhPT0iT0siKXRocm93IG5ldyBFcnJvcihTdHJpbmcoZGF0YS5lcnJvcnx8ZGF0YS5tZXNzYWdlfHxkYXRhLnN0YXR1cykpOwogICAgICBjb25zdCByYXc9QXJyYXkuaXNBcnJheShkYXRhJiZkYXRhLmNhbmRsZXMpP2RhdGEuY2FuZGxlczooZGF0YSYmZGF0YS5yZXN1bHQmJkFycmF5LmlzQXJyYXkoZGF0YS5yZXN1bHQuY2FuZGxlcyk/ZGF0YS5yZXN1bHQuY2FuZGxlczpbXSk7CiAgICAgIHJldHVybiBub3JtYWxpemVDYW5kbGVzKHJhdyk7CiAgICB9KSgpOwogICAgY2FuZGxlQ2FjaGUuc2V0KHN5bWJvbCx0YXNrKTt0cnl7cmV0dXJuIGF3YWl0IHRhc2t9Y2F0Y2goZXJyb3Ipe2NhbmRsZUNhY2hlLmRlbGV0ZShzeW1ib2wpO3Rocm93IGVycm9yfQogIH07CiAgY29uc3Qgd2lkdGhPZj1ib3g9Pk1hdGgubWF4KDMyMCxNYXRoLmZsb29yKGJveC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS53aWR0aHx8Ym94LmNsaWVudFdpZHRofHw5MDApKTsKICBjb25zdCB3YWl0TGlicmFyeT1hc3luYygpPT57Zm9yKGxldCBuPTA7bjwxMDA7bisrKXtpZih3aW5kb3cuTGlnaHR3ZWlnaHRDaGFydHMmJnR5cGVvZiB3aW5kb3cuTGlnaHR3ZWlnaHRDaGFydHMuY3JlYXRlQ2hhcnQ9PT0iZnVuY3Rpb24iKXJldHVybiB3aW5kb3cuTGlnaHR3ZWlnaHRDaGFydHM7YXdhaXQgc2xlZXAoMTAwKX10aHJvdyBuZXcgRXJyb3IoItCb0L7QutCw0LvRjNC90LDRjyDQsdC40LHQu9C40L7RgtC10LrQsCDQs9GA0LDRhNC40LrQvtCyINC90LUg0LfQsNCz0YDRg9C30LjQu9Cw0YHRjCIpfTsKICBjb25zdCBtYWtlU2VyaWVzPShhcGksb3B0aW9ucyk9PntpZihhcGkmJnR5cGVvZiBhcGkuYWRkQ2FuZGxlc3RpY2tTZXJpZXM9PT0iZnVuY3Rpb24iKXJldHVybiBhcGkuYWRkQ2FuZGxlc3RpY2tTZXJpZXMob3B0aW9ucyk7aWYoYXBpJiZ0eXBlb2YgYXBpLmFkZFNlcmllcz09PSJmdW5jdGlvbiImJndpbmRvdy5MaWdodHdlaWdodENoYXJ0cyYmd2luZG93LkxpZ2h0d2VpZ2h0Q2hhcnRzLkNhbmRsZXN0aWNrU2VyaWVzKXJldHVybiBhcGkuYWRkU2VyaWVzKHdpbmRvdy5MaWdodHdlaWdodENoYXJ0cy5DYW5kbGVzdGlja1NlcmllcyxvcHRpb25zKTt0aHJvdyBuZXcgRXJyb3IoItCS0LXRgNGB0LjRjyBMaWdodHdlaWdodCBDaGFydHMg0L3QtSDQv9C+0LTQtNC10YDQttC40LLQsNC10YLRgdGPIil9OwogIGNvbnN0IGVuc3VyZUNoYXJ0PWNhcmQ9PnsKICAgIGlmKGNhcmQuZGF0YXNldC5jaGFydFN0YXRlPT09InJlYWR5IilyZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGNoYXJ0cy5nZXQoY2FyZC5kYXRhc2V0LnN5bWJvbCkpOwogICAgaWYoY2FyZC5kYXRhc2V0LmNoYXJ0U3RhdGU9PT0ibG9hZGluZyImJmNhcmQuX2NoYXJ0UHJvbWlzZSlyZXR1cm4gY2FyZC5fY2hhcnRQcm9taXNlOwogICAgY29uc3Qgc3ltYm9sPWNhcmQuZGF0YXNldC5zeW1ib2wsYm94PWNhcmQucXVlcnlTZWxlY3RvcigiLnR2LXdpZGdldC1zbG90IiksZXJyb3JCb3g9Y2FyZC5xdWVyeVNlbGVjdG9yKCIudHYtY2hhcnQtZXJyb3IiKTsKICAgIGNhcmQuZGF0YXNldC5jaGFydFN0YXRlPSJsb2FkaW5nIjtpZihlcnJvckJveCllcnJvckJveC50ZXh0Q29udGVudD0i0JfQsNCz0YDRg9C30LrQsCDQuNC90YLQtdGA0LDQutGC0LjQstC90L7Qs9C+INCz0YDQsNGE0LjQutCwLi4uIjsKICAgIGNhcmQuX2NoYXJ0UHJvbWlzZT0oYXN5bmMoKT0+e3RyeXsKICAgICAgY29uc3QgW2NhbmRsZXMsbGlicmFyeV09YXdhaXQgUHJvbWlzZS5hbGwoW2xvYWRDYW5kbGVzKHN5bWJvbCksd2FpdExpYnJhcnkoKV0pO2F3YWl0IGFmdGVyTGF5b3V0KCk7aWYoIWNhcmQub3Blbil7Y2FyZC5kYXRhc2V0LmNoYXJ0U3RhdGU9ImlkbGUiO3JldHVybiBudWxsfQogICAgICBib3gucmVwbGFjZUNoaWxkcmVuKCk7CiAgICAgIGNvbnN0IGJhY2tncm91bmQ9bGlicmFyeS5Db2xvclR5cGUmJmxpYnJhcnkuQ29sb3JUeXBlLlNvbGlkP3t0eXBlOmxpYnJhcnkuQ29sb3JUeXBlLlNvbGlkLGNvbG9yOiIjMDgxODIzIn06e2NvbG9yOiIjMDgxODIzIn07CiAgICAgIGNvbnN0IGFwaT1saWJyYXJ5LmNyZWF0ZUNoYXJ0KGJveCx7d2lkdGg6d2lkdGhPZihib3gpLGhlaWdodDo0MzAsbGF5b3V0OntiYWNrZ3JvdW5kLHRleHRDb2xvcjoiIzhmYTViNiJ9LGdyaWQ6e3ZlcnRMaW5lczp7Y29sb3I6InJnYmEoMTQzLDE2NSwxODIsLjA4KSJ9LGhvcnpMaW5lczp7Y29sb3I6InJnYmEoMTQzLDE2NSwxODIsLjA4KSJ9fSxyaWdodFByaWNlU2NhbGU6e2JvcmRlckNvbG9yOiJyZ2JhKDE0MywxNjUsMTgyLC4yKSJ9LHRpbWVTY2FsZTp7Ym9yZGVyQ29sb3I6InJnYmEoMTQzLDE2NSwxODIsLjIpIix0aW1lVmlzaWJsZTpmYWxzZSxzZWNvbmRzVmlzaWJsZTpmYWxzZX19KTsKICAgICAgY29uc3Qgc2VyaWVzPW1ha2VTZXJpZXMoYXBpLHt1cENvbG9yOiIjNjNlNmJlIixkb3duQ29sb3I6IiNmZjZiODEiLHdpY2tVcENvbG9yOiIjNjNlNmJlIix3aWNrRG93bkNvbG9yOiIjZmY2YjgxIixib3JkZXJWaXNpYmxlOmZhbHNlLHByaWNlRm9ybWF0Ont0eXBlOiJwcmljZSIscHJlY2lzaW9uOjgsbWluTW92ZTowLjAwMDAwMDAxfX0pOwogICAgICBzZXJpZXMuc2V0RGF0YShjYW5kbGVzKTthcGkudGltZVNjYWxlKCkuZml0Q29udGVudCgpOwogICAgICBjb25zdCByZXNpemU9KCk9Pntjb25zdCB3aWR0aD13aWR0aE9mKGJveCk7aWYod2lkdGg+MClhcGkuYXBwbHlPcHRpb25zKHt3aWR0aH0pfTtjb25zdCBvYnNlcnZlcj10eXBlb2YgUmVzaXplT2JzZXJ2ZXI9PT0iZnVuY3Rpb24iP25ldyBSZXNpemVPYnNlcnZlcihyZXNpemUpOm51bGw7aWYob2JzZXJ2ZXIpb2JzZXJ2ZXIub2JzZXJ2ZShib3gpO2Vsc2Ugd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoInJlc2l6ZSIscmVzaXplKTsKICAgICAgY29uc3QgaW5zdGFuY2U9e3N5bWJvbCxhcGksc2VyaWVzLG9ic2VydmVyLHJlc2l6ZX07Y2hhcnRzLnNldChzeW1ib2wsaW5zdGFuY2UpO2NhcmQuZGF0YXNldC5jaGFydFN0YXRlPSJyZWFkeSI7aWYoZXJyb3JCb3gpZXJyb3JCb3gudGV4dENvbnRlbnQ9IiI7cmV0dXJuIGluc3RhbmNlOwogICAgfWNhdGNoKGVycm9yKXtjYXJkLmRhdGFzZXQuY2hhcnRTdGF0ZT0iZmFsbGJhY2siO2lmKGVycm9yQm94KWVycm9yQm94LnRleHRDb250ZW50PSLQn9C+0LrQsNC30LDQvSDRgdC10YDQstC10YDQvdGL0Lkg0LPRgNCw0YTQuNC6LiDQmNC90YLQtdGA0LDQutGC0LjQstC90YvQuSDRgNC10LbQuNC8OiAiK1N0cmluZyhlcnJvciYmZXJyb3IubWVzc2FnZXx8ZXJyb3IpO2NvbnNvbGUuZXJyb3IoIkdyYW0xIGNoYXJ0IGVycm9yIixzeW1ib2wsZXJyb3IpO3JldHVybiBudWxsfWZpbmFsbHl7Y2FyZC5fY2hhcnRQcm9taXNlPW51bGx9fSkoKTsKICAgIHJldHVybiBjYXJkLl9jaGFydFByb21pc2U7CiAgfTsKICBjb25zdCBzdGFydD1hc3luYygpPT57CiAgICBjb25zdCByb290PWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJ0dkNoYXJ0cyIpO2lmKCFyb290KXJldHVybjsKICAgIHJvb3QuZGF0YXNldC5jaGFydFVpPVZFUlNJT047CiAgICByb290LnF1ZXJ5U2VsZWN0b3JBbGwoImRldGFpbHMudHYtYWNjb3JkaW9uLWl0ZW0iKS5mb3JFYWNoKGNhcmQ9PntjYXJkLmFkZEV2ZW50TGlzdGVuZXIoInRvZ2dsZSIsKCk9PntpZighY2FyZC5vcGVuKXJldHVybjtyb290LnF1ZXJ5U2VsZWN0b3JBbGwoImRldGFpbHMudHYtYWNjb3JkaW9uLWl0ZW0iKS5mb3JFYWNoKGl0ZW09PntpZihpdGVtIT09Y2FyZClpdGVtLm9wZW49ZmFsc2V9KTtlbnN1cmVDaGFydChjYXJkKX0pfSk7CiAgICBjb25zdCBmaXJzdD1yb290LnF1ZXJ5U2VsZWN0b3IoImRldGFpbHMudHYtYWNjb3JkaW9uLWl0ZW1bb3Blbl0iKXx8cm9vdC5xdWVyeVNlbGVjdG9yKCJkZXRhaWxzLnR2LWFjY29yZGlvbi1pdGVtIik7aWYoZmlyc3Qpe2ZpcnN0Lm9wZW49dHJ1ZTthd2FpdCBhZnRlckxheW91dCgpO2F3YWl0IGVuc3VyZUNoYXJ0KGZpcnN0KX0KICB9OwogIHdpbmRvdy5fX0dSQU0xX0NIQVJUX1VJX1ZFUlNJT05fXz1WRVJTSU9OOwogIGlmKGRvY3VtZW50LnJlYWR5U3RhdGU9PT0ibG9hZGluZyIpZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigiRE9NQ29udGVudExvYWRlZCIsc3RhcnQse29uY2U6dHJ1ZX0pO2Vsc2Ugc3RhcnQoKTsKfSkoKTsK\\\"\\n00407: _GRAM1_CHART_SYMBOLS = {\\\"HBAR\\\", \\\"POL\\\", \\\"AVAX\\\", \\\"LINK\\\"}\\n00408: \\n00409: @app.get(\\\"/vendor/gram1-charts-v4.js\\\", include_in_schema=False)\\n00410: def _gram1_chart_javascript_v4():\\n00411: return _Gram1Response(content=_gram1_base64.b64decode(_GRAM1_CHART_JS_B64), media_type=\\\"application/javascript\\\", headers={\\\"Cache-Control\\\":\\\"no-store\\\"})\\n00412: \\n00413: async def _gram1_chart_data_v4(symbol: str, request: _Gram1Request):\\n00414: fn = globals().get(\\\"klines\\\")\",\"token\":\"/vendor/gram1-charts-v4.js\"}]},\"changes_made\":false,\"command_id\":\"GRAM1-SITE-CHARTS-V38R1-SUDO-PROBE-LINECOUNT-FIX-20260801T124200Z\",\"container_files\":{\"app.py\":[{\"bytes\":32843,\"path\":\"app.py\",\"sha256\":\"3b55b78c015173eea66b67afb8fb9371f0d7d2e38a7679d4c433749f4c07301d\"},{\"bytes\":32843,\"path\":\"/app/app.py\",\"sha256\":\"3b55b78c015173eea66b67afb8fb9371f0d7d2e38a7679d4c433749f4c07301d\"}],\"templates/index.html\":[{\"bytes\":29232,\"path\":\"templates/index.html\",\"sha256\":\"541157608706ebb8d4a415a13ee1d1972947bc5b6cbe26c0eae36b6cabc9b7a5\"},{\"bytes\":29232,\"path\":\"/app/templates/index.html\",\"sha256\":\"541157608706ebb8d4a415a13ee1d1972947bc5b6cbe26c0eae36b6cabc9b7a5\"}]},\"control_plane_error\":null,\"diagnosis\":[\"ACCORDION_INIT_IS_INLINE_AND_NOT_DEFERRED\",\"LEGACY_CHART_RENDERER_STILL_TARGETS_SAME_ID_CHART\",\"SERVER_SVG_ACCORDION_IS_INSIDE_LEGACY_CHART_TARGET\",\"LEGACY_NO_DATA_STATE_TEXT_STILL_EXISTS_IN_JAVASCRIPT\",\"TWO_RENDERING_PIPELINES_CAN_STACK_IN_ONE_CONTAINER\",\"RUNNING_WEB_CONTAINER_USES_CURRENT_HOST_TEMPLATE\"],\"duration_seconds\":0.171,\"exact_state\":{\"app.py\":{\"bytes\":32843,\"sha256\":\"3b55b78c015173eea66b67afb8fb9371f0d7d2e38a7679d4c433749f4c07301d\"},\"compose\":{\"path\":\"/opt/stacks/crypto-portfolio/compose.yaml\",\"sha256\":\"6d344e46b3de94ad86131a914df2ff3ff25e9d82f7ce0557cbffa468b8cdc1b8\"},\"templates/index.html\":{\"bytes\":29232,\"sha256\":\"541157608706ebb8d4a415a13ee1d1972947bc5b6cbe26c0eae36b6cabc9b7a5\"},\"worker.py\":{\"bytes\":10300,\"sha256\":\"4a7b16ece826fe2d938f392db6b0b5ca028dcb6ff02fdff4ab431f114edf0e5c\"}},\"gitea_changed\":false,\"host_container_index_match\":true,\"mutation_outcome\":\"NO_MUTATION\",\"production_site_changed\":false,\"rc\":0,\"repair_plan_constraints\":[\"Do not put the new accordion inside the legacy #chart target.\",\"Choose one rendering pipeline for the visible coin-chart area.\",\"Remove or rewire the stale chart status badge instead of leaving «Нет данных».\",\"Initialize the accordion after DOM creation via external defer script or DOMContentLoaded.\",\"Enforce exactly one open section and verify it in a real DOM engine.\"],\"rollback_restored\":null,\"rollback_started\":false,\"schema\":1,\"status\":\"OK\",\"template_analysis\":{\"chart_elements\":[{\"body_bytes\":1946,\"body_end\":\"<details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"HBAR\\\" open><summary class=\\\"tv-accordion-head\\\"><span>HBAR / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/HBAR?ui=v35\\\" alt=\\\"Свечной график HBAR / USDT\\\" loading=\\\"eager\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"POL\\\"><summary class=\\\"tv-accordion-head\\\"><span>POL / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/POL?ui=v35\\\" alt=\\\"Свечной график POL / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"AVAX\\\"><summary class=\\\"tv-accordion-head\\\"><span>AVAX / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/AVAX?ui=v35\\\" alt=\\\"Свечной график AVAX / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"LINK\\\"><summary class=\\\"tv-accordion-head\\\"><span>LINK / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/LINK?ui=v35\\\" alt=\\\"Свечной график LINK / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details>\",\"body_sha256\":\"bbc7836d04ccc13777a3aba6fd0502d6f94dd4fd2c8bdbb93e3150c7c7209411\",\"body_start\":\"<details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"HBAR\\\" open><summary class=\\\"tv-accordion-head\\\"><span>HBAR / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/HBAR?ui=v35\\\" alt=\\\"Свечной график HBAR / USDT\\\" loading=\\\"eager\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"POL\\\"><summary class=\\\"tv-accordion-head\\\"><span>POL / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/POL?ui=v35\\\" alt=\\\"Свечной график POL / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"AVAX\\\"><summary class=\\\"tv-accordion-head\\\"><span>AVAX / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/AVAX?ui=v35\\\" alt=\\\"Свечной график AVAX / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"LINK\\\"><summary class=\\\"tv-accordion-head\\\"><span>LINK / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/LINK?ui=v35\\\" alt=\\\"Свечной график LINK / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details>\",\"contains_no_data\":false,\"details_count\":4,\"line\":6,\"open_details_count\":1,\"open_tag\":\"<svg id=\\\"chart\\\" class=\\\"chart\\\" viewBox=\\\"0 0 900 270\\\" preserveAspectRatio=\\\"none\\\" data-chart-ui=\\\"GRAM1_DIRECT_SERVER_V5\\\">\",\"server_svg_count\":4}],\"diagnosis\":[\"ACCORDION_INIT_IS_INLINE_AND_NOT_DEFERRED\",\"LEGACY_CHART_RENDERER_STILL_TARGETS_SAME_ID_CHART\",\"SERVER_SVG_ACCORDION_IS_INSIDE_LEGACY_CHART_TARGET\",\"LEGACY_NO_DATA_STATE_TEXT_STILL_EXISTS_IN_JAVASCRIPT\",\"TWO_RENDERING_PIPELINES_CAN_STACK_IN_ONE_CONTAINER\"],\"legacy_chart_scripts\":[{\"body_sha256\":\"e6afa17d7b94d9da5036d55bf6b8c623ec036ca05e9dea9c789432631c1b6ca7\",\"chart_refs\":[\"chart.innerHTML\",\"Нет данных\"],\"id\":null,\"index\":0,\"line\":9,\"snippet\":\"00005: <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>\\n00006: <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\\\" data-chart-ui=\\\"GRAM1_DIRECT_SERVER_V5\\\"><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"HBAR\\\" open><summary class=\\\"tv-accordion-head\\\"><span>HBAR / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/HBAR?ui=v35\\\" alt=\\\"Свечной график HBAR / USDT\\\" loading=\\\"eager\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"POL\\\"><summary class=\\\"tv-accordion-head\\\"><span>POL / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/POL?ui=v35\\\" alt=\\\"Свечной график POL / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"AVAX\\\"><summary class=\\\"tv-accordion-head\\\"><span>AVAX / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/AVAX?ui=v35\\\" alt=\\\"Свечной график AVAX / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"LINK\\\"><summary class=\\\"tv-accordion-head\\\"><span>LINK / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/LINK?ui=v35\\\" alt=\\\"Свечной график LINK / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details></svg></div><div class=\\\"panel sharp-moves-panel\\\"><div class=\\\"title\\\"><h2>Резкие движения</h2></div><div id=\\\"moves\\\" class=\\\"moves\\\"></div></div></section>\\n00007: <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>\\n00008: <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_below\\\">Капитализация ниже</option><option value=\\\"pump\\\">Резкий памп</option><option value=\\\"dump\\\">Резкое падение</option></select><input id=\\\"aThreshold\\\" type=\\\"number\\\" step=\\\"any\\\" placeholder=\\\"Порог\\\"><select id=\\\"aWindow\\\" class=\\\"hidden\\\"><option value=\\\"300\\\">5 минут</option><option value=\\\"900\\\" selected>15 минут</option><option value=\\\"1800\\\">30 минут</option><option value=\\\"3600\\\">1 час</option><option value=\\\"14400\\\">4 часа</option><option value=\\\"86400\\\">24 часа</option></select><input id=\\\"aEmail\\\" class=\\\"full\\\" type=\\\"email\\\" value=\\\"aleisaev@yandex.ru\\\" placeholder=\\\"Email\\\"><button id=\\\"addAlert\\\" class=\\\"btn full\\\">Создать оповещение</button></div></div><div class=\\\"panel\\\"><div class=\\\"title\\\"><h2>Мои оповещения</h2></div><div id=\\\"alertList\\\"></div></div></section></main>\\n00009: <script>let state=null;let selected=\\\"hedera-hashgraph\\\";let currency=localStorage.getItem(\\\"portfolio_currency\\\")===\\\"RUB\\\"?\\\"RUB\\\":\\\"USD\\\";const fx=()=>Number(state&&state.market&&state.market.usd_rub||0);const cv=n=>Number(n||0)*(currency===\\\"RUB\\\"?fx():1);const money=(n,d=2)=>new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{style:\\\"currency\\\",currency:currency,minimumFractionDigits:d,maximumFractionDigits:d}).format(cv(n));const compactMoney=n=>(currency===\\\"RUB\\\"?\\\"₽\\\":\\\"$\\\")+new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{notation:\\\"compact\\\",maximumFractionDigits:2}).format(cv(n));const inputPrice=n=>Number((Number(n||0)*(currency===\\\"RUB\\\"?fx():1)).toFixed(currency===\\\"RUB\\\"?2:8));const coin=()=>state.market.coins[selected];async function load(){let r=await fetch(\\\"/api/state\\\");if(r.status===401){location=\\\"/login\\\";return}state=await r.json();render()}function render(){let m=state.market.coins||{};let ids=Object.keys(state.coins);/* ALERT_COIN_SELECTOR_EARLY_V1 */aCoin.innerHTML=ids.map(id=>\\\"<option value=\\\\\\\"\\\"+id+\\\"\\\\\\\">\\\"+state.coins[id]+\\\"</option>\\\").join(\\\"\\\");if(currency===\\\"RUB\\\"&&!(fx()>0)){currency=\\\"USD\\\"}document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.classList.toggle(\\\"active\\\",x.dataset.currency===currency));avgHeader.textContent=\\\"Средняя покупка (\\\"+currency+\\\")\\\";coins.innerHTML=ids.map(id=>{let c=m[id]||{};let ch=Number(c.change_24h||0);return \\\"<div class=\\\\\\\"card coin \\\"+(id===selected?\\\"active\\\":\\\"\\\")+\\\"\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\"><div class=\\\\\\\"coin-head\\\\\\\"><span class=\\\\\\\"coin-symbol\\\\\\\">\\\"+state.coins[id]+\\\"</span><b class=\\\\\\\"coin-price\\\\\\\">\\\"+money(c.price,2)+\\\"</b></div><div class=\\\\\\\"\\\"+(ch>=0?\\\"good\\\":\\\"bad\\\")+\\\"\\\\\\\">\\\"+(ch>=0?\\\"+\\\":\\\"\\\")+ch.toFixed(2)+\\\"%</div><small class=\\\\\\\"muted\\\\\\\">Cap \\\"+compactMoney(c.market_cap)+\\\"</small></div>\\\"}).join(\\\"\\\");document.querySelectorAll(\\\".coin\\\").forEach(x=>{let id=x.dataset.id;let head=x.querySelector(\\\".coin-head\\\");let img=document.createElement(\\\"img\\\");img.className=\\\"coin-icon\\\";img.src=\\\"/coin-icon/\\\"+id;img.alt=state.coins[id];img.loading=\\\"lazy\\\";head.prepend(img);x.onclick=()=>{selected=id;render()}});let total=0,inv=0,daily=0;rows.innerHTML=ids.map(id=>{let c=m[id]||{};let h=state.holdings[id]||{quantity:0,avg_buy:0};let value=Number(h.quantity)*Number(c.price||0);let cost=Number(h.quantity)*Number(h.avg_buy);let p=value-cost;total+=value;inv+=cost;daily+=value*Number(c.change_24h||0)/100;return \\\"<tr><td><b>\\\"+state.coins[id]+\\\"</b><div class=\\\\\\\"muted\\\\\\\">\\\"+(c.name||id)+\\\"</div></td><td>\\\"+money(c.price,2)+\\\"</td><td>\\\"+money(c.market_cap,0)+\\\"</td><td><input class=\\\\\\\"num qty\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"any\\\\\\\" value=\\\\\\\"\\\"+h.quantity+\\\"\\\\\\\"></td><td><input class=\\\\\\\"num avg\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"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=\\\\\\\"#8fa5b6\\\\\\\">История ещё загружается</text>\\\";return}let min=Math.min(...a),max=Math.max(...a),w=900,h=270,p=18;let pts=a.map((v,i)=>(p+i*(w-2*p)/(a.length-1)).toFixed(1)+\\\",\\\"+(p+(max-v)*(h-2*p)/(max-min||1)).toFixed(1)).join(\\\" \\\");chart.innerHTML=\\\"<defs><linearGradient id=\\\\\\\"g\\\\\\\"><stop stop-color=\\\\\\\"#63e6be\\\\\\\"/><stop offset=\\\\\\\"1\\\\\\\" stop-color=\\\\\\\"#69a7ff\\\\\\\"/></linearGradient></defs><polyline fill=\\\\\\\"none\\\\\\\" stroke=\\\\\\\"url(#g)\\\\\\\" stroke-width=\\\\\\\"3\\\\\\\" points=\\\\\\\"\\\"+pts+\\\"\\\\\\\"/>\\\"}function renderMoves(){let a=Object.values(state.market.coins||{}).map(c=>({s:c.symbol,p:Number(c.change_1h||0),price:c.price})).sort((x,y)=>Math.abs(y.p)-Math.abs(x.p));moves.innerHTML=a.map(x=>\\\"<div class=\\\\\\\"move\\\\\\\"><span class=\\\\\\\"muted\\\\\\\">Изменение за 1 час</span><b class=\\\\\\\"\\\"+(x.p>=0?\\\"good\\\":\\\"bad\\\")+\\\"\\\\\\\">\\\"+x.s+\\\" \\\"+(x.p>=0?\\\"+\\\":\\\"\\\")+x.p.toFixed(2)+\\\"%</b><div>\\\"+money(x.price,2)+\\\"</div></div>\\\").join(\\\"\\\")}function label(a){let c=state.coins[a.coin_id];let k={price_above:\\\"цена ≥\\\",price_below:\\\"цена ≤\\\",cap_above:\\\"капитализация ≥\\\",cap_below:\\\"капитализация ≤\\\",pump:\\\"памп ≥ +\\\",dump:\\\"падение ≥ −\\\"}[a.kind];return c+\\\": \\\"+k+\\\" \\\"+a.threshold+(a.kind===\\\"pump\\\"||a.kind===\\\"dump\\\"?\\\"% за \\\"+Math.round(a.window_seconds/60)+\\\" мин\\\":\\\" USD\\\")}function renderAlerts(){alertList.innerHTML=state.alerts.length?state.alerts.map(a=>\\\"<div class=\\\\\\\"alert\\\\\\\"><div><b>\\\"+label(a)+\\\"</b><div class=\\\\\\\"muted\\\\\\\">\\\"+a.email+\\\"</div></div><button class=\\\\\\\"btn ghost del\\\\\\\" data-id=\\\\\\\"\\\"+a.id+\\\"\\\\\\\">Удалить</button></div>\\\").join(\\\"\\\"):\\\"<span class=\\\\\\\"muted\\\\\\\">Оповещений пока нет</span>\\\";document.querySelectorAll(\\\".del\\\").forEach(x=>x.onclick=async()=>{await fetch(\\\"/api/alert/\\\"+x.dataset.id,{method:\\\"DELETE\\\"});load()})}aKind.onchange=()=>aWindow.classList.toggle(\\\"hidden\\\",!([\\\"pump\\\",\\\"dump\\\"].includes(aKind.value)));addAlert.onclick=async()=>{let dynamic=[\\\"pump\\\",\\\"dump\\\"].includes(aKind.value);let r=await fetch(\\\"/api/alert\\\",{method:\\\"POST\\\",headers:{\\\"Content-Type\\\":\\\"application/json\\\"},body:JSON.stringify({coin_id:aCoin.value,kind:aKind.value,threshold:Number(aThreshold.value),window_seconds:dynamic?Number(aWindow.value):0,email:aEmail.value})});if(!r.ok){alert(\\\"Проверьте параметры\\\")};aThreshold.value=\\\"\\\";load()};document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.onclick=()=>{currency=x.dataset.currency;localStorage.setItem(\\\"portfolio_currency\\\",currency);render()});load();setInterval(load,60000)</script><style>/* CRYPTO_UI_V2 */.coins{grid-template-columns:repeat(2,minmax(250px,1fr))!important;gap:14px!important}.coin{min-height:120px;display:flex!important;flex-direction:column;align-items:center;justify-content:center;text-align:center}.moves{grid-template-columns:1fr!important}.move{text-align:center;min-height:72px;display:flex;flex-direction:column;justify-content:center}.alert{display:grid!important;grid-template-columns:minmax(0,1fr) minmax(170px,auto) auto!important;align-items:center!important}.sale{padding:8px 12px;border:1px solid var(--l);border-radius:10px;text-align:center;color:var(--g);font-weight:800;white-space:nowrap}@media(max-width:750px){.coins{grid-template-columns:1fr!important}.alert{grid-template-columns:1fr auto!important}.sa\\n---\\n00005: <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>\\n00006: <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\\\" data-chart-ui=\\\"GRAM1_DIRECT_SERVER_V5\\\"><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"HBAR\\\" open><summary class=\\\"tv-accordion-head\\\"><span>HBAR / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/HBAR?ui=v35\\\" alt=\\\"Свечной график HBAR / USDT\\\" loading=\\\"eager\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"POL\\\"><summary class=\\\"tv-accordion-head\\\"><span>POL / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/POL?ui=v35\\\" alt=\\\"Свечной график POL / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"AVAX\\\"><summary class=\\\"tv-accordion-head\\\"><span>AVAX / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/AVAX?ui=v35\\\" alt=\\\"Свечной график AVAX / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"LINK\\\"><summary class=\\\"tv-accordion-head\\\"><span>LINK / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/LINK?ui=v35\\\" alt=\\\"Свечной график LINK / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details></svg></div><div class=\\\"panel sharp-moves-panel\\\"><div class=\\\"title\\\"><h2>Резкие движения</h2></div><div id=\\\"moves\\\" class=\\\"moves\\\"></div></div></section>\\n00007: <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>\\n00008: <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_below\\\">Капитализация ниже</option><option value=\\\"pump\\\">Резкий памп</option><option value=\\\"dump\\\">Резкое падение</option></select><input id=\\\"aThreshold\\\" type=\\\"number\\\" step=\\\"any\\\" placeholder=\\\"Порог\\\"><select id=\\\"aWindow\\\" class=\\\"hidden\\\"><option value=\\\"300\\\">5 минут</option><option value=\\\"900\\\" selected>15 минут</option><option value=\\\"1800\\\">30 минут</option><option value=\\\"3600\\\">1 час</option><option value=\\\"14400\\\">4 часа</option><option value=\\\"86400\\\">24 часа</option></select><input id=\\\"aEmail\\\" class=\\\"full\\\" type=\\\"email\\\" value=\\\"aleisaev@yandex.ru\\\" placeholder=\\\"Email\\\"><button id=\\\"addAlert\\\" class=\\\"btn full\\\">Создать оповещение</button></div></div><div class=\\\"panel\\\"><div class=\\\"title\\\"><h2>Мои оповещения</h2></div><div id=\\\"alertList\\\"></div></div></section></main>\\n00009: <script>let state=null;let selected=\\\"hedera-hashgraph\\\";let currency=localStorage.getItem(\\\"portfolio_currency\\\")===\\\"RUB\\\"?\\\"RUB\\\":\\\"USD\\\";const fx=()=>Number(state&&state.market&&state.market.usd_rub||0);const cv=n=>Number(n||0)*(currency===\\\"RUB\\\"?fx():1);const money=(n,d=2)=>new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{style:\\\"currency\\\",currency:currency,minimumFractionDigits:d,maximumFractionDigits:d}).format(cv(n));const compactMoney=n=>(currency===\\\"RUB\\\"?\\\"₽\\\":\\\"$\\\")+new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{notation:\\\"compact\\\",maximumFractionDigits:2}).format(cv(n));const inputPrice=n=>Number((Number(n||0)*(currency===\\\"RUB\\\"?fx():1)).toFixed(currency===\\\"RUB\\\"?2:8));const coin=()=>state.market.coins[selected];async function load(){let r=await fetch(\\\"/api/state\\\");if(r.status===401){location=\\\"/login\\\";return}state=await r.json();render()}function render(){let m=state.market.coins||{};let ids=Object.keys(state.coins);/* ALERT_COIN_SELECTOR_EARLY_V1 */aCoin.innerHTML=ids.map(id=>\\\"<option value=\\\\\\\"\\\"+id+\\\"\\\\\\\">\\\"+state.coins[id]+\\\"</option>\\\").join(\\\"\\\");if(currency===\\\"RUB\\\"&&!(fx()>0)){currency=\\\"USD\\\"}document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.classList.toggle(\\\"active\\\",x.dataset.currency===currency));avgHeader.textContent=\\\"Средняя покупка (\\\"+currency+\\\")\\\";coins.innerHTML=ids.map(id=>{let c=m[id]||{};let ch=Number(c.change_24h||0);return \\\"<div class=\\\\\\\"card coin \\\"+(id===selected?\\\"active\\\":\\\"\\\")+\\\"\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\"><div class=\\\\\\\"coin-head\\\\\\\"><span class=\\\\\\\"coin-symbol\\\\\\\">\\\"+state.coins[id]+\\\"</span><b class=\\\\\\\"coin-price\\\\\\\">\\\"+money(c.price,2)+\\\"</b></div><div class=\\\\\\\"\\\"+(ch>=0?\\\"good\\\":\\\"bad\\\")+\\\"\\\\\\\">\\\"+(ch>=0?\\\"+\\\"\",\"src\":null},{\"body_sha256\":\"b6fedba59295891e784ab98cef7690efe81553715d35a9cf6a31c30d08efb5a9\",\"chart_refs\":[\"replaceChildren(\"],\"id\":\"ALERT_LIST_RENDERER_V5\",\"index\":2,\"line\":109,\"snippet\":\"00135: return symbol+\\\": \\\"+kind+\\\" \\\"+alertMoney(data,alert.threshold);\\n00136: };\\n00137: const draw=data=>{\\n00138: const box=document.getElementById(\\\"alertList\\\");\\n00139: if(!box||!data||!Array.isArray(data.alerts))return;\\n00140: lastState=data;\\n00141: box.replaceChildren();\\n00142: if(data.alerts.length===0){\\n00143: box.append(make(\\\"span\\\",\\\"muted\\\",\\\"Оповещений пока нет\\\"));\",\"src\":null},{\"body_sha256\":\"90a765a83fb8a5a0542dee1abf8c32af0f248aa411aa203848f73758bc3f9b40\",\"chart_refs\":[\"getElementById(\\\"chart\\\")\",\"appendChild(\"],\"id\":\"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1\",\"index\":5,\"line\":222,\"snippet\":\"00218: <script src=\\\"/vendor/lightweight-charts.js\\\"></script>\\n00219: <script id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_SCRIPT_V5\\\">(()=>{const root=document.getElementById(\\\"chart\\\");if(!root)return;root.dataset.chartUi=\\\"GRAM1_DIRECT_SERVER_V5\\\";const items=[...root.querySelectorAll(\\\"details.tv-accordion-item\\\")];items.forEach(item=>item.addEventListener(\\\"toggle\\\",()=>{if(!item.open)return;items.forEach(other=>{if(other!==item)other.open=false})}));})();</script>\\n00220: \\n00221: <style id=\\\"SHARP_MOVES_LAYOUT_V2\\\">.sharp-moves-panel{grid-column:1 / -1;justify-self:center;width:min(100%,860px);margin-inline:auto}.sharp-moves-panel .title{justify-content:center}.sharp-moves-panel .title h2{width:100%;text-align:center}#moves{grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:12px!important}@media(max-width:750px){.sharp-moves-panel{width:100%}#moves{grid-template-columns:1fr!important}}</style>\\n00222: <script id=\\\"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1\\\">(()=>{const move=()=>{const chart=document.getElementById(\\\"chart\\\");const panel=chart&&chart.closest(\\\".panel\\\");const main=document.querySelector(\\\"main\\\");if(!panel||!main)return;main.appendChild(panel);panel.style.gridColumn=\\\"1 / -1\\\";const title=document.getElementById(\\\"chartTitle\\\");if(title)title.textContent=\\\"Графики монет\\\";const note=panel.querySelector(\\\".muted\\\");if(note)note.textContent=\\\"Свечи Binance · 1 месяц · весь доступный период\\\"};document.addEventListener(\\\"DOMContentLoaded\\\",move);window.addEventListener(\\\"load\\\",move);setTimeout(move,300);setTimeout(move,1500);setTimeout(move,4000)})();</script></body></html>\\n---\\n00218: <script src=\\\"/vendor/lightweight-charts.js\\\"></script>\\n00219: <script id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_SCRIPT_V5\\\">(()=>{const root=document.getElementById(\\\"chart\\\");if(!root)return;root.dataset.chartUi=\\\"GRAM1_DIRECT_SERVER_V5\\\";const items=[...root.querySelectorAll(\\\"details.tv-accordion-item\\\")];items.forEach(item=>item.addEventListener(\\\"toggle\\\",()=>{if(!item.open)return;items.forEach(other=>{if(other!==item)other.open=false})}));})();</script>\\n00220: \\n00221: <style id=\\\"SHARP_MOVES_LAYOUT_V2\\\">.sharp-moves-panel{grid-column:1 / -1;justify-self:center;width:min(100%,860px);margin-inline:auto}.sharp-moves-panel .title{justify-content:center}.sharp-moves-panel .title h2{width:100%;text-align:center}#moves{grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:12px!important}@media(max-width:750px){.sharp-moves-panel{width:100%}#moves{grid-template-columns:1fr!important}}</style>\\n00222: <script id=\\\"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1\\\">(()=>{const move=()=>{const chart=document.getElementById(\\\"chart\\\");const panel=chart&&chart.closest(\\\".panel\\\");const main=document.querySelector(\\\"main\\\");if(!panel||!main)return;main.appendChild(panel);panel.style.gridColumn=\\\"1 / -1\\\";const title=document.getElementById(\\\"chartTitle\\\");if(title)title.textContent=\\\"Графики монет\\\";const note=panel.querySelector(\\\".muted\\\");if(note)note.textContent=\\\"Свечи Binance · 1 месяц · весь доступный период\\\"};document.addEventListener(\\\"DOMContentLoaded\\\",move);window.addEventListener(\\\"load\\\",move);setTimeout(move,300);setTimeout(move,1500);setTimeout(move,4000)})();</script></body></html>\",\"src\":null}],\"no_data_occurrences\":[{\"inside_chart\":false,\"inside_script\":true,\"line\":9,\"snippet\":\"00005: <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>\\n00006: <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\\\" data-chart-ui=\\\"GRAM1_DIRECT_SERVER_V5\\\"><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"HBAR\\\" open><summary class=\\\"tv-accordion-head\\\"><span>HBAR / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/HBAR?ui=v35\\\" alt=\\\"Свечной график HBAR / USDT\\\" loading=\\\"eager\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"POL\\\"><summary class=\\\"tv-accordion-head\\\"><span>POL / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/POL?ui=v35\\\" alt=\\\"Свечной график POL / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"AVAX\\\"><summary class=\\\"tv-accordion-head\\\"><span>AVAX / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/AVAX?ui=v35\\\" alt=\\\"Свечной график AVAX / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"LINK\\\"><summary class=\\\"tv-accordion-head\\\"><span>LINK / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/LINK?ui=v35\\\" alt=\\\"Свечной график LINK / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details></svg></div><div class=\\\"panel sharp-moves-panel\\\"><div class=\\\"title\\\"><h2>Резкие движения</h2></div><div id=\\\"moves\\\" class=\\\"moves\\\"></div></div></section>\\n00007: <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>\\n00008: <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_below\\\">Капитализация ниже</option><option value=\\\"pump\\\">Резкий памп</option><option value=\\\"dump\\\">Резкое падение</option></select><input id=\\\"aThreshold\\\" type=\\\"number\\\" step=\\\"any\\\" placeholder=\\\"Порог\\\"><select id=\\\"aWindow\\\" class=\\\"hidden\\\"><option value=\\\"300\\\">5 минут</option><option value=\\\"900\\\" selected>15 минут</option><option value=\\\"1800\\\">30 минут</option><option value=\\\"3600\\\">1 час</option><option value=\\\"14400\\\">4 часа</option><option value=\\\"86400\\\">24 часа</option></select><input id=\\\"aEmail\\\" class=\\\"full\\\" type=\\\"email\\\" value=\\\"aleisaev@yandex.ru\\\" placeholder=\\\"Email\\\"><button id=\\\"addAlert\\\" class=\\\"btn full\\\">Создать оповещение</button></div></div><div class=\\\"panel\\\"><div class=\\\"title\\\"><h2>Мои оповещения</h2></div><div id=\\\"alertList\\\"></div></div></section></main>\\n00009: <script>let state=null;let selected=\\\"hedera-hashgraph\\\";let currency=localStorage.getItem(\\\"portfolio_currency\\\")===\\\"RUB\\\"?\\\"RUB\\\":\\\"USD\\\";const fx=()=>Number(state&&state.market&&state.market.usd_rub||0);const cv=n=>Number(n||0)*(currency===\\\"RUB\\\"?fx():1);const money=(n,d=2)=>new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{style:\\\"currency\\\",currency:currency,minimumFractionDigits:d,maximumFractionDigits:d}).format(cv(n));const compactMoney=n=>(currency===\\\"RUB\\\"?\\\"₽\\\":\\\"$\\\")+new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{notation:\\\"compact\\\",maximumFractionDigits:2}).format(cv(n));const inputPrice=n=>Number((Number(n||0)*(currency===\\\"RUB\\\"?fx():1)).toFixed(currency===\\\"RUB\\\"?2:8));const coin=()=>state.market.coins[selected];async function load(){let r=await fetch(\\\"/api/state\\\");if(r.status===401){location=\\\"/login\\\";return}state=await r.json();render()}function render(){let m=state.market.coins||{};let ids=Object.keys(state.coins);/* ALERT_COIN_SELECTOR_EARLY_V1 */aCoin.innerHTML=ids.map(id=>\\\"<option value=\\\\\\\"\\\"+id+\\\"\\\\\\\">\\\"+state.coins[id]+\\\"</option>\\\").join(\\\"\\\");if(currency===\\\"RUB\\\"&&!(fx()>0)){currency=\\\"USD\\\"}document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.classList.toggle(\\\"active\\\",x.dataset.currency===currency));avgHeader.textContent=\\\"Средняя покупка (\\\"+currency+\\\")\\\";coins.innerHTML=ids.map(id=>{let c=m[id]||{};let ch=Number(c.change_24h||0);return \\\"<div class=\\\\\\\"card coin \\\"+(id===selected?\\\"active\\\":\\\"\\\")+\\\"\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\"><div class=\\\\\\\"coin-head\\\\\\\"><span class=\\\\\\\"coin-symbol\\\\\\\">\\\"+state.coins[id]+\\\"</span><b class=\\\\\\\"coin-price\\\\\\\">\\\"+money(c.price,2)+\\\"</b></div><div class=\\\\\\\"\\\"+(ch>=0?\\\"good\\\":\\\"bad\\\")+\\\"\\\\\\\">\\\"+(ch>=0?\\\"+\\\":\\\"\\\")+ch.toFixed(2)+\\\"%</div><small class=\\\\\\\"muted\\\\\\\">Cap \\\"+compactMoney(c.market_cap)+\\\"</small></div>\\\"}).join(\\\"\\\");document.querySelectorAll(\\\".coin\\\").forEach(x=>{let id=x.dataset.id;let head=x.querySelector(\\\".coin-head\\\");let img=document.createElement(\\\"img\\\");img.className=\\\"coin-icon\\\";img.src=\\\"/coin-icon/\\\"+id;img.alt=state.coins[id];img.loading=\\\"lazy\\\";head.prepend(img);x.onclick=()=>{selected=id;render()}});let total=0,inv=0,daily=0;rows.innerHTML=ids.map(id=>{let c=m[id]||{};let h=state.holdings[id]||{quantity:0,avg_buy:0};let value=Number(h.quantity)*Number(c.price||0);let cost=Number(h.quantity)*Number(h.avg_buy);let p=value-cost;total+=value;inv+=cost;daily+=value*Number(c.change_24h||0)/100;return \\\"<tr><td><b>\\\"+state.coins[id]+\\\"</b><div class=\\\\\\\"muted\\\\\\\">\\\"+(c.name||id)+\\\"</div></td><td>\\\"+money(c.price,2)+\\\"</td><td>\\\"+money(c.market_cap,0)+\\\"</td><td><input class=\\\\\\\"num qty\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"any\\\\\\\" value=\\\\\\\"\\\"+h.quantity+\\\"\\\\\\\"></td><td><input class=\\\\\\\"num avg\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"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=\\\\\\\"#8fa5b6\\\\\\\">История ещё загружается</text>\\\";return}let min=Math.min(...a),max=Math.max(...a),w=900,h=270,p=18;let pts=a.map((v,i)=>(p+i*(w-2*p)/(a.length-1)).toFixed(1)+\\\",\\\"+(p+(max-v)*(h-2*p)/(max-min||1)).toFixed(1)).join(\\\" \\\");chart.innerHTML=\\\"<defs><linearGradient id=\\\\\\\"g\\\\\\\"><stop stop-color=\\\\\\\"#63e6be\\\\\\\"/><stop offset=\\\\\\\"1\\\\\\\" stop-color=\\\\\\\"#69a7ff\\\\\\\"/></linearGradient></defs><polyline fill=\\\\\\\"none\\\\\\\" stroke=\\\\\\\"url(#g)\\\\\\\" stroke-width=\\\\\\\"3\\\\\\\" points=\\\\\\\"\\\"+pts+\\\"\\\\\\\"/>\\\"}function renderMoves(){let a=Object.values(state.market.coins||{}).map(c=>({s:c.symbol,p:Number(c.change_1h||0),price:c.price})).sort((x,y)=>Math.abs(y.p)-Math.abs(x.p));moves.innerHTML=a.map(x=>\\\"<div class=\\\\\\\"move\\\\\\\"><span class=\\\\\\\"muted\\\\\\\">Изменение за 1 час</span><b class=\\\\\\\"\\\"+(x.p>=0?\\\"good\\\":\\\"bad\\\")+\\\"\\\\\\\">\\\"+x.s+\\\" \\\"+(x.p>=0?\\\"+\\\":\\\"\\\")+x.p.toFixed(2)+\\\"%</b><div>\\\"+money(x.price,2)+\\\"</div></div>\\\").join(\\\"\\\")}function label(a){let c=state.coins[a.coin_id];let k={price_above:\\\"цена ≥\\\",price_below:\\\"цена ≤\\\",cap_above:\\\"капитализация ≥\\\",cap_below:\\\"капитализация ≤\\\",pump:\\\"памп ≥ +\\\",dump:\\\"падение ≥ −\\\"}[a.kind];return c+\\\": \\\"+k+\\\" \\\"+a.threshold+(a.kind===\\\"pump\\\"||a.kind===\\\"dump\\\"?\\\"% за \\\"+Math.round(a.window_seconds/60)+\\\" мин\\\":\\\" USD\\\")}function renderAlerts(){alertList.innerHTML=state.alerts.length?state.alerts.map(a=>\\\"<div class=\\\\\\\"alert\\\\\\\"><div><b>\\\"+label(a)+\\\"</b><div class=\\\\\\\"muted\\\\\\\">\\\"+a.email+\\\"</div></div><button class=\\\\\\\"btn ghost del\\\\\\\" data-id=\\\\\\\"\\\"+a.id+\\\"\\\\\\\">Удалить</button></div>\\\").join(\\\"\\\"):\\\"<span class=\\\\\\\"muted\\\\\\\">Оповещений пока нет</span>\\\";document.querySelectorAll(\\\".del\\\").forEach(x=>x.onclick=async()=>{await fetch(\\\"/api/alert/\\\"+x.dataset.id,{method:\\\"DELETE\\\"});load()})}aKind.onchange=()=>aWindow.classList.toggle(\\\"hidden\\\",!([\\\"pump\\\",\\\"dump\\\"].includes(aKind.value)));addAlert.onclick=async()=>{let dynamic=[\\\"pump\\\",\\\"dump\\\"].includes(aKind.value);let r=await fetch(\\\"/api/alert\\\",{method:\\\"POST\\\",headers:{\\\"Content-Type\\\":\\\"application/json\\\"},body:JSON.stringify({coin_id:aCoin.value,kind:aKind.value,threshold:Number(aThreshold.value),window_seconds:dynamic?Number(aWindow.value):0,email:aEmail.value})});if(!r.ok){alert(\\\"Проверьте параметры\\\")};aThreshold.value=\\\"\\\";load()};document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.onclick=()=>{currency=x.dataset.currency;localStorage.setItem(\\\"portfolio_currency\\\",currency);render()});load();setInterval(load,60000)</script><style>/* CRYPTO_UI_V2 */.coins{grid-template-columns:repeat(2,minmax(250px,1fr))!important;gap:14px!important}.coin{min-height:120px;display:flex!important;flex-direction:column;align-items:center;justify-content:center;text-align:center}.moves{grid-template-columns:1fr!important}.move{text-align:center;min-height:72px;display:flex;flex-direction:column;justify-content:center}.alert{display:grid!important;grid-template-columns:minmax(0,1fr) minmax(170px,auto) auto!important;align-items:center!important}.sale{padding:8px 12px;border:1px solid var(--l);border-radius:10px;text-align:center;color:var(--g);font-weight:800;white-space:nowrap}@media(max-width:750px){.coins{grid-template-columns:1fr!important}.alert{grid-template-columns:1fr auto!important}.sa\"}],\"relevant_occurrences\":[{\"inside_chart\":true,\"inside_script\":false,\"line\":6,\"snippet\":\"00001: <!doctype html>\\n00002: <html lang=\\\"ru\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1\\\"><title>Gram Crypto</title>\\n00003: <style>:root{--bg:#071018;--p:#0f1d29;--p2:#0a1620;--l:#213644;--t:#f3f7fb;--m:#8fa5b6;--g:#63e6be;--r:#ff6b81;--b:#69a7ff}*{box-sizing:border-box}body{margin:0;background:radial-gradient(circle at 85% 0,#17395a55,transparent 32%),var(--bg);color:var(--t);font:14px system-ui}.app{max-width:1500px;margin:auto;padding:24px}.top{display:flex;justify-content:space-between;align-items:center;margin-bottom:18px}.top-actions{display:flex;align-items:center;gap:10px}.currency-switch{display:flex;gap:4px;padding:4px;border:1px solid var(--l);border-radius:12px;background:#091722}.currency-btn{border:0;border-radius:8px;padding:7px 10px;background:transparent;color:var(--m);font-weight:800;cursor:pointer}.currency-btn.active{background:var(--b);color:#071018}h1{margin:0;font-size:28px}.muted{color:var(--m)}button,input,select{font:inherit}.btn{border:0;border-radius:10px;padding:10px 13px;background:var(--g);color:#071018;font-weight:800;cursor:pointer}.ghost{background:#122431;color:var(--t);border:1px solid var(--l)}.grid4{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}.card,.panel{border:1px solid var(--l);background:linear-gradient(155deg,var(--p),var(--p2));border-radius:16px;padding:16px}.card strong{display:block;font-size:23px;margin-top:7px}.coins{display:grid;grid-template-columns:repeat(2,1fr);gap:9px;margin:14px 0}.coin{cursor:pointer}.coin.active{border-color:#4d829f}.coin-head{display:grid;grid-template-columns:26px minmax(0,1fr);grid-template-areas:\\\"icon name\\\" \\\". price\\\";align-items:center;column-gap:9px;row-gap:7px;margin-bottom:6px}.coin-icon{grid-area:icon;width:26px;height:26px;border-radius:50%;object-fit:cover;background:#132635;box-shadow:0 0 12px #69a7ff66}.coin-symbol{grid-area:name;font-size:22px;font-weight:900;letter-spacing:.7px;color:var(--g);text-shadow:0 0 12px #63e6be66}.coin-price{grid-area:price;font-size:17px}.asset-cell{position:relative;padding-left:43px}.asset-cell>.table-coin-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);width:23px;height:23px}.asset-symbol{font-size:17px;font-weight:900;letter-spacing:.3px;color:var(--g);text-shadow:0 0 10px #63e6be55}.coin b{font-size:17px}.good{color:var(--g)}.bad{color:var(--r)}.layout{display:grid;grid-template-columns:1.6fr .8fr;gap:14px}.title{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.title h2{font-size:16px;margin:0}.chart{width:100%;height:270px}.moves{display:grid;grid-template-columns:repeat(3,1fr);gap:9px}.move{padding:12px;border:1px solid var(--l);border-radius:12px;background:#091722}.table{overflow:auto}table{width:100%;border-collapse:collapse;min-width:900px}th,td{text-align:left;padding:10px;border-bottom:1px solid var(--l)}th{font-size:11px;color:var(--m)}input,select{background:#08141d;color:var(--t);border:1px solid var(--l);border-radius:9px;padding:9px}.num{width:115px}.alerts{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:14px}.form{display:grid;grid-template-columns:1fr 1fr;gap:9px}.full{grid-column:1/-1}.alert{display:flex;justify-content:space-between;gap:10px;border:1px solid var(--l);border-radius:11px;padding:11px;margin-bottom:8px;background:#091722}.hidden{display:none}.status{font-size:12px;padding:7px 10px;border:1px solid var(--l);border-radius:999px}@media(max-width:1050px){.coins{grid-template-columns:repeat(4,1fr)}.grid4{grid-template-columns:repeat(2,1fr)}}@media(max-width:750px){.app{padding:14px}.top{align-items:flex-start;gap:12px}.top-actions{flex-wrap:wrap;justify-content:flex-end}.layout,.alerts{grid-template-columns:1fr}.coins{grid-template-columns:repeat(2,1fr)}.moves{grid-template-columns:1fr}.grid4{grid-template-columns:1fr}.form{grid-template-columns:1fr}.full{grid-column:auto}}</style></head>\\n00004: <body><main class=\\\"app\\\"><div class=\\\"top\\\"><div><div class=\\\"muted\\\">https://gram1.ru/</div><h1>Мой криптопортфель</h1></div><div class=\\\"top-actions\\\"><div class=\\\"currency-switch\\\"><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>\\n00005: <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>\\n00006: <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\\\" data-chart-ui=\\\"GRAM1_DIRECT_SERVER_V5\\\"><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"HBAR\\\" open><summary class=\\\"tv-accordion-head\\\"><span>HBAR / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/HBAR?ui=v35\\\" alt=\\\"Свечной график HBAR / USDT\\\" loading=\\\"eager\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"POL\\\"><summary class=\\\"tv-accordion-head\\\"><span>POL / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/POL?ui=v35\\\" alt=\\\"Свечной график POL / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"AVAX\\\"><summary class=\\\"tv-accordion-head\\\"><span>AVAX / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/AVAX?ui=v35\\\" alt=\\\"Свечной график AVAX / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"LINK\\\"><summary class=\\\"tv-accordion-head\\\"><span>LINK / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/LINK?ui=v35\\\" alt=\\\"Свечной график LINK / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details></svg></div><div class=\\\"panel sharp-moves-panel\\\"><div class=\\\"title\\\"><h2>Резкие движения</h2></div><div id=\\\"moves\\\" class=\\\"moves\\\"></div></div></section>\\n00007: <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>\\n00008: <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_below\\\">Капитализация ниже</option><option value=\\\"pump\\\">Резкий памп</option><option value=\\\"dump\\\">Резкое падение</option></select><input id=\\\"aThreshold\\\" type=\\\"number\\\" step=\\\"any\\\" placeholder=\\\"Порог\\\"><select id=\\\"aWindow\\\" class=\\\"hidden\\\"><option value=\\\"300\\\">5 минут</option><option value=\\\"900\\\" selected>15 минут</option><option value=\\\"1800\\\">30 минут</option><option value=\\\"3600\\\">1 час</option><option value=\\\"14400\\\">4 часа</option><option value=\\\"86400\\\">24 часа</option></select><input id=\\\"aEmail\\\" class=\\\"full\\\" type=\\\"email\\\" value=\\\"aleisaev@yandex.ru\\\" placeholder=\\\"Email\\\"><button id=\\\"addAlert\\\" class=\\\"btn full\\\">Создать оповещение</button></div></div><div class=\\\"panel\\\"><div class=\\\"title\\\"><h2>Мои оповещения</h2></div><div id=\\\"alertList\\\"></div></div></section></main>\\n00009: <script>let state=null;let selected=\\\"hedera-hashgraph\\\";let currency=localStorage.getItem(\\\"portfolio_currency\\\")===\\\"RUB\\\"?\\\"RUB\\\":\\\"USD\\\";const fx=()=>Number(state&&state.market&&state.market.usd_rub||0);const cv=n=>Number(n||0)*(currency===\\\"RUB\\\"?fx():1);const money=(n,d=2)=>new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{style:\\\"currency\\\",currency:currency,minimumFractionDigits:d,maximumFractionDigits:d}).format(cv(n));const compactMoney=n=>(currency===\\\"RUB\\\"?\\\"₽\\\":\\\"$\\\")+new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{notation:\\\"compact\\\",maximumFractionDigits:2}).format(cv(n));const inputPrice=n=>Number((Number(n||0)*(currency===\\\"RUB\\\"?fx():1)).toFixed(currency===\\\"RUB\\\"?2:8));const coin=()=>state.market.coins[selected];async function load(){let r=await fetch(\\\"/api/state\\\");if(r.status===401){location=\\\"/login\\\";return}state=await r.json();render()}function render(){let m=state.market.coins||{};let ids=Object.keys(state.coins);/* ALERT_COIN_SELECTOR_EARLY_V1 */aCoin.innerHTML=ids.map(id=>\\\"<option value=\\\\\\\"\\\"+id+\\\"\\\\\\\">\\\"+state.coins[id]+\\\"</option>\\\").join(\\\"\\\");if(currency===\\\"RUB\\\"&&!(fx()>0)){currency=\\\"USD\\\"}document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.classList.toggle(\\\"active\\\",x.dataset.currency===currency));avgHeader.textContent=\\\"Средняя покупка (\\\"+currency+\\\")\\\";coins.innerHTML=ids.map(id=>{let c=m[id]||{};let ch=Number(c.change_24h||0);return \\\"<div class=\\\\\\\"card coin \\\"+(id===selected?\\\"active\\\":\\\"\\\")+\\\"\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\"><div class=\\\\\\\"coin-head\\\\\\\"><span class=\\\\\\\"coin-symbol\\\\\\\">\\\"+state.coins[id]+\\\"</span><b class=\\\\\\\"coin-price\\\\\\\">\\\"+money(c.price,2)+\\\"</b></div><div class=\\\\\\\"\\\"+(ch>=0?\\\"good\\\":\\\"bad\\\")+\\\"\\\\\\\">\\\"+(ch>=0?\\\"+\\\":\\\"\\\")+ch.toFixed(2)+\\\"%</div><small class=\\\\\\\"muted\\\\\\\">Cap \\\"+compactMoney(c.market_cap)+\\\"</small></div>\\\"}).join(\\\"\\\");document.querySelectorAll(\\\".coin\\\").forEach(x=>{let id=x.dataset.id;let head=x.querySelector(\\\".coin-head\\\");let img=document.createElement(\\\"img\\\");img.className=\\\"coin-icon\\\";img.src=\\\"/coin-icon/\\\"+id;img.alt=state.coins[id];img.loading=\\\"lazy\\\";head.prepend(img);x.onclick=()=>{selected=id;render()}});let total=0,inv=0,daily=0;rows.innerHTML=ids.map(id=>{let c=m[id]||{};let h=state.holdings[id]||{quantity:0,avg_buy:0};let value=Number(h.quantity)*Number(c.price||0);let cost=Number(h.quantity)*Number(h.avg_buy);let p=value-cost;total+=value;inv+=cost;daily+=value*Number(c.change_24h||0)/100;return \\\"<tr><td><b>\\\"+state.coins[id]+\\\"</b><div class=\\\\\\\"muted\\\\\\\">\\\"+(c.name||id)+\\\"</div></td><td>\\\"+money(c.price,2)+\\\"</td><td>\\\"+money(c.market_cap,0)+\\\"</td><td><input class=\\\\\\\"num qty\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"any\\\\\\\" value=\\\\\\\"\\\"+h.quantity+\\\"\\\\\\\"></td><td><input class=\\\\\\\"num avg\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"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=mone\",\"token\":\"/api/chart-svg/\"},{\"inside_chart\":true,\"inside_script\":false,\"line\":6,\"snippet\":\"00001: <!doctype html>\\n00002: <html lang=\\\"ru\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1\\\"><title>Gram Crypto</title>\\n00003: <style>:root{--bg:#071018;--p:#0f1d29;--p2:#0a1620;--l:#213644;--t:#f3f7fb;--m:#8fa5b6;--g:#63e6be;--r:#ff6b81;--b:#69a7ff}*{box-sizing:border-box}body{margin:0;background:radial-gradient(circle at 85% 0,#17395a55,transparent 32%),var(--bg);color:var(--t);font:14px system-ui}.app{max-width:1500px;margin:auto;padding:24px}.top{display:flex;justify-content:space-between;align-items:center;margin-bottom:18px}.top-actions{display:flex;align-items:center;gap:10px}.currency-switch{display:flex;gap:4px;padding:4px;border:1px solid var(--l);border-radius:12px;background:#091722}.currency-btn{border:0;border-radius:8px;padding:7px 10px;background:transparent;color:var(--m);font-weight:800;cursor:pointer}.currency-btn.active{background:var(--b);color:#071018}h1{margin:0;font-size:28px}.muted{color:var(--m)}button,input,select{font:inherit}.btn{border:0;border-radius:10px;padding:10px 13px;background:var(--g);color:#071018;font-weight:800;cursor:pointer}.ghost{background:#122431;color:var(--t);border:1px solid var(--l)}.grid4{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}.card,.panel{border:1px solid var(--l);background:linear-gradient(155deg,var(--p),var(--p2));border-radius:16px;padding:16px}.card strong{display:block;font-size:23px;margin-top:7px}.coins{display:grid;grid-template-columns:repeat(2,1fr);gap:9px;margin:14px 0}.coin{cursor:pointer}.coin.active{border-color:#4d829f}.coin-head{display:grid;grid-template-columns:26px minmax(0,1fr);grid-template-areas:\\\"icon name\\\" \\\". price\\\";align-items:center;column-gap:9px;row-gap:7px;margin-bottom:6px}.coin-icon{grid-area:icon;width:26px;height:26px;border-radius:50%;object-fit:cover;background:#132635;box-shadow:0 0 12px #69a7ff66}.coin-symbol{grid-area:name;font-size:22px;font-weight:900;letter-spacing:.7px;color:var(--g);text-shadow:0 0 12px #63e6be66}.coin-price{grid-area:price;font-size:17px}.asset-cell{position:relative;padding-left:43px}.asset-cell>.table-coin-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);width:23px;height:23px}.asset-symbol{font-size:17px;font-weight:900;letter-spacing:.3px;color:var(--g);text-shadow:0 0 10px #63e6be55}.coin b{font-size:17px}.good{color:var(--g)}.bad{color:var(--r)}.layout{display:grid;grid-template-columns:1.6fr .8fr;gap:14px}.title{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.title h2{font-size:16px;margin:0}.chart{width:100%;height:270px}.moves{display:grid;grid-template-columns:repeat(3,1fr);gap:9px}.move{padding:12px;border:1px solid var(--l);border-radius:12px;background:#091722}.table{overflow:auto}table{width:100%;border-collapse:collapse;min-width:900px}th,td{text-align:left;padding:10px;border-bottom:1px solid var(--l)}th{font-size:11px;color:var(--m)}input,select{background:#08141d;color:var(--t);border:1px solid var(--l);border-radius:9px;padding:9px}.num{width:115px}.alerts{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:14px}.form{display:grid;grid-template-columns:1fr 1fr;gap:9px}.full{grid-column:1/-1}.alert{display:flex;justify-content:space-between;gap:10px;border:1px solid var(--l);border-radius:11px;padding:11px;margin-bottom:8px;background:#091722}.hidden{display:none}.status{font-size:12px;padding:7px 10px;border:1px solid var(--l);border-radius:999px}@media(max-width:1050px){.coins{grid-template-columns:repeat(4,1fr)}.grid4{grid-template-columns:repeat(2,1fr)}}@media(max-width:750px){.app{padding:14px}.top{align-items:flex-start;gap:12px}.top-actions{flex-wrap:wrap;justify-content:flex-end}.layout,.alerts{grid-template-columns:1fr}.coins{grid-template-columns:repeat(2,1fr)}.moves{grid-template-columns:1fr}.grid4{grid-template-columns:1fr}.form{grid-template-columns:1fr}.full{grid-column:auto}}</style></head>\\n00004: <body><main class=\\\"app\\\"><div class=\\\"top\\\"><div><div class=\\\"muted\\\">https://gram1.ru/</div><h1>Мой криптопортфель</h1></div><div class=\\\"top-actions\\\"><div class=\\\"currency-switch\\\"><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>\\n00005: <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>\\n00006: <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\\\" data-chart-ui=\\\"GRAM1_DIRECT_SERVER_V5\\\"><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"HBAR\\\" open><summary class=\\\"tv-accordion-head\\\"><span>HBAR / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/HBAR?ui=v35\\\" alt=\\\"Свечной график HBAR / USDT\\\" loading=\\\"eager\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"POL\\\"><summary class=\\\"tv-accordion-head\\\"><span>POL / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/POL?ui=v35\\\" alt=\\\"Свечной график POL / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"AVAX\\\"><summary class=\\\"tv-accordion-head\\\"><span>AVAX / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/AVAX?ui=v35\\\" alt=\\\"Свечной график AVAX / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"LINK\\\"><summary class=\\\"tv-accordion-head\\\"><span>LINK / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/LINK?ui=v35\\\" alt=\\\"Свечной график LINK / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details></svg></div><div class=\\\"panel sharp-moves-panel\\\"><div class=\\\"title\\\"><h2>Резкие движения</h2></div><div id=\\\"moves\\\" class=\\\"moves\\\"></div></div></section>\\n00007: <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>\\n00008: <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_below\\\">Капитализация ниже</option><option value=\\\"pump\\\">Резкий памп</option><option value=\\\"dump\\\">Резкое падение</option></select><input id=\\\"aThreshold\\\" type=\\\"number\\\" step=\\\"any\\\" placeholder=\\\"Порог\\\"><select id=\\\"aWindow\\\" class=\\\"hidden\\\"><option value=\\\"300\\\">5 минут</option><option value=\\\"900\\\" selected>15 минут</option><option value=\\\"1800\\\">30 минут</option><option value=\\\"3600\\\">1 час</option><option value=\\\"14400\\\">4 часа</option><option value=\\\"86400\\\">24 часа</option></select><input id=\\\"aEmail\\\" class=\\\"full\\\" type=\\\"email\\\" value=\\\"aleisaev@yandex.ru\\\" placeholder=\\\"Email\\\"><button id=\\\"addAlert\\\" class=\\\"btn full\\\">Создать оповещение</button></div></div><div class=\\\"panel\\\"><div class=\\\"title\\\"><h2>Мои оповещения</h2></div><div id=\\\"alertList\\\"></div></div></section></main>\\n00009: <script>let state=null;let selected=\\\"hedera-hashgraph\\\";let currency=localStorage.getItem(\\\"portfolio_currency\\\")===\\\"RUB\\\"?\\\"RUB\\\":\\\"USD\\\";const fx=()=>Number(state&&state.market&&state.market.usd_rub||0);const cv=n=>Number(n||0)*(currency===\\\"RUB\\\"?fx():1);const money=(n,d=2)=>new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{style:\\\"currency\\\",currency:currency,minimumFractionDigits:d,maximumFractionDigits:d}).format(cv(n));const compactMoney=n=>(currency===\\\"RUB\\\"?\\\"₽\\\":\\\"$\\\")+new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{notation:\\\"compact\\\",maximumFractionDigits:2}).format(cv(n));const inputPrice=n=>Number((Number(n||0)*(currency===\\\"RUB\\\"?fx():1)).toFixed(currency===\\\"RUB\\\"?2:8));const coin=()=>state.market.coins[selected];async function load(){let r=await fetch(\\\"/api/state\\\");if(r.status===401){location=\\\"/login\\\";return}state=await r.json();render()}function render(){let m=state.market.coins||{};let ids=Object.keys(state.coins);/* ALERT_COIN_SELECTOR_EARLY_V1 */aCoin.innerHTML=ids.map(id=>\\\"<option value=\\\\\\\"\\\"+id+\\\"\\\\\\\">\\\"+state.coins[id]+\\\"</option>\\\").join(\\\"\\\");if(currency===\\\"RUB\\\"&&!(fx()>0)){currency=\\\"USD\\\"}document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.classList.toggle(\\\"active\\\",x.dataset.currency===currency));avgHeader.textContent=\\\"Средняя покупка (\\\"+currency+\\\")\\\";coins.innerHTML=ids.map(id=>{let c=m[id]||{};let ch=Number(c.change_24h||0);return \\\"<div class=\\\\\\\"card coin \\\"+(id===selected?\\\"active\\\":\\\"\\\")+\\\"\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\"><div class=\\\\\\\"coin-head\\\\\\\"><span class=\\\\\\\"coin-symbol\\\\\\\">\\\"+state.coins[id]+\\\"</span><b class=\\\\\\\"coin-price\\\\\\\">\\\"+money(c.price,2)+\\\"</b></div><div class=\\\\\\\"\\\"+(ch>=0?\\\"good\\\":\\\"bad\\\")+\\\"\\\\\\\">\\\"+(ch>=0?\\\"+\\\":\\\"\\\")+ch.toFixed(2)+\\\"%</div><small class=\\\\\\\"muted\\\\\\\">Cap \\\"+compactMoney(c.market_cap)+\\\"</small></div>\\\"}).join(\\\"\\\");document.querySelectorAll(\\\".coin\\\").forEach(x=>{let id=x.dataset.id;let head=x.querySelector(\\\".coin-head\\\");let img=document.createElement(\\\"img\\\");img.className=\\\"coin-icon\\\";img.src=\\\"/coin-icon/\\\"+id;img.alt=state.coins[id];img.loading=\\\"lazy\\\";head.prepend(img);x.onclick=()=>{selected=id;render()}});let total=0,inv=0,daily=0;rows.innerHTML=ids.map(id=>{let c=m[id]||{};let h=state.holdings[id]||{quantity:0,avg_buy:0};let value=Number(h.quantity)*Number(c.price||0);let cost=Number(h.quantity)*Number(h.avg_buy);let p=value-cost;total+=value;inv+=cost;daily+=value*Number(c.change_24h||0)/100;return \\\"<tr><td><b>\\\"+state.coins[id]+\\\"</b><div class=\\\\\\\"muted\\\\\\\">\\\"+(c.name||id)+\\\"</div></td><td>\\\"+money(c.price,2)+\\\"</td><td>\\\"+money(c.market_cap,0)+\\\"</td><td><input class=\\\\\\\"num qty\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"any\\\\\\\" value=\\\\\\\"\\\"+h.quantity+\\\"\\\\\\\"></td><td><input class=\\\\\\\"num avg\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"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=mone\",\"token\":\"GRAM1_DIRECT_SERVER_V5\"},{\"inside_chart\":false,\"inside_script\":true,\"line\":9,\"snippet\":\"00004: <body><main class=\\\"app\\\"><div class=\\\"top\\\"><div><div class=\\\"muted\\\">https://gram1.ru/</div><h1>Мой криптопортфель</h1></div><div class=\\\"top-actions\\\"><div class=\\\"currency-switch\\\"><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>\\n00005: <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>\\n00006: <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\\\" data-chart-ui=\\\"GRAM1_DIRECT_SERVER_V5\\\"><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"HBAR\\\" open><summary class=\\\"tv-accordion-head\\\"><span>HBAR / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/HBAR?ui=v35\\\" alt=\\\"Свечной график HBAR / USDT\\\" loading=\\\"eager\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"POL\\\"><summary class=\\\"tv-accordion-head\\\"><span>POL / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/POL?ui=v35\\\" alt=\\\"Свечной график POL / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"AVAX\\\"><summary class=\\\"tv-accordion-head\\\"><span>AVAX / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/AVAX?ui=v35\\\" alt=\\\"Свечной график AVAX / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"LINK\\\"><summary class=\\\"tv-accordion-head\\\"><span>LINK / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/LINK?ui=v35\\\" alt=\\\"Свечной график LINK / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details></svg></div><div class=\\\"panel sharp-moves-panel\\\"><div class=\\\"title\\\"><h2>Резкие движения</h2></div><div id=\\\"moves\\\" class=\\\"moves\\\"></div></div></section>\\n00007: <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>\\n00008: <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_below\\\">Капитализация ниже</option><option value=\\\"pump\\\">Резкий памп</option><option value=\\\"dump\\\">Резкое падение</option></select><input id=\\\"aThreshold\\\" type=\\\"number\\\" step=\\\"any\\\" placeholder=\\\"Порог\\\"><select id=\\\"aWindow\\\" class=\\\"hidden\\\"><option value=\\\"300\\\">5 минут</option><option value=\\\"900\\\" selected>15 минут</option><option value=\\\"1800\\\">30 минут</option><option value=\\\"3600\\\">1 час</option><option value=\\\"14400\\\">4 часа</option><option value=\\\"86400\\\">24 часа</option></select><input id=\\\"aEmail\\\" class=\\\"full\\\" type=\\\"email\\\" value=\\\"aleisaev@yandex.ru\\\" placeholder=\\\"Email\\\"><button id=\\\"addAlert\\\" class=\\\"btn full\\\">Создать оповещение</button></div></div><div class=\\\"panel\\\"><div class=\\\"title\\\"><h2>Мои оповещения</h2></div><div id=\\\"alertList\\\"></div></div></section></main>\\n00009: <script>let state=null;let selected=\\\"hedera-hashgraph\\\";let currency=localStorage.getItem(\\\"portfolio_currency\\\")===\\\"RUB\\\"?\\\"RUB\\\":\\\"USD\\\";const fx=()=>Number(state&&state.market&&state.market.usd_rub||0);const cv=n=>Number(n||0)*(currency===\\\"RUB\\\"?fx():1);const money=(n,d=2)=>new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{style:\\\"currency\\\",currency:currency,minimumFractionDigits:d,maximumFractionDigits:d}).format(cv(n));const compactMoney=n=>(currency===\\\"RUB\\\"?\\\"₽\\\":\\\"$\\\")+new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{notation:\\\"compact\\\",maximumFractionDigits:2}).format(cv(n));const inputPrice=n=>Number((Number(n||0)*(currency===\\\"RUB\\\"?fx():1)).toFixed(currency===\\\"RUB\\\"?2:8));const coin=()=>state.market.coins[selected];async function load(){let r=await fetch(\\\"/api/state\\\");if(r.status===401){location=\\\"/login\\\";return}state=await r.json();render()}function render(){let m=state.market.coins||{};let ids=Object.keys(state.coins);/* ALERT_COIN_SELECTOR_EARLY_V1 */aCoin.innerHTML=ids.map(id=>\\\"<option value=\\\\\\\"\\\"+id+\\\"\\\\\\\">\\\"+state.coins[id]+\\\"</option>\\\").join(\\\"\\\");if(currency===\\\"RUB\\\"&&!(fx()>0)){currency=\\\"USD\\\"}document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.classList.toggle(\\\"active\\\",x.dataset.currency===currency));avgHeader.textContent=\\\"Средняя покупка (\\\"+currency+\\\")\\\";coins.innerHTML=ids.map(id=>{let c=m[id]||{};let ch=Number(c.change_24h||0);return \\\"<div class=\\\\\\\"card coin \\\"+(id===selected?\\\"active\\\":\\\"\\\")+\\\"\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\"><div class=\\\\\\\"coin-head\\\\\\\"><span class=\\\\\\\"coin-symbol\\\\\\\">\\\"+state.coins[id]+\\\"</span><b class=\\\\\\\"coin-price\\\\\\\">\\\"+money(c.price,2)+\\\"</b></div><div class=\\\\\\\"\\\"+(ch>=0?\\\"good\\\":\\\"bad\\\")+\\\"\\\\\\\">\\\"+(ch>=0?\\\"+\\\":\\\"\\\")+ch.toFixed(2)+\\\"%</div><small class=\\\\\\\"muted\\\\\\\">Cap \\\"+compactMoney(c.market_cap)+\\\"</small></div>\\\"}).join(\\\"\\\");document.querySelectorAll(\\\".coin\\\").forEach(x=>{let id=x.dataset.id;let head=x.querySelector(\\\".coin-head\\\");let img=document.createElement(\\\"img\\\");img.className=\\\"coin-icon\\\";img.src=\\\"/coin-icon/\\\"+id;img.alt=state.coins[id];img.loading=\\\"lazy\\\";head.prepend(img);x.onclick=()=>{selected=id;render()}});let total=0,inv=0,daily=0;rows.innerHTML=ids.map(id=>{let c=m[id]||{};let h=state.holdings[id]||{quantity:0,avg_buy:0};let value=Number(h.quantity)*Number(c.price||0);let cost=Number(h.quantity)*Number(h.avg_buy);let p=value-cost;total+=value;inv+=cost;daily+=value*Number(c.change_24h||0)/100;return \\\"<tr><td><b>\\\"+state.coins[id]+\\\"</b><div class=\\\\\\\"muted\\\\\\\">\\\"+(c.name||id)+\\\"</div></td><td>\\\"+money(c.price,2)+\\\"</td><td>\\\"+money(c.market_cap,0)+\\\"</td><td><input class=\\\\\\\"num qty\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"any\\\\\\\" value=\\\\\\\"\\\"+h.quantity+\\\"\\\\\\\"></td><td><input class=\\\\\\\"num avg\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"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=\\\\\\\"#8fa5b6\\\\\\\">История ещё загружается</text>\\\";return}let min=Math.min(...a),max=Math.max(...a),w=900,h=270,p=18;let pts=a.map((v,i)=>(p+i*(w-2*p)/(a.length-1)).toFixed(1)+\\\",\\\"+(p+(max-v)*(h-2*p)/(max-min||1)).toFixed(1)).join(\\\" \\\");chart.innerHTML=\\\"<defs><linearGradient id=\\\\\\\"g\\\\\\\"><stop stop-color=\\\\\\\"#63e6be\\\\\\\"/><stop offset=\\\\\\\"1\\\\\\\" stop-color=\\\\\\\"#69a7ff\\\\\\\"/></linearGradient></defs><polyline fill=\\\\\\\"none\\\\\\\" stroke=\\\\\\\"url(#g)\\\\\\\" stroke-width=\\\\\\\"3\\\\\\\" points=\\\\\\\"\\\"+pts+\\\"\\\\\\\"/>\\\"}function renderMoves(){let a=Object.values(state.market.coins||{}).map(c=>({s:c.symbol,p:Number(c.change_1h||0),price:c.price})).sort((x,y)=>Math.abs(y.p)-Math.abs(x.p));moves.innerHTML=a.map(x=>\\\"<div class=\\\\\\\"move\\\\\\\"><span class=\\\\\\\"muted\\\\\\\">Изменение за 1 час</span><b class=\\\\\\\"\\\"+(x.p>=0?\\\"good\\\":\\\"bad\\\")+\\\"\\\\\\\">\\\"+x.s+\\\" \\\"+(x.p>=0?\\\"+\\\":\\\"\\\")+x.p.toFixed(2)+\\\"%</b><div>\\\"+money(x.price,2)+\\\"</div></div>\\\").join(\\\"\\\")}function label(a){let c=state.coins[a.coin_id];let k={price_above:\\\"цена ≥\\\",price_below:\\\"цена ≤\\\",cap_above:\\\"капитализация ≥\\\",cap_below:\\\"капитализация ≤\\\",pump:\\\"памп ≥ +\\\",dump:\\\"падение ≥ −\\\"}[a.kind];return c+\\\": \\\"+k+\\\" \\\"+a.threshold+(a.kind===\\\"pump\\\"||a.kind===\\\"dump\\\"?\\\"% за \\\"+Math.round(a.window_seconds/60)+\\\" мин\\\":\\\" USD\\\")}function renderAlerts(){alertList.innerHTML=state.alerts.length?state.alerts.map(a=>\\\"<div class=\\\\\\\"alert\\\\\\\"><div><b>\\\"+label(a)+\\\"</b><div class=\\\\\\\"muted\\\\\\\">\\\"+a.email+\\\"</div></div><button class=\\\\\\\"btn ghost del\\\\\\\" data-id=\\\\\\\"\\\"+a.id+\\\"\\\\\\\">Удалить</button></div>\\\").join(\\\"\\\"):\\\"<span class=\\\\\\\"muted\\\\\\\">Оповещений пока нет</span>\\\";document.querySelectorAll(\\\".del\\\").forEach(x=>x.onclick=async()=>{await fetch(\\\"/api/alert/\\\"+x.dataset.id,{method:\\\"DELETE\\\"});load()})}aKind.onchange=()=>aWindow.classList.toggle(\\\"hidden\\\",!([\\\"pump\\\",\\\"dump\\\"].includes(aKind.value)));addAlert.onclick=async()=>{let dynamic=[\\\"pump\\\",\\\"dump\\\"].includes(aKind.value);let r=await fetch(\\\"/api/alert\\\",{method:\\\"POST\\\",headers:{\\\"Content-Type\\\":\\\"application/json\\\"},body:JSON.stringify({coin_id:aCoin.value,kind:aKind.value,threshold:Number(aThreshold.value),window_seconds:dynamic?Number(aWindow.value):0,email:aEmail.value})});if(!r.ok){alert(\\\"Проверьте параметры\\\")};aThreshold.value=\\\"\\\";load()};document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.onclick=()=>{currency=x.dataset.currency;localStorage.setItem(\\\"portfolio_currency\\\",currency);render()});load();setInterval(load,60000)</script><style>/* CRYPTO_UI_V2 */.coins{grid-template-columns:repeat(2,minmax(250px,1fr))!important;gap:14px!important}.coin{min-height:120px;display:flex!important;flex-direction:column;align-items:center;justify-content:center;text-align:center}.moves{grid-template-columns:1fr!important}.move{text-align:center;min-height:72px;displa\",\"token\":\"chart.innerHTML\"},{\"inside_chart\":false,\"inside_script\":true,\"line\":9,\"snippet\":\"00004: <body><main class=\\\"app\\\"><div class=\\\"top\\\"><div><div class=\\\"muted\\\">https://gram1.ru/</div><h1>Мой криптопортфель</h1></div><div class=\\\"top-actions\\\"><div class=\\\"currency-switch\\\"><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>\\n00005: <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>\\n00006: <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\\\" data-chart-ui=\\\"GRAM1_DIRECT_SERVER_V5\\\"><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"HBAR\\\" open><summary class=\\\"tv-accordion-head\\\"><span>HBAR / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/HBAR?ui=v35\\\" alt=\\\"Свечной график HBAR / USDT\\\" loading=\\\"eager\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"POL\\\"><summary class=\\\"tv-accordion-head\\\"><span>POL / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/POL?ui=v35\\\" alt=\\\"Свечной график POL / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"AVAX\\\"><summary class=\\\"tv-accordion-head\\\"><span>AVAX / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/AVAX?ui=v35\\\" alt=\\\"Свечной график AVAX / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details><details name=\\\"coin-chart\\\" class=\\\"tv-accordion-item\\\" data-symbol=\\\"LINK\\\"><summary class=\\\"tv-accordion-head\\\"><span>LINK / USDT</span><span class=\\\"tv-accordion-chevron\\\" aria-hidden=\\\"true\\\">⌄</span></summary><div class=\\\"tv-accordion-body\\\"><img class=\\\"tv-server-chart\\\" src=\\\"/api/chart-svg/LINK?ui=v35\\\" alt=\\\"Свечной график LINK / USDT\\\" loading=\\\"lazy\\\"><div class=\\\"tv-chart-note muted\\\">Binance USDT · 1 месяц · весь доступный период</div></div></details></svg></div><div class=\\\"panel sharp-moves-panel\\\"><div class=\\\"title\\\"><h2>Резкие движения</h2></div><div id=\\\"moves\\\" class=\\\"moves\\\"></div></div></section>\\n00007: <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>\\n00008: <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_below\\\">Капитализация ниже</option><option value=\\\"pump\\\">Резкий памп</option><option value=\\\"dump\\\">Резкое падение</option></select><input id=\\\"aThreshold\\\" type=\\\"number\\\" step=\\\"any\\\" placeholder=\\\"Порог\\\"><select id=\\\"aWindow\\\" class=\\\"hidden\\\"><option value=\\\"300\\\">5 минут</option><option value=\\\"900\\\" selected>15 минут</option><option value=\\\"1800\\\">30 минут</option><option value=\\\"3600\\\">1 час</option><option value=\\\"14400\\\">4 часа</option><option value=\\\"86400\\\">24 часа</option></select><input id=\\\"aEmail\\\" class=\\\"full\\\" type=\\\"email\\\" value=\\\"aleisaev@yandex.ru\\\" placeholder=\\\"Email\\\"><button id=\\\"addAlert\\\" class=\\\"btn full\\\">Создать оповещение</button></div></div><div class=\\\"panel\\\"><div class=\\\"title\\\"><h2>Мои оповещения</h2></div><div id=\\\"alertList\\\"></div></div></section></main>\\n00009: <script>let state=null;let selected=\\\"hedera-hashgraph\\\";let currency=localStorage.getItem(\\\"portfolio_currency\\\")===\\\"RUB\\\"?\\\"RUB\\\":\\\"USD\\\";const fx=()=>Number(state&&state.market&&state.market.usd_rub||0);const cv=n=>Number(n||0)*(currency===\\\"RUB\\\"?fx():1);const money=(n,d=2)=>new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{style:\\\"currency\\\",currency:currency,minimumFractionDigits:d,maximumFractionDigits:d}).format(cv(n));const compactMoney=n=>(currency===\\\"RUB\\\"?\\\"₽\\\":\\\"$\\\")+new Intl.NumberFormat(currency===\\\"RUB\\\"?\\\"ru-RU\\\":\\\"en-US\\\",{notation:\\\"compact\\\",maximumFractionDigits:2}).format(cv(n));const inputPrice=n=>Number((Number(n||0)*(currency===\\\"RUB\\\"?fx():1)).toFixed(currency===\\\"RUB\\\"?2:8));const coin=()=>state.market.coins[selected];async function load(){let r=await fetch(\\\"/api/state\\\");if(r.status===401){location=\\\"/login\\\";return}state=await r.json();render()}function render(){let m=state.market.coins||{};let ids=Object.keys(state.coins);/* ALERT_COIN_SELECTOR_EARLY_V1 */aCoin.innerHTML=ids.map(id=>\\\"<option value=\\\\\\\"\\\"+id+\\\"\\\\\\\">\\\"+state.coins[id]+\\\"</option>\\\").join(\\\"\\\");if(currency===\\\"RUB\\\"&&!(fx()>0)){currency=\\\"USD\\\"}document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.classList.toggle(\\\"active\\\",x.dataset.currency===currency));avgHeader.textContent=\\\"Средняя покупка (\\\"+currency+\\\")\\\";coins.innerHTML=ids.map(id=>{let c=m[id]||{};let ch=Number(c.change_24h||0);return \\\"<div class=\\\\\\\"card coin \\\"+(id===selected?\\\"active\\\":\\\"\\\")+\\\"\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\"><div class=\\\\\\\"coin-head\\\\\\\"><span class=\\\\\\\"coin-symbol\\\\\\\">\\\"+state.coins[id]+\\\"</span><b class=\\\\\\\"coin-price\\\\\\\">\\\"+money(c.price,2)+\\\"</b></div><div class=\\\\\\\"\\\"+(ch>=0?\\\"good\\\":\\\"bad\\\")+\\\"\\\\\\\">\\\"+(ch>=0?\\\"+\\\":\\\"\\\")+ch.toFixed(2)+\\\"%</div><small class=\\\\\\\"muted\\\\\\\">Cap \\\"+compactMoney(c.market_cap)+\\\"</small></div>\\\"}).join(\\\"\\\");document.querySelectorAll(\\\".coin\\\").forEach(x=>{let id=x.dataset.id;let head=x.querySelector(\\\".coin-head\\\");let img=document.createElement(\\\"img\\\");img.className=\\\"coin-icon\\\";img.src=\\\"/coin-icon/\\\"+id;img.alt=state.coins[id];img.loading=\\\"lazy\\\";head.prepend(img);x.onclick=()=>{selected=id;render()}});let total=0,inv=0,daily=0;rows.innerHTML=ids.map(id=>{let c=m[id]||{};let h=state.holdings[id]||{quantity:0,avg_buy:0};let value=Number(h.quantity)*Number(c.price||0);let cost=Number(h.quantity)*Number(h.avg_buy);let p=value-cost;total+=value;inv+=cost;daily+=value*Number(c.change_24h||0)/100;return \\\"<tr><td><b>\\\"+state.coins[id]+\\\"</b><div class=\\\\\\\"muted\\\\\\\">\\\"+(c.name||id)+\\\"</div></td><td>\\\"+money(c.price,2)+\\\"</td><td>\\\"+money(c.market_cap,0)+\\\"</td><td><input class=\\\\\\\"num qty\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"any\\\\\\\" value=\\\\\\\"\\\"+h.quantity+\\\"\\\\\\\"></td><td><input class=\\\\\\\"num avg\\\\\\\" data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\" type=\\\\\\\"number\\\\\\\" step=\\\\\\\"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=\\\\\\\"#8fa5b6\\\\\\\">История ещё загружается</text>\\\";return}let min=Math.min(...a),max=Math.max(...a),w=900,h=270,p=18;let pts=a.map((v,i)=>(p+i*(w-2*p)/(a.length-1)).toFixed(1)+\\\",\\\"+(p+(max-v)*(h-2*p)/(max-min||1)).toFixed(1)).join(\\\" \\\");chart.innerHTML=\\\"<defs><linearGradient id=\\\\\\\"g\\\\\\\"><stop stop-color=\\\\\\\"#63e6be\\\\\\\"/><stop offset=\\\\\\\"1\\\\\\\" stop-color=\\\\\\\"#69a7ff\\\\\\\"/></linearGradient></defs><polyline fill=\\\\\\\"none\\\\\\\" stroke=\\\\\\\"url(#g)\\\\\\\" stroke-width=\\\\\\\"3\\\\\\\" points=\\\\\\\"\\\"+pts+\\\"\\\\\\\"/>\\\"}function renderMoves(){let a=Object.values(state.market.coins||{}).map(c=>({s:c.symbol,p:Number(c.change_1h||0),price:c.price})).sort((x,y)=>Math.abs(y.p)-Math.abs(x.p));moves.innerHTML=a.map(x=>\\\"<div class=\\\\\\\"move\\\\\\\"><span class=\\\\\\\"muted\\\\\\\">Изменение за 1 час</span><b class=\\\\\\\"\\\"+(x.p>=0?\\\"good\\\":\\\"bad\\\")+\\\"\\\\\\\">\\\"+x.s+\\\" \\\"+(x.p>=0?\\\"+\\\":\\\"\\\")+x.p.toFixed(2)+\\\"%</b><div>\\\"+money(x.price,2)+\\\"</div></div>\\\").join(\\\"\\\")}function label(a){let c=state.coins[a.coin_id];let k={price_above:\\\"цена ≥\\\",price_below:\\\"цена ≤\\\",cap_above:\\\"капитализация ≥\\\",cap_below:\\\"капитализация ≤\\\",pump:\\\"памп ≥ +\\\",dump:\\\"падение ≥ −\\\"}[a.kind];return c+\\\": \\\"+k+\\\" \\\"+a.threshold+(a.kind===\\\"pump\\\"||a.kind===\\\"dump\\\"?\\\"% за \\\"+Math.round(a.window_seconds/60)+\\\" мин\\\":\\\" USD\\\")}function renderAlerts(){alertList.innerHTML=state.alerts.length?state.alerts.map(a=>\\\"<div class=\\\\\\\"alert\\\\\\\"><div><b>\\\"+label(a)+\\\"</b><div class=\\\\\\\"muted\\\\\\\">\\\"+a.email+\\\"</div></div><button class=\\\\\\\"btn ghost del\\\\\\\" data-id=\\\\\\\"\\\"+a.id+\\\"\\\\\\\">Удалить</button></div>\\\").join(\\\"\\\"):\\\"<span class=\\\\\\\"muted\\\\\\\">Оповещений пока нет</span>\\\";document.querySelectorAll(\\\".del\\\").forEach(x=>x.onclick=async()=>{await fetch(\\\"/api/alert/\\\"+x.dataset.id,{method:\\\"DELETE\\\"});load()})}aKind.onchange=()=>aWindow.classList.toggle(\\\"hidden\\\",!([\\\"pump\\\",\\\"dump\\\"].includes(aKind.value)));addAlert.onclick=async()=>{let dynamic=[\\\"pump\\\",\\\"dump\\\"].includes(aKind.value);let r=await fetch(\\\"/api/alert\\\",{method:\\\"POST\\\",headers:{\\\"Content-Type\\\":\\\"application/json\\\"},body:JSON.stringify({coin_id:aCoin.value,kind:aKind.value,threshold:Number(aThreshold.value),window_seconds:dynamic?Number(aWindow.value):0,email:aEmail.value})});if(!r.ok){alert(\\\"Проверьте параметры\\\")};aThreshold.value=\\\"\\\";load()};document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.onclick=()=>{currency=x.dataset.currency;localStorage.setItem(\\\"portfolio_currency\\\",currency);render()});load();setInterval(load,60000)</script><style>/* CRYPTO_UI_V2 */.coins{grid-template-columns:repeat(2,minmax(250px,1fr))!important;gap:14px!important}.coin{min-height:120px;display:flex!important;flex-direction:column;align-items:center;justify-content:center;text-align:center}.moves{grid-template-columns:1fr!important}.move{text-align:center;min-height:72px;displa\",\"token\":\"Нет данных\"},{\"inside_chart\":false,\"inside_script\":true,\"line\":141,\"snippet\":\"00136: };\\n00137: const draw=data=>{\\n00138: const box=document.getElementById(\\\"alertList\\\");\\n00139: if(!box||!data||!Array.isArray(data.alerts))return;\\n00140: lastState=data;\\n00141: box.replaceChildren();\\n00142: if(data.alerts.length===0){\\n00143: box.append(make(\\\"span\\\",\\\"muted\\\",\\\"Оповещений пока нет\\\"));\\n00144: return;\\n00145: }\\n00146: data.alerts.forEach(alert=>{\",\"token\":\"replaceChildren(\"},{\"inside_chart\":false,\"inside_script\":false,\"line\":217,\"snippet\":\"00212: if(document.readyState===\\\"loading\\\")document.addEventListener(\\\"DOMContentLoaded\\\",install,{once:true});else install();\\n00213: const redrawForCurrency=()=>{if(lastState)draw(lastState);};\\n00214: document.querySelectorAll(\\\".currency-btn\\\").forEach(button=>button.addEventListener(\\\"click\\\",()=>setTimeout(redrawForCurrency,0)));\\n00215: })();\\n00216: </script>\\n00217: <style id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_STYLE_V5\\\">#chart{display:block!important;margin-top:12px;border:1px solid var(--l);border-radius:16px;overflow:hidden;background:#081823;min-height:56px}#tvCharts{display:none!important}.tv-accordion-item{display:block;border-bottom:1px solid var(--l);background:#081823}.tv-accordion-item:last-child{border-bottom:0}.tv-accordion-head{list-style:none;cursor:pointer;padding:14px 16px;font-weight:800;display:flex;align-items:center;justify-content:space-between;gap:12px;user-select:none}.tv-accordion-head::-webkit-details-marker{display:none}.tv-accordion-chevron{font-size:18px;transition:transform .2s ease}.tv-accordion-item[open] .tv-accordion-chevron{transform:rotate(180deg)}.tv-accordion-body{display:block;border-top:1px solid var(--l)}.tv-server-chart{display:block;width:100%;height:auto;min-height:320px;max-height:430px;object-fit:fill;border:0;background:#081823}.tv-chart-note{min-height:20px;padding:0 14px 12px}@media(max-width:750px){.tv-accordion-head{padding:12px 14px}.tv-server-chart{min-height:260px}}</style>\\n00218: <script src=\\\"/vendor/lightweight-charts.js\\\"></script>\\n00219: <script id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_SCRIPT_V5\\\">(()=>{const root=document.getElementById(\\\"chart\\\");if(!root)return;root.dataset.chartUi=\\\"GRAM1_DIRECT_SERVER_V5\\\";const items=[...root.querySelectorAll(\\\"details.tv-accordion-item\\\")];items.forEach(item=>item.addEventListener(\\\"toggle\\\",()=>{if(!item.open)return;items.forEach(other=>{if(other!==item)other.open=false})}));})();</script>\\n00220: \\n00221: <style id=\\\"SHARP_MOVES_LAYOUT_V2\\\">.sharp-moves-panel{grid-column:1 / -1;justify-self:center;width:min(100%,860px);margin-inline:auto}.sharp-moves-panel .title{justify-content:center}.sharp-moves-panel .title h2{width:100%;text-align:center}#moves{grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:12px!important}@media(max-width:750px){.sharp-moves-panel{width:100%}#moves{grid-template-columns:1fr!important}}</style>\\n00222: <script id=\\\"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1\\\">(()=>{const move=()=>{const chart=document.getElementById(\\\"chart\\\");const panel=chart&&chart.closest(\\\".panel\\\");const main=document.querySelector(\\\"main\\\");if(!panel||!main)return;main.appendChild(panel);panel.style.gridColumn=\\\"1 / -1\\\";const title=document.getElementById(\\\"chartTitle\\\");if(title)title.textContent=\\\"Графики монет\\\";const note=panel.querySelector(\\\".muted\\\");if(note)note.textContent=\\\"Свечи Binance · 1 месяц · весь доступный период\\\"};document.addEventListener(\\\"DOMContentLoaded\\\",move);window.addEventListener(\\\"load\\\",move);setTimeout(move,300);setTimeout(move,1500);setTimeout(move,4000)})();</script></body></html>\",\"token\":\"tvCharts\"},{\"inside_chart\":false,\"inside_script\":true,\"line\":219,\"snippet\":\"00214: document.querySelectorAll(\\\".currency-btn\\\").forEach(button=>button.addEventListener(\\\"click\\\",()=>setTimeout(redrawForCurrency,0)));\\n00215: })();\\n00216: </script>\\n00217: <style id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_STYLE_V5\\\">#chart{display:block!important;margin-top:12px;border:1px solid var(--l);border-radius:16px;overflow:hidden;background:#081823;min-height:56px}#tvCharts{display:none!important}.tv-accordion-item{display:block;border-bottom:1px solid var(--l);background:#081823}.tv-accordion-item:last-child{border-bottom:0}.tv-accordion-head{list-style:none;cursor:pointer;padding:14px 16px;font-weight:800;display:flex;align-items:center;justify-content:space-between;gap:12px;user-select:none}.tv-accordion-head::-webkit-details-marker{display:none}.tv-accordion-chevron{font-size:18px;transition:transform .2s ease}.tv-accordion-item[open] .tv-accordion-chevron{transform:rotate(180deg)}.tv-accordion-body{display:block;border-top:1px solid var(--l)}.tv-server-chart{display:block;width:100%;height:auto;min-height:320px;max-height:430px;object-fit:fill;border:0;background:#081823}.tv-chart-note{min-height:20px;padding:0 14px 12px}@media(max-width:750px){.tv-accordion-head{padding:12px 14px}.tv-server-chart{min-height:260px}}</style>\\n00218: <script src=\\\"/vendor/lightweight-charts.js\\\"></script>\\n00219: <script id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_SCRIPT_V5\\\">(()=>{const root=document.getElementById(\\\"chart\\\");if(!root)return;root.dataset.chartUi=\\\"GRAM1_DIRECT_SERVER_V5\\\";const items=[...root.querySelectorAll(\\\"details.tv-accordion-item\\\")];items.forEach(item=>item.addEventListener(\\\"toggle\\\",()=>{if(!item.open)return;items.forEach(other=>{if(other!==item)other.open=false})}));})();</script>\\n00220: \\n00221: <style id=\\\"SHARP_MOVES_LAYOUT_V2\\\">.sharp-moves-panel{grid-column:1 / -1;justify-self:center;width:min(100%,860px);margin-inline:auto}.sharp-moves-panel .title{justify-content:center}.sharp-moves-panel .title h2{width:100%;text-align:center}#moves{grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:12px!important}@media(max-width:750px){.sharp-moves-panel{width:100%}#moves{grid-template-columns:1fr!important}}</style>\\n00222: <script id=\\\"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1\\\">(()=>{const move=()=>{const chart=document.getElementById(\\\"chart\\\");const panel=chart&&chart.closest(\\\".panel\\\");const main=document.querySelector(\\\"main\\\");if(!panel||!main)return;main.appendChild(panel);panel.style.gridColumn=\\\"1 / -1\\\";const title=document.getElementById(\\\"chartTitle\\\");if(title)title.textContent=\\\"Графики монет\\\";const note=panel.querySelector(\\\".muted\\\");if(note)note.textContent=\\\"Свечи Binance · 1 месяц · весь доступный период\\\"};document.addEventListener(\\\"DOMContentLoaded\\\",move);window.addEventListener(\\\"load\\\",move);setTimeout(move,300);setTimeout(move,1500);setTimeout(move,4000)})();</script></body></html>\",\"token\":\"GRAM1_CHARTS_DIRECT_VISIBLE_SCRIPT_V5\"},{\"inside_chart\":false,\"inside_script\":true,\"line\":219,\"snippet\":\"00214: document.querySelectorAll(\\\".currency-btn\\\").forEach(button=>button.addEventListener(\\\"click\\\",()=>setTimeout(redrawForCurrency,0)));\\n00215: })();\\n00216: </script>\\n00217: <style id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_STYLE_V5\\\">#chart{display:block!important;margin-top:12px;border:1px solid var(--l);border-radius:16px;overflow:hidden;background:#081823;min-height:56px}#tvCharts{display:none!important}.tv-accordion-item{display:block;border-bottom:1px solid var(--l);background:#081823}.tv-accordion-item:last-child{border-bottom:0}.tv-accordion-head{list-style:none;cursor:pointer;padding:14px 16px;font-weight:800;display:flex;align-items:center;justify-content:space-between;gap:12px;user-select:none}.tv-accordion-head::-webkit-details-marker{display:none}.tv-accordion-chevron{font-size:18px;transition:transform .2s ease}.tv-accordion-item[open] .tv-accordion-chevron{transform:rotate(180deg)}.tv-accordion-body{display:block;border-top:1px solid var(--l)}.tv-server-chart{display:block;width:100%;height:auto;min-height:320px;max-height:430px;object-fit:fill;border:0;background:#081823}.tv-chart-note{min-height:20px;padding:0 14px 12px}@media(max-width:750px){.tv-accordion-head{padding:12px 14px}.tv-server-chart{min-height:260px}}</style>\\n00218: <script src=\\\"/vendor/lightweight-charts.js\\\"></script>\\n00219: <script id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_SCRIPT_V5\\\">(()=>{const root=document.getElementById(\\\"chart\\\");if(!root)return;root.dataset.chartUi=\\\"GRAM1_DIRECT_SERVER_V5\\\";const items=[...root.querySelectorAll(\\\"details.tv-accordion-item\\\")];items.forEach(item=>item.addEventListener(\\\"toggle\\\",()=>{if(!item.open)return;items.forEach(other=>{if(other!==item)other.open=false})}));})();</script>\\n00220: \\n00221: <style id=\\\"SHARP_MOVES_LAYOUT_V2\\\">.sharp-moves-panel{grid-column:1 / -1;justify-self:center;width:min(100%,860px);margin-inline:auto}.sharp-moves-panel .title{justify-content:center}.sharp-moves-panel .title h2{width:100%;text-align:center}#moves{grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:12px!important}@media(max-width:750px){.sharp-moves-panel{width:100%}#moves{grid-template-columns:1fr!important}}</style>\\n00222: <script id=\\\"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1\\\">(()=>{const move=()=>{const chart=document.getElementById(\\\"chart\\\");const panel=chart&&chart.closest(\\\".panel\\\");const main=document.querySelector(\\\"main\\\");if(!panel||!main)return;main.appendChild(panel);panel.style.gridColumn=\\\"1 / -1\\\";const title=document.getElementById(\\\"chartTitle\\\");if(title)title.textContent=\\\"Графики монет\\\";const note=panel.querySelector(\\\".muted\\\");if(note)note.textContent=\\\"Свечи Binance · 1 месяц · весь доступный период\\\"};document.addEventListener(\\\"DOMContentLoaded\\\",move);window.addEventListener(\\\"load\\\",move);setTimeout(move,300);setTimeout(move,1500);setTimeout(move,4000)})();</script></body></html>\",\"token\":\"GRAM1_DIRECT_SERVER_V5\"},{\"inside_chart\":false,\"inside_script\":true,\"line\":219,\"snippet\":\"00214: document.querySelectorAll(\\\".currency-btn\\\").forEach(button=>button.addEventListener(\\\"click\\\",()=>setTimeout(redrawForCurrency,0)));\\n00215: })();\\n00216: </script>\\n00217: <style id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_STYLE_V5\\\">#chart{display:block!important;margin-top:12px;border:1px solid var(--l);border-radius:16px;overflow:hidden;background:#081823;min-height:56px}#tvCharts{display:none!important}.tv-accordion-item{display:block;border-bottom:1px solid var(--l);background:#081823}.tv-accordion-item:last-child{border-bottom:0}.tv-accordion-head{list-style:none;cursor:pointer;padding:14px 16px;font-weight:800;display:flex;align-items:center;justify-content:space-between;gap:12px;user-select:none}.tv-accordion-head::-webkit-details-marker{display:none}.tv-accordion-chevron{font-size:18px;transition:transform .2s ease}.tv-accordion-item[open] .tv-accordion-chevron{transform:rotate(180deg)}.tv-accordion-body{display:block;border-top:1px solid var(--l)}.tv-server-chart{display:block;width:100%;height:auto;min-height:320px;max-height:430px;object-fit:fill;border:0;background:#081823}.tv-chart-note{min-height:20px;padding:0 14px 12px}@media(max-width:750px){.tv-accordion-head{padding:12px 14px}.tv-server-chart{min-height:260px}}</style>\\n00218: <script src=\\\"/vendor/lightweight-charts.js\\\"></script>\\n00219: <script id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_SCRIPT_V5\\\">(()=>{const root=document.getElementById(\\\"chart\\\");if(!root)return;root.dataset.chartUi=\\\"GRAM1_DIRECT_SERVER_V5\\\";const items=[...root.querySelectorAll(\\\"details.tv-accordion-item\\\")];items.forEach(item=>item.addEventListener(\\\"toggle\\\",()=>{if(!item.open)return;items.forEach(other=>{if(other!==item)other.open=false})}));})();</script>\\n00220: \\n00221: <style id=\\\"SHARP_MOVES_LAYOUT_V2\\\">.sharp-moves-panel{grid-column:1 / -1;justify-self:center;width:min(100%,860px);margin-inline:auto}.sharp-moves-panel .title{justify-content:center}.sharp-moves-panel .title h2{width:100%;text-align:center}#moves{grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:12px!important}@media(max-width:750px){.sharp-moves-panel{width:100%}#moves{grid-template-columns:1fr!important}}</style>\\n00222: <script id=\\\"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1\\\">(()=>{const move=()=>{const chart=document.getElementById(\\\"chart\\\");const panel=chart&&chart.closest(\\\".panel\\\");const main=document.querySelector(\\\"main\\\");if(!panel||!main)return;main.appendChild(panel);panel.style.gridColumn=\\\"1 / -1\\\";const title=document.getElementById(\\\"chartTitle\\\");if(title)title.textContent=\\\"Графики монет\\\";const note=panel.querySelector(\\\".muted\\\");if(note)note.textContent=\\\"Свечи Binance · 1 месяц · весь доступный период\\\"};document.addEventListener(\\\"DOMContentLoaded\\\",move);window.addEventListener(\\\"load\\\",move);setTimeout(move,300);setTimeout(move,1500);setTimeout(move,4000)})();</script></body></html>\",\"token\":\"getElementById(\\\"chart\\\")\"},{\"inside_chart\":false,\"inside_script\":true,\"line\":222,\"snippet\":\"00217: <style id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_STYLE_V5\\\">#chart{display:block!important;margin-top:12px;border:1px solid var(--l);border-radius:16px;overflow:hidden;background:#081823;min-height:56px}#tvCharts{display:none!important}.tv-accordion-item{display:block;border-bottom:1px solid var(--l);background:#081823}.tv-accordion-item:last-child{border-bottom:0}.tv-accordion-head{list-style:none;cursor:pointer;padding:14px 16px;font-weight:800;display:flex;align-items:center;justify-content:space-between;gap:12px;user-select:none}.tv-accordion-head::-webkit-details-marker{display:none}.tv-accordion-chevron{font-size:18px;transition:transform .2s ease}.tv-accordion-item[open] .tv-accordion-chevron{transform:rotate(180deg)}.tv-accordion-body{display:block;border-top:1px solid var(--l)}.tv-server-chart{display:block;width:100%;height:auto;min-height:320px;max-height:430px;object-fit:fill;border:0;background:#081823}.tv-chart-note{min-height:20px;padding:0 14px 12px}@media(max-width:750px){.tv-accordion-head{padding:12px 14px}.tv-server-chart{min-height:260px}}</style>\\n00218: <script src=\\\"/vendor/lightweight-charts.js\\\"></script>\\n00219: <script id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_SCRIPT_V5\\\">(()=>{const root=document.getElementById(\\\"chart\\\");if(!root)return;root.dataset.chartUi=\\\"GRAM1_DIRECT_SERVER_V5\\\";const items=[...root.querySelectorAll(\\\"details.tv-accordion-item\\\")];items.forEach(item=>item.addEventListener(\\\"toggle\\\",()=>{if(!item.open)return;items.forEach(other=>{if(other!==item)other.open=false})}));})();</script>\\n00220: \\n00221: <style id=\\\"SHARP_MOVES_LAYOUT_V2\\\">.sharp-moves-panel{grid-column:1 / -1;justify-self:center;width:min(100%,860px);margin-inline:auto}.sharp-moves-panel .title{justify-content:center}.sharp-moves-panel .title h2{width:100%;text-align:center}#moves{grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:12px!important}@media(max-width:750px){.sharp-moves-panel{width:100%}#moves{grid-template-columns:1fr!important}}</style>\\n00222: <script id=\\\"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1\\\">(()=>{const move=()=>{const chart=document.getElementById(\\\"chart\\\");const panel=chart&&chart.closest(\\\".panel\\\");const main=document.querySelector(\\\"main\\\");if(!panel||!main)return;main.appendChild(panel);panel.style.gridColumn=\\\"1 / -1\\\";const title=document.getElementById(\\\"chartTitle\\\");if(title)title.textContent=\\\"Графики монет\\\";const note=panel.querySelector(\\\".muted\\\");if(note)note.textContent=\\\"Свечи Binance · 1 месяц · весь доступный период\\\"};document.addEventListener(\\\"DOMContentLoaded\\\",move);window.addEventListener(\\\"load\\\",move);setTimeout(move,300);setTimeout(move,1500);setTimeout(move,4000)})();</script></body></html>\",\"token\":\"appendChild(\"},{\"inside_chart\":false,\"inside_script\":true,\"line\":222,\"snippet\":\"00217: <style id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_STYLE_V5\\\">#chart{display:block!important;margin-top:12px;border:1px solid var(--l);border-radius:16px;overflow:hidden;background:#081823;min-height:56px}#tvCharts{display:none!important}.tv-accordion-item{display:block;border-bottom:1px solid var(--l);background:#081823}.tv-accordion-item:last-child{border-bottom:0}.tv-accordion-head{list-style:none;cursor:pointer;padding:14px 16px;font-weight:800;display:flex;align-items:center;justify-content:space-between;gap:12px;user-select:none}.tv-accordion-head::-webkit-details-marker{display:none}.tv-accordion-chevron{font-size:18px;transition:transform .2s ease}.tv-accordion-item[open] .tv-accordion-chevron{transform:rotate(180deg)}.tv-accordion-body{display:block;border-top:1px solid var(--l)}.tv-server-chart{display:block;width:100%;height:auto;min-height:320px;max-height:430px;object-fit:fill;border:0;background:#081823}.tv-chart-note{min-height:20px;padding:0 14px 12px}@media(max-width:750px){.tv-accordion-head{padding:12px 14px}.tv-server-chart{min-height:260px}}</style>\\n00218: <script src=\\\"/vendor/lightweight-charts.js\\\"></script>\\n00219: <script id=\\\"GRAM1_CHARTS_DIRECT_VISIBLE_SCRIPT_V5\\\">(()=>{const root=document.getElementById(\\\"chart\\\");if(!root)return;root.dataset.chartUi=\\\"GRAM1_DIRECT_SERVER_V5\\\";const items=[...root.querySelectorAll(\\\"details.tv-accordion-item\\\")];items.forEach(item=>item.addEventListener(\\\"toggle\\\",()=>{if(!item.open)return;items.forEach(other=>{if(other!==item)other.open=false})}));})();</script>\\n00220: \\n00221: <style id=\\\"SHARP_MOVES_LAYOUT_V2\\\">.sharp-moves-panel{grid-column:1 / -1;justify-self:center;width:min(100%,860px);margin-inline:auto}.sharp-moves-panel .title{justify-content:center}.sharp-moves-panel .title h2{width:100%;text-align:center}#moves{grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:12px!important}@media(max-width:750px){.sharp-moves-panel{width:100%}#moves{grid-template-columns:1fr!important}}</style>\\n00222: <script id=\\\"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1\\\">(()=>{const move=()=>{const chart=document.getElementById(\\\"chart\\\");const panel=chart&&chart.closest(\\\".panel\\\");const main=document.querySelector(\\\"main\\\");if(!panel||!main)return;main.appendChild(panel);panel.style.gridColumn=\\\"1 / -1\\\";const title=document.getElementById(\\\"chartTitle\\\");if(title)title.textContent=\\\"Графики монет\\\";const note=panel.querySelector(\\\".muted\\\");if(note)note.textContent=\\\"Свечи Binance · 1 месяц · весь доступный период\\\"};document.addEventListener(\\\"DOMContentLoaded\\\",move);window.addEventListener(\\\"load\\\",move);setTimeout(move,300);setTimeout(move,1500);setTimeout(move,4000)})();</script></body></html>\",\"token\":\"getElementById(\\\"chart\\\")\"}],\"scripts\":[{\"async\":false,\"body_sha256\":\"e6afa17d7b94d9da5036d55bf6b8c623ec036ca05e9dea9c789432631c1b6ca7\",\"chart_refs\":[\"chart.innerHTML\",\"Нет данных\"],\"defer\":false,\"id\":null,\"index\":0,\"line\":9,\"src\":null,\"type\":null},{\"async\":false,\"body_sha256\":\"f30846a0a26517bd6e8303d552aeba19887576c54b4a70678e234a5e1f6e7582\",\"chart_refs\":[],\"defer\":false,\"id\":\"ALERT_CREATE_OVERRIDE_V3\",\"index\":1,\"line\":9,\"src\":null,\"type\":null},{\"async\":false,\"body_sha256\":\"b6fedba59295891e784ab98cef7690efe81553715d35a9cf6a31c30d08efb5a9\",\"chart_refs\":[\"replaceChildren(\"],\"defer\":false,\"id\":\"ALERT_LIST_RENDERER_V5\",\"index\":2,\"line\":109,\"src\":null,\"type\":null},{\"async\":false,\"body_sha256\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\",\"chart_refs\":[],\"defer\":false,\"id\":null,\"index\":3,\"line\":218,\"src\":\"/vendor/lightweight-charts.js\",\"type\":null},{\"async\":false,\"body_sha256\":\"6f74b154d9dce15511aff93e6f92a28c111d4160f3ed55f11ed33eb5016c8384\",\"chart_refs\":[\"getElementById(\\\"chart\\\")\",\"GRAM1_DIRECT_SERVER_V5\"],\"defer\":false,\"id\":\"GRAM1_CHARTS_DIRECT_VISIBLE_SCRIPT_V5\",\"index\":4,\"line\":219,\"src\":null,\"type\":null},{\"async\":false,\"body_sha256\":\"90a765a83fb8a5a0542dee1abf8c32af0f248aa411aa203848f73758bc3f9b40\",\"chart_refs\":[\"getElementById(\\\"chart\\\")\",\"appendChild(\"],\"defer\":false,\"id\":\"MOVE_CHARTS_TO_BOTTOM_ALLTIME_V1\",\"index\":5,\"line\":222,\"src\":null,\"type\":null}],\"styles\":[{\"body_sha256\":\"3771004bd01fc75eefe9d744e1e53a584c70d396fdb6543c98df7e7626dd007e\",\"id\":\"GRAM1_CHARTS_DIRECT_VISIBLE_STYLE_V5\",\"index\":2,\"line\":217,\"snippet\":\"#chart{display:block!important;margin-top:12px;border:1px solid var(--l);border-radius:16px;overflow:hidden;background:#081823;min-height:56px}#tvCharts{display:none!important}.tv-accordion-item{display:block;border-bottom:1px solid var(--l);background:#081823}.tv-accordion-item:last-child{border-bottom:0}.tv-accordion-head{list-style:none;cursor:pointer;padding:14px 16px;font-weight:800;display:flex;align-items:center;justify-content:space-between;gap:12px;user-select:none}.tv-accordion-head::-webkit-details-marker{display:none}.tv-accordion-chevron{font-size:18px;transition:transform .2s ease}.tv-accordion-item[open] .tv-accordion-chevron{transform:rotate(180deg)}.tv-accordion-body{display:block;border-top:1px solid var(--l)}.tv-server-chart{display:block;width:100%;height:auto;min-height:320px;max-height:430px;object-fit:fill;border:0;background:#081823}.tv-chart-note{min-height:20px;padding:0 14px 12px}@media(max-width:750px){.tv-accordion-head{padding:12px 14px}.tv-server-chart{min-height:260px}}\"}],\"template_bytes\":29232,\"template_sha256\":\"541157608706ebb8d4a415a13ee1d1972947bc5b6cbe26c0eae36b6cabc9b7a5\"}},\"rollback_restored\":null,\"rollback_started\":false,\"schema\":1,\"selected_method\":\"ALIAS_PINNED_AS_DEBIAN\",\"ssh_method_probes\":[{\"duration_seconds\":0.123,\"identity_count\":null,\"identity_sudo_read_ok\":true,\"name\":\"ALIAS_PINNED_AS_DEBIAN\",\"rc\":0,\"stderr\":\"\"},{\"duration_seconds\":0.121,\"identity_count\":2,\"identity_sudo_read_ok\":true,\"name\":\"DIRECT_PINNED_AS_DEBIAN_NO_PROXY\",\"rc\":0,\"stderr\":\"\"}],\"status\":\"OK\"}\n"
|
||
}
|