Мастер-разбор · всё в одном

TakeProfit: виджеты, логика, код, дизайн

Полный реверс терминала: архитектура, все виджеты, чарт-движок, Indie DSL, рисование, стакан, трейдинг, данные, авторизация, сохранение и дизайн-система — с verbatim-кодом и живыми скринами.

📦 полный граф 352 чанка / 12 МБ 🧠 ядро BgH94ARf.js · 3.1 МБ 🔬 18 подсистем · параллельный разбор 📅 12 июня 2026
high подтверждено кодомmedium косвенноlow не подтверждено
01

Архитектура — общий скелет

TakeProfit — браузерный торговый терминал на единой WebGL-сцене. Ни один кусок не «обычный сайт»: график, стакан и пузыри — это GPU/Canvas-движки, а данные идут бинарным Protobuf через воркеры.

каркас
SvelteKit + Svelte 5 (runes)
график: модель/ввод
Lightweight Charts (форк TradingView)
график: рендер
PixiJS v7 · WebGL2 (instancing)
виджеты/докинг
Lumino DockPanel (@lumino/widgets+dragdrop)
drag-and-drop
нативный HTML5 + тач-мост
данные
Connect/gRPC-web · Protobuf (@bufbuild)
стриминг
SharedWorker + Comlink (не WebSocket)
скрипты
Indie® DSL (CodeMirror 6 + Lezer-Python)
стакан/пузыри
Canvas 2D (TxOrderBook)
auth
Stytch (Google OAuth) + cookie-сессия
настройки
REST /api/settings (ky), remote
дизайн
Tailwind (tw-) + design tokens, 13 тем
🧩

Главный приём производительности: весь workspace рисуется в один WebGL2-canvas на весь вьюпорт (как игровой движок), поверх — DOM-оверлеи виджетов (<section class="widget">) в докинг-сетке Lumino. Рендер on-demand (0 draw-call в покое).

Проверено по полному графу: весь код — 352 чанка / 12 МБ (замыкание импортов завершено). Во всём графе 0 ссылок на .wasm и нет WebAssembly.instantiate для расчётов → клиентского WASM нет; «WASM на C/C++» из маркетинга — серверный (рантайм Indie R_WASM) либо преувеличен.

Живой workspace: Indie-редактор · Watchlist · ордер-панель · График (BB+RSI) · Screener — докинг-сетка Lumino поверх единого canvas
Живой workspace: Indie-редактор · Watchlist · ордер-панель · График (BB+RSI) · Screener — докинг-сетка Lumino поверх единого canvas
02

Система виджетов и докинг (Lumino) достоверность: high

Рабочее пространство — мозаика виджетов на Lumino DockPanel (быв. PhosphorJS; подтверждено CSS lm-DockPanel-*, MIME application/vnd.lumino.widget-factory, GOLDEN_RATIO=.618, BoxEngine.adjust). Поверх — кастомный LayoutManager TakeProfit.

Логика работы

Код (verbatim)

DEFAULT_EDGES={top:12,right:40,bottom:40,left:40}

Px-пороги root-зон drop.

case"root-top":...l=h.height*F.GOLDEN_RATIO;break;

Оверлей root-top = 0.618 высоты (GOLDEN_RATIO=.618).

moveHandle(e,t){...for(let r of this._sizers)r.size>0&&(r.sizeHint=r.size);j.adjust(this._sizers,e,n),this.parent&&this.parent.update()}

Сплиттер: фиксация sizeHint + zero-sum adjust.

s.DRAG_THRESHOLD=5,s.DETACH_THRESHOLD=20

TabBar: старт drag при 5px, отрыв за contentRect±20px.

resize до: чарт 362px
resize до: чарт 362px
resize после: чарт 618px — live-перелейаут
resize после: чарт 618px — live-перелейаут
03

Все виджеты (каталог) достоверность: high

Хаб «Add Widgets» (правый drawer с превью-карточками и «+»). Полный список типов виджетов, найденный в коде и хабе:

ВиджетЛогика / источник данных
ChartГрафик: Lightweight Charts-модель + Pixi/WebGL2-рендер. Свечи/бары/линия/area/baseline, индикаторы в пейнах, drawing tools, crosshair, объёмы. См. §04–05.
WatchlistСписки инструментов с live-котировками (QuoteApi/QuoteStream). Цена, изменение, %, биржа.
ScreenerСкринер акций: StockScreenerApi/Search + CategoryApi, фильтры, сортировка по капитализации/метрикам.
FinancialsФундаментал: FundamentalApi (P/E, P/S, P/B, EV/EBITDA, dividend yield…), периоды Yr/QTR.
Market DepthСтакан (DOM) + Order Flow Bubbles на Canvas2D (движок «TxOrderBook»). См. §08.
Indie® Code EditorIDE на CodeMirror 6 для DSL Indie: индикаторы и стратегии, серверная компиляция. См. §06.
Strategy / BacktestingБэктест стратегий Indie: equity/drawdown, отчёт через StrategyReportStylistApi, диапазон 5000/20000 свечей.
FeedЛента комьюнити прямо в workspace.
NotesЗаметки/доки с блочным редактором (drag блоков, поиск anchor бинарным поиском).
AccountПрофиль, подписки, ключи бирж (TradingApi/StoreApiKey).
Lime / J2T / IlotcosБрокерские order/portfolio-виджеты — сторонние iframe (бэкенд Finam; J2T через MT5), мост postMessage. См. §09.
Widget Hub: карточки с живыми превью (Chart, Watchlist, Indie® Code Editor, Strategy/Backtesting, Market Depth…)
Widget Hub: карточки с живыми превью (Chart, Watchlist, Indie® Code Editor, Strategy/Backtesting, Market Depth…)
04

Чарт-движок: LWC-модель + Pixi WebGL2 достоверность: high

Главное открытие: график — форк TradingView Lightweight Charts (scene-модель, оси, API addCandlestickSeries/coordinateToPrice, crosshair) с рендером, заменённым на кастомный PixiJS v7 / WebGL2. Каждый тип серии — свой GLSL-шейдер; цена/лог/процент считаются прямо в вершинном шейдере. Это и есть «первые WebGL-графики».

Рендереры серий (GPU-инстансинг)

Прайс-скейлы и crosshair

Код (verbatim)

e[r+n++]=i.time,e[r+n++]=-i.open,e[r+n++]=-i.high,e[r+n++]=-i.low,e[r+n++]=-i.close,e[r+n++]=i.color.r/255,...

setBarData: OHLC пишутся отрицательными (экран Y вниз), цвет /255.

#define SCALE_NORMAL 0  #define SCALE_LOG 1  #define SCALE_PERCENT 2  #define SCALE_INDEXED 3\nfloat toLog(float p){...return 0.4342944819*log(m+coordOffset)+logicalOffset;}

4 режима прайс-скейла в вершинном шейдере.

outColor=isRenderInteractive?vec4(uidColor,1.0)*step(0.0,mask):vColor*mask;

GPU-picking: второй проход — уникальный id-цвет серии.

График META 1h на весь экран (Pixi/WebGL2)
График META 1h на весь экран (Pixi/WebGL2)
Crosshair: ценовой + временной пиллы, OHLC-ридаут
Crosshair: ценовой + временной пиллы, OHLC-ридаут
05

Pan/Zoom, анимации и рендер-планировщик достоверность: high

Pan / Zoom (Lightweight Charts interaction)

Анимации (3 слоя)

On-demand рендер-планировщик

Код (verbatim)

_onMousewheel=e=>{let t=e.deltaX/100,i=-e.deltaY/100;...this.model().zoomTime(n,r)...this.model().scrollChart(-80*t)}

Wheel: вертикаль → зум времени (якорь у курсора), горизонталь → скролл (×80).

getPosition(e){...return t.position+this._speedPxPerMsec*(Math.pow(this._dumpingCoeff,i)-1)/Math.log(this._dumpingCoeff)}

Инерция: экспоненциальное затухание (dumping=.997).

r.ticker.autoStart=!1,r.ticker.stop();const n=()=>{r?.scenes.some(a=>a.isNeedRedraw())&&r.ticker.update(),requestAnimationFrame(n)}

On-demand: рендер только когда сцена «грязная».

пан до
пан до
пан после: BB пересчитались под новый диапазон в реальном времени
пан после: BB пересчитались под новый диапазон в реальном времени
06

Indie® DSL + движок индикаторов достоверность: high

Собственный Python-подобный язык скриптов. Редактор = CodeMirror 6 + грамматика Lezer-Python (язык name:"indie") + тема oneDark. Компиляция/исполнение — на сервере (gRPC-стрим), на клиенте только парсинг для автокомплита/линтинга. # indie:lang_version = 5.

Стандартная библиотека (каталог символов)

Исполнение — серверное

Код (verbatim)

ut=Mt.define({name:"indie",parser:pn.configure({props:[...IfStatement:i=>/^\s*(else:|elif )/.test(i.textAfter)?...]})})

Язык «indie» определён на парсере Lezer-Python с Python-отступами.

Es=r=>`# indie:lang_version = 5\nfrom indie import indicator\n\n@indicator('My Indie 1', overlay_main_pane=True)\ndef Main(self):\n    return self.close[0]\n`

Дефолтный шаблон индикатора.

makeEnum("...indicator.v2.Runtime",[{no:1,name:"R_PYTHON"},{no:2,name:"R_WASM"}])

Серверный исполнитель: Python или WASM.

Indie® Code Editor: CodeMirror 6, подсветка Python-подобного DSL (from indie import / @indicator / def Main)
Indie® Code Editor: CodeMirror 6, подсветка Python-подобного DSL (from indie import / @indicator / def Main)
07

Инструменты рисования + Object Tree достоверность: high

Проприетарный слой поверх LWC-модели, рендер геометрии — Pixi v7. Точки хранятся в логических координатах {time, indexOffset, price} (устойчивы к скроллу/зуму), не в пикселях.

Каталог (7 групп тулбара)

Магнит, слои, персистентность

Код (verbatim)

const mm={time:NaN,indexOffset:0,price:NaN},pt={coordinate:mm,name:"Point"}

Точка = логическая координата (время/индекс/цена), не пиксели.

getDrawingStoreBySyncSetting(t){return t==="global"?this.#t:t==="workspace"?this.#e:t==="channel"?this.#s:this.#r}

4-уровневая персистентность рисунков (включая per-channel #s).

N={PANE:"pane",DRAWING:"drawing",FOLDER:"folder",MAIN_SYMBOL:"mainSymbol",INDICATOR:"indicator",SYMBOL:"symbol"}

Типы узлов Object Tree.

Таймфреймы: ticks/seconds/minutes + custom
Таймфреймы: ticks/seconds/minutes + custom
Типы графика: Candles / Line / Bars
Типы графика: Candles / Line / Bars
08

Market Depth + Order Flow Bubbles достоверность: high

Стакан и пузыри — собственный движок «TxOrderBook UI lib» на чистом Canvas 2D (НЕ Pixi): пять наложенных <canvas>-слоёв с DPR-масштабированием под Retina. Данные — gRPC-стрим OrderBookApi/GetOrderBookStream в Web Worker.

Логика

Код (verbatim)

getOrderBookStream:{name:"GetOrderBookStream",I:sE,O:oE,kind:at.ServerStreaming}

Источник DOM — серверный gRPC-стрим (Connect).

i.arc(b,d,m+u,0,2*Math.PI),i.fill(),i.arc(b,d,m,0,2*Math.PI),i.fill()

Пузырь = два Canvas2D arc() (outline + fill), без Pixi.

computeRawR(t,e){const[s]=ri(t,[.95]);return{rRaw:s?De(e/s):1}}

Размер импульса нормируется на 95-й перцентиль объёма.

09

Трейдинг и ордера достоверность: high

Нативный тикет = Connect-RPC TradingApi (UI на Svelte 5). Брокеры Lime / J2T / Ilotcos — сторонние iframe (бэкенд Finam; J2T через MT5) с протоколом postMessage.

Логика

Код (verbatim)

const a={idempotencyToken:crypto.randomUUID(),order:{order:{case:"baseOrder",value:{side:Zi[i.direction],quantity:Ge(i.quantity),orderType:$i(i),stop:ea(i),takeProfit:Xt(...),stopLoss:Xt(...),reduceOnly:i.reduceOnly}}}};await Ji.placeOrder(a)

Сборка PlaceOrder (oneof baseOrder) + Connect-RPC; идемпотентность UUID.

takerFeeRate:55e-5,makerFeeRate:2e-4

Дефолтные комиссии (Bybit-подобные).

LimeOrder:{production:"https://lime.widget.takeprofit.com/order/index.html",nonProduction:".../takeprofit-widgets-lime.finam.dev/order/"}

Брокер-iframe (non-prod на finam.dev → бэкенд Finam).

10

Слой данных — Connect/Protobuf + стриминг достоверность: high

Connect-Web (@connectrpc/connect) поверх protobuf-es (@bufbuild/protobuf), два протокола: gRPC-web (application/grpc-web+proto) и Connect (application/connect+proto). Стриминг — Connect server-streaming по HTTP (не WebSocket), мультиплекс в SharedWorker + Comlink.

Полный каталог сервисов (19+1, ★ = server-streaming)

Service typeName (takeprofit.*)Методы
marketdata.external.quote.v1.QuoteApiQuoteStream ★(SS), ListQuotes
marketdata.external.candle.v1.CandleOffsetApiQueryCandleOffsets
marketdata.external.candle.v1.ExtrapolationApiListBars, GetBarTimestamp, GetBarsCount, ListOffsets
marketdata.order_book.v1.OrderBookApiGetOrderBookStream ★(SS)
marketdata.external.fundamental.v2.FundamentalApiGetFundamentalCatalogue, ListCategories, ListModes, ListFundamentals
marketdata.external.ratio.v2.CatalogueApiGetRatioCatalogue, ListModes
indicator.controller.v2.ControllerApiGetIndicatorStream ★(SS), GetIndicatorHistory, GetIndicatorsInfo, GetStrategyOrdersAndTradesHistory
indicator.code_migrator.v1.CodeMigratorApiMigrateCode
indicator.strategy_report_stylist.v1GetStrategyReportLayout
trading.trading.v1.TradingApiPlaceOrder, CancelOrder, UpdateOrder, GetEventsStream ★(SS), GetPlaceOrderData(+Stream ★SS), StoreApiKey/UpdateApiKey/ListAllApiKeyInfos/DeleteApiKey, ListAdaptors, GetAdaptorCapabilities, SetLeverage, SetMarginMode, SwitchSpotMarginTrade, SetPositionMode
alerts.tpi_alerts.v1.AlertsApiCreate/Get/Update/Delete/Start/Stop/ListAlerts, GetUserAlertLog, Mark/DeleteAlertLogs
alerts.tpi_notifications.v1.NotificationsApiTestUserWebhook
alerts.tpi_popup_sender.v1.PopupSenderApiSendUserNotification, GetUserNotificationStream ★(SS)
reference.external.exchange.v1.ExchangeApiListExchanges, ListCategories, ListAliases
reference.external.industry.v1.IndustryApiGetIndustriesTree
reference.external.security.v1.SecurityInfoApiListSecurities, ListSecurityClasses
screener.external.v1.SearcherApiSearch, ListProperties, ListCategories, SearchWithGroups
screener.external.v2.CategoryApiListCategories, ListCategoryGroups
screener.external.v2.StockScreenerApiSearch
telegram_connector.v1.TelegramConnectorApiGenerateAuthLink, CheckUserAuth, DisconnectTelegramAccount, SendMessage

Транспорт, эндпоинты, авторизация

Стриминг-пайплайн (SharedWorker)

Код (verbatim)

typeName:"takeprofit.marketdata.external.quote.v1.QuoteApi",methods:{quoteStream:{name:"QuoteStream",I:dee,O:hee,kind:qi.ServerStreaming},listQuotes:{...,kind:qi.Unary}}

Сервис котировок: стрим QuoteStream + унарный ListQuotes.

m?t=new SharedWorker(e,r):t=new Worker(e,r);...port.postMessage({type:"REGISTER",clientID:this.#s})

Мультиплекс стримов между вкладками через SharedWorker.

pushOne=r=>{...e.bids=n(e.bids,r.bids,"desc"),e.asks=n(e.asks,r.asks,"asc"),e.updateType="UPDATE"...};pushMany=r=>{...e.updateType="SNAPSHOT"...}

Orderbook: pushMany=SNAPSHOT, pushOne=delta UPDATE.

11

Авторизация (Stytch) + сессия достоверность: high

Stytch (self-hosted на api.stytch.takeprofit.com) для OAuth-старта + самописный бэкенд /api/auth/*. HTTP-клиент — ky.

const Tt={prod:{host:"https://api.stytch.takeprofit.com",token:"public-token-live-7c583685-…-1eec86c7e28f"}}

Stytch public token, self-hosted домен.

const r=await ci.get("api/auth/token",{credentials:"include",headers:t?{cookie:`token=${t}`}:void 0});return{result:i.data.accessToken}

Refresh: cookie-сессия token → accessToken.

#t=async(e,n,r)=>{if([401].includes(r.status))return await this.requestRefresh(),this.transport(e,n)}

Авто-refresh и повтор при 401.

12

Сохранение workspace и настроек достоверность: high

Раскладка и настройки — на сервере (REST /api/settings через ky), НЕ в браузере. localStorage/IndexedDB для layout не используются.

saveDataTreeThrottled=NL(this.saveDataTree,FBe) // FBe=5000

Throttle сохранения дерева — раз в 5с (lodash).

t=hh.extend({prefixUrl:"/api/settings",retry:3});this.#e={rootSettings:new SettingsObjectService(...),workspaces:new SettingsArrayService(...),...}

REST-бэкенд = ky на /api/settings, 7 сервисов по разделам.

13

Дизайн-система и темизация достоверность: high

Собственная токен-система поверх Tailwind CSS (префикс tw-, 711 утилит). Темы = статический CSS ([data-theme=slug]) для пресетов + рантайм-инъекция CSS-переменных для пользовательских.

#d(r){...document.documentElement.style.setProperty(`--colors-${s}`,r[s]),document.documentElement.style.setProperty(`--colors-var-${s}`,`${n.red} ${n.green} ${n.blue}`)}

Двухканальная запись токена: hex + «R G B» для Tailwind-alpha.

#i(r){document.documentElement.setAttribute("data-theme",r);...meta[theme-color]=…}

Применение пресета = атрибут data-theme=<slug>.

Тёмная тема в деле: мультипейн цена+BB / объёмы / RSI — все цвета из --colors-chart-* токенов
Тёмная тема в деле: мультипейн цена+BB / объёмы / RSI — все цвета из --colors-chart-* токенов
14

Честные пробелы достоверность: mixed