website: start containing lc code in wrapper

This commit is contained in:
nym21
2024-12-03 17:31:56 +01:00
parent 141cd819a1
commit 783aed5826
10 changed files with 535 additions and 489 deletions
+107 -48
View File
@@ -968,76 +968,135 @@
pointer-events: none; pointer-events: none;
} }
.chart > .panes { .chart {
position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1;
min-height: 0; min-height: 0;
> .pane { > legend {
z-index: 20; display: flex;
position: relative; align-items: center;
min-height: 0px; gap: 1.5rem;
width: 100%; margin-left: var(--negative-main-padding);
cursor: crosshair; margin-right: var(--negative-main-padding);
height: 100%; padding-left: var(--main-padding);
padding-right: var(--main-padding);
padding-bottom: 1.25rem;
overflow-x: auto;
min-width: 0;
&:has(+ .chart-wrapper:not([hidden])) { > div {
height: calc(100% - 62px); flex: 0;
}
> fieldset {
pointer-events: none;
position: absolute;
left: 0px;
top: 0px;
z-index: 10;
display: flex; display: flex;
align-items: center; align-items: center;
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
gap: 0.5rem;
> div.field { > label {
margin: -0.375rem 0;
color: var(--color);
> span {
display: flex !important;
}
&:has(input:not(:checked)) {
color: var(--off-color);
> span.main > span.name {
text-decoration-thickness: 1.5px;
text-decoration-color: var(--color);
text-decoration-line: line-through;
}
&:hover {
* {
color: var(--off-color) !important;
}
> span.main > span.name {
text-decoration-color: var(--orange) !important;
}
}
}
}
> a {
padding: 0.375rem;
margin: -0.375rem;
}
}
}
> .panes {
position: relative;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
> .pane {
z-index: 20;
position: relative;
min-height: 0px;
width: 100%;
cursor: crosshair;
height: 100%;
&:has(+ .chart-wrapper:not([hidden])) {
height: calc(100% - 62px);
}
> fieldset {
pointer-events: none;
position: absolute;
left: 0px;
top: 0px;
z-index: 10;
display: flex; display: flex;
align-items: center; align-items: center;
font-size: var(--font-size-xs); font-size: var(--font-size-xs);
line-height: var(--line-height-xs); line-height: var(--line-height-xs);
gap: 0.5rem; gap: 0.5rem;
> legend, > div.field {
> div {
flex-shrink: 0;
}
> hr {
min-width: 1rem;
}
label {
padding: 0.5rem;
margin: -0.5rem;
}
> div {
display: flex; display: flex;
align-items: center;
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
gap: 0.5rem; gap: 0.5rem;
> legend,
> div {
flex-shrink: 0;
}
> hr {
min-width: 1rem;
}
label {
padding: 0.5rem;
margin: -0.5rem;
}
> div {
display: flex;
gap: 0.5rem;
}
} }
} }
> .lightweight-chart {
height: 100%;
margin-right: var(--negative-main-padding);
}
} }
> .lightweight-chart { > .shadow-bottom {
height: 100%; bottom: 1.75rem;
margin-right: var(--negative-main-padding); width: 80px;
left: auto;
} }
} }
> .shadow-bottom {
bottom: 1.75rem;
width: 80px;
left: auto;
}
} }
</style> </style>
+122
View File
@@ -0,0 +1,122 @@
import { Signal } from "../solid-signals/types";
import { Accessor } from "../solid-signals/2024-11-02/types/signals";
import { Owner } from "../solid-signals/2024-11-02/types/core/owner";
import {
DeepPartial,
BaselineStyleOptions,
CandlestickStyleOptions,
LineStyleOptions,
SeriesOptionsCommon,
Time,
CandlestickData,
ISeriesApi,
BaselineData,
} from "./v4.2.0/types";
import { Color } from "../../scripts/types/self";
interface BaseSeriesBlueprint {
title: string;
defaultActive?: boolean;
}
interface BaselineSeriesBlueprint extends BaseSeriesBlueprint {
type: "Baseline";
color?: Color;
options?: DeepPartial<BaselineStyleOptions & SeriesOptionsCommon>;
data?: Accessor<BaselineData<Time>[]>;
}
interface CandlestickSeriesBlueprint extends BaseSeriesBlueprint {
type: "Candlestick";
color?: Color;
options?: DeepPartial<CandlestickStyleOptions & SeriesOptionsCommon>;
data?: Accessor<CandlestickData<Time>[]>;
}
interface LineSeriesBlueprint extends BaseSeriesBlueprint {
type?: "Line";
color: Color;
options?: DeepPartial<LineStyleOptions & SeriesOptionsCommon>;
data?: Accessor<LineData<Time>[]>;
}
type AnySpecificSeriesBlueprint =
| BaselineSeriesBlueprint
| CandlestickSeriesBlueprint
| LineSeriesBlueprint;
type SeriesType = NonNullable<AnySpecificSeriesBlueprint["type"]>;
type PriceSeriesType = "Candlestick" | "Line";
type RemoveSeriesBlueprintFluff<Blueprint extends AnySpecificSeriesBlueprint> =
Omit<Blueprint, "type" | "title">;
type SplitSeriesBlueprint = {
datasetPath: AnyDatasetPath;
main?: boolean;
} & AnySpecificSeriesBlueprint;
type SingleSeriesBlueprint = AnySpecificSeriesBlueprint;
interface CreateBaseSeriesParameters extends BaseSeriesBlueprint {
id: string;
disabled?: Accessor<boolean>;
color?: Color;
}
interface BaseSeries {
id: string;
title: string;
color: Color | Color[];
active: Signal<boolean>;
visible: Accessor<boolean>;
disabled: Accessor<boolean>;
}
interface SingleSeries extends BaseSeries {
iseries: ISeriesApi<any>;
}
interface SplitSeries extends BaseSeries {
chunks: Array<Accessor<ISeriesApi<SeriesType> | undefined>>;
dataset: ResourceDataset<TimeScale, number>;
}
interface CreateSingleSeriesParameters {
blueprint: SingleSeriesBlueprint;
id: string;
}
interface CreateSplitSeriesParameters<S extends TimeScale> {
dataset: ResourceDataset<S>;
blueprint: SplitSeriesBlueprint;
id: string;
index: number;
setMinMaxMarkersWhenIdle: VoidFunction;
disabled?: Accessor<boolean>;
}
type ChartPane = IChartApi & {
whitespace: ISeriesApi<"Line">;
hidden: () => boolean;
setHidden: (b: boolean) => void;
setInitialVisibleTimeRange: VoidFunction;
createSingleSeries: (a: CreateSingleSeriesParameters) => SingleSeries;
createSplitSeries: <S extends TimeScale>(
a: CreateSplitSeriesParameters<S>,
) => SplitSeries;
splitSeries: SplitSeries[];
};
interface CreatePaneParameters {
unit: Unit;
paneIndex?: number;
whitespace?: true;
options?: DeepPartial<ChartOptions>;
config?: SingleSeriesBlueprint[];
}
interface Marker {
weight: number;
time: Time;
value: number;
seriesChunk: ISeriesApi<any>;
}
interface HoveredLegend {
label: HTMLLabelElement;
series: SingleSeries | SplitSeries;
}
+146 -94
View File
@@ -357,6 +357,7 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
* @param {TimeScale} param0.scale * @param {TimeScale} param0.scale
* @param {"static" | "moveable"} param0.kind * @param {"static" | "moveable"} param0.kind
* @param {Utilities} param0.utils * @param {Utilities} param0.utils
* @param {Owner | null} [param0.owner]
* @param {CreatePaneParameters[]} [param0.config] * @param {CreatePaneParameters[]} [param0.config]
*/ */
function createChart({ function createChart({
@@ -368,7 +369,13 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
scale, scale,
config, config,
utils, utils,
owner: _owner,
}) { }) {
/** @type {SplitSeries[]} */
const chartSplitSeries = [];
let owner = _owner || signals.getOwner();
const div = window.document.createElement("div"); const div = window.document.createElement("div");
div.classList.add("chart"); div.classList.add("chart");
parent.append(div); parent.append(div);
@@ -541,14 +548,14 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
inputName: utils.stringToId(`selected-${series.title}-${extraName}`), inputName: utils.stringToId(`selected-${series.title}-${extraName}`),
inputValue: "value", inputValue: "value",
labelTitle: "Click to toggle", labelTitle: "Click to toggle",
onClick: (event) => { onClick: () => {
event.preventDefault();
event.stopPropagation();
input.checked = !input.checked;
series.active.set(input.checked); series.active.set(input.checked);
}, },
type: "solo",
}); });
input.checked = series.active();
const spanMain = window.document.createElement("span"); const spanMain = window.document.createElement("span");
spanMain.classList.add("main"); spanMain.classList.add("main");
label.append(spanMain); label.append(spanMain);
@@ -568,10 +575,6 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
hoveredLegend.set(undefined); hoveredLegend.set(undefined);
}); });
signals.createEffect(series.active, (checked) => {
input.checked = checked;
});
function shouldHighlight() { function shouldHighlight() {
const hovered = hoveredLegend(); const hovered = hoveredLegend();
return ( return (
@@ -680,7 +683,7 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
} }
} else { } else {
applySeriesOption({ applySeriesOption({
series: series.series, series: series.iseries,
hovered, hovered,
}); });
} }
@@ -727,6 +730,9 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
chartDiv.classList.add("lightweight-chart"); chartDiv.classList.add("lightweight-chart");
chartWrapper.append(chartDiv); chartWrapper.append(chartDiv);
/** @type {SplitSeries[]} */
const paneSplitSeries = [];
options = { ...options }; options = { ...options };
if (kind === "static") { if (kind === "static") {
options.handleScale = false; options.handleScale = false;
@@ -748,9 +754,9 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
}); });
/** /**
* @param {CreateBaselineSeriesParams} args * @param {RemoveSeriesBlueprintFluff<BaselineSeriesBlueprint>} args
*/ */
function createBaseLineSeries({ color, options, owner, data }) { function createBaseLineSeries({ color, options, data }) {
const topLineColor = color || colors.profit; const topLineColor = color || colors.profit;
const bottomLineColor = color || colors.loss; const bottomLineColor = color || colors.loss;
@@ -784,16 +790,20 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
}); });
if (data) { if (data) {
series.setData(data); signals.runWithOwner(owner, () => {
signals.createEffect(data, (data) => {
series.setData(data);
});
});
} }
return series; return series;
} }
/** /**
* @param {CreateCandlestickSeriesParams} args * @param {RemoveSeriesBlueprintFluff<CandlestickSeriesBlueprint>} args
*/ */
function createCandlestickSeries({ options, owner, data }) { function createCandlestickSeries({ options, data }) {
function computeColors() { function computeColors() {
const upColor = colors.profit(); const upColor = colors.profit();
const downColor = colors.loss(); const downColor = colors.loss();
@@ -825,16 +835,20 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
}); });
if (data) { if (data) {
series.setData(data); signals.runWithOwner(owner, () => {
signals.createEffect(data, (data) => {
series.setData(data);
});
});
} }
return series; return series;
} }
/** /**
* @param {CreateLineSeriesParams} args * @param {RemoveSeriesBlueprintFluff<LineSeriesBlueprint>} args
*/ */
function createLineSeries({ color, options, owner, data }) { function createLineSeries({ color, options, data }) {
function computeColors() { function computeColors() {
return { return {
color: color(), color: color(),
@@ -847,49 +861,41 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
...computeColors(), ...computeColors(),
}); });
if (data) {
series.setData(data);
}
signals.runWithOwner(owner, () => { signals.runWithOwner(owner, () => {
signals.createEffect(computeColors, (computeColors) => { signals.createEffect(computeColors, (computeColors) => {
series.applyOptions(computeColors); series.applyOptions(computeColors);
}); });
}); });
if (data) {
signals.runWithOwner(owner, () => {
signals.createEffect(data, (data) => {
series.setData(data);
});
});
}
return series; return series;
} }
/** /**
* @template {TimeScale} S * @template {TimeScale} S
* @param {CreateSplitSeriesParameters<S>} args * @param {CreateBaseSeriesParameters} args
*/ */
function createSplitSeries({ function createBaseSeries({
option, id,
index: seriesIndex,
disabled: _disabled, disabled: _disabled,
setMinMaxMarkersWhenIdle, title,
dataset, color,
seriesBlueprint, defaultActive,
splitSeries,
}) { }) {
const { const keyPrefix = id;
title, const paramKey = utils.stringToId(title);
color, const storageKey = `${keyPrefix}-${paramKey}`;
defaultActive,
type,
options: seriesOptions,
} = seriesBlueprint;
/** @type {Signal<ISeriesApi<SeriesType> | undefined>[]} */
const chunks = new Array(dataset.fetchedJSONs.length);
const id = utils.stringToId(title);
const storageId = utils.stringToId(`${option.id}-${title}`);
const active = signals.createSignal( const active = signals.createSignal(
utils.url.readBoolParam(id) ?? utils.url.readBoolParam(paramKey) ??
utils.storage.readBool(storageId) ?? utils.storage.readBool(storageKey) ??
defaultActive ?? defaultActive ??
true, true,
); );
@@ -898,12 +904,10 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
const visible = signals.createMemo(() => active() && !disabled()); const visible = signals.createMemo(() => active() && !disabled());
/** @satisfies {SplitSeries} */ /** @satisfies {BaseSeries} */
const series = { const series = {
active, active,
chunks,
color: color || [colors.profit, colors.loss], color: color || [colors.profit, colors.loss],
dataset,
disabled, disabled,
id, id,
title, title,
@@ -918,32 +922,97 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
} }
if (active !== (defaultActive || true)) { if (active !== (defaultActive || true)) {
utils.url.writeParam(id, active); utils.url.writeParam(paramKey, active);
utils.storage.write(storageId, active); utils.storage.write(storageKey, active);
} else { } else {
utils.url.removeParam(id); utils.url.removeParam(paramKey);
utils.storage.remove(storageId); utils.storage.remove(storageKey);
} }
}, },
); );
splitSeries.push(series); return series;
}
const owner = signals.getOwner(); const chartPane = /** @type {ChartPane} */ (_chart);
chartPane.createSingleSeries = function ({ blueprint, id }) {
/** @type {ISeriesApi<SeriesType>} */
let s;
switch (blueprint.type) {
case "Baseline": {
s = createBaseLineSeries(blueprint);
break;
}
case "Candlestick": {
s = createCandlestickSeries(blueprint);
break;
}
default:
case "Line": {
s = createLineSeries(blueprint);
break;
}
}
/** @satisfies {SingleSeries} */
const series = {
...createBaseSeries({
id,
title: blueprint.title,
color: blueprint.color,
defaultActive: blueprint.defaultActive,
}),
iseries: s,
};
signals.createEffect(series.visible, (visible) => {
series.iseries.applyOptions({
visible,
});
});
createLegend({ series });
return series;
};
chartPane.createSplitSeries = function ({
id,
index: seriesIndex,
disabled,
setMinMaxMarkersWhenIdle,
dataset,
blueprint,
}) {
/** @satisfies {SplitSeries} */
const series = {
...createBaseSeries({
id,
title: blueprint.title,
color: blueprint.color,
defaultActive: blueprint.defaultActive,
disabled,
}),
dataset,
chunks: new Array(dataset.fetchedJSONs.length),
};
paneSplitSeries.push(series);
chartSplitSeries.unshift(series);
dataset.fetchedJSONs.forEach((json, index) => { dataset.fetchedJSONs.forEach((json, index) => {
const chunk = signals.createSignal( const chunk = signals.createSignal(
/** @type {ISeriesApi<SeriesType> | undefined} */ (undefined), /** @type {ISeriesApi<SeriesType> | undefined} */ (undefined),
); );
chunks[index] = chunk; series.chunks[index] = chunk;
const isMyTurn = signals.createMemo(() => { const isMyTurn = signals.createMemo(() => {
if (seriesIndex <= 0) return true; if (seriesIndex <= 0) return true;
const previousSeriesChunk = splitSeries.at(seriesIndex - 1)?.chunks[ const previousSeriesChunk = paneSplitSeries.at(seriesIndex - 1)
index ?.chunks[index];
];
const isPreviousSeriesOnChart = previousSeriesChunk?.(); const isPreviousSeriesOnChart = previousSeriesChunk?.();
return !!isPreviousSeriesOnChart; return !!isPreviousSeriesOnChart;
@@ -957,28 +1026,25 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
let s = chunk(); let s = chunk();
if (!s) { if (!s) {
switch (type) { switch (blueprint.type) {
case "Baseline": { case "Baseline": {
s = createBaseLineSeries({ s = createBaseLineSeries({
color, color: blueprint.color,
options: seriesOptions, options: blueprint.options,
owner,
}); });
break; break;
} }
case "Candlestick": { case "Candlestick": {
s = createCandlestickSeries({ s = createCandlestickSeries({
options: seriesOptions, options: blueprint.options,
owner,
}); });
break; break;
} }
default: default:
case "Line": { case "Line": {
s = createLineSeries({ s = createLineSeries({
color, color: blueprint.color,
options: seriesOptions, options: blueprint.options,
owner,
}); });
break; break;
} }
@@ -1069,23 +1135,17 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
}); });
}); });
createLegend({ series, extraName: type }); createLegend({ series, extraName: blueprint.type });
return series; return series;
} };
const chartPane = /** @type {ChartPane} */ (_chart);
chartPane.createSplitSeries = createSplitSeries;
chartPane.createBaseLineSeries = createBaseLineSeries;
chartPane.createCandlesticksSeries = createCandlestickSeries;
chartPane.createLineSeries = createLineSeries;
chartPane.hidden = () => { chartPane.hidden = () => {
return chartWrapper.hidden; return chartWrapper.hidden;
}; };
chartPane.setHidden = (b) => { chartPane.setHidden = (b) => {
chartWrapper.hidden = b; chartWrapper.hidden = b;
}; };
chartPane.splitSeries = paneSplitSeries;
chartPane.setInitialVisibleTimeRange = () => { chartPane.setInitialVisibleTimeRange = () => {
const range = visibleTimeRange(); const range = visibleTimeRange();
@@ -1143,26 +1203,15 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
} }
createUnitAndModeElements(); createUnitAndModeElements();
config?.forEach((params) => {
// createLegend(params);
switch (params.kind) {
case "line": {
chartPane.createLineSeries(params);
break;
}
case "candle": {
chartPane.createCandlesticksSeries(params);
break;
}
case "baseline": {
chartPane.createBaseLineSeries(params);
break;
}
}
});
switch (kind) { switch (kind) {
case "static": { case "static": {
config?.forEach((params) => {
chartPane.createSingleSeries({
id: utils.stringToId(params.title),
blueprint: params,
});
});
chartPane.timeScale().fitContent(); chartPane.timeScale().fitContent();
break; break;
@@ -1199,9 +1248,11 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
* *
* @param {Object} param0 * @param {Object} param0
* @param {TimeScale} param0.scale * @param {TimeScale} param0.scale
* @param {Owner | null} param0.owner
*/ */
function reset({ scale: _scale }) { function reset({ scale: _scale, owner: _owner }) {
scale = _scale; scale = _scale;
owner = _owner;
panes.forEach((pane) => pane.remove()); panes.forEach((pane) => pane.remove());
panes.length = 0; panes.length = 0;
legendElement.innerHTML = ""; legendElement.innerHTML = "";
@@ -1265,6 +1316,7 @@ export default import("./v4.2.0/script.js").then((lightweightCharts) => {
saveVisibleRange, saveVisibleRange,
getTicksToWidthRatio, getTicksToWidthRatio,
debouncedSaveVisibleRange, debouncedSaveVisibleRange,
splitSeries: chartSplitSeries,
}; };
} }
+21 -23
View File
@@ -33,7 +33,7 @@ const importSignals = import("./2024-11-02/script.js").then((_signals) => {
/** /**
* @template T * @template T
* @param {T} initialValue * @param {T} initialValue
* @param {SignalOptions<T> & {save?: {id?: string; param?: string; serialize: (v: NonNullable<T>) => string; deserialize: (v: string) => NonNullable<T>}}} [options] * @param {SignalOptions<T> & {save?: {keyPrefix: string; key: string; serialize: (v: NonNullable<T>) => string; deserialize: (v: string) => NonNullable<T>}}} [options]
* @returns {Signal<T>} * @returns {Signal<T>}
*/ */
createSignal(initialValue, options) { createSignal(initialValue, options) {
@@ -51,14 +51,14 @@ const importSignals = import("./2024-11-02/script.js").then((_signals) => {
if (options?.save) { if (options?.save) {
const save = options.save; const save = options.save;
const paramKey = save.key;
const storageKey = `${save.keyPrefix}-${paramKey}`;
let serialized = /** @type {string | null} */ (null); let serialized = /** @type {string | null} */ (null);
if (save.param) { serialized = new URLSearchParams(window.location.search).get(paramKey);
serialized = new URLSearchParams(window.location.search).get(
save.param, if (serialized === null) {
); serialized = localStorage.getItem(storageKey);
}
if (serialized === null && save.id) {
serialized = localStorage.getItem(save.id);
} }
if (serialized) { if (serialized) {
set(save.deserialize(serialized)); set(save.deserialize(serialized));
@@ -68,7 +68,7 @@ const importSignals = import("./2024-11-02/script.js").then((_signals) => {
this.createEffect(get, (value) => { this.createEffect(get, (value) => {
if (!save) return; if (!save) return;
if (!firstEffect && save.id) { if (!firstEffect) {
if ( if (
value !== undefined && value !== undefined &&
value !== null && value !== null &&
@@ -76,24 +76,22 @@ const importSignals = import("./2024-11-02/script.js").then((_signals) => {
initialValue === null || initialValue === null ||
save.serialize(value) !== save.serialize(initialValue)) save.serialize(value) !== save.serialize(initialValue))
) { ) {
localStorage.setItem(save.id, save.serialize(value)); localStorage.setItem(storageKey, save.serialize(value));
} else { } else {
localStorage.removeItem(save.id); localStorage.removeItem(storageKey);
} }
} }
if (save.param) { if (
if ( value !== undefined &&
value !== undefined && value !== null &&
value !== null && (initialValue === undefined ||
(initialValue === undefined || initialValue === null ||
initialValue === null || save.serialize(value) !== save.serialize(initialValue))
save.serialize(value) !== save.serialize(initialValue)) ) {
) { writeParam(paramKey, save.serialize(value));
writeParam(save.param, save.serialize(value)); } else {
} else { removeParam(paramKey);
removeParam(save.param);
}
} }
firstEffect = false; firstEffect = false;
+16 -33
View File
@@ -1,8 +1,7 @@
// @ts-check // @ts-check
/** /**
* @import { ChartPane, HoveredLegend, PriceSeriesType, SplitSeries } from "./types/self" * @import { PriceSeriesType } from '../packages/lightweight-charts/types';
* @import { Options } from './options';
*/ */
/** /**
@@ -110,14 +109,12 @@ export function init({
* @param {VoidFunction} args.setMinMaxMarkersWhenIdle * @param {VoidFunction} args.setMinMaxMarkersWhenIdle
* @param {Option} args.option * @param {Option} args.option
* @param {ChartPane} args.chartPane * @param {ChartPane} args.chartPane
* @param {SplitSeries[]} args.chartSeries
*/ */
function createPriceSeries({ function createPriceSeries({
type, type,
setMinMaxMarkersWhenIdle, setMinMaxMarkersWhenIdle,
option, option,
chartPane, chartPane,
chartSeries: splitSeries,
}) { }) {
const s = scale(); const s = scale();
@@ -131,17 +128,17 @@ export function init({
const title = "BTC Price"; const title = "BTC Price";
/** @type {SeriesBlueprint} */ /** @type {SplitSeriesBlueprint} */
let seriesBlueprint; let blueprint;
if (type === "Candlestick") { if (type === "Candlestick") {
seriesBlueprint = { blueprint = {
datasetPath, datasetPath,
title, title,
type: "Candlestick", type: "Candlestick",
}; };
} else { } else {
seriesBlueprint = { blueprint = {
datasetPath, datasetPath,
title, title,
color: colors.default, color: colors.default,
@@ -151,11 +148,10 @@ export function init({
const disabled = signals.createMemo(() => priceSeriesType() !== type); const disabled = signals.createMemo(() => priceSeriesType() !== type);
const priceSeries = chartPane.createSplitSeries({ const priceSeries = chartPane.createSplitSeries({
seriesBlueprint, blueprint,
dataset, dataset,
option, id: option.id,
index: -1, index: -1,
splitSeries,
disabled, disabled,
setMinMaxMarkersWhenIdle, setMinMaxMarkersWhenIdle,
}); });
@@ -199,9 +195,6 @@ export function init({
(list) => (list ? [list] : []), (list) => (list ? [list] : []),
); );
/** @type {SplitSeries[]} */
const allSeries = [];
chartsBlueprints.map((seriesBlueprints, paneIndex) => { chartsBlueprints.map((seriesBlueprints, paneIndex) => {
const chartPane = chart.createPane({ const chartPane = chart.createPane({
paneIndex, paneIndex,
@@ -209,9 +202,6 @@ export function init({
whitespace: true, whitespace: true,
}); });
/** @type {SplitSeries[]} */
const splitSeries = [];
function setMinMaxMarkers() { function setMinMaxMarkers() {
try { try {
const { from, to } = chart.visibleTimeRange(); const { from, to } = chart.visibleTimeRange();
@@ -226,8 +216,8 @@ export function init({
const ids = chart.visibleDatasetIds(); const ids = chart.visibleDatasetIds();
for (let i = 0; i < splitSeries.length; i++) { for (let i = 0; i < chartPane.splitSeries.length; i++) {
const { chunks, dataset } = splitSeries[i]; const { chunks, dataset } = chartPane.splitSeries[i];
for (let j = 0; j < ids.length; j++) { for (let j = 0; j < ids.length; j++) {
const id = ids[j]; const id = ids[j];
@@ -370,7 +360,6 @@ export function init({
function _createPriceSeries(type) { function _createPriceSeries(type) {
return createPriceSeries({ return createPriceSeries({
chartPane, chartPane,
chartSeries: splitSeries,
option, option,
setMinMaxMarkersWhenIdle, setMinMaxMarkersWhenIdle,
type, type,
@@ -392,21 +381,17 @@ export function init({
createLinkPriceSeriesEffect(); createLinkPriceSeriesEffect();
} }
[...seriesBlueprints].reverse().forEach((seriesBlueprint, index) => { [...seriesBlueprints].reverse().forEach((blueprint, index) => {
const dataset = datasets.getOrCreate( const dataset = datasets.getOrCreate(scale, blueprint.datasetPath);
scale,
seriesBlueprint.datasetPath,
);
// Don't trigger reactivity by design // Don't trigger reactivity by design
activeDatasets().add(dataset); activeDatasets().add(dataset);
chartPane.createSplitSeries({ chartPane.createSplitSeries({
index, index,
seriesBlueprint, blueprint,
option, id: option.id,
setMinMaxMarkersWhenIdle, setMinMaxMarkersWhenIdle,
splitSeries,
dataset, dataset,
}); });
}); });
@@ -415,16 +400,14 @@ export function init({
activeDatasets.set((s) => s); activeDatasets.set((s) => s);
splitSeries.forEach((series) => { chartPane.splitSeries.forEach((series) => {
allSeries.unshift(series);
signals.createEffect(series.active, () => { signals.createEffect(series.active, () => {
setMinMaxMarkersWhenIdle(); setMinMaxMarkersWhenIdle();
}); });
}); });
const chartVisible = signals.createMemo(() => const chartVisible = signals.createMemo(() =>
splitSeries.some((series) => series.visible()), chartPane.splitSeries.some((series) => series.visible()),
); );
function createChartVisibilityEffect() { function createChartVisibilityEffect() {
@@ -504,7 +487,7 @@ export function init({
function createApplyChartOptionEffect() { function createApplyChartOptionEffect() {
signals.createEffect(selected, (option) => { signals.createEffect(selected, (option) => {
chart.reset({ scale: option.scale }); chart.reset({ scale: option.scale, owner: signals.getOwner() });
applyChartOption(option); applyChartOption(option);
}); });
} }
+33 -6
View File
@@ -1,8 +1,9 @@
// @ts-check // @ts-check
/** /**
* @import { Option, ResourceDataset, TimeScale, TimeRange, Unit, Marker, Weighted, DatasetPath, OHLC, FetchedJSON, DatasetValue, FetchedResult, AnyDatasetPath, SeriesBlueprint, BaselineSpecificSeriesBlueprint, CandlestickSpecificSeriesBlueprint, LineSpecificSeriesBlueprint, SpecificSeriesBlueprintWithChart, Color, DatasetCandlestickData, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, AnyPath, SimulationOption, Frequency, CreatePaneParameters, CreateBaselineSeriesParams, CreateCandlestickSeriesParams, CreateLineSeriesParams, LastValues, HoveredLegend, ChartPane, SplitSeries, SingleSeries, CreateSplitSeriesParameters } from "./types/self" * @import { Option, ResourceDataset, TimeScale, TimeRange, Unit, Weighted, DatasetPath, OHLC, FetchedJSON, DatasetValue, FetchedResult, AnyDatasetPath, Color, DatasetCandlestickData, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, AnyPath, SimulationOption, Frequency, LastValues } from "./types/self"
* @import {createChart as CreateClassicChart, createChartEx as CreateCustomChart, LineStyleOptions} from "../packages/lightweight-charts/v4.2.0/types"; * @import {createChart as CreateClassicChart, createChartEx as CreateCustomChart, LineStyleOptions } from "../packages/lightweight-charts/v4.2.0/types";
* @import { Marker, CreatePaneParameters, HoveredLegend, ChartPane, SplitSeries, SingleSeries, CreateSplitSeriesParameters, LineSeriesBlueprint, CandlestickSeriesBlueprint, BaselineSeriesBlueprint, CreateBaseSeriesParameters, BaseSeries, RemoveSeriesBlueprintFluff, SplitSeriesBlueprint } from "../packages/lightweight-charts/types";
* @import * as _ from "../packages/ufuzzy/v1.0.14/types" * @import * as _ from "../packages/ufuzzy/v1.0.14/types"
* @import { DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, SingleValueData, ISeriesApi, Time, LineData, LogicalRange, SeriesMarker, CandlestickData, SeriesType, BaselineStyleOptions, SeriesOptionsCommon } from "../packages/lightweight-charts/v4.2.0/types" * @import { DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, SingleValueData, ISeriesApi, Time, LineData, LogicalRange, SeriesMarker, CandlestickData, SeriesType, BaselineStyleOptions, SeriesOptionsCommon } from "../packages/lightweight-charts/v4.2.0/types"
* @import { DatePath, HeightPath, LastPath } from "./types/paths"; * @import { DatePath, HeightPath, LastPath } from "./types/paths";
@@ -232,6 +233,7 @@ const utils = {
* @param {string} args.inputValue * @param {string} args.inputValue
* @param {boolean} [args.inputChecked=false] * @param {boolean} [args.inputChecked=false]
* @param {string} args.labelTitle * @param {string} args.labelTitle
* @param {'solo' | 'multi'} args.type
* @param {(event: MouseEvent) => void} [args.onClick] * @param {(event: MouseEvent) => void} [args.onClick]
*/ */
createLabeledInput({ createLabeledInput({
@@ -241,23 +243,27 @@ const utils = {
inputChecked = false, inputChecked = false,
labelTitle, labelTitle,
onClick, onClick,
type,
}) { }) {
const label = window.document.createElement("label"); const label = window.document.createElement("label");
inputId = inputId.toLowerCase(); inputId = inputId.toLowerCase();
const input = window.document.createElement("input"); const input = window.document.createElement("input");
input.type = "radio"; if (type === "multi") {
input.name = inputName; input.type = "radio";
input.name = inputName;
} else {
input.type = "checkbox";
}
input.id = inputId; input.id = inputId;
input.value = inputValue; input.value = inputValue;
input.checked = inputChecked; input.checked = inputChecked;
label.append(input); label.append(input);
label.id = `${inputId}-label`; label.id = `${inputId}-label`;
// @ts-ignore
label.for = inputId;
label.title = labelTitle; label.title = labelTitle;
label.htmlFor = inputId;
if (onClick) { if (onClick) {
label.addEventListener("click", onClick); label.addEventListener("click", onClick);
@@ -362,6 +368,7 @@ const utils = {
inputValue, inputValue,
inputChecked: inputValue === selected, inputChecked: inputValue === selected,
labelTitle: choice, labelTitle: choice,
type: "multi",
}); });
const text = window.document.createTextNode(choice); const text = window.document.createTextNode(choice);
@@ -835,6 +842,26 @@ const utils = {
return new Date(v); return new Date(v);
}, },
}, },
boolean: {
/**
* @param {boolean} v
*/
serialize(v) {
return String(v);
},
/**
* @param {string} v
*/
deserialize(v) {
if (v === "true") {
return true;
} else if (v === "false") {
return false;
} else {
throw "deser bool err";
}
},
},
}, },
formatters: { formatters: {
dollars: new Intl.NumberFormat("en-US", { dollars: new Intl.NumberFormat("en-US", {
+42 -40
View File
@@ -1,7 +1,8 @@
// @ts-check // @ts-check
/** /**
* @import { AnySpecificSeriesBlueprint, CohortOption, CohortOptions, Color, DefaultCohortOption, DefaultCohortOptions, OptionPath, OptionsGroup, PartialChartOption, PartialOptionsGroup, PartialOptionsTree, RatioOption, RatioOptions, SplitSeries, SeriesBlueprint, SeriesBlueprintParam, SeriesBluePrintType, TimeScale } from "./types/self" * @import { CohortOption, CohortOptions, Color, DefaultCohortOption, DefaultCohortOptions, OptionPath, OptionsGroup, PartialChartOption, PartialOptionsGroup, PartialOptionsTree, RatioOption, RatioOptions, TimeScale } from "./types/self"
* @import {AnySpecificSeriesBlueprint, SplitSeriesBlueprint} from '../packages/lightweight-charts/types';
*/ */
const DATE_TO_PREFIX = "date-to-"; const DATE_TO_PREFIX = "date-to-";
@@ -2819,12 +2820,12 @@ function createPartialOptions(colors) {
/** /**
* @template {AnyPossibleCohortId} T * @template {AnyPossibleCohortId} T
* @param {CohortOption<T> | CohortOptions<T>} arg * @param {CohortOption<T> | CohortOptions<T>} arg
* @param {SeriesBlueprintParam<T> & Omit<AnySpecificSeriesBlueprint, 'color'>} blueprint * @param {{ title: string; singleColor?: Color; genPath: (id: T, scale: TimeScale) => AnyDatasetPath} & Omit<AnySpecificSeriesBlueprint, 'color'>} blueprint
*/ */
toSeriesBlueprints(arg, blueprint) { generateSeriesBlueprints(arg, blueprint) {
return this.toList(arg).map( return this.toList(arg).map(
({ scale, datasetId, color, name }) => ({ scale, datasetId, color, name }) =>
/** @satisfies {SeriesBlueprint} */ ({ /** @satisfies {SplitSeriesBlueprint} */ ({
title: cohortOptionOrOptions.toLegendName( title: cohortOptionOrOptions.toLegendName(
arg, arg,
name, name,
@@ -2886,7 +2887,7 @@ function createPartialOptions(colors) {
title: `Number Of ${title} Unspent Transaction Outputs`, title: `Number Of ${title} Unspent Transaction Outputs`,
description: "", description: "",
unit: "Count", unit: "Count",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Count", title: "Count",
genPath: (id, scale) => genPath: (id, scale) =>
/** @type {const} */ ( /** @type {const} */ (
@@ -2932,7 +2933,7 @@ function createPartialOptions(colors) {
title: `${title} Realized Price`, title: `${title} Realized Price`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
top: cohortOptionOrOptions.toSeriesBlueprints(arg, { top: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Realized Price", title: "Realized Price",
genPath: (id, scale) => genPath: (id, scale) =>
/** @type {const} */ ( /** @type {const} */ (
@@ -2955,7 +2956,7 @@ function createPartialOptions(colors) {
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: [ bottom: [
...cohortOptionOrOptions.toSeriesBlueprints( ...cohortOptionOrOptions.generateSeriesBlueprints(
arg, arg,
{ {
@@ -2985,7 +2986,7 @@ function createPartialOptions(colors) {
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: [ bottom: [
...cohortOptionOrOptions.toSeriesBlueprints( ...cohortOptionOrOptions.generateSeriesBlueprints(
arg, arg,
{ {
@@ -3006,7 +3007,7 @@ function createPartialOptions(colors) {
title: `${title} Realized Profit`, title: `${title} Realized Profit`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Realized Profit", title: "Realized Profit",
singleColor: colors.profit, singleColor: colors.profit,
genPath: (id, scale) => { genPath: (id, scale) => {
@@ -3023,7 +3024,7 @@ function createPartialOptions(colors) {
title: `${title} Realized Loss`, title: `${title} Realized Loss`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Realized Loss", title: "Realized Loss",
singleColor: colors.loss, singleColor: colors.loss,
genPath: (id, scale) => { genPath: (id, scale) => {
@@ -3071,7 +3072,7 @@ function createPartialOptions(colors) {
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: [ bottom: [
...cohortOptionOrOptions.toSeriesBlueprints(arg, { ...cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Net PNL", title: "Net PNL",
type: "Baseline", type: "Baseline",
genPath: (id, scale) => { genPath: (id, scale) => {
@@ -3092,7 +3093,7 @@ function createPartialOptions(colors) {
description: "", description: "",
unit: "Percentage", unit: "Percentage",
bottom: [ bottom: [
...cohortOptionOrOptions.toSeriesBlueprints(arg, { ...cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Net", title: "Net",
type: "Baseline", type: "Baseline",
genPath: (id) => genPath: (id) =>
@@ -3113,7 +3114,7 @@ function createPartialOptions(colors) {
title: `${title} Cumulative Realized Profit`, title: `${title} Cumulative Realized Profit`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Cumulative Realized Profit", title: "Cumulative Realized Profit",
singleColor: colors.profit, singleColor: colors.profit,
genPath: (id, scale) => genPath: (id, scale) =>
@@ -3128,7 +3129,7 @@ function createPartialOptions(colors) {
title: `${title} Cumulative Realized Loss`, title: `${title} Cumulative Realized Loss`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Cumulative Realized Loss", title: "Cumulative Realized Loss",
singleColor: colors.loss, singleColor: colors.loss,
genPath: (id, scale) => genPath: (id, scale) =>
@@ -3144,7 +3145,7 @@ function createPartialOptions(colors) {
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: [ bottom: [
...cohortOptionOrOptions.toSeriesBlueprints(arg, { ...cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Cumulative Net Realized PNL", title: "Cumulative Net Realized PNL",
type: "Baseline", type: "Baseline",
genPath: (id, scale) => genPath: (id, scale) =>
@@ -3162,7 +3163,7 @@ function createPartialOptions(colors) {
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: [ bottom: [
...cohortOptionOrOptions.toSeriesBlueprints(arg, { ...cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Cumulative Net Realized PNL 30d Change", title: "Cumulative Net Realized PNL 30d Change",
type: "Baseline", type: "Baseline",
genPath: (id, scale) => genPath: (id, scale) =>
@@ -3182,7 +3183,7 @@ function createPartialOptions(colors) {
description: "", description: "",
unit: "Ratio", unit: "Ratio",
bottom: [ bottom: [
...cohortOptionOrOptions.toSeriesBlueprints(arg, { ...cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Ratio", title: "Ratio",
type: "Baseline", type: "Baseline",
options: { options: {
@@ -3210,7 +3211,7 @@ function createPartialOptions(colors) {
description: "", description: "",
unit: "Percentage", unit: "Percentage",
bottom: [ bottom: [
...cohortOptionOrOptions.toSeriesBlueprints(arg, { ...cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "SOPR", title: "SOPR",
type: "Baseline", type: "Baseline",
options: { options: {
@@ -3233,7 +3234,7 @@ function createPartialOptions(colors) {
description: "", description: "",
unit: "Percentage", unit: "Percentage",
bottom: [ bottom: [
...cohortOptionOrOptions.toSeriesBlueprints(arg, { ...cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "aSOPR", title: "aSOPR",
type: "Baseline", type: "Baseline",
options: { options: {
@@ -3263,7 +3264,7 @@ function createPartialOptions(colors) {
title: `${title} Value Created`, title: `${title} Value Created`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Value", title: "Value",
singleColor: colors.profit, singleColor: colors.profit,
genPath: (id, scale) => { genPath: (id, scale) => {
@@ -3280,7 +3281,7 @@ function createPartialOptions(colors) {
title: `${title} Adjusted Value Created`, title: `${title} Adjusted Value Created`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Adjusted Value", title: "Adjusted Value",
singleColor: colors.profit, singleColor: colors.profit,
genPath: (id, scale) => { genPath: (id, scale) => {
@@ -3302,7 +3303,7 @@ function createPartialOptions(colors) {
title: `${title} Value Destroyed`, title: `${title} Value Destroyed`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Value", title: "Value",
singleColor: colors.loss, singleColor: colors.loss,
genPath: (id, scale) => { genPath: (id, scale) => {
@@ -3319,7 +3320,7 @@ function createPartialOptions(colors) {
title: `${title} Adjusted Value Destroyed`, title: `${title} Adjusted Value Destroyed`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Adjusted Value", title: "Adjusted Value",
singleColor: colors.loss, singleColor: colors.loss,
genPath: (id, scale) => { genPath: (id, scale) => {
@@ -3341,7 +3342,7 @@ function createPartialOptions(colors) {
title: `${title} Sell Side Risk Ratio`, title: `${title} Sell Side Risk Ratio`,
description: "", description: "",
unit: "Percentage", unit: "Percentage",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Ratio", title: "Ratio",
genPath: (id) => genPath: (id) =>
`${scale}-to-${datasetIdToPrefix(id)}sell-side-risk-ratio`, `${scale}-to-${datasetIdToPrefix(id)}sell-side-risk-ratio`,
@@ -3368,7 +3369,7 @@ function createPartialOptions(colors) {
title: `${title} Unrealized Profit`, title: `${title} Unrealized Profit`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Profit", title: "Profit",
genPath: (id) => genPath: (id) =>
`${scale}-to-${datasetIdToPrefix(id)}unrealized-profit`, `${scale}-to-${datasetIdToPrefix(id)}unrealized-profit`,
@@ -3381,7 +3382,7 @@ function createPartialOptions(colors) {
title: `${title} Unrealized Loss`, title: `${title} Unrealized Loss`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Loss", title: "Loss",
genPath: (id) => genPath: (id) =>
`${scale}-to-${datasetIdToPrefix(id)}unrealized-loss`, `${scale}-to-${datasetIdToPrefix(id)}unrealized-loss`,
@@ -3419,7 +3420,7 @@ function createPartialOptions(colors) {
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
bottom: [ bottom: [
...cohortOptionOrOptions.toSeriesBlueprints(arg, { ...cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Net Unrealized PNL", title: "Net Unrealized PNL",
genPath: (id) => genPath: (id) =>
`${scale}-to-${datasetIdToPrefix( `${scale}-to-${datasetIdToPrefix(
@@ -3437,7 +3438,7 @@ function createPartialOptions(colors) {
description: "", description: "",
unit: "Percentage", unit: "Percentage",
bottom: [ bottom: [
...cohortOptionOrOptions.toSeriesBlueprints(arg, { ...cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Relative Net Unrealized PNL", title: "Relative Net Unrealized PNL",
genPath: (id) => genPath: (id) =>
`${scale}-to-${datasetIdToPrefix( `${scale}-to-${datasetIdToPrefix(
@@ -3505,7 +3506,7 @@ function createPartialOptions(colors) {
title: `${title} Total supply`, title: `${title} Total supply`,
description: "", description: "",
unit: "Bitcoin", unit: "Bitcoin",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Supply", title: "Supply",
genPath: (id) => `${scale}-to-${datasetIdToPrefix(id)}supply`, genPath: (id) => `${scale}-to-${datasetIdToPrefix(id)}supply`,
}), }),
@@ -3516,7 +3517,7 @@ function createPartialOptions(colors) {
title: `${title} Supply In Profit`, title: `${title} Supply In Profit`,
description: "", description: "",
unit: "Bitcoin", unit: "Bitcoin",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Supply", title: "Supply",
singleColor: colors.profit, singleColor: colors.profit,
genPath: (id) => genPath: (id) =>
@@ -3529,7 +3530,7 @@ function createPartialOptions(colors) {
title: `${title} Supply In Loss`, title: `${title} Supply In Loss`,
description: "", description: "",
unit: "Bitcoin", unit: "Bitcoin",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Supply", title: "Supply",
singleColor: colors.loss, singleColor: colors.loss,
genPath: (id) => genPath: (id) =>
@@ -3581,7 +3582,7 @@ function createPartialOptions(colors) {
title: `${title} Total supply Relative To Circulating Supply`, title: `${title} Total supply Relative To Circulating Supply`,
description: "", description: "",
unit: "Percentage", unit: "Percentage",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Supply", title: "Supply",
genPath: (id) => genPath: (id) =>
`${scale}-to-${datasetIdToPrefix( `${scale}-to-${datasetIdToPrefix(
@@ -3595,7 +3596,7 @@ function createPartialOptions(colors) {
title: `${title} Supply In Profit Relative To Circulating Supply`, title: `${title} Supply In Profit Relative To Circulating Supply`,
description: "", description: "",
unit: "Percentage", unit: "Percentage",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Supply", title: "Supply",
singleColor: colors.profit, singleColor: colors.profit,
genPath: (id) => genPath: (id) =>
@@ -3610,7 +3611,7 @@ function createPartialOptions(colors) {
title: `${title} Supply In Loss Relative To Circulating Supply`, title: `${title} Supply In Loss Relative To Circulating Supply`,
description: "", description: "",
unit: "Percentage", unit: "Percentage",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Supply", title: "Supply",
singleColor: colors.loss, singleColor: colors.loss,
genPath: (id) => genPath: (id) =>
@@ -3668,7 +3669,7 @@ function createPartialOptions(colors) {
title: `${title} Supply In Profit Relative To Own Supply`, title: `${title} Supply In Profit Relative To Own Supply`,
description: "", description: "",
unit: "Percentage", unit: "Percentage",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Supply", title: "Supply",
singleColor: colors.profit, singleColor: colors.profit,
genPath: (id) => genPath: (id) =>
@@ -3683,7 +3684,7 @@ function createPartialOptions(colors) {
title: `${title} Supply In Loss Relative To Own Supply`, title: `${title} Supply In Loss Relative To Own Supply`,
description: "", description: "",
unit: "Percentage", unit: "Percentage",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Supply", title: "Supply",
singleColor: colors.loss, singleColor: colors.loss,
genPath: (id) => genPath: (id) =>
@@ -3732,7 +3733,7 @@ function createPartialOptions(colors) {
title: `${title} Average Price Paid - Realized Price`, title: `${title} Average Price Paid - Realized Price`,
description: "", description: "",
unit: "US Dollars", unit: "US Dollars",
top: cohortOptionOrOptions.toSeriesBlueprints(arg, { top: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Average", title: "Average",
genPath: (id) => genPath: (id) =>
`${scale}-to-${datasetIdToPrefix(id)}realized-price`, `${scale}-to-${datasetIdToPrefix(id)}realized-price`,
@@ -3768,7 +3769,7 @@ function createPartialOptions(colors) {
title: `${title} ${percentile.title}`, title: `${title} ${percentile.title}`,
description: "", description: "",
unit: /** @type {const} */ ("US Dollars"), unit: /** @type {const} */ ("US Dollars"),
top: cohortOptionOrOptions.toSeriesBlueprints(arg, { top: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Relative Net Unrealized PNL", title: "Relative Net Unrealized PNL",
genPath: (id) => genPath: (id) =>
generatePath({ generatePath({
@@ -3923,7 +3924,7 @@ function createPartialOptions(colors) {
title: `${title} Address Count`, title: `${title} Address Count`,
description: "", description: "",
unit: "Count", unit: "Count",
bottom: cohortOptionOrOptions.toSeriesBlueprints(arg, { bottom: cohortOptionOrOptions.generateSeriesBlueprints(arg, {
title: "Address Count", title: "Address Count",
genPath: (id) => `${scale}-to-${id}-address-count`, genPath: (id) => `${scale}-to-${id}-address-count`,
}), }),
@@ -5199,7 +5200,7 @@ export function initOptions({
const optionsIds = env.localhost ? [] : undefined; const optionsIds = env.localhost ? [] : undefined;
/** /**
* @param {SeriesBlueprint[]} array * @param {SplitSeriesBlueprint[]} array
*/ */
function getMainIdFromBlueprints(array) { function getMainIdFromBlueprints(array) {
const searchArray = array.filter( const searchArray = array.filter(
@@ -5316,6 +5317,7 @@ export function initOptions({
onClick: () => { onClick: () => {
selected.set(option); selected.set(option);
}, },
type: "multi",
}); });
const anchor = utils.dom.createAnchorElement({ const anchor = utils.dom.createAnchorElement({
+46 -57
View File
@@ -33,15 +33,15 @@ export function init({
const frequencies = computeFrequencies(); const frequencies = computeFrequencies();
const storagePrefix = "save-in-bitcoin"; const keyPrefix = "save-in-bitcoin";
const settings = { const settings = {
dollars: { dollars: {
initial: { initial: {
amount: signals.createSignal(/** @type {number | null} */ (1000), { amount: signals.createSignal(/** @type {number | null} */ (1000), {
save: { save: {
...utils.serde.number, ...utils.serde.number,
id: `${storagePrefix}-initial-amount`, keyPrefix,
param: "initial-amount", key: "initial-amount",
}, },
}), }),
}, },
@@ -49,8 +49,8 @@ export function init({
amount: signals.createSignal(/** @type {number | null} */ (150), { amount: signals.createSignal(/** @type {number | null} */ (150), {
save: { save: {
...utils.serde.number, ...utils.serde.number,
id: `${storagePrefix}-top-up-amount`, keyPrefix,
param: "top-up-amount", key: "top-up-amount",
}, },
}), }),
frenquency: signals.createSignal( frenquency: signals.createSignal(
@@ -58,8 +58,8 @@ export function init({
{ {
save: { save: {
...frequencies.serde, ...frequencies.serde,
id: `${storagePrefix}-top-up-freq`, keyPrefix,
param: "top-up-freq", key: "top-up-freq",
}, },
}, },
), ),
@@ -70,15 +70,15 @@ export function init({
initial: signals.createSignal(/** @type {number | null} */ (1000), { initial: signals.createSignal(/** @type {number | null} */ (1000), {
save: { save: {
...utils.serde.number, ...utils.serde.number,
id: `${storagePrefix}-initial-swap`, keyPrefix,
param: "initial-swap", key: "initial-swap",
}, },
}), }),
recurrent: signals.createSignal(/** @type {number | null} */ (5), { recurrent: signals.createSignal(/** @type {number | null} */ (5), {
save: { save: {
...utils.serde.number, ...utils.serde.number,
id: `${storagePrefix}-recurrent-swap`, keyPrefix,
param: "recurrent-swap", key: "recurrent-swap",
}, },
}), }),
frequency: signals.createSignal( frequency: signals.createSignal(
@@ -86,8 +86,8 @@ export function init({
{ {
save: { save: {
...frequencies.serde, ...frequencies.serde,
id: `${storagePrefix}-swap-freq`, keyPrefix,
param: "swap-freq", key: "swap-freq",
}, },
}, },
), ),
@@ -99,16 +99,16 @@ export function init({
{ {
save: { save: {
...utils.serde.date, ...utils.serde.date,
id: `${storagePrefix}-interval-start`, keyPrefix,
param: "interval-start", key: "interval-start",
}, },
}, },
), ),
end: signals.createSignal(/** @type {Date | null} */ (new Date()), { end: signals.createSignal(/** @type {Date | null} */ (new Date()), {
save: { save: {
...utils.serde.date, ...utils.serde.date,
id: `${storagePrefix}-interval-end`, keyPrefix,
param: "interval-end", key: "interval-end",
}, },
}), }),
}, },
@@ -116,8 +116,8 @@ export function init({
percentage: signals.createSignal(/** @type {number | null} */ (0.25), { percentage: signals.createSignal(/** @type {number | null} */ (0.25), {
save: { save: {
...utils.serde.number, ...utils.serde.number,
id: `${storagePrefix}-percentage`, keyPrefix,
param: "percentage", key: "percentage",
}, },
}), }),
}, },
@@ -652,32 +652,28 @@ export function init({
unit: "US Dollars", unit: "US Dollars",
config: [ config: [
{ {
title: "Bitcoin Value", title: "Fees Paid",
kind: "line", type: "Line",
color: colors.amber, color: colors.rose,
owner, data: () => totalFeesPaidData,
data: bitcoinValueData,
}, },
{ {
title: "Dollars Left", title: "Dollars Left",
kind: "line", type: "Line",
color: colors.offDollars, color: colors.offDollars,
owner, data: () => dollarsLeftData,
data: dollarsLeftData,
}, },
{ {
title: "Dollars Converted", title: "Dollars Converted",
kind: "line", type: "Line",
color: colors.dollars, color: colors.dollars,
owner, data: () => totalInvestedAmountData,
data: totalInvestedAmountData,
}, },
{ {
title: "Fees Paid", title: "Bitcoin Value",
kind: "line", type: "Line",
color: colors.rose, color: colors.amber,
owner, data: () => bitcoinValueData,
data: totalFeesPaidData,
}, },
], ],
}, },
@@ -698,10 +694,9 @@ export function init({
config: [ config: [
{ {
title: "Bitcoin Stack", title: "Bitcoin Stack",
kind: "line", type: "Line",
color: colors.bitcoin, color: colors.bitcoin,
owner, data: () => bitcoinData,
data: bitcoinData,
}, },
], ],
}, },
@@ -722,17 +717,15 @@ export function init({
config: [ config: [
{ {
title: "Bitcoin Price", title: "Bitcoin Price",
kind: "line", type: "Line",
owner,
color: colors.default, color: colors.default,
data: bitcoinPriceData, data: () => bitcoinPriceData,
}, },
{ {
title: "Average Price Paid", title: "Average Price Paid",
kind: "line", type: "Line",
owner,
color: colors.lightDollars, color: colors.lightDollars,
data: averagePricePaidData, data: () => averagePricePaidData,
}, },
], ],
}, },
@@ -753,9 +746,8 @@ export function init({
config: [ config: [
{ {
title: "Return Of Investment", title: "Return Of Investment",
kind: "baseline", type: "Baseline",
owner, data: () => resultData,
data: resultData,
// TODO: Doesn't work for some reason // TODO: Doesn't work for some reason
// options: { // options: {
// baseLineColor: "#888", // baseLineColor: "#888",
@@ -780,23 +772,22 @@ export function init({
kind: "static", kind: "static",
scale: "date", scale: "date",
utils, utils,
owner,
config: [ config: [
{ {
unit: "Percentage", unit: "Percentage",
config: [ config: [
{ {
title: "Unprofitable Days Ratio", title: "Unprofitable Days Ratio",
kind: "line", type: "Line",
owner,
color: colors.red, color: colors.red,
data: unprofitableDaysRatioData, data: () => unprofitableDaysRatioData,
}, },
{ {
title: "Profitable Days Ratio", title: "Profitable Days Ratio",
kind: "line", type: "Line",
owner,
color: colors.green, color: colors.green,
data: profitableDaysRatioData, data: () => profitableDaysRatioData,
}, },
], ],
}, },
@@ -818,8 +809,7 @@ function createInputField({ name, input }) {
const label = window.document.createElement("label"); const label = window.document.createElement("label");
div.append(label); div.append(label);
// @ts-ignore label.htmlFor = input.id;
label.for = input.id;
label.innerHTML = name; label.innerHTML = name;
div.append(input); div.append(input);
@@ -856,8 +846,7 @@ function createFieldElement({ title, description, input }) {
throw `Input should've an ID`; throw `Input should've an ID`;
} }
// @ts-ignore label.htmlFor = forId;
label.for = forId;
return div; return div;
} }
+2 -134
View File
@@ -17,7 +17,6 @@ import {
BaselineData, BaselineData,
} from "../../packages/lightweight-charts/v4.2.0/types"; } from "../../packages/lightweight-charts/v4.2.0/types";
import { DatePath, HeightPath, LastPath } from "./paths"; import { DatePath, HeightPath, LastPath } from "./paths";
import { Owner } from "../../packages/solid-signals/2024-11-02/types/core/owner";
import { AnyPossibleCohortId } from "../options"; import { AnyPossibleCohortId } from "../options";
import { Signal } from "../../packages/solid-signals/types"; import { Signal } from "../../packages/solid-signals/types";
@@ -42,53 +41,6 @@ type AnyPath = AnyDatasetPath | LastPath;
type Color = () => string; type Color = () => string;
type ColorName = keyof Colors; type ColorName = keyof Colors;
interface BaselineSpecificSeriesBlueprint {
type: "Baseline";
color?: Color;
options?: DeepPartial<BaselineStyleOptions & SeriesOptionsCommon>;
data?: BaselineData<Time>[];
}
interface CandlestickSpecificSeriesBlueprint {
type: "Candlestick";
color?: Color;
options?: DeepPartial<CandlestickStyleOptions & SeriesOptionsCommon>;
data?: CandlestickData<Time>[];
}
interface LineSpecificSeriesBlueprint {
type?: "Line";
color: Color;
options?: DeepPartial<LineStyleOptions & SeriesOptionsCommon>;
data?: LineData<Time>[];
}
type AnySpecificSeriesBlueprint =
| BaselineSpecificSeriesBlueprint
| CandlestickSpecificSeriesBlueprint
| LineSpecificSeriesBlueprint;
type SpecificSeriesBlueprintWithChart<A extends AnySpecificSeriesBlueprint> = {
owner: Owner | null;
} & Omit<A, "type">;
type CreateBaselineSeriesParams =
SpecificSeriesBlueprintWithChart<BaselineSpecificSeriesBlueprint>;
type CreateLineSeriesParams =
SpecificSeriesBlueprintWithChart<LineSpecificSeriesBlueprint>;
type CreateCandlestickSeriesParams =
SpecificSeriesBlueprintWithChart<CandlestickSpecificSeriesBlueprint>;
type SeriesBlueprint = {
datasetPath: AnyDatasetPath;
title: string;
defaultActive?: boolean;
main?: boolean;
formatNumber?: false;
} & AnySpecificSeriesBlueprint;
type SeriesBluePrintType = NonNullable<SeriesBlueprint["type"]>;
type Unit = type Unit =
| "" | ""
| "Bitcoin" | "Bitcoin"
@@ -137,8 +89,8 @@ interface PartialChartOption extends PartialOption {
shortTitle?: string; shortTitle?: string;
unit: Unit; unit: Unit;
description: string; description: string;
top?: SeriesBlueprint[]; top?: SplitSeriesBlueprint[];
bottom?: SeriesBlueprint[]; bottom?: SplitSeriesBlueprint[];
dashboard?: { dashboard?: {
ignoreName?: boolean; ignoreName?: boolean;
skip?: boolean; skip?: boolean;
@@ -240,7 +192,6 @@ interface ResourceDataset<
fetch: (id: number) => Promise<void>; fetch: (id: number) => Promise<void>;
fetchRange: (start: number, end: number) => Promise<void[]>; fetchRange: (start: number, end: number) => Promise<void[]>;
fetchedJSONs: FetchedResult<Scale, Type>[]; fetchedJSONs: FetchedResult<Scale, Type>[];
// drop: VoidFunction;
} }
type ValuedCandlestickData = CandlestickData & Valued; type ValuedCandlestickData = CandlestickData & Valued;
@@ -299,50 +250,12 @@ interface FetchedHeightDataset<Type> extends Versioned {
map: Type[]; map: Type[];
} }
type PriceSeriesType = "Candlestick" | "Line";
interface _Series {
id: string;
title: string;
color: Color | Color[];
active: Signal<boolean>;
visible: Accessor<boolean>;
}
interface SingleSeries extends _Series {
series: ISeriesApi<SeriesType>;
}
interface SplitSeries extends _Series {
disabled: Accessor<boolean>;
chunks: Array<Accessor<ISeriesApi<SeriesType> | undefined>>;
dataset: ResourceDataset<TimeScale, number>;
}
interface Marker {
weight: number;
time: Time;
value: number;
seriesChunk: ISeriesApi<any>;
}
interface Weighted { interface Weighted {
weight: number; weight: number;
} }
type DatasetCandlestickData = DatasetValue<CandlestickData> & { year: number }; type DatasetCandlestickData = DatasetValue<CandlestickData> & { year: number };
declare global {
interface Window {
MyNamespace: any;
}
}
interface HoveredLegend {
label: HTMLLabelElement;
series: SingleSeries | SplitSeries;
}
type NotFunction<T> = T extends Function ? never : T; type NotFunction<T> = T extends Function ? never : T;
type Groups = import("../options").Groups; type Groups = import("../options").Groups;
@@ -367,12 +280,6 @@ interface CohortOptions<Id extends AnyPossibleCohortId> {
list: CohortOption<Id>[]; list: CohortOption<Id>[];
} }
interface SeriesBlueprintParam<T> {
title: string;
singleColor?: Color;
genPath: (id: T, scale: TimeScale) => AnyDatasetPath;
}
interface RatioOption { interface RatioOption {
scale: TimeScale; scale: TimeScale;
color: Color; color: Color;
@@ -394,43 +301,4 @@ interface Frequency {
} }
type Frequencies = { name: string; list: Frequency[] }; type Frequencies = { name: string; list: Frequency[] };
interface CreatePaneParameters {
unit: Unit;
paneIndex?: number;
whitespace?: true;
options?: DeepPartial<ChartOptions>;
config?: (
| ({ kind: "line"; title: string } & CreateLineSeriesParams)
| ({ kind: "candle"; title: string } & CreateCandlestickSeriesParams)
| ({ kind: "baseline"; title: string } & CreateBaselineSeriesParams)
)[];
}
interface CreateSplitSeriesParameters<S extends TimeScale> {
dataset: ResourceDataset<S>;
seriesBlueprint: SeriesBlueprint;
option: Option;
index: number;
splitSeries: SplitSeries[];
setMinMaxMarkersWhenIdle: VoidFunction;
disabled?: Accessor<boolean>;
}
type ChartPane = IChartApi & {
whitespace: ISeriesApi<"Line">;
createBaseLineSeries: (
a: CreateBaselineSeriesParams,
) => ISeriesApi<"Baseline">;
createCandlesticksSeries: (
a: CreateCandlestickSeriesParams,
) => ISeriesApi<"Candlestick">;
createLineSeries: (a: CreateLineSeriesParams) => ISeriesApi<"Line">;
hidden: () => boolean;
setHidden: (b: boolean) => void;
setInitialVisibleTimeRange: VoidFunction;
createSplitSeries: <S extends TimeScale>(
a: CreateSplitSeriesParameters<S>,
) => SplitSeries;
};
type LastValues = Record<LastPath, number> | null; type LastValues = Record<LastPath, number> | null;
-54
View File
@@ -28,60 +28,6 @@
flex: 1; flex: 1;
margin-top: 2rem; margin-top: 2rem;
display: flex;
flex-direction: column;
min-height: 0;
> legend {
display: flex;
align-items: center;
gap: 1.5rem;
margin-left: var(--negative-main-padding);
margin-right: var(--negative-main-padding);
margin-bottom: 0.5rem;
padding-left: var(--main-padding);
padding-right: var(--main-padding);
padding-bottom: 0.75rem;
overflow-x: auto;
min-width: 0;
> div {
flex: 0;
display: flex;
align-items: center;
> label {
margin: -0.375rem 0;
color: var(--color);
&:has(input:not(:checked)) {
color: var(--off-color);
> span.main > span.name {
text-decoration-thickness: 1.5px;
text-decoration-color: var(--color);
text-decoration-line: line-through;
}
&:hover {
* {
color: var(--off-color) !important;
}
> span.main > span.name {
text-decoration-color: var(--orange) !important;
}
}
}
}
> a {
padding: 0.375rem;
margin: -0.375rem;
}
}
}
.lightweight-chart { .lightweight-chart {
margin-left: var(--negative-main-padding); margin-left: var(--negative-main-padding);
} }