Superfície de otimização
SurfaceWidget apresenta os resultados de um varrimento de parâmetros como uma paisagem tridimensional: uma métrica sobre dois eixos discretos, em que o valor da métrica define tanto a altura como a cor. O controlo aceita os mesmos dados que o mapa de calor de otimização, pelo que um mesmo conjunto de resultados pode ser visto como mapa plano, como superfície ou como ambos ao mesmo tempo.

Criação e atualização
import {
HeatDirections,
SurfaceWidget,
type SurfaceData,
type TradingHost,
} from '@stocksharp/trading-controls';
declare const host: TradingHost;
const surface = SurfaceWidget.create(
document.querySelector<HTMLElement>('#surface')!,
{},
{ host },
);
const sweep: SurfaceData = {
xLabel: 'Fast',
yLabel: 'Slow',
metricLabel: 'Net profit',
betterWhen: HeatDirections.Higher,
cells: [
{ x: '10', y: '50', value: 1_250 },
{ x: '10', y: '80', value: -320 },
{ x: '20', y: '50', value: 2_480 },
{ x: '20', y: '80', value: 640 },
],
};
surface.update(sweep);
A única dependência é host; a interface SurfaceDeps não tem outros campos. O segundo argumento de create é o estado guardado da instância: a superfície não o lê nem escreve nele.
update substitui todo o conjunto. A superfície corresponde a um varrimento completo, pelo que não existe atualização parcial: metade de um varrimento sobre metade de outro daria uma paisagem feita de dois resumos diferentes.
A propriedade estática SurfaceWidget.TYPE é igual a ControlTypes.OptimizationSurface — ao identificador optimizationSurface.
Dados
A célula HeatCell é um par de valores dos eixos mais a métrica medida: { x, y, value }. Os valores dos eixos são texto porque o eixo é discreto: 10, 00:05:00 e True são posições equivalentes nele. Os valores numéricos são ordenados como números e os restantes como texto, pelo que um varrimento de 5, 8, 12, 40 se mantém em sequência.
O campo betterWhen é obrigatório e aceita HeatDirections.Higher ou HeatDirections.Lower. Sem ele, uma paisagem de drawdowns elevaria o pior canto ao cume.
Várias execuções sobre o mesmo par são reduzidas à sua média — isso é uma célula. Um par por onde o varrimento não passou fica como buraco: uma face só é desenhada quando os seus quatro cantos são conhecidos, e a lacuna não é interpolada — um resultado ausente não é igual a um resultado nulo. Se não houver células de todo, ou se pelo menos um dos eixos tiver menos de dois valores distintos, em vez da paisagem é apresentada uma mensagem substituta com o texto da chave NoOptimizationResults.
As etiquetas xLabel, yLabel e metricLabel vão para os rótulos dos eixos; metricLabel é ainda apresentada no cabeçalho do painel.
Apresentação
A altura de uma face é a posição do valor em relação ao ponto de referência da escala, comprimida entre o piso e o topo: o ponto de referência cai a meia altura, pelo que o piso não significa «pior resultado», mas sim o limite inferior da escala. A cor vem de host.presentation.canvasPalette(): up para os valores melhores do que o ponto de referência, down para os piores, com a saturação a aumentar à medida que se afastam dele. As faces são preenchidas das mais distantes para as mais próximas, de modo que uma crista em primeiro plano tapa o que está atrás, e são contornadas com a cor grid para que a grelha se leia onde duas faces vizinhas tenham quase o mesmo tom.
A projeção é ortográfica: a superfície lê-se comparando alturas em todo o campo, e a perspetiva encurtaria o lado distante de uma crista em relação ao próximo.
Sob a paisagem são desenhadas duas arestas do piso e o eixo vertical da escala. Nos eixos paramétricos são apresentadas até oito marcas: enquanto os valores couberem, todas; depois disso, uma em cada duas, uma em cada três e assim sucessivamente, sendo a primeira e a última sempre rotuladas. No eixo vertical há cinco marcas, rotuladas com valores da métrica. As etiquetas passam para as arestas do piso mais próximas do observador — ao rodar, isso é recalculado para que os números não fiquem por cima da grelha.
Vista e gestos
Todos os gestos chegam por eventos de ponteiro, pelo que o rato, a caneta e o dedo seguem o mesmo caminho:
- arrastar com um ponteiro roda a superfície: na horizontal muda o
yaw, na vertical opitch; - dois ponteiros alteram a escala através da distância entre eles; a rotação continua a caber a um único ponteiro;
- a roda também altera a escala. O delta é convertido em «cliques» independentemente de o navegador o reportar em píxeis, linhas ou páginas, e é limitado a dois cliques por evento, para que o rato e o painel tátil deem um passo comparável.
O controlo reserva para si o gesto sobre o canvas, caso contrário arrastar no telemóvel e a roda num navegador de secretária deslocariam a página em vez da paisagem.
A inclinação e a escala são limitadas: o pitch vai de MIN_PITCH (0,12) a MAX_PITCH (1,45) e a escala de 0,4 a 4. Com inclinação zero cada face degeneraria numa linha e, num ângulo reto, a superfície tornar-se-ia um mapa plano, isto é, outro controlo. A rotação yaw não é limitada, dá a volta: virar a paisagem ao contrário para ver o lado oposto de uma crista é um gesto com sentido. A vista inicial é DEFAULT_VIEW; o botão no cabeçalho do painel (ResetView) devolve-a.
Quando o ponteiro não está a rodar a superfície, o controlo procura o ponto medido mais próximo num raio de 22 píxeis CSS. O ponto encontrado é contornado com um anel da cor up e, na faixa por cima do canvas, aparece uma linha com o valor do eixo xLabel, o valor do eixo yLabel e a métrica. A faixa fica sobre o canvas e não no cabeçalho do painel: a leitura diz respeito ao ponto sob o ponteiro e deve estar junto dele. Um par por onde o varrimento não passou não é oferecido ao ponteiro; em caso de igualdade de distância é escolhido o ponto mais próximo do observador.
O que faz o controlo e o que fica a cargo do anfitrião
O controlo trata dos gestos, acompanha o tamanho do canvas através de ResizeObserver e redesenha a paisagem para o tamanho e a densidade de píxeis atuais do ecrã, trata do botão de reposição da vista e do botão de fecho do painel, que invoca host.close(), e ainda se regista com host.register e cancela o registo em dispose. As cores e o tipo de letra do canvas vêm de host.presentation.canvasPalette(). Todo o texto visível é pedido através de host.t: OptimizationSurface, ResetView, ClosePanel, OptimizationSurfaceChart, NoOptimizationResults.
Os dados são fornecidos pelo anfitrião: o controlo não lança a otimização, não subscreve o seu andamento e não sabe como os resultados foram obtidos — desenha o que lhe for passado em update. Não há clique sobre uma face: a face corresponde a uma célula e não a uma execução isolada, pelo que não há nada para abrir a partir dela — premir roda a paisagem.
O controlo não guarda definições próprias em host.preferences, não tem chaves e não chama host.persistState. A vista atual está disponível através do método view() — se for preciso repô-la entre sessões, é o anfitrião que guarda e devolve esses valores.
Métodos públicos
update(data)— mostrar o conjunto de resultados completo.view()— devolver uma cópia da vista atual (yaw,pitch,zoom).resetView()— repor a vista emDEFAULT_VIEW.dispose()— desligar o observador de tamanho, chamarhost.unregistere retirar o elemento raiz.
Funções auxiliares
A geometria foi retirada do controlo para um módulo separado e é exportada pelo pacote — sobre ela pode construir-se um desenho próprio:
surfaceLayout(input)— dispor as células em faces, eixos e vértices para dadas dimensões e vista;nullquando não há nada para desenhar.project(nx, ny, nz, view, box)— projetar um ponto do cubo unitário nocanvas.dragView(view, dx, dy)— a vista depois de arrastar esse número de píxeis.zoomView(view, factor)— a vista depois de alterar a escala.clampView(view)— a vista limitada aos valores admissíveis.DEFAULT_VIEW,MIN_PITCH,MAX_PITCH— a vista inicial e os limites da inclinação.