mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-30 06:02:10 -07:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 31110a740d | |||
| b64d8b1d7f | |||
| c46006aacc | |||
| 92f81b1493 | |||
| 70213cfc8f | |||
| 8a82bf5c50 | |||
| 37405384a2 | |||
| 54ea6cc53b | |||
| 339c00d815 | |||
| ea6b4dcde2 | |||
| 2b84623d1e | |||
| c8b3afa56b | |||
| 1348f3c24c | |||
| 62208ce3e1 |
Generated
+18
-18
@@ -388,7 +388,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk"
|
name = "brk"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brk_cli",
|
"brk_cli",
|
||||||
"brk_computer",
|
"brk_computer",
|
||||||
@@ -407,7 +407,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_cli"
|
name = "brk_cli"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoincore-rpc",
|
"bitcoincore-rpc",
|
||||||
"brk_computer",
|
"brk_computer",
|
||||||
@@ -432,7 +432,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_computer"
|
name = "brk_computer"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
"bitcoincore-rpc",
|
"bitcoincore-rpc",
|
||||||
@@ -453,7 +453,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_core"
|
name = "brk_core"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
@@ -474,7 +474,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_exit"
|
name = "brk_exit"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brk_logger",
|
"brk_logger",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
@@ -483,7 +483,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_fetcher"
|
name = "brk_fetcher"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brk_core",
|
"brk_core",
|
||||||
"brk_logger",
|
"brk_logger",
|
||||||
@@ -496,7 +496,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_indexer"
|
name = "brk_indexer"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
"bitcoincore-rpc",
|
"bitcoincore-rpc",
|
||||||
@@ -514,7 +514,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_logger"
|
name = "brk_logger"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
@@ -524,7 +524,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_parser"
|
name = "brk_parser"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
"bitcoincore-rpc",
|
"bitcoincore-rpc",
|
||||||
@@ -539,7 +539,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_query"
|
name = "brk_query"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brk_computer",
|
"brk_computer",
|
||||||
"brk_core",
|
"brk_core",
|
||||||
@@ -557,7 +557,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_server"
|
name = "brk_server"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"bitcoincore-rpc",
|
"bitcoincore-rpc",
|
||||||
@@ -586,7 +586,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_state"
|
name = "brk_state"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"brk_core",
|
"brk_core",
|
||||||
@@ -600,7 +600,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_store"
|
name = "brk_store"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"brk_core",
|
"brk_core",
|
||||||
@@ -610,7 +610,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_vec"
|
name = "brk_vec"
|
||||||
version = "0.0.49"
|
version = "0.0.54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"brk_core",
|
"brk_core",
|
||||||
@@ -1698,9 +1698,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memmap2"
|
name = "memmap2"
|
||||||
@@ -3352,9 +3352,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
checksum = "d3bfe459f85da17560875b8bf1423d6f113b7a87a5d942e7da0ac71be7c61f8b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
|
|||||||
+14
-14
@@ -4,7 +4,7 @@ members = ["crates/*"]
|
|||||||
package.description = "The Bitcoin Research Kit is a suite of tools designed to extract, compute and display data stored on a Bitcoin Core node"
|
package.description = "The Bitcoin Research Kit is a suite of tools designed to extract, compute and display data stored on a Bitcoin Core node"
|
||||||
package.license = "MIT"
|
package.license = "MIT"
|
||||||
package.edition = "2024"
|
package.edition = "2024"
|
||||||
package.version = "0.0.49"
|
package.version = "0.0.54"
|
||||||
package.homepage = "https://bitcoinresearchkit.org"
|
package.homepage = "https://bitcoinresearchkit.org"
|
||||||
package.repository = "https://github.com/bitcoinresearchkit/brk"
|
package.repository = "https://github.com/bitcoinresearchkit/brk"
|
||||||
|
|
||||||
@@ -22,19 +22,19 @@ axum = "0.8.4"
|
|||||||
bincode = { version = "2.0.1", features = ["serde"] }
|
bincode = { version = "2.0.1", features = ["serde"] }
|
||||||
bitcoin = { version = "0.32.6", features = ["serde"] }
|
bitcoin = { version = "0.32.6", features = ["serde"] }
|
||||||
bitcoincore-rpc = "0.19.0"
|
bitcoincore-rpc = "0.19.0"
|
||||||
brk_cli = { version = "0.0.49", path = "crates/brk_cli" }
|
brk_cli = { version = "0.0.54", path = "crates/brk_cli" }
|
||||||
brk_computer = { version = "0.0.49", path = "crates/brk_computer" }
|
brk_computer = { version = "0.0.54", path = "crates/brk_computer" }
|
||||||
brk_core = { version = "0.0.49", path = "crates/brk_core" }
|
brk_core = { version = "0.0.54", path = "crates/brk_core" }
|
||||||
brk_exit = { version = "0.0.49", path = "crates/brk_exit" }
|
brk_exit = { version = "0.0.54", path = "crates/brk_exit" }
|
||||||
brk_fetcher = { version = "0.0.49", path = "crates/brk_fetcher" }
|
brk_fetcher = { version = "0.0.54", path = "crates/brk_fetcher" }
|
||||||
brk_indexer = { version = "0.0.49", path = "crates/brk_indexer" }
|
brk_indexer = { version = "0.0.54", path = "crates/brk_indexer" }
|
||||||
brk_logger = { version = "0.0.49", path = "crates/brk_logger" }
|
brk_logger = { version = "0.0.54", path = "crates/brk_logger" }
|
||||||
brk_parser = { version = "0.0.49", path = "crates/brk_parser" }
|
brk_parser = { version = "0.0.54", path = "crates/brk_parser" }
|
||||||
brk_query = { version = "0.0.49", path = "crates/brk_query" }
|
brk_query = { version = "0.0.54", path = "crates/brk_query" }
|
||||||
brk_server = { version = "0.0.49", path = "crates/brk_server" }
|
brk_server = { version = "0.0.54", path = "crates/brk_server" }
|
||||||
brk_state = { version = "0.0.49", path = "crates/brk_state" }
|
brk_state = { version = "0.0.54", path = "crates/brk_state" }
|
||||||
brk_store = { version = "0.0.49", path = "crates/brk_store" }
|
brk_store = { version = "0.0.54", path = "crates/brk_store" }
|
||||||
brk_vec = { version = "0.0.49", path = "crates/brk_vec" }
|
brk_vec = { version = "0.0.54", path = "crates/brk_vec" }
|
||||||
byteview = "=0.6.1"
|
byteview = "=0.6.1"
|
||||||
clap = { version = "4.5.40", features = ["string"] }
|
clap = { version = "4.5.40", features = ["string"] }
|
||||||
clap_derive = "4.5.40"
|
clap_derive = "4.5.40"
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ struct Cli {
|
|||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
enum Commands {
|
enum Commands {
|
||||||
/// Run the indexer, computer and server
|
/// Run the indexer, computer and server, use `run -h` for more information
|
||||||
Run(RunConfig),
|
Run(RunConfig),
|
||||||
/// Query generated datasets via the `run` command in a similar fashion as the server's API
|
/// Query generated datasets via the `run` command in a similar fashion as the server's API, use `query -h` for more information
|
||||||
Query(QueryArgs),
|
Query(QueryArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ pub fn query(params: QueryParams) -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let format = config.format();
|
let format = config.format();
|
||||||
|
|
||||||
let mut indexer = Indexer::new(&config.outputsdir(), format, config.check_collisions())?;
|
let mut indexer = Indexer::new(&config.outputsdir(), config.check_collisions())?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|
||||||
let mut computer = Computer::new(&config.outputsdir(), config.fetcher(), format);
|
let mut computer = Computer::new(&config.outputsdir(), config.fetcher(), format);
|
||||||
|
|||||||
+30
-15
@@ -7,7 +7,7 @@ use std::{
|
|||||||
|
|
||||||
use bitcoincore_rpc::{self, Auth, Client, RpcApi};
|
use bitcoincore_rpc::{self, Auth, Client, RpcApi};
|
||||||
use brk_computer::Computer;
|
use brk_computer::Computer;
|
||||||
use brk_core::{default_bitcoin_path, default_brk_path, dot_brk_path};
|
use brk_core::{default_bitcoin_path, default_brk_path, default_on_error, dot_brk_path};
|
||||||
use brk_exit::Exit;
|
use brk_exit::Exit;
|
||||||
use brk_fetcher::Fetcher;
|
use brk_fetcher::Fetcher;
|
||||||
use brk_indexer::Indexer;
|
use brk_indexer::Indexer;
|
||||||
@@ -29,7 +29,7 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let format = config.format();
|
let format = config.format();
|
||||||
|
|
||||||
let mut indexer = Indexer::new(&config.outputsdir(), format, config.check_collisions())?;
|
let mut indexer = Indexer::new(&config.outputsdir(), config.check_collisions())?;
|
||||||
indexer.import_stores()?;
|
indexer.import_stores()?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|
||||||
@@ -109,62 +109,77 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
|||||||
#[derive(Parser, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
#[derive(Parser, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||||
pub struct RunConfig {
|
pub struct RunConfig {
|
||||||
/// Bitcoin main directory path, defaults: ~/.bitcoin, ~/Library/Application\ Support/Bitcoin, saved
|
/// Bitcoin main directory path, defaults: ~/.bitcoin, ~/Library/Application\ Support/Bitcoin, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long, value_name = "PATH")]
|
||||||
bitcoindir: Option<String>,
|
bitcoindir: Option<String>,
|
||||||
|
|
||||||
/// Bitcoin blocks directory path, default: --bitcoindir/blocks, saved
|
/// Bitcoin blocks directory path, default: --bitcoindir/blocks, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long, value_name = "PATH")]
|
||||||
blocksdir: Option<String>,
|
blocksdir: Option<String>,
|
||||||
|
|
||||||
/// Bitcoin Research Kit outputs directory path, default: ~/.brk, saved
|
/// Bitcoin Research Kit outputs directory path, default: ~/.brk, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long, value_name = "PATH")]
|
||||||
brkdir: Option<String>,
|
brkdir: Option<String>,
|
||||||
|
|
||||||
/// Executed by the runner, default: all, saved
|
/// Activated services, default: all, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
mode: Option<Mode>,
|
services: Option<Services>,
|
||||||
|
|
||||||
/// Computation mode for compatible datasets, `lazy` computes data whenever requested without saving it, `eager` computes the data once and saves it to disk, default: Lazy, saved
|
/// Computation of computed datasets, `lazy` computes data whenever requested without saving it, `eager` computes the data once and saves it to disk, default: `lazy`, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
computation: Option<Computation>,
|
computation: Option<Computation>,
|
||||||
|
|
||||||
/// Activate compression of datasets, set to true to save disk space or false if prioritize speed, default: compressed, saved
|
/// Format of computed datasets, `compressed` to save disk space (experimental), `raw` to prioritize speed, default: `raw`, saved
|
||||||
#[arg(short, long, value_name = "FORMAT")]
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
|
#[arg(short, long)]
|
||||||
format: Option<Format>,
|
format: Option<Format>,
|
||||||
|
|
||||||
/// Activate fetching prices from exchanges APIs and the computation of all related datasets, default: true, saved
|
/// Activate fetching prices from exchanges APIs and the computation of all related datasets, default: true, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(short = 'F', long, value_name = "BOOL")]
|
#[arg(short = 'F', long, value_name = "BOOL")]
|
||||||
fetch: Option<bool>,
|
fetch: Option<bool>,
|
||||||
|
|
||||||
/// Website served by the server (if active), default: default, saved
|
/// Website served by the server (if active), default: default, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
website: Option<Website>,
|
website: Option<Website>,
|
||||||
|
|
||||||
/// Bitcoin RPC ip, default: localhost, saved
|
/// Bitcoin RPC ip, default: localhost, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "IP")]
|
#[arg(long, value_name = "IP")]
|
||||||
rpcconnect: Option<String>,
|
rpcconnect: Option<String>,
|
||||||
|
|
||||||
/// Bitcoin RPC port, default: 8332, saved
|
/// Bitcoin RPC port, default: 8332, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PORT")]
|
#[arg(long, value_name = "PORT")]
|
||||||
rpcport: Option<u16>,
|
rpcport: Option<u16>,
|
||||||
|
|
||||||
/// Bitcoin RPC cookie file, default: --bitcoindir/.cookie, saved
|
/// Bitcoin RPC cookie file, default: --bitcoindir/.cookie, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long, value_name = "PATH")]
|
||||||
rpccookiefile: Option<String>,
|
rpccookiefile: Option<String>,
|
||||||
|
|
||||||
/// Bitcoin RPC username, saved
|
/// Bitcoin RPC username, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "USERNAME")]
|
#[arg(long, value_name = "USERNAME")]
|
||||||
rpcuser: Option<String>,
|
rpcuser: Option<String>,
|
||||||
|
|
||||||
/// Bitcoin RPC password, saved
|
/// Bitcoin RPC password, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PASSWORD")]
|
#[arg(long, value_name = "PASSWORD")]
|
||||||
rpcpassword: Option<String>,
|
rpcpassword: Option<String>,
|
||||||
|
|
||||||
/// Delay between runs, default: 0, saved
|
/// Delay between runs, default: 0, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "SECONDS")]
|
#[arg(long, value_name = "SECONDS")]
|
||||||
delay: Option<u64>,
|
delay: Option<u64>,
|
||||||
|
|
||||||
/// DEV: Activate checking address hashes for collisions when indexing, default: false, saved
|
/// DEV: Activate checking address hashes for collisions when indexing, default: false, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "BOOL")]
|
#[arg(long, value_name = "BOOL")]
|
||||||
check_collisions: Option<bool>,
|
check_collisions: Option<bool>,
|
||||||
}
|
}
|
||||||
@@ -192,8 +207,8 @@ impl RunConfig {
|
|||||||
config_saved.brkdir = Some(brkdir);
|
config_saved.brkdir = Some(brkdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mode) = config_args.mode.take() {
|
if let Some(services) = config_args.services.take() {
|
||||||
config_saved.mode = Some(mode);
|
config_saved.services = Some(services);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(computation) = config_args.computation.take() {
|
if let Some(computation) = config_args.computation.take() {
|
||||||
@@ -255,7 +270,7 @@ impl RunConfig {
|
|||||||
// info!("Configuration {{");
|
// info!("Configuration {{");
|
||||||
// info!(" bitcoindir: {:?}", config.bitcoindir);
|
// info!(" bitcoindir: {:?}", config.bitcoindir);
|
||||||
// info!(" brkdir: {:?}", config.brkdir);
|
// info!(" brkdir: {:?}", config.brkdir);
|
||||||
// info!(" mode: {:?}", config.mode);
|
// info!(" services: {:?}", config.services);
|
||||||
// info!(" website: {:?}", config.website);
|
// info!(" website: {:?}", config.website);
|
||||||
// info!(" rpcconnect: {:?}", config.rpcconnect);
|
// info!(" rpcconnect: {:?}", config.rpcconnect);
|
||||||
// info!(" rpcport: {:?}", config.rpcport);
|
// info!(" rpcport: {:?}", config.rpcport);
|
||||||
@@ -375,13 +390,13 @@ impl RunConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&self) -> bool {
|
pub fn process(&self) -> bool {
|
||||||
self.mode
|
self.services
|
||||||
.is_none_or(|m| m == Mode::All || m == Mode::Processor)
|
.is_none_or(|m| m == Services::All || m == Services::Processor)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve(&self) -> bool {
|
pub fn serve(&self) -> bool {
|
||||||
self.mode
|
self.services
|
||||||
.is_none_or(|m| m == Mode::All || m == Mode::Server)
|
.is_none_or(|m| m == Services::All || m == Services::Server)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_cookiefile(&self) -> PathBuf {
|
fn path_cookiefile(&self) -> PathBuf {
|
||||||
@@ -449,7 +464,7 @@ impl RunConfig {
|
|||||||
PartialOrd,
|
PartialOrd,
|
||||||
Ord,
|
Ord,
|
||||||
)]
|
)]
|
||||||
pub enum Mode {
|
pub enum Services {
|
||||||
#[default]
|
#[default]
|
||||||
All,
|
All,
|
||||||
Processor,
|
Processor,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ pub fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let format = Format::Raw;
|
let format = Format::Raw;
|
||||||
|
|
||||||
let mut indexer = Indexer::new(outputs_dir, format, true)?;
|
let mut indexer = Indexer::new(outputs_dir, true)?;
|
||||||
indexer.import_stores()?;
|
indexer.import_stores()?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|
||||||
|
|||||||
@@ -226,7 +226,11 @@ impl ComputedValueVecsFromTxindex {
|
|||||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||||
[
|
[
|
||||||
self.sats.vecs(),
|
self.sats.vecs(),
|
||||||
|
vec![&self.bitcoin_txindex as &dyn AnyCollectableVec],
|
||||||
self.bitcoin.vecs(),
|
self.bitcoin.vecs(),
|
||||||
|
self.dollars_txindex
|
||||||
|
.as_ref()
|
||||||
|
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
|
||||||
self.dollars.as_ref().map_or(vec![], |v| v.vecs()),
|
self.dollars.as_ref().map_or(vec![], |v| v.vecs()),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ impl Vecs {
|
|||||||
fetcher: Option<&mut Fetcher>,
|
fetcher: Option<&mut Fetcher>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> color_eyre::Result<()> {
|
) -> color_eyre::Result<()> {
|
||||||
|
info!("Computing indexes...");
|
||||||
let starting_indexes = self.indexes.compute(indexer, starting_indexes, exit)?;
|
let starting_indexes = self.indexes.compute(indexer, starting_indexes, exit)?;
|
||||||
|
|
||||||
info!("Computing constants...");
|
info!("Computing constants...");
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ mod checked_sub;
|
|||||||
mod paths;
|
mod paths;
|
||||||
mod pause;
|
mod pause;
|
||||||
mod rlimit;
|
mod rlimit;
|
||||||
|
mod serde;
|
||||||
|
|
||||||
pub use bytes::*;
|
pub use bytes::*;
|
||||||
pub use checked_sub::*;
|
pub use checked_sub::*;
|
||||||
pub use paths::*;
|
pub use paths::*;
|
||||||
pub use pause::*;
|
pub use pause::*;
|
||||||
pub use rlimit::*;
|
pub use rlimit::*;
|
||||||
|
pub use serde::*;
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
|
|
||||||
|
pub fn default_on_error<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
T: Deserialize<'de> + Default,
|
||||||
|
{
|
||||||
|
match T::deserialize(deserializer) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(_) => Ok(T::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ use brk_core::default_bitcoin_path;
|
|||||||
use brk_exit::Exit;
|
use brk_exit::Exit;
|
||||||
use brk_indexer::Indexer;
|
use brk_indexer::Indexer;
|
||||||
use brk_parser::Parser;
|
use brk_parser::Parser;
|
||||||
use brk_vec::Format;
|
|
||||||
|
|
||||||
fn main() -> color_eyre::Result<()> {
|
fn main() -> color_eyre::Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
@@ -25,7 +24,7 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let outputs = Path::new("../../_outputs");
|
let outputs = Path::new("../../_outputs");
|
||||||
|
|
||||||
let mut indexer = Indexer::new(outputs, Format::Raw, false)?;
|
let mut indexer = Indexer::new(outputs, false)?;
|
||||||
|
|
||||||
indexer.import_stores()?;
|
indexer.import_stores()?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use brk_core::{
|
|||||||
use bitcoin::{Transaction, TxIn, TxOut};
|
use bitcoin::{Transaction, TxIn, TxOut};
|
||||||
use brk_exit::Exit;
|
use brk_exit::Exit;
|
||||||
use brk_parser::Parser;
|
use brk_parser::Parser;
|
||||||
use brk_vec::{AnyVec, Format, VecIterator};
|
use brk_vec::{AnyVec, VecIterator};
|
||||||
use color_eyre::eyre::{ContextCompat, eyre};
|
use color_eyre::eyre::{ContextCompat, eyre};
|
||||||
use fjall::TransactionalKeyspace;
|
use fjall::TransactionalKeyspace;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
@@ -42,21 +42,15 @@ pub struct Indexer {
|
|||||||
vecs: Option<Vecs>,
|
vecs: Option<Vecs>,
|
||||||
stores: Option<Stores>,
|
stores: Option<Stores>,
|
||||||
check_collisions: bool,
|
check_collisions: bool,
|
||||||
format: Format,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Indexer {
|
impl Indexer {
|
||||||
pub fn new(
|
pub fn new(outputs_dir: &Path, check_collisions: bool) -> color_eyre::Result<Self> {
|
||||||
outputs_dir: &Path,
|
|
||||||
format: Format,
|
|
||||||
check_collisions: bool,
|
|
||||||
) -> color_eyre::Result<Self> {
|
|
||||||
setrlimit()?;
|
setrlimit()?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path: outputs_dir.to_owned(),
|
path: outputs_dir.to_owned(),
|
||||||
vecs: None,
|
vecs: None,
|
||||||
stores: None,
|
stores: None,
|
||||||
format,
|
|
||||||
check_collisions,
|
check_collisions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -65,7 +59,6 @@ impl Indexer {
|
|||||||
self.vecs = Some(Vecs::forced_import(
|
self.vecs = Some(Vecs::forced_import(
|
||||||
&self.path.join("vecs/indexed"),
|
&self.path.join("vecs/indexed"),
|
||||||
VERSION + Version::ZERO,
|
VERSION + Version::ZERO,
|
||||||
self.format,
|
|
||||||
)?);
|
)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,11 +66,7 @@ pub struct Vecs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Vecs {
|
impl Vecs {
|
||||||
pub fn forced_import(
|
pub fn forced_import(path: &Path, version: Version) -> color_eyre::Result<Self> {
|
||||||
path: &Path,
|
|
||||||
version: Version,
|
|
||||||
format: Format,
|
|
||||||
) -> color_eyre::Result<Self> {
|
|
||||||
fs::create_dir_all(path)?;
|
fs::create_dir_all(path)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -78,7 +74,7 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"txindex",
|
"txindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_blockhash: IndexedVec::forced_import(
|
height_to_blockhash: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
@@ -90,145 +86,145 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"difficulty",
|
"difficulty",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_emptyoutputindex: IndexedVec::forced_import(
|
height_to_first_emptyoutputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_emptyoutputindex",
|
"first_emptyoutputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_inputindex: IndexedVec::forced_import(
|
height_to_first_inputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_inputindex",
|
"first_inputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_opreturnindex: IndexedVec::forced_import(
|
height_to_first_opreturnindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_opreturnindex",
|
"first_opreturnindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_outputindex: IndexedVec::forced_import(
|
height_to_first_outputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_outputindex",
|
"first_outputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2aindex: IndexedVec::forced_import(
|
height_to_first_p2aindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2aindex",
|
"first_p2aindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2msindex: IndexedVec::forced_import(
|
height_to_first_p2msindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2msindex",
|
"first_p2msindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2pk33index: IndexedVec::forced_import(
|
height_to_first_p2pk33index: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2pk33index",
|
"first_p2pk33index",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2pk65index: IndexedVec::forced_import(
|
height_to_first_p2pk65index: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2pk65index",
|
"first_p2pk65index",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2pkhindex: IndexedVec::forced_import(
|
height_to_first_p2pkhindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2pkhindex",
|
"first_p2pkhindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2shindex: IndexedVec::forced_import(
|
height_to_first_p2shindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2shindex",
|
"first_p2shindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2trindex: IndexedVec::forced_import(
|
height_to_first_p2trindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2trindex",
|
"first_p2trindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2wpkhindex: IndexedVec::forced_import(
|
height_to_first_p2wpkhindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2wpkhindex",
|
"first_p2wpkhindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2wshindex: IndexedVec::forced_import(
|
height_to_first_p2wshindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2wshindex",
|
"first_p2wshindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_txindex: IndexedVec::forced_import(
|
height_to_first_txindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_txindex",
|
"first_txindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_unknownoutputindex: IndexedVec::forced_import(
|
height_to_first_unknownoutputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_unknownoutputindex",
|
"first_unknownoutputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_timestamp: IndexedVec::forced_import(
|
height_to_timestamp: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"timestamp",
|
"timestamp",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_total_size: IndexedVec::forced_import(
|
height_to_total_size: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"total_size",
|
"total_size",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_weight: IndexedVec::forced_import(
|
height_to_weight: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"weight",
|
"weight",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
inputindex_to_outputindex: IndexedVec::forced_import(
|
inputindex_to_outputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"outputindex",
|
"outputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
opreturnindex_to_txindex: IndexedVec::forced_import(
|
opreturnindex_to_txindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"txindex",
|
"txindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
outputindex_to_outputtype: IndexedVec::forced_import(
|
outputindex_to_outputtype: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"outputtype",
|
"outputtype",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
outputindex_to_outputtypeindex: IndexedVec::forced_import(
|
outputindex_to_outputtypeindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"outputtypeindex",
|
"outputtypeindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
outputindex_to_value: IndexedVec::forced_import(
|
outputindex_to_value: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"value",
|
"value",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
p2aindex_to_p2abytes: IndexedVec::forced_import(
|
p2aindex_to_p2abytes: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
@@ -240,7 +236,7 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"txindex",
|
"txindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
p2pk33index_to_p2pk33bytes: IndexedVec::forced_import(
|
p2pk33index_to_p2pk33bytes: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
@@ -288,13 +284,13 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"base_size",
|
"base_size",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
txindex_to_first_inputindex: IndexedVec::forced_import(
|
txindex_to_first_inputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_inputindex",
|
"first_inputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
txindex_to_first_outputindex: IndexedVec::forced_import(
|
txindex_to_first_outputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
@@ -306,19 +302,19 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"is_explicitly_rbf",
|
"is_explicitly_rbf",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
txindex_to_rawlocktime: IndexedVec::forced_import(
|
txindex_to_rawlocktime: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"rawlocktime",
|
"rawlocktime",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
txindex_to_total_size: IndexedVec::forced_import(
|
txindex_to_total_size: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"total_size",
|
"total_size",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
txindex_to_txid: IndexedVec::forced_import(
|
txindex_to_txid: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
@@ -330,13 +326,13 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"txversion",
|
"txversion",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
unknownoutputindex_to_txindex: IndexedVec::forced_import(
|
unknownoutputindex_to_txindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"txindex",
|
"txindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let format = Format::Compressed;
|
let format = Format::Compressed;
|
||||||
|
|
||||||
let mut indexer = Indexer::new(outputs_dir, format, true)?;
|
let mut indexer = Indexer::new(outputs_dir, true)?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|
||||||
let mut computer = Computer::new(outputs_dir, None, format);
|
let mut computer = Computer::new(outputs_dir, None, format);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ pub fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let format = Format::Compressed;
|
let format = Format::Compressed;
|
||||||
|
|
||||||
let mut indexer = Indexer::new(outputs_dir, format, true)?;
|
let mut indexer = Indexer::new(outputs_dir, true)?;
|
||||||
indexer.import_stores()?;
|
indexer.import_stores()?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ pub async fn variant_handler(
|
|||||||
Query(params_opt): Query<ParamsOpt>,
|
Query(params_opt): Query<ParamsOpt>,
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
|
let variant = variant.replace("_", "-");
|
||||||
let mut split = variant.split(TO_SEPARATOR);
|
let mut split = variant.split(TO_SEPARATOR);
|
||||||
let params = Params::from((
|
let params = Params::from((
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, ValueEnum,
|
Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, ValueEnum,
|
||||||
)]
|
)]
|
||||||
pub enum Format {
|
pub enum Format {
|
||||||
#[default]
|
|
||||||
Compressed,
|
Compressed,
|
||||||
|
#[default]
|
||||||
Raw,
|
Raw,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ where
|
|||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_or_read_(&self, index: usize, mmap: &Mmap) -> Result<Option<Value<T>>> {
|
fn get_or_read_(&self, index: usize, mmap: &Mmap) -> Result<Option<Value<T>>> {
|
||||||
let stored_len = mmap.len() / Self::SIZE_OF_T;
|
let stored_len = self.stored_len_(mmap);
|
||||||
|
|
||||||
if index >= stored_len {
|
if index >= stored_len {
|
||||||
let pushed = self.pushed();
|
let pushed = self.pushed();
|
||||||
@@ -53,6 +53,7 @@ where
|
|||||||
fn mmap(&self) -> &ArcSwap<Mmap>;
|
fn mmap(&self) -> &ArcSwap<Mmap>;
|
||||||
|
|
||||||
fn stored_len(&self) -> usize;
|
fn stored_len(&self) -> usize;
|
||||||
|
fn stored_len_(&self, mmap: &Mmap) -> usize;
|
||||||
|
|
||||||
fn pushed(&self) -> &[T];
|
fn pushed(&self) -> &[T];
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ use crate::{
|
|||||||
const ONE_KIB: usize = 1024;
|
const ONE_KIB: usize = 1024;
|
||||||
const ONE_MIB: usize = ONE_KIB * ONE_KIB;
|
const ONE_MIB: usize = ONE_KIB * ONE_KIB;
|
||||||
pub const MAX_CACHE_SIZE: usize = 100 * ONE_MIB;
|
pub const MAX_CACHE_SIZE: usize = 100 * ONE_MIB;
|
||||||
pub const MAX_PAGE_SIZE: usize = 16 * ONE_KIB;
|
pub const MAX_PAGE_SIZE: usize = 64 * ONE_KIB;
|
||||||
|
|
||||||
|
const VERSION: Version = Version::ONE;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CompressedVec<I, T> {
|
pub struct CompressedVec<I, T> {
|
||||||
@@ -39,7 +41,9 @@ where
|
|||||||
pub const CACHE_LENGTH: usize = MAX_CACHE_SIZE / Self::PAGE_SIZE;
|
pub const CACHE_LENGTH: usize = MAX_CACHE_SIZE / Self::PAGE_SIZE;
|
||||||
|
|
||||||
/// Same as import but will reset the folder under certain errors, so be careful !
|
/// Same as import but will reset the folder under certain errors, so be careful !
|
||||||
pub fn forced_import(path: &Path, version: Version) -> Result<Self> {
|
pub fn forced_import(path: &Path, mut version: Version) -> Result<Self> {
|
||||||
|
version = version + VERSION;
|
||||||
|
|
||||||
let res = Self::import(path, version);
|
let res = Self::import(path, version);
|
||||||
match res {
|
match res {
|
||||||
Err(Error::WrongEndian)
|
Err(Error::WrongEndian)
|
||||||
@@ -129,7 +133,7 @@ where
|
|||||||
page_index * Self::PER_PAGE
|
page_index * Self::PER_PAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stored_len_(pages_meta: &Guard<Arc<CompressedPagesMetadata>>) -> usize {
|
fn stored_len__(pages_meta: &Guard<Arc<CompressedPagesMetadata>>) -> usize {
|
||||||
if let Some(last) = pages_meta.last() {
|
if let Some(last) = pages_meta.last() {
|
||||||
(pages_meta.len() - 1) * Self::PER_PAGE + last.values_len as usize
|
(pages_meta.len() - 1) * Self::PER_PAGE + last.values_len as usize
|
||||||
} else {
|
} else {
|
||||||
@@ -178,7 +182,11 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn stored_len(&self) -> usize {
|
fn stored_len(&self) -> usize {
|
||||||
Self::stored_len_(&self.pages_meta.load())
|
Self::stored_len__(&self.pages_meta.load())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn stored_len_(&self, _: &Mmap) -> usize {
|
||||||
|
self.stored_len()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -477,7 +485,7 @@ where
|
|||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
let pages_meta = self.pages_meta.load();
|
let pages_meta = self.pages_meta.load();
|
||||||
let stored_len = CompressedVec::<I, T>::stored_len_(&pages_meta);
|
let stored_len = CompressedVec::<I, T>::stored_len__(&pages_meta);
|
||||||
CompressedVecIterator {
|
CompressedVecIterator {
|
||||||
vec: self,
|
vec: self,
|
||||||
guard: self.mmap().load(),
|
guard: self.mmap().load(),
|
||||||
|
|||||||
@@ -104,7 +104,11 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn stored_len(&self) -> usize {
|
fn stored_len(&self) -> usize {
|
||||||
self.mmap.load().len() / Self::SIZE_OF_T
|
self.stored_len_(&self.mmap.load())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn stored_len_(&self, mmap: &Mmap) -> usize {
|
||||||
|
mmap.len() / Self::SIZE_OF_T
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -73,6 +73,13 @@ where
|
|||||||
StoredVec::Compressed(v) => v.stored_len(),
|
StoredVec::Compressed(v) => v.stored_len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
|
fn stored_len_(&self, mmap: &Mmap) -> usize {
|
||||||
|
match self {
|
||||||
|
StoredVec::Raw(v) => v.stored_len_(mmap),
|
||||||
|
StoredVec::Compressed(v) => v.stored_len_(mmap),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pushed(&self) -> &[T] {
|
fn pushed(&self) -> &[T] {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
@@ -965,7 +965,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
margin: -0.75rem var(--negative-main-padding);
|
margin: -0.5rem var(--negative-main-padding);
|
||||||
padding: 0.75rem var(--main-padding);
|
padding: 0.75rem var(--main-padding);
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@@ -1113,7 +1113,7 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const preferredColorSchemeMatchMedia = window.matchMedia(
|
const preferredColorSchemeMatchMedia = window.matchMedia(
|
||||||
"(prefers-color-scheme: dark)",
|
"(prefers-color-scheme: dark)"
|
||||||
);
|
);
|
||||||
|
|
||||||
const themeColor = window.document.createElement("meta");
|
const themeColor = window.document.createElement("meta");
|
||||||
@@ -1123,7 +1123,7 @@
|
|||||||
/** @param {boolean} dark */
|
/** @param {boolean} dark */
|
||||||
function updateThemeColor(dark) {
|
function updateThemeColor(dark) {
|
||||||
const theme = getComputedStyle(
|
const theme = getComputedStyle(
|
||||||
window.document.documentElement,
|
window.document.documentElement
|
||||||
).getPropertyValue(dark ? "--black" : "--white");
|
).getPropertyValue(dark ? "--black" : "--white");
|
||||||
themeColor.content = theme;
|
themeColor.content = theme;
|
||||||
}
|
}
|
||||||
@@ -1133,7 +1133,7 @@
|
|||||||
"change",
|
"change",
|
||||||
({ matches }) => {
|
({ matches }) => {
|
||||||
updateThemeColor(matches);
|
updateThemeColor(matches);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if ("standalone" in window.navigator && !!window.navigator.standalone) {
|
if ("standalone" in window.navigator && !!window.navigator.standalone) {
|
||||||
@@ -1141,7 +1141,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
navigator.serviceWorker.register("/service-worker.js");
|
window.addEventListener("load", () => {
|
||||||
|
navigator.serviceWorker.register("/service-worker.js");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,10 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Series
|
* @typedef {Object} Series
|
||||||
* @property {ISeriesApi<SeriesType>} inner
|
* @property {ISeriesApi<SeriesType, number>} inner
|
||||||
* @property {string} id
|
* @property {string} id
|
||||||
* @property {Signal<boolean>} active
|
* @property {Signal<boolean>} active
|
||||||
|
* @property {Signal<boolean>} hasData
|
||||||
* @property {VoidFunction} remove
|
* @property {VoidFunction} remove
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -26,8 +27,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {ChartData<_SingleValueData>} SingleValueData
|
* @typedef {ChartData<_SingleValueData<number>>} SingleValueData
|
||||||
* @typedef {ChartData<_CandlestickData>} CandlestickData
|
* @typedef {ChartData<_CandlestickData<number>>} CandlestickData
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {function({ iseries: ISeriesApi<any, number>; unit: Unit; index: Index }): void} SetDataCallback
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
||||||
@@ -123,7 +128,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
// ..._options,
|
// ..._options,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
ichart.priceScale("right").applyOptions({
|
ichart.priceScale("right").applyOptions({
|
||||||
@@ -155,7 +160,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let timeScaleSet = false;
|
let timeScaleSet = false;
|
||||||
@@ -163,30 +168,30 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
signals.createEffect(index, (index) => {
|
signals.createEffect(index, (index) => {
|
||||||
timeScaleSet = false;
|
timeScaleSet = false;
|
||||||
|
|
||||||
|
const minBarSpacing =
|
||||||
|
index === /** @satisfies {MonthIndex} */ (7)
|
||||||
|
? 1
|
||||||
|
: index === /** @satisfies {QuarterIndex} */ (19)
|
||||||
|
? 3
|
||||||
|
: index === /** @satisfies {YearIndex} */ (23)
|
||||||
|
? 12
|
||||||
|
: index === /** @satisfies {DecadeIndex} */ (1)
|
||||||
|
? 120
|
||||||
|
: 0.5;
|
||||||
|
|
||||||
ichart.applyOptions({
|
ichart.applyOptions({
|
||||||
timeScale: {
|
timeScale: {
|
||||||
timeVisible:
|
timeVisible:
|
||||||
index === /** @satisfies {Height} */ (5) ||
|
index === /** @satisfies {Height} */ (5) ||
|
||||||
index === /** @satisfies {DifficultyEpoch} */ (2) ||
|
index === /** @satisfies {DifficultyEpoch} */ (2) ||
|
||||||
index === /** @satisfies {HalvingEpoch} */ (4),
|
index === /** @satisfies {HalvingEpoch} */ (4),
|
||||||
|
...(!fitContent
|
||||||
|
? {
|
||||||
|
minBarSpacing,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!fitContent) {
|
|
||||||
ichart.applyOptions({
|
|
||||||
timeScale: {
|
|
||||||
minBarSpacing:
|
|
||||||
index === /** @satisfies {MonthIndex} */ (7)
|
|
||||||
? 1
|
|
||||||
: index === /** @satisfies {QuarterIndex} */ (19)
|
|
||||||
? 3
|
|
||||||
: index === /** @satisfies {YearIndex} */ (23)
|
|
||||||
? 12
|
|
||||||
: index === /** @satisfies {DecadeIndex} */ (1)
|
|
||||||
? 120
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const activeResources = /** @type {Set<VecResource>} */ (new Set());
|
const activeResources = /** @type {Set<VecResource>} */ (new Set());
|
||||||
@@ -195,12 +200,12 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
activeResources.forEach((v) => {
|
activeResources.forEach((v) => {
|
||||||
v.fetch();
|
v.fetch();
|
||||||
});
|
});
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (fitContent) {
|
if (fitContent) {
|
||||||
new ResizeObserver(() => ichart.timeScale().fitContent()).observe(
|
new ResizeObserver(() => ichart.timeScale().fitContent()).observe(
|
||||||
chartDiv,
|
chartDiv
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +234,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
const children = Array.from(parent.childNodes).filter(
|
const children = Array.from(parent.childNodes).filter(
|
||||||
(element) =>
|
(element) =>
|
||||||
/** @type {HTMLElement} */ (element).dataset.position ===
|
/** @type {HTMLElement} */ (element).dataset.position ===
|
||||||
position,
|
position
|
||||||
);
|
);
|
||||||
|
|
||||||
if (children.length === 1) {
|
if (children.length === 1) {
|
||||||
@@ -251,7 +256,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
|
|
||||||
fieldset.append(createChild(pane));
|
fieldset.append(createChild(pane));
|
||||||
}),
|
}),
|
||||||
paneIndex ? 50 : 0,
|
paneIndex ? 50 : 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,14 +298,15 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {ISeriesApi<SeriesType>} args.iseries
|
* @param {ISeriesApi<SeriesType, number>} args.iseries
|
||||||
* @param {string} args.name
|
* @param {string} args.name
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @param {number} args.order
|
* @param {number} args.order
|
||||||
* @param {Color[]} args.colors
|
* @param {Color[]} args.colors
|
||||||
* @param {SeriesType} args.seriesType
|
* @param {SeriesType} args.seriesType
|
||||||
* @param {VecId} [args.vecId]
|
* @param {VecId} [args.vecId]
|
||||||
* @param {Accessor<WhitespaceData[]>} [args.data]
|
* @param {SetDataCallback} [args.setDataCallback]
|
||||||
|
* @param {Accessor<WhitespaceData<number>[]>} [args.data]
|
||||||
* @param {number} args.paneIndex
|
* @param {number} args.paneIndex
|
||||||
* @param {boolean} [args.defaultActive]
|
* @param {boolean} [args.defaultActive]
|
||||||
*/
|
*/
|
||||||
@@ -311,6 +317,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
unit,
|
unit,
|
||||||
order,
|
order,
|
||||||
seriesType,
|
seriesType,
|
||||||
|
setDataCallback,
|
||||||
paneIndex,
|
paneIndex,
|
||||||
defaultActive,
|
defaultActive,
|
||||||
colors,
|
colors,
|
||||||
@@ -327,13 +334,15 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const hasData = signals.createSignal(false);
|
||||||
|
|
||||||
let url = /** @type {string | undefined} */ (undefined);
|
let url = /** @type {string | undefined} */ (undefined);
|
||||||
|
|
||||||
signals.createEffect(active, (active) =>
|
signals.createEffect(active, (active) =>
|
||||||
// Or remove ?
|
// Or remove ?
|
||||||
iseries.applyOptions({
|
iseries.applyOptions({
|
||||||
visible: active,
|
visible: active,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
iseries.setSeriesOrder(order);
|
iseries.setSeriesOrder(order);
|
||||||
@@ -345,9 +354,11 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
const series = {
|
const series = {
|
||||||
inner: iseries,
|
inner: iseries,
|
||||||
active,
|
active,
|
||||||
|
hasData,
|
||||||
id,
|
id,
|
||||||
remove() {
|
remove() {
|
||||||
dispose();
|
dispose();
|
||||||
|
// @ts-ignore
|
||||||
chart.inner.removeSeries(iseries);
|
chart.inner.removeSeries(iseries);
|
||||||
if (_valuesResource) {
|
if (_valuesResource) {
|
||||||
activeResources.delete(_valuesResource);
|
activeResources.delete(_valuesResource);
|
||||||
@@ -361,7 +372,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
index,
|
index,
|
||||||
index === /** @satisfies {Height} */ (5)
|
index === /** @satisfies {Height} */ (5)
|
||||||
? "timestamp-fixed"
|
? "timestamp-fixed"
|
||||||
: "timestamp",
|
: "timestamp"
|
||||||
);
|
);
|
||||||
timeResource.fetch();
|
timeResource.fetch();
|
||||||
|
|
||||||
@@ -377,13 +388,13 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
const fetchedKey = vecsResources.defaultFetchedKey;
|
const fetchedKey = vecsResources.defaultFetchedKey;
|
||||||
signals.createEffect(
|
signals.createEffect(
|
||||||
() => [
|
() => [
|
||||||
timeResource.fetched[fetchedKey].vec(),
|
timeResource.fetched().get(fetchedKey)?.vec(),
|
||||||
valuesResource.fetched[fetchedKey].vec(),
|
valuesResource.fetched().get(fetchedKey)?.vec(),
|
||||||
],
|
],
|
||||||
([indexes, _ohlcs]) => {
|
([indexes, _ohlcs]) => {
|
||||||
if (!indexes || !_ohlcs) return;
|
if (!indexes?.length || !_ohlcs?.length) return;
|
||||||
|
|
||||||
const seriesData = series.inner.data();
|
// const seriesData = series.inner.data();
|
||||||
// const set = seriesData.length === 0;
|
// const set = seriesData.length === 0;
|
||||||
|
|
||||||
const ohlcs = /** @type {OHLCTuple[]} */ (_ohlcs);
|
const ohlcs = /** @type {OHLCTuple[]} */ (_ohlcs);
|
||||||
@@ -446,6 +457,12 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
// iseries.update(bar, true);
|
// iseries.update(bar, true);
|
||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
|
hasData.set(true);
|
||||||
|
setDataCallback?.({
|
||||||
|
iseries,
|
||||||
|
index,
|
||||||
|
unit,
|
||||||
|
});
|
||||||
|
|
||||||
timeScaleSetCallback?.(() => {
|
timeScaleSetCallback?.(() => {
|
||||||
if (
|
if (
|
||||||
@@ -461,7 +478,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
timeScaleSet = true;
|
timeScaleSet = true;
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
activeResources.delete(valuesResource);
|
activeResources.delete(valuesResource);
|
||||||
@@ -471,6 +488,12 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
} else if (data) {
|
} else if (data) {
|
||||||
signals.createEffect(data, (data) => {
|
signals.createEffect(data, (data) => {
|
||||||
iseries.setData(data);
|
iseries.setData(data);
|
||||||
|
hasData.set(true);
|
||||||
|
setDataCallback?.({
|
||||||
|
iseries,
|
||||||
|
index: index(),
|
||||||
|
unit,
|
||||||
|
});
|
||||||
|
|
||||||
if (fitContent) {
|
if (fitContent) {
|
||||||
ichart.timeScale().fitContent();
|
ichart.timeScale().fitContent();
|
||||||
@@ -520,6 +543,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
* @param {number} [args.paneIndex]
|
* @param {number} [args.paneIndex]
|
||||||
* @param {boolean} [args.defaultActive]
|
* @param {boolean} [args.defaultActive]
|
||||||
* @param {boolean} [args.inverse]
|
* @param {boolean} [args.inverse]
|
||||||
|
* @param {SetDataCallback} [args.setDataCallback]
|
||||||
* @param {DeepPartial<CandlestickStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [args.options]
|
* @param {DeepPartial<CandlestickStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [args.options]
|
||||||
*/
|
*/
|
||||||
addCandlestickSeries({
|
addCandlestickSeries({
|
||||||
@@ -529,22 +553,29 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
order,
|
order,
|
||||||
paneIndex = 0,
|
paneIndex = 0,
|
||||||
defaultActive,
|
defaultActive,
|
||||||
|
setDataCallback,
|
||||||
data,
|
data,
|
||||||
inverse,
|
inverse,
|
||||||
}) {
|
}) {
|
||||||
const green = inverse ? colors.red : colors.green;
|
const green = inverse ? colors.red : colors.green;
|
||||||
const red = inverse ? colors.green : colors.red;
|
const red = inverse ? colors.green : colors.red;
|
||||||
const iseries = ichart.addSeries(
|
|
||||||
/** @type {SeriesDefinition<'Candlestick'>} */ (lc.CandlestickSeries),
|
/** @type {ISeriesApi<'Candlestick', number>} */
|
||||||
{
|
const iseries = /** @type {any} */ (
|
||||||
upColor: green(),
|
ichart.addSeries(
|
||||||
downColor: red(),
|
/** @type {SeriesDefinition<'Candlestick'>} */ (
|
||||||
wickUpColor: green(),
|
lc.CandlestickSeries
|
||||||
wickDownColor: red(),
|
),
|
||||||
borderVisible: false,
|
{
|
||||||
visible: defaultActive !== false,
|
upColor: green(),
|
||||||
},
|
downColor: red(),
|
||||||
paneIndex,
|
wickUpColor: green(),
|
||||||
|
wickDownColor: red(),
|
||||||
|
borderVisible: false,
|
||||||
|
visible: defaultActive !== false,
|
||||||
|
},
|
||||||
|
paneIndex
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return addSeries({
|
return addSeries({
|
||||||
@@ -556,6 +587,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
seriesType: "Candlestick",
|
seriesType: "Candlestick",
|
||||||
unit,
|
unit,
|
||||||
data,
|
data,
|
||||||
|
setDataCallback,
|
||||||
defaultActive,
|
defaultActive,
|
||||||
vecId,
|
vecId,
|
||||||
});
|
});
|
||||||
@@ -565,9 +597,10 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
* @param {string} args.name
|
* @param {string} args.name
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @param {number} args.order
|
* @param {number} args.order
|
||||||
* @param {Accessor<LineData[]>} [args.data]
|
* @param {Accessor<LineData<number>[]>} [args.data]
|
||||||
* @param {VecId} [args.vecId]
|
* @param {VecId} [args.vecId]
|
||||||
* @param {Color} [args.color]
|
* @param {Color} [args.color]
|
||||||
|
* @param {SetDataCallback} [args.setDataCallback]
|
||||||
* @param {number} [args.paneIndex]
|
* @param {number} [args.paneIndex]
|
||||||
* @param {boolean} [args.defaultActive]
|
* @param {boolean} [args.defaultActive]
|
||||||
* @param {DeepPartial<LineStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [args.options]
|
* @param {DeepPartial<LineStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [args.options]
|
||||||
@@ -577,6 +610,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
name,
|
name,
|
||||||
unit,
|
unit,
|
||||||
order,
|
order,
|
||||||
|
setDataCallback,
|
||||||
color,
|
color,
|
||||||
paneIndex = 0,
|
paneIndex = 0,
|
||||||
defaultActive,
|
defaultActive,
|
||||||
@@ -585,16 +619,19 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
}) {
|
}) {
|
||||||
color ||= unit === "USD" ? colors.green : colors.orange;
|
color ||= unit === "USD" ? colors.green : colors.orange;
|
||||||
|
|
||||||
const iseries = ichart.addSeries(
|
/** @type {ISeriesApi<'Line', number>} */
|
||||||
/** @type {SeriesDefinition<'Line'>} */ (lc.LineSeries),
|
const iseries = /** @type {any} */ (
|
||||||
{
|
ichart.addSeries(
|
||||||
lineWidth: /** @type {any} */ (1.5),
|
/** @type {SeriesDefinition<'Line'>} */ (lc.LineSeries),
|
||||||
visible: defaultActive !== false,
|
{
|
||||||
priceLineVisible: false,
|
lineWidth: /** @type {any} */ (1.5),
|
||||||
color: color(),
|
visible: defaultActive !== false,
|
||||||
...options,
|
priceLineVisible: false,
|
||||||
},
|
color: color(),
|
||||||
paneIndex,
|
...options,
|
||||||
|
},
|
||||||
|
paneIndex
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const priceLineOptions = options?.createPriceLine;
|
const priceLineOptions = options?.createPriceLine;
|
||||||
@@ -610,6 +647,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
paneIndex,
|
paneIndex,
|
||||||
seriesType: "Line",
|
seriesType: "Line",
|
||||||
unit,
|
unit,
|
||||||
|
setDataCallback,
|
||||||
data,
|
data,
|
||||||
defaultActive,
|
defaultActive,
|
||||||
vecId,
|
vecId,
|
||||||
@@ -620,8 +658,9 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
* @param {string} args.name
|
* @param {string} args.name
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @param {number} args.order
|
* @param {number} args.order
|
||||||
* @param {Accessor<BaselineData[]>} [args.data]
|
* @param {Accessor<BaselineData<number>[]>} [args.data]
|
||||||
* @param {VecId} [args.vecId]
|
* @param {VecId} [args.vecId]
|
||||||
|
* @param {SetDataCallback} [args.setDataCallback]
|
||||||
* @param {number} [args.paneIndex]
|
* @param {number} [args.paneIndex]
|
||||||
* @param {boolean} [args.defaultActive]
|
* @param {boolean} [args.defaultActive]
|
||||||
* @param {DeepPartial<BaselineStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [args.options]
|
* @param {DeepPartial<BaselineStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [args.options]
|
||||||
@@ -632,31 +671,35 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
unit,
|
unit,
|
||||||
order,
|
order,
|
||||||
paneIndex: _paneIndex,
|
paneIndex: _paneIndex,
|
||||||
|
setDataCallback,
|
||||||
defaultActive,
|
defaultActive,
|
||||||
data,
|
data,
|
||||||
options,
|
options,
|
||||||
}) {
|
}) {
|
||||||
const paneIndex = _paneIndex ?? 0;
|
const paneIndex = _paneIndex ?? 0;
|
||||||
|
|
||||||
const iseries = ichart.addSeries(
|
/** @type {ISeriesApi<'Baseline', number>} */
|
||||||
/** @type {SeriesDefinition<'Baseline'>} */ (lc.BaselineSeries),
|
const iseries = /** @type {any} */ (
|
||||||
{
|
ichart.addSeries(
|
||||||
lineWidth: /** @type {any} */ (1.5),
|
/** @type {SeriesDefinition<'Baseline'>} */ (lc.BaselineSeries),
|
||||||
visible: defaultActive !== false,
|
{
|
||||||
baseValue: {
|
lineWidth: /** @type {any} */ (1.5),
|
||||||
price: options?.createPriceLine?.value ?? 0,
|
visible: defaultActive !== false,
|
||||||
|
baseValue: {
|
||||||
|
price: options?.createPriceLine?.value ?? 0,
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
topLineColor: options?.topLineColor ?? colors.green(),
|
||||||
|
bottomLineColor: options?.bottomLineColor ?? colors.red(),
|
||||||
|
priceLineVisible: false,
|
||||||
|
bottomFillColor1: "transparent",
|
||||||
|
bottomFillColor2: "transparent",
|
||||||
|
topFillColor1: "transparent",
|
||||||
|
topFillColor2: "transparent",
|
||||||
|
lineVisible: true,
|
||||||
},
|
},
|
||||||
...options,
|
paneIndex
|
||||||
topLineColor: options?.topLineColor ?? colors.green(),
|
)
|
||||||
bottomLineColor: options?.bottomLineColor ?? colors.red(),
|
|
||||||
priceLineVisible: false,
|
|
||||||
bottomFillColor1: "transparent",
|
|
||||||
bottomFillColor2: "transparent",
|
|
||||||
topFillColor1: "transparent",
|
|
||||||
topFillColor2: "transparent",
|
|
||||||
lineVisible: true,
|
|
||||||
},
|
|
||||||
paneIndex,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const priceLineOptions = options?.createPriceLine;
|
const priceLineOptions = options?.createPriceLine;
|
||||||
@@ -674,6 +717,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => {
|
|||||||
order,
|
order,
|
||||||
paneIndex,
|
paneIndex,
|
||||||
seriesType: "Baseline",
|
seriesType: "Baseline",
|
||||||
|
setDataCallback,
|
||||||
unit,
|
unit,
|
||||||
data,
|
data,
|
||||||
defaultActive,
|
defaultActive,
|
||||||
@@ -824,7 +868,7 @@ function createLegend({ signals, utils }) {
|
|||||||
} else {
|
} else {
|
||||||
spanColor.style.backgroundColor = tameColor(color);
|
spanColor.style.backgroundColor = tameColor(color);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -930,7 +974,7 @@ function createPaneHeightObserver({ ichart, paneIndex, signals, utils }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ISeriesApi<SeriesType>} series
|
* @param {ISeriesApi<SeriesType, number>} series
|
||||||
* @param {DeepPartial<CreatePriceLine>} options
|
* @param {DeepPartial<CreatePriceLine>} options
|
||||||
* @param {Colors} colors
|
* @param {Colors} colors
|
||||||
*/
|
*/
|
||||||
@@ -944,18 +988,21 @@ function createPriceLine(series, options, colors) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {number} value */
|
/**
|
||||||
function numberToShortUSFormat(value) {
|
* @param {number} value
|
||||||
|
* @param {0 | 2} [digits]
|
||||||
|
*/
|
||||||
|
function numberToShortUSFormat(value, digits) {
|
||||||
const absoluteValue = Math.abs(value);
|
const absoluteValue = Math.abs(value);
|
||||||
|
|
||||||
if (isNaN(value)) {
|
if (isNaN(value)) {
|
||||||
return "";
|
return "";
|
||||||
} else if (absoluteValue < 10) {
|
} else if (absoluteValue < 10) {
|
||||||
return numberToUSFormat(value, 3);
|
return numberToUSFormat(value, Math.min(3, digits || 10));
|
||||||
} else if (absoluteValue < 1_000) {
|
} else if (absoluteValue < 1_000) {
|
||||||
return numberToUSFormat(value, 2);
|
return numberToUSFormat(value, Math.min(2, digits || 10));
|
||||||
} else if (absoluteValue < 10_000) {
|
} else if (absoluteValue < 10_000) {
|
||||||
return numberToUSFormat(value, 1);
|
return numberToUSFormat(value, Math.min(1, digits || 10));
|
||||||
} else if (absoluteValue < 1_000_000) {
|
} else if (absoluteValue < 1_000_000) {
|
||||||
return numberToUSFormat(value, 0);
|
return numberToUSFormat(value, 0);
|
||||||
} else if (absoluteValue >= 900_000_000_000_000_000) {
|
} else if (absoluteValue >= 900_000_000_000_000_000) {
|
||||||
@@ -978,17 +1025,17 @@ function numberToShortUSFormat(value) {
|
|||||||
if (modulused === 0) {
|
if (modulused === 0) {
|
||||||
return `${numberToUSFormat(
|
return `${numberToUSFormat(
|
||||||
value / (1_000_000 * 1_000 ** letterIndex),
|
value / (1_000_000 * 1_000 ** letterIndex),
|
||||||
3,
|
3
|
||||||
)}${letter}`;
|
)}${letter}`;
|
||||||
} else if (modulused === 1) {
|
} else if (modulused === 1) {
|
||||||
return `${numberToUSFormat(
|
return `${numberToUSFormat(
|
||||||
value / (1_000_000 * 1_000 ** letterIndex),
|
value / (1_000_000 * 1_000 ** letterIndex),
|
||||||
2,
|
2
|
||||||
)}${letter}`;
|
)}${letter}`;
|
||||||
} else {
|
} else {
|
||||||
return `${numberToUSFormat(
|
return `${numberToUSFormat(
|
||||||
value / (1_000_000 * 1_000 ** letterIndex),
|
value / (1_000_000 * 1_000 ** letterIndex),
|
||||||
1,
|
1
|
||||||
)}${letter}`;
|
)}${letter}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1038,7 +1085,7 @@ function createOklchToRGBA() {
|
|||||||
return rgb.map((c) =>
|
return rgb.map((c) =>
|
||||||
Math.abs(c) > 0.0031308
|
Math.abs(c) > 0.0031308
|
||||||
? (c < 0 ? -1 : 1) * (1.055 * Math.abs(c) ** (1 / 2.4) - 0.055)
|
? (c < 0 ? -1 : 1) * (1.055 * Math.abs(c) ** (1 / 2.4) - 0.055)
|
||||||
: 12.92 * c,
|
: 12.92 * c
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -1050,7 +1097,7 @@ function createOklchToRGBA() {
|
|||||||
1, 0.3963377773761749, 0.2158037573099136, 1, -0.1055613458156586,
|
1, 0.3963377773761749, 0.2158037573099136, 1, -0.1055613458156586,
|
||||||
-0.0638541728258133, 1, -0.0894841775298119, -1.2914855480194092,
|
-0.0638541728258133, 1, -0.0894841775298119, -1.2914855480194092,
|
||||||
]),
|
]),
|
||||||
lab,
|
lab
|
||||||
);
|
);
|
||||||
const LMS = /** @type {[number, number, number]} */ (
|
const LMS = /** @type {[number, number, number]} */ (
|
||||||
LMSg.map((val) => val ** 3)
|
LMSg.map((val) => val ** 3)
|
||||||
@@ -1061,7 +1108,7 @@ function createOklchToRGBA() {
|
|||||||
-0.0405757452148008, 1.112286803280317, -0.0717110580655164,
|
-0.0405757452148008, 1.112286803280317, -0.0717110580655164,
|
||||||
-0.0763729366746601, -0.4214933324022432, 1.5869240198367816,
|
-0.0763729366746601, -0.4214933324022432, 1.5869240198367816,
|
||||||
]),
|
]),
|
||||||
LMS,
|
LMS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -1074,7 +1121,7 @@ function createOklchToRGBA() {
|
|||||||
-0.9692436362808796, 1.8759675015077202, 0.04155505740717559,
|
-0.9692436362808796, 1.8759675015077202, 0.04155505740717559,
|
||||||
0.05563007969699366, -0.20397695888897652, 1.0569715142428786,
|
0.05563007969699366, -0.20397695888897652, 1.0569715142428786,
|
||||||
],
|
],
|
||||||
xyz,
|
xyz
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1097,8 +1144,8 @@ function createOklchToRGBA() {
|
|||||||
});
|
});
|
||||||
const rgb = srgbLinear2rgb(
|
const rgb = srgbLinear2rgb(
|
||||||
xyz2rgbLinear(
|
xyz2rgbLinear(
|
||||||
oklab2xyz(oklch2oklab(/** @type {[number, number, number]} */ (lch))),
|
oklab2xyz(oklch2oklab(/** @type {[number, number, number]} */ (lch)))
|
||||||
),
|
)
|
||||||
).map((v) => {
|
).map((v) => {
|
||||||
return Math.max(Math.min(Math.round(v * 255), 255), 0);
|
return Math.max(Math.min(Math.round(v * 255), 255), 0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,13 +31,13 @@ const importSignals = import("./v0.3.2-treeshaked/script.js").then(
|
|||||||
if (dispose) {
|
if (dispose) {
|
||||||
dispose();
|
dispose();
|
||||||
dispose = null;
|
dispose = null;
|
||||||
console.debug("effectCount = ", --effectCount);
|
// console.log("effectCount = ", --effectCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
_signals.createEffect(compute, (v, oldV) => {
|
_signals.createEffect(compute, (v, oldV) => {
|
||||||
console.debug("effectCount = ", ++effectCount);
|
// console.log("effectCount = ", ++effectCount);
|
||||||
cleanup();
|
cleanup();
|
||||||
signals.createRoot((_dispose) => {
|
signals.createRoot((_dispose) => {
|
||||||
dispose = _dispose;
|
dispose = _dispose;
|
||||||
@@ -62,7 +62,7 @@ const importSignals = import("./v0.3.2-treeshaked/script.js").then(
|
|||||||
createSignal(initialValue, options) {
|
createSignal(initialValue, options) {
|
||||||
const [get, set] = this.createSolidSignal(
|
const [get, set] = this.createSolidSignal(
|
||||||
/** @type {any} */ (initialValue),
|
/** @type {any} */ (initialValue),
|
||||||
options,
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -77,13 +77,17 @@ const importSignals = import("./v0.3.2-treeshaked/script.js").then(
|
|||||||
const paramKey = save.key;
|
const paramKey = save.key;
|
||||||
const storageKey = this.createMemo(
|
const storageKey = this.createMemo(
|
||||||
() =>
|
() =>
|
||||||
`${typeof save.keyPrefix === "string" ? save.keyPrefix : save.keyPrefix()}-${paramKey}`,
|
`${
|
||||||
|
typeof save.keyPrefix === "string"
|
||||||
|
? save.keyPrefix
|
||||||
|
: save.keyPrefix()
|
||||||
|
}-${paramKey}`
|
||||||
);
|
);
|
||||||
|
|
||||||
let serialized = /** @type {string | null} */ (null);
|
let serialized = /** @type {string | null} */ (null);
|
||||||
if (options.save.serializeParam !== false) {
|
if (options.save.serializeParam !== false) {
|
||||||
serialized = new URLSearchParams(window.location.search).get(
|
serialized = new URLSearchParams(window.location.search).get(
|
||||||
paramKey,
|
paramKey
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (serialized === null) {
|
if (serialized === null) {
|
||||||
@@ -91,7 +95,7 @@ const importSignals = import("./v0.3.2-treeshaked/script.js").then(
|
|||||||
}
|
}
|
||||||
if (serialized) {
|
if (serialized) {
|
||||||
set(() =>
|
set(() =>
|
||||||
serialized ? save.deserialize(serialized) : initialValue,
|
serialized ? save.deserialize(serialized) : initialValue
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +104,7 @@ const importSignals = import("./v0.3.2-treeshaked/script.js").then(
|
|||||||
if (!firstRun1) {
|
if (!firstRun1) {
|
||||||
serialized = localStorage.getItem(storageKey);
|
serialized = localStorage.getItem(storageKey);
|
||||||
set(() =>
|
set(() =>
|
||||||
serialized ? save.deserialize(serialized) : initialValue,
|
serialized ? save.deserialize(serialized) : initialValue
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
firstRun1 = false;
|
firstRun1 = false;
|
||||||
@@ -146,7 +150,7 @@ const importSignals = import("./v0.3.2-treeshaked/script.js").then(
|
|||||||
};
|
};
|
||||||
|
|
||||||
return signals;
|
return signals;
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,7 +170,7 @@ function writeParam(key, value) {
|
|||||||
window.history.replaceState(
|
window.history.replaceState(
|
||||||
null,
|
null,
|
||||||
"",
|
"",
|
||||||
`${window.location.pathname}?${urlParams.toString()}`,
|
`${window.location.pathname}?${urlParams.toString()}`
|
||||||
);
|
);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const keyPrefix = "chart";
|
const keyPrefix = "chart";
|
||||||
|
const ONE_BTC_IN_SATS = 100_000_000;
|
||||||
|
const AUTO = "auto";
|
||||||
|
const LINE = "line";
|
||||||
|
const CANDLE = "candle";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {Colors} args.colors
|
* @param {Colors} args.colors
|
||||||
* @param {LightweightCharts} args.lightweightCharts
|
* @param {LightweightCharts} args.lightweightCharts
|
||||||
* @param {Accessor<ChartOption>} args.selected
|
* @param {Accessor<ChartOption>} args.option
|
||||||
* @param {Signals} args.signals
|
* @param {Signals} args.signals
|
||||||
* @param {Utilities} args.utils
|
* @param {Utilities} args.utils
|
||||||
* @param {WebSockets} args.webSockets
|
* @param {WebSockets} args.webSockets
|
||||||
@@ -18,7 +22,7 @@ export function init({
|
|||||||
colors,
|
colors,
|
||||||
elements,
|
elements,
|
||||||
lightweightCharts,
|
lightweightCharts,
|
||||||
selected,
|
option,
|
||||||
signals,
|
signals,
|
||||||
utils,
|
utils,
|
||||||
webSockets,
|
webSockets,
|
||||||
@@ -31,10 +35,15 @@ export function init({
|
|||||||
const { headerElement, headingElement } = utils.dom.createHeader();
|
const { headerElement, headingElement } = utils.dom.createHeader();
|
||||||
elements.charts.append(headerElement);
|
elements.charts.append(headerElement);
|
||||||
|
|
||||||
const { index, fieldset } = createIndexSelector({ signals, utils });
|
const { index, fieldset } = createIndexSelector({
|
||||||
|
option,
|
||||||
|
vecIdToIndexes,
|
||||||
|
signals,
|
||||||
|
utils,
|
||||||
|
});
|
||||||
|
|
||||||
const TIMERANGE_LS_KEY = signals.createMemo(
|
const TIMERANGE_LS_KEY = signals.createMemo(
|
||||||
() => `chart-timerange-${index()}`,
|
() => `chart-timerange-${index()}`
|
||||||
);
|
);
|
||||||
|
|
||||||
let firstRun = true;
|
let firstRun = true;
|
||||||
@@ -89,20 +98,37 @@ export function init({
|
|||||||
from.set(t.from);
|
from.set(t.from);
|
||||||
to.set(t.to);
|
to.set(t.to);
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
elements.charts.append(fieldset);
|
elements.charts.append(fieldset);
|
||||||
|
|
||||||
const { field: seriesTypeField, selected: topSeriesType } =
|
const { field: seriesTypeField, selected: topSeriesType_ } =
|
||||||
utils.dom.createHorizontalChoiceField({
|
utils.dom.createHorizontalChoiceField({
|
||||||
defaultValue: "Line",
|
defaultValue: CANDLE,
|
||||||
keyPrefix,
|
keyPrefix,
|
||||||
key: "seriestype-0",
|
key: "seriestype-0",
|
||||||
choices: /** @type {const} */ (["Candles", "Line"]),
|
choices: /** @type {const} */ ([AUTO, CANDLE, LINE]),
|
||||||
signals,
|
signals,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const topSeriesType = signals.createMemo(() => {
|
||||||
|
const topSeriesType = topSeriesType_();
|
||||||
|
if (topSeriesType === AUTO) {
|
||||||
|
const t = to();
|
||||||
|
const f = from();
|
||||||
|
if (!t || !f) return null;
|
||||||
|
const diff = t - f;
|
||||||
|
if (diff / chart.inner.paneSize().width <= 0.5) {
|
||||||
|
return CANDLE;
|
||||||
|
} else {
|
||||||
|
return LINE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return topSeriesType;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const { field: topUnitField, selected: topUnit } =
|
const { field: topUnitField, selected: topUnit } =
|
||||||
utils.dom.createHorizontalChoiceField({
|
utils.dom.createHorizontalChoiceField({
|
||||||
defaultValue: "USD",
|
defaultValue: "USD",
|
||||||
@@ -128,7 +154,89 @@ export function init({
|
|||||||
const seriesListTop = /** @type {Series[]} */ ([]);
|
const seriesListTop = /** @type {Series[]} */ ([]);
|
||||||
const seriesListBottom = /** @type {Series[]} */ ([]);
|
const seriesListBottom = /** @type {Series[]} */ ([]);
|
||||||
|
|
||||||
signals.createEffect(selected, (option) => {
|
/**
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {ISeriesApi<any, number>} params.iseries
|
||||||
|
* @param {Unit} params.unit
|
||||||
|
* @param {Index} params.index
|
||||||
|
*/
|
||||||
|
function printLatest({ iseries, unit, index }) {
|
||||||
|
const _latest = webSockets.kraken1dCandle.latest();
|
||||||
|
|
||||||
|
if (!_latest) return;
|
||||||
|
|
||||||
|
const latest = { ..._latest };
|
||||||
|
|
||||||
|
if (unit === "Sats") {
|
||||||
|
latest.open = Math.floor(ONE_BTC_IN_SATS / latest.open);
|
||||||
|
latest.high = Math.floor(ONE_BTC_IN_SATS / latest.high);
|
||||||
|
latest.low = Math.floor(ONE_BTC_IN_SATS / latest.low);
|
||||||
|
latest.close = Math.floor(ONE_BTC_IN_SATS / latest.close);
|
||||||
|
latest.value = Math.floor(ONE_BTC_IN_SATS / latest.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const last_ = iseries.data().at(-1);
|
||||||
|
if (!last_) return;
|
||||||
|
const last = { ...last_ };
|
||||||
|
|
||||||
|
if ("close" in last) {
|
||||||
|
last.close = latest.close;
|
||||||
|
}
|
||||||
|
if ("value" in last) {
|
||||||
|
last.value = latest.value;
|
||||||
|
}
|
||||||
|
const date = new Date(latest.time * 1000);
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case /** @satisfies {Height} */ (5): {
|
||||||
|
if ("close" in last) {
|
||||||
|
last.low = Math.min(last.low, latest.close);
|
||||||
|
last.high = Math.max(last.high, latest.close);
|
||||||
|
}
|
||||||
|
iseries.update(last);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case /** @satisfies {DateIndex} */ (0): {
|
||||||
|
iseries.update(latest);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
if (index === /** @satisfies {WeekIndex} */ (22)) {
|
||||||
|
date.setUTCDate(date.getUTCDate() - ((date.getUTCDay() + 6) % 7));
|
||||||
|
} else if (index === /** @satisfies {MonthIndex} */ (7)) {
|
||||||
|
date.setUTCDate(1);
|
||||||
|
} else if (index === /** @satisfies {QuarterIndex} */ (19)) {
|
||||||
|
const month = date.getUTCMonth();
|
||||||
|
date.setUTCMonth(month - (month % 3), 1);
|
||||||
|
} else if (index === /** @satisfies {YearIndex} */ (23)) {
|
||||||
|
date.setUTCMonth(0, 1);
|
||||||
|
} else if (index === /** @satisfies {DecadeIndex} */ (1)) {
|
||||||
|
date.setUTCFullYear(
|
||||||
|
Math.floor(date.getUTCFullYear() / 10) * 10,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw Error("Unsupported");
|
||||||
|
}
|
||||||
|
|
||||||
|
const time = date.valueOf() / 1000;
|
||||||
|
|
||||||
|
if (time === last.time) {
|
||||||
|
if ("close" in last) {
|
||||||
|
last.low = Math.min(last.low, latest.low);
|
||||||
|
last.high = Math.max(last.high, latest.high);
|
||||||
|
}
|
||||||
|
iseries.update(last);
|
||||||
|
} else {
|
||||||
|
latest.time = time;
|
||||||
|
iseries.update(latest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signals.createEffect(option, (option) => {
|
||||||
headingElement.innerHTML = option.title;
|
headingElement.innerHTML = option.title;
|
||||||
|
|
||||||
const bottomUnits = /** @type {readonly Unit[]} */ (
|
const bottomUnits = /** @type {readonly Unit[]} */ (
|
||||||
@@ -166,80 +274,92 @@ export function init({
|
|||||||
|
|
||||||
signals.createEffect(index, (index) => {
|
signals.createEffect(index, (index) => {
|
||||||
signals.createEffect(
|
signals.createEffect(
|
||||||
() => [topUnit(), topSeriesType()],
|
() => ({
|
||||||
([topUnit, topSeriesType]) => {
|
topUnit: topUnit(),
|
||||||
|
topSeriesType: topSeriesType(),
|
||||||
|
}),
|
||||||
|
({ topUnit, topSeriesType }) => {
|
||||||
|
/** @type {Series | undefined} */
|
||||||
|
let series;
|
||||||
|
|
||||||
switch (topUnit) {
|
switch (topUnit) {
|
||||||
case "USD": {
|
case "USD": {
|
||||||
switch (topSeriesType) {
|
switch (topSeriesType) {
|
||||||
case "Candles": {
|
case CANDLE: {
|
||||||
const series = chart.addCandlestickSeries({
|
series = chart.addCandlestickSeries({
|
||||||
vecId: "ohlc",
|
vecId: "ohlc",
|
||||||
name: "Price",
|
name: "Price",
|
||||||
unit: topUnit,
|
unit: topUnit,
|
||||||
|
setDataCallback: printLatest,
|
||||||
order: 0,
|
order: 0,
|
||||||
});
|
});
|
||||||
seriesListTop[0]?.remove();
|
|
||||||
seriesListTop[0] = series;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Line": {
|
case LINE: {
|
||||||
const series = chart.addLineSeries({
|
series = chart.addLineSeries({
|
||||||
vecId: "close",
|
vecId: "close",
|
||||||
name: "Price",
|
name: "Price",
|
||||||
unit: topUnit,
|
unit: topUnit,
|
||||||
color: colors.default,
|
color: colors.default,
|
||||||
|
setDataCallback: printLatest,
|
||||||
options: {
|
options: {
|
||||||
priceLineVisible: true,
|
priceLineVisible: true,
|
||||||
},
|
},
|
||||||
order: 0,
|
order: 0,
|
||||||
});
|
});
|
||||||
seriesListTop[0]?.remove();
|
|
||||||
seriesListTop[0] = series;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// signals.createEffect(webSockets.kraken1dCandle.latest, (latest) => {
|
|
||||||
// if (!latest) return;
|
|
||||||
// const last = /** @type { CandlestickData | undefined} */ (
|
|
||||||
// candles.data().at(-1)
|
|
||||||
// );
|
|
||||||
// if (!last) return;
|
|
||||||
// candles?.update({ ...last, close: latest.close });
|
|
||||||
// });
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Sats": {
|
case "Sats": {
|
||||||
switch (topSeriesType) {
|
switch (topSeriesType) {
|
||||||
case "Candles": {
|
case CANDLE: {
|
||||||
const series = chart.addCandlestickSeries({
|
series = chart.addCandlestickSeries({
|
||||||
vecId: "ohlc-in-sats",
|
vecId: "ohlc-in-sats",
|
||||||
name: "Price",
|
name: "Price",
|
||||||
unit: topUnit,
|
unit: topUnit,
|
||||||
inverse: true,
|
inverse: true,
|
||||||
|
setDataCallback: printLatest,
|
||||||
order: 0,
|
order: 0,
|
||||||
});
|
});
|
||||||
seriesListTop[0]?.remove();
|
|
||||||
seriesListTop[0] = series;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Line": {
|
case LINE: {
|
||||||
const series = chart.addLineSeries({
|
series = chart.addLineSeries({
|
||||||
vecId: "close-in-sats",
|
vecId: "close-in-sats",
|
||||||
name: "Price",
|
name: "Price",
|
||||||
unit: topUnit,
|
unit: topUnit,
|
||||||
color: colors.default,
|
color: colors.default,
|
||||||
|
setDataCallback: printLatest,
|
||||||
options: {
|
options: {
|
||||||
priceLineVisible: true,
|
priceLineVisible: true,
|
||||||
},
|
},
|
||||||
order: 0,
|
order: 0,
|
||||||
});
|
});
|
||||||
seriesListTop[0]?.remove();
|
|
||||||
seriesListTop[0] = series;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
if (!series) throw Error("Unreachable");
|
||||||
|
|
||||||
|
seriesListTop[0]?.remove();
|
||||||
|
seriesListTop[0] = series;
|
||||||
|
|
||||||
|
// setDataCallback insimport("./options").tead of hasData
|
||||||
|
signals.createEffect(
|
||||||
|
() => ({
|
||||||
|
latest: webSockets.kraken1dCandle.latest(),
|
||||||
|
hasData: series.hasData(),
|
||||||
|
}),
|
||||||
|
({ latest, hasData }) => {
|
||||||
|
if (!series || !latest || !hasData) return;
|
||||||
|
printLatest({ iseries: series.inner, unit: topUnit, index });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
[
|
[
|
||||||
@@ -300,7 +420,7 @@ export function init({
|
|||||||
blueprint.color?.() ?? blueprint.colors?.[1](),
|
blueprint.color?.() ?? blueprint.colors?.[1](),
|
||||||
},
|
},
|
||||||
order,
|
order,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -318,13 +438,13 @@ export function init({
|
|||||||
paneIndex,
|
paneIndex,
|
||||||
options: blueprint.options,
|
options: blueprint.options,
|
||||||
order,
|
order,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
firstRun = false;
|
firstRun = false;
|
||||||
@@ -334,25 +454,53 @@ export function init({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
|
* @param {Accessor<ChartOption>} args.option
|
||||||
|
* @param {VecIdToIndexes} args.vecIdToIndexes
|
||||||
* @param {Signals} args.signals
|
* @param {Signals} args.signals
|
||||||
* @param {Utilities} args.utils
|
* @param {Utilities} args.utils
|
||||||
*/
|
*/
|
||||||
function createIndexSelector({ signals, utils }) {
|
function createIndexSelector({ option, vecIdToIndexes, signals, utils }) {
|
||||||
|
const choices_ = /** @type {const} */ ([
|
||||||
|
"timestamp",
|
||||||
|
"date",
|
||||||
|
"week",
|
||||||
|
// "difficulty epoch",
|
||||||
|
"month",
|
||||||
|
"quarter",
|
||||||
|
"year",
|
||||||
|
// "halving epoch",
|
||||||
|
"decade",
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** @type {Accessor<typeof choices_>} */
|
||||||
|
const choices = signals.createMemo(() => {
|
||||||
|
const o = option();
|
||||||
|
|
||||||
|
if (!Object.keys(o.top).length && !Object.keys(o.bottom).length) {
|
||||||
|
return [...choices_];
|
||||||
|
}
|
||||||
|
const rawIndexes = new Set(
|
||||||
|
[Object.values(o.top), Object.values(o.bottom)]
|
||||||
|
.flat(2)
|
||||||
|
.map((blueprint) => vecIdToIndexes[blueprint.key])
|
||||||
|
.flat()
|
||||||
|
);
|
||||||
|
|
||||||
|
const serializedIndexes = [...rawIndexes].flatMap((index) => {
|
||||||
|
const c = utils.serde.chartableIndex.serialize(index);
|
||||||
|
return c ? [c] : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
return /** @type {any} */ (
|
||||||
|
choices_.filter((choice) => serializedIndexes.includes(choice))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const { field, selected } = utils.dom.createHorizontalChoiceField({
|
const { field, selected } = utils.dom.createHorizontalChoiceField({
|
||||||
defaultValue: "date",
|
defaultValue: "date",
|
||||||
keyPrefix,
|
keyPrefix,
|
||||||
key: "index",
|
key: "index",
|
||||||
choices: /**@type {const} */ ([
|
choices,
|
||||||
"timestamp",
|
|
||||||
"date",
|
|
||||||
"week",
|
|
||||||
// "difficulty epoch",
|
|
||||||
"month",
|
|
||||||
"quarter",
|
|
||||||
"year",
|
|
||||||
// "halving epoch",
|
|
||||||
"decade",
|
|
||||||
]),
|
|
||||||
id: "index",
|
id: "index",
|
||||||
signals,
|
signals,
|
||||||
});
|
});
|
||||||
@@ -361,25 +509,8 @@ function createIndexSelector({ signals, utils }) {
|
|||||||
fieldset.append(field);
|
fieldset.append(field);
|
||||||
fieldset.dataset.size = "sm";
|
fieldset.dataset.size = "sm";
|
||||||
|
|
||||||
const index = signals.createMemo(
|
const index = signals.createMemo(() =>
|
||||||
/** @returns {ChartableIndex} */ () => {
|
utils.serde.chartableIndex.deserialize(selected())
|
||||||
switch (selected()) {
|
|
||||||
case "timestamp":
|
|
||||||
return /** @satisfies {Height} */ (5);
|
|
||||||
case "date":
|
|
||||||
return /** @satisfies {DateIndex} */ (0);
|
|
||||||
case "week":
|
|
||||||
return /** @satisfies {WeekIndex} */ (22);
|
|
||||||
case "month":
|
|
||||||
return /** @satisfies {MonthIndex} */ (7);
|
|
||||||
case "quarter":
|
|
||||||
return /** @satisfies {QuarterIndex} */ (19);
|
|
||||||
case "year":
|
|
||||||
return /** @satisfies {YearIndex} */ (23);
|
|
||||||
case "decade":
|
|
||||||
return /** @satisfies {DecadeIndex} */ (1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return { fieldset, index };
|
return { fieldset, index };
|
||||||
|
|||||||
+257
-183
@@ -1,7 +1,7 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, ChartableIndex,CreatePriceLineOptions, CreatePriceLine } from "./options"
|
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, ChartableIndex,CreatePriceLineOptions, CreatePriceLine, SeriesType } from "./options"
|
||||||
* @import { Valued, SingleValueData, CandlestickData, ChartData, OHLCTuple, Series } from "../packages/lightweight-charts/wrapper"
|
* @import { Valued, SingleValueData, CandlestickData, ChartData, OHLCTuple, Series } from "../packages/lightweight-charts/wrapper"
|
||||||
* @import * as _ from "../packages/ufuzzy/v1.0.18/types"
|
* @import * as _ from "../packages/ufuzzy/v1.0.18/types"
|
||||||
* @import { createChart as CreateClassicChart, LineStyleOptions, DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, ISeriesApi, Time, LineData, LogicalRange, BaselineStyleOptions, SeriesOptionsCommon, BaselineData, CandlestickStyleOptions } from "../packages/lightweight-charts/v5.0.7-treeshaked/types"
|
* @import { createChart as CreateClassicChart, LineStyleOptions, DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, ISeriesApi, Time, LineData, LogicalRange, BaselineStyleOptions, SeriesOptionsCommon, BaselineData, CandlestickStyleOptions } from "../packages/lightweight-charts/v5.0.7-treeshaked/types"
|
||||||
@@ -67,14 +67,14 @@ function initPackages() {
|
|||||||
const imports = {
|
const imports = {
|
||||||
async signals() {
|
async signals() {
|
||||||
return import("../packages/solid-signals/wrapper.js").then((d) =>
|
return import("../packages/solid-signals/wrapper.js").then((d) =>
|
||||||
d.default.then((d) => d),
|
d.default.then((d) => d)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
async lightweightCharts() {
|
async lightweightCharts() {
|
||||||
return window.document.fonts.ready.then(() =>
|
return window.document.fonts.ready.then(() =>
|
||||||
import("../packages/lightweight-charts/wrapper.js").then((d) =>
|
import("../packages/lightweight-charts/wrapper.js").then((d) =>
|
||||||
d.default.then((d) => d),
|
d.default.then((d) => d)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
async leanQr() {
|
async leanQr() {
|
||||||
@@ -82,7 +82,7 @@ function initPackages() {
|
|||||||
},
|
},
|
||||||
async ufuzzy() {
|
async ufuzzy() {
|
||||||
return import("../packages/ufuzzy/v1.0.18/script.js").then(
|
return import("../packages/ufuzzy/v1.0.18/script.js").then(
|
||||||
({ default: d }) => d,
|
({ default: d }) => d
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -362,11 +362,11 @@ function createUtils() {
|
|||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {T[number]} args.defaultValue
|
* @param {T[number]} args.defaultValue
|
||||||
* @param {string} [args.id]
|
* @param {string} [args.id]
|
||||||
* @param {T} args.choices
|
* @param {T | Accessor<T>} args.choices
|
||||||
* @param {string} [args.keyPrefix]
|
* @param {string} [args.keyPrefix]
|
||||||
* @param {string} args.key
|
* @param {string} args.key
|
||||||
* @param {boolean} [args.sorted]
|
* @param {boolean} [args.sorted]
|
||||||
* @param {{createEffect: CreateEffect, createSignal: Signals["createSignal"]}} args.signals
|
* @param {{createEffect: CreateEffect, createMemo: CreateMemo, createSignal: Signals["createSignal"]}} args.signals
|
||||||
*/
|
*/
|
||||||
createHorizontalChoiceField({
|
createHorizontalChoiceField({
|
||||||
id,
|
id,
|
||||||
@@ -377,13 +377,21 @@ function createUtils() {
|
|||||||
signals,
|
signals,
|
||||||
sorted,
|
sorted,
|
||||||
}) {
|
}) {
|
||||||
const choices = sorted
|
const choices = signals.createMemo(() => {
|
||||||
? /** @type {T} */ (
|
/** @type {T} */
|
||||||
/** @type {any} */ (
|
let c;
|
||||||
unsortedChoices.toSorted((a, b) => a.localeCompare(b))
|
if (typeof unsortedChoices === "function") {
|
||||||
|
c = unsortedChoices();
|
||||||
|
} else {
|
||||||
|
c = unsortedChoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted
|
||||||
|
? /** @type {T} */ (
|
||||||
|
/** @type {any} */ (c.toSorted((a, b) => a.localeCompare(b)))
|
||||||
)
|
)
|
||||||
)
|
: c;
|
||||||
: unsortedChoices;
|
});
|
||||||
|
|
||||||
/** @type {Signal<T[number]>} */
|
/** @type {Signal<T[number]>} */
|
||||||
const selected = signals.createSignal(defaultValue, {
|
const selected = signals.createSignal(defaultValue, {
|
||||||
@@ -394,31 +402,39 @@ function createUtils() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!choices.includes(selected())) {
|
|
||||||
console.log(choices, "don't include", selected());
|
|
||||||
selected.set(() => defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
const field = window.document.createElement("div");
|
const field = window.document.createElement("div");
|
||||||
field.classList.add("field");
|
field.classList.add("field");
|
||||||
|
|
||||||
const div = window.document.createElement("div");
|
const div = window.document.createElement("div");
|
||||||
field.append(div);
|
field.append(div);
|
||||||
|
|
||||||
choices.forEach((choice) => {
|
signals.createEffect(choices, (choices) => {
|
||||||
const inputValue = choice;
|
const s = selected();
|
||||||
const { label } = this.createLabeledInput({
|
if (!choices.includes(s)) {
|
||||||
inputId: `${id ?? key}-${choice.toLowerCase()}`,
|
if (choices.includes(defaultValue)) {
|
||||||
inputName: id ?? key,
|
selected.set(() => defaultValue);
|
||||||
inputValue,
|
} else if (choices.length) {
|
||||||
inputChecked: inputValue === selected(),
|
selected.set(() => choices[0]);
|
||||||
labelTitle: choice,
|
}
|
||||||
type: "radio",
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const text = window.document.createTextNode(choice);
|
div.innerHTML = "";
|
||||||
label.append(text);
|
|
||||||
div.append(label);
|
choices.forEach((choice) => {
|
||||||
|
const inputValue = choice;
|
||||||
|
const { label } = this.createLabeledInput({
|
||||||
|
inputId: `${id ?? key}-${choice.toLowerCase()}`,
|
||||||
|
inputName: id ?? key,
|
||||||
|
inputValue,
|
||||||
|
inputChecked: inputValue === selected(),
|
||||||
|
labelTitle: choice,
|
||||||
|
type: "radio",
|
||||||
|
});
|
||||||
|
|
||||||
|
const text = window.document.createTextNode(choice);
|
||||||
|
label.append(text);
|
||||||
|
div.append(label);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
field.addEventListener("change", (event) => {
|
field.addEventListener("change", (event) => {
|
||||||
@@ -580,7 +596,7 @@ function createUtils() {
|
|||||||
window.history.pushState(
|
window.history.pushState(
|
||||||
null,
|
null,
|
||||||
"",
|
"",
|
||||||
`${pathname}?${urlParams.toString()}`,
|
`${pathname}?${urlParams.toString()}`
|
||||||
);
|
);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
},
|
},
|
||||||
@@ -597,7 +613,7 @@ function createUtils() {
|
|||||||
window.history.replaceState(
|
window.history.replaceState(
|
||||||
null,
|
null,
|
||||||
"",
|
"",
|
||||||
`${pathname}?${urlParams.toString()}`,
|
`${pathname}?${urlParams.toString()}`
|
||||||
);
|
);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
},
|
},
|
||||||
@@ -1093,6 +1109,116 @@ function createUtils() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
index: {
|
||||||
|
/**
|
||||||
|
* @param {Index} v
|
||||||
|
*/
|
||||||
|
serialize(v) {
|
||||||
|
switch (v) {
|
||||||
|
case /** @satisfies {DateIndex} */ (0):
|
||||||
|
return "dateindex";
|
||||||
|
case /** @satisfies {DecadeIndex} */ (1):
|
||||||
|
return "decadeindex";
|
||||||
|
case /** @satisfies {DifficultyEpoch} */ (2):
|
||||||
|
return "difficultyepoch";
|
||||||
|
case /** @satisfies {EmptyOutputIndex} */ (3):
|
||||||
|
return "emptyoutputindex";
|
||||||
|
case /** @satisfies {HalvingEpoch} */ (4):
|
||||||
|
return "halvingepoch";
|
||||||
|
case /** @satisfies {Height} */ (5):
|
||||||
|
return "height";
|
||||||
|
case /** @satisfies {InputIndex} */ (6):
|
||||||
|
return "inputindex";
|
||||||
|
case /** @satisfies {MonthIndex} */ (7):
|
||||||
|
return "monthindex";
|
||||||
|
case /** @satisfies {OpReturnIndex} */ (8):
|
||||||
|
return "opreturnindex";
|
||||||
|
case /** @satisfies {OutputIndex} */ (9):
|
||||||
|
return "outputindex";
|
||||||
|
case /** @satisfies {P2AIndex} */ (10):
|
||||||
|
return "p2aindex";
|
||||||
|
case /** @satisfies {P2MSIndex} */ (11):
|
||||||
|
return "p2msindex";
|
||||||
|
case /** @satisfies {P2PK33Index} */ (12):
|
||||||
|
return "p2pk33index";
|
||||||
|
case /** @satisfies {P2PK65Index} */ (13):
|
||||||
|
return "p2pk65index";
|
||||||
|
case /** @satisfies {P2PKHIndex} */ (14):
|
||||||
|
return "p2pkhindex";
|
||||||
|
case /** @satisfies {P2SHIndex} */ (15):
|
||||||
|
return "p2shindex";
|
||||||
|
case /** @satisfies {P2TRIndex} */ (16):
|
||||||
|
return "p2trindex";
|
||||||
|
case /** @satisfies {P2WPKHIndex} */ (17):
|
||||||
|
return "p2wpkhindex";
|
||||||
|
case /** @satisfies {P2WSHIndex} */ (18):
|
||||||
|
return "p2wshindex";
|
||||||
|
case /** @satisfies {QuarterIndex} */ (19):
|
||||||
|
return "quarterindex";
|
||||||
|
case /** @satisfies {TxIndex} */ (20):
|
||||||
|
return "txindex";
|
||||||
|
case /** @satisfies {UnknownOutputIndex} */ (21):
|
||||||
|
return "unknownoutputindex";
|
||||||
|
case /** @satisfies {WeekIndex} */ (22):
|
||||||
|
return "weekindex";
|
||||||
|
case /** @satisfies {YearIndex} */ (23):
|
||||||
|
return "yearindex";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
chartableIndex: {
|
||||||
|
/**
|
||||||
|
* @param {Index} v
|
||||||
|
*/
|
||||||
|
serialize(v) {
|
||||||
|
switch (v) {
|
||||||
|
case /** @satisfies {DateIndex} */ (0):
|
||||||
|
return "date";
|
||||||
|
case /** @satisfies {DecadeIndex} */ (1):
|
||||||
|
return "decade";
|
||||||
|
// case /** @satisfies {DifficultyEpoch} */ (2):
|
||||||
|
// return "difficulty";
|
||||||
|
// case /** @satisfies {HalvingEpoch} */ (4):
|
||||||
|
// return "halving";
|
||||||
|
case /** @satisfies {Height} */ (5):
|
||||||
|
return "timestamp";
|
||||||
|
case /** @satisfies {MonthIndex} */ (7):
|
||||||
|
return "month";
|
||||||
|
case /** @satisfies {QuarterIndex} */ (19):
|
||||||
|
return "quarter";
|
||||||
|
case /** @satisfies {WeekIndex} */ (22):
|
||||||
|
return "week";
|
||||||
|
case /** @satisfies {YearIndex} */ (23):
|
||||||
|
return "year";
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {string} v
|
||||||
|
* @returns {ChartableIndex}
|
||||||
|
*/
|
||||||
|
deserialize(v) {
|
||||||
|
switch (v) {
|
||||||
|
case "timestamp":
|
||||||
|
return /** @satisfies {Height} */ (5);
|
||||||
|
case "date":
|
||||||
|
return /** @satisfies {DateIndex} */ (0);
|
||||||
|
case "week":
|
||||||
|
return /** @satisfies {WeekIndex} */ (22);
|
||||||
|
case "month":
|
||||||
|
return /** @satisfies {MonthIndex} */ (7);
|
||||||
|
case "quarter":
|
||||||
|
return /** @satisfies {QuarterIndex} */ (19);
|
||||||
|
case "year":
|
||||||
|
return /** @satisfies {YearIndex} */ (23);
|
||||||
|
case "decade":
|
||||||
|
return /** @satisfies {DecadeIndex} */ (1);
|
||||||
|
default:
|
||||||
|
throw Error("Unsupported");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatters = {
|
const formatters = {
|
||||||
@@ -1120,8 +1246,8 @@ function createUtils() {
|
|||||||
today.getUTCDate(),
|
today.getUTCDate(),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -1223,7 +1349,7 @@ function createUtils() {
|
|||||||
*/
|
*/
|
||||||
function getNumberOfDaysBetweenTwoDates(oldest, youngest) {
|
function getNumberOfDaysBetweenTwoDates(oldest, youngest) {
|
||||||
return Math.round(
|
return Math.round(
|
||||||
Math.abs((youngest.getTime() - oldest.getTime()) / date.ONE_DAY_IN_MS),
|
Math.abs((youngest.getTime() - oldest.getTime()) / date.ONE_DAY_IN_MS)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1333,62 +1459,6 @@ function createUtils() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Index} index
|
|
||||||
*/
|
|
||||||
function vecIndexToString(index) {
|
|
||||||
switch (index) {
|
|
||||||
case /** @satisfies {DateIndex} */ (0):
|
|
||||||
return "dateindex";
|
|
||||||
case /** @satisfies {DecadeIndex} */ (1):
|
|
||||||
return "decadeindex";
|
|
||||||
case /** @satisfies {DifficultyEpoch} */ (2):
|
|
||||||
return "difficultyepoch";
|
|
||||||
case /** @satisfies {EmptyOutputIndex} */ (3):
|
|
||||||
return "emptyoutputindex";
|
|
||||||
case /** @satisfies {HalvingEpoch} */ (4):
|
|
||||||
return "halvingepoch";
|
|
||||||
case /** @satisfies {Height} */ (5):
|
|
||||||
return "height";
|
|
||||||
case /** @satisfies {InputIndex} */ (6):
|
|
||||||
return "inputindex";
|
|
||||||
case /** @satisfies {MonthIndex} */ (7):
|
|
||||||
return "monthindex";
|
|
||||||
case /** @satisfies {OpReturnIndex} */ (8):
|
|
||||||
return "opreturnindex";
|
|
||||||
case /** @satisfies {OutputIndex} */ (9):
|
|
||||||
return "outputindex";
|
|
||||||
case /** @satisfies {P2AIndex} */ (10):
|
|
||||||
return "p2aindex";
|
|
||||||
case /** @satisfies {P2MSIndex} */ (11):
|
|
||||||
return "p2msindex";
|
|
||||||
case /** @satisfies {P2PK33Index} */ (12):
|
|
||||||
return "p2pk33index";
|
|
||||||
case /** @satisfies {P2PK65Index} */ (13):
|
|
||||||
return "p2pk65index";
|
|
||||||
case /** @satisfies {P2PKHIndex} */ (14):
|
|
||||||
return "p2pkhindex";
|
|
||||||
case /** @satisfies {P2SHIndex} */ (15):
|
|
||||||
return "p2shindex";
|
|
||||||
case /** @satisfies {P2TRIndex} */ (16):
|
|
||||||
return "p2trindex";
|
|
||||||
case /** @satisfies {P2WPKHIndex} */ (17):
|
|
||||||
return "p2wpkhindex";
|
|
||||||
case /** @satisfies {P2WSHIndex} */ (18):
|
|
||||||
return "p2wshindex";
|
|
||||||
case /** @satisfies {QuarterIndex} */ (19):
|
|
||||||
return "quarterindex";
|
|
||||||
case /** @satisfies {TxIndex} */ (20):
|
|
||||||
return "txindex";
|
|
||||||
case /** @satisfies {UnknownOutputIndex} */ (21):
|
|
||||||
return "unknownoutputindex";
|
|
||||||
case /** @satisfies {WeekIndex} */ (22):
|
|
||||||
return "weekindex";
|
|
||||||
case /** @satisfies {YearIndex} */ (23):
|
|
||||||
return "yearindex";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Index} index
|
* @param {Index} index
|
||||||
* @param {VecId} vecId
|
* @param {VecId} vecId
|
||||||
@@ -1396,7 +1466,7 @@ function createUtils() {
|
|||||||
* @param {number} [to]
|
* @param {number} [to]
|
||||||
*/
|
*/
|
||||||
function genPath(index, vecId, from, to) {
|
function genPath(index, vecId, from, to) {
|
||||||
let path = `/query?index=${vecIndexToString(index)}&values=${vecId}`;
|
let path = `/query?index=${serde.index.serialize(index)}&values=${vecId}`;
|
||||||
if (from !== undefined) {
|
if (from !== undefined) {
|
||||||
path += `&from=${from}`;
|
path += `&from=${from}`;
|
||||||
}
|
}
|
||||||
@@ -1494,8 +1564,11 @@ function createVecsResources(signals, utils) {
|
|||||||
return signals.runWithOwner(owner, () => {
|
return signals.runWithOwner(owner, () => {
|
||||||
/** @typedef {T extends number ? SingleValueData : CandlestickData} Value */
|
/** @typedef {T extends number ? SingleValueData : CandlestickData} Value */
|
||||||
|
|
||||||
const fetchedRecord =
|
const fetchedRecord = signals.createSignal(
|
||||||
/** @type {Record<string, {loading: boolean, at: Date | null, vec: Signal<T[] | null>}>} */ ({});
|
/** @type {Map<string, {loading: boolean, at: Date | null, vec: Signal<T[] | null>}>} */ (
|
||||||
|
new Map()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: utils.api.genUrl(index, id, defaultFrom),
|
url: utils.api.genUrl(index, id, defaultFrom),
|
||||||
@@ -1513,12 +1586,18 @@ function createVecsResources(signals, utils) {
|
|||||||
const from = args?.from ?? defaultFrom;
|
const from = args?.from ?? defaultFrom;
|
||||||
const to = args?.to ?? defaultTo;
|
const to = args?.to ?? defaultTo;
|
||||||
const fetchedKey = genFetchedKey({ from, to });
|
const fetchedKey = genFetchedKey({ from, to });
|
||||||
fetchedRecord[fetchedKey] ??= {
|
if (!fetchedRecord().has(fetchedKey)) {
|
||||||
loading: false,
|
fetchedRecord.set((map) => {
|
||||||
at: null,
|
map.set(fetchedKey, {
|
||||||
vec: signals.createSignal(/** @type {T[] | null} */ (null)),
|
loading: false,
|
||||||
};
|
at: null,
|
||||||
const fetched = fetchedRecord[fetchedKey];
|
vec: signals.createSignal(/** @type {T[] | null} */ (null)),
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const fetched = fetchedRecord().get(fetchedKey);
|
||||||
|
if (!fetched) throw Error("Unreachable");
|
||||||
if (fetched.loading) return fetched.vec();
|
if (fetched.loading) return fetched.vec();
|
||||||
if (fetched.at) {
|
if (fetched.at) {
|
||||||
const diff = new Date().getTime() - fetched.at.getTime();
|
const diff = new Date().getTime() - fetched.at.getTime();
|
||||||
@@ -1529,12 +1608,14 @@ function createVecsResources(signals, utils) {
|
|||||||
const res = /** @type {T[] | null} */ (
|
const res = /** @type {T[] | null} */ (
|
||||||
await utils.api.fetchVec(
|
await utils.api.fetchVec(
|
||||||
(values) => {
|
(values) => {
|
||||||
fetched.vec.set(/** @type {T[]} */ (values));
|
if (values.length || !fetched.vec()) {
|
||||||
|
fetched.vec.set(values);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
index,
|
index,
|
||||||
id,
|
id,
|
||||||
from,
|
from,
|
||||||
to,
|
to
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
fetched.at = new Date();
|
fetched.at = new Date();
|
||||||
@@ -1795,7 +1876,7 @@ function initWebSockets(signals, utils) {
|
|||||||
|
|
||||||
window.document.addEventListener(
|
window.document.addEventListener(
|
||||||
"visibilitychange",
|
"visibilitychange",
|
||||||
reinitWebSocketIfDocumentNotHidden,
|
reinitWebSocketIfDocumentNotHidden
|
||||||
);
|
);
|
||||||
|
|
||||||
window.document.addEventListener("online", reinitWebSocket);
|
window.document.addEventListener("online", reinitWebSocket);
|
||||||
@@ -1804,7 +1885,7 @@ function initWebSockets(signals, utils) {
|
|||||||
ws?.close();
|
ws?.close();
|
||||||
window.document.removeEventListener(
|
window.document.removeEventListener(
|
||||||
"visibilitychange",
|
"visibilitychange",
|
||||||
reinitWebSocketIfDocumentNotHidden,
|
reinitWebSocketIfDocumentNotHidden
|
||||||
);
|
);
|
||||||
window.document.removeEventListener("online", reinitWebSocket);
|
window.document.removeEventListener("online", reinitWebSocket);
|
||||||
live.set(false);
|
live.set(false);
|
||||||
@@ -1817,9 +1898,8 @@ function initWebSockets(signals, utils) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {(candle: CandlestickData) => void} callback
|
* @param {(candle: CandlestickData) => void} callback
|
||||||
* @param {number} interval
|
|
||||||
*/
|
*/
|
||||||
function krakenCandleWebSocketCreator(callback, interval) {
|
function krakenCandleWebSocketCreator(callback) {
|
||||||
const ws = new WebSocket("wss://ws.kraken.com/v2");
|
const ws = new WebSocket("wss://ws.kraken.com/v2");
|
||||||
|
|
||||||
ws.addEventListener("open", () => {
|
ws.addEventListener("open", () => {
|
||||||
@@ -1831,7 +1911,7 @@ function initWebSockets(signals, utils) {
|
|||||||
symbol: ["BTC/USD"],
|
symbol: ["BTC/USD"],
|
||||||
interval: 1440,
|
interval: 1440,
|
||||||
},
|
},
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1842,14 +1922,10 @@ function initWebSockets(signals, utils) {
|
|||||||
|
|
||||||
const { interval_begin, open, high, low, close } = result.data.at(-1);
|
const { interval_begin, open, high, low, close } = result.data.at(-1);
|
||||||
|
|
||||||
const date = new Date(interval_begin);
|
|
||||||
|
|
||||||
const dateStr = utils.date.toString(date);
|
|
||||||
|
|
||||||
/** @type {CandlestickData} */
|
/** @type {CandlestickData} */
|
||||||
const candle = {
|
const candle = {
|
||||||
index: -1,
|
index: -1,
|
||||||
time: dateStr,
|
time: new Date(interval_begin).valueOf() / 1000,
|
||||||
open: Number(open),
|
open: Number(open),
|
||||||
high: Number(high),
|
high: Number(high),
|
||||||
low: Number(low),
|
low: Number(low),
|
||||||
@@ -1863,8 +1939,9 @@ function initWebSockets(signals, utils) {
|
|||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {ReturnType<typeof createWebsocket<CandlestickData>>} */
|
||||||
const kraken1dCandle = createWebsocket((callback) =>
|
const kraken1dCandle = createWebsocket((callback) =>
|
||||||
krakenCandleWebSocketCreator(callback, 1440),
|
krakenCandleWebSocketCreator(callback)
|
||||||
);
|
);
|
||||||
|
|
||||||
kraken1dCandle.open();
|
kraken1dCandle.open();
|
||||||
@@ -1923,7 +2000,7 @@ function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const frame = window.document.getElementById(
|
const frame = window.document.getElementById(
|
||||||
/** @type {string} */ (input.value),
|
/** @type {string} */ (input.value)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
@@ -2021,23 +2098,23 @@ function main() {
|
|||||||
|
|
||||||
function initDark() {
|
function initDark() {
|
||||||
const preferredColorSchemeMatchMedia = window.matchMedia(
|
const preferredColorSchemeMatchMedia = window.matchMedia(
|
||||||
"(prefers-color-scheme: dark)",
|
"(prefers-color-scheme: dark)"
|
||||||
);
|
);
|
||||||
const dark = signals.createSignal(
|
const dark = signals.createSignal(
|
||||||
preferredColorSchemeMatchMedia.matches,
|
preferredColorSchemeMatchMedia.matches
|
||||||
);
|
);
|
||||||
preferredColorSchemeMatchMedia.addEventListener(
|
preferredColorSchemeMatchMedia.addEventListener(
|
||||||
"change",
|
"change",
|
||||||
({ matches }) => {
|
({ matches }) => {
|
||||||
dark.set(matches);
|
dark.set(matches);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
return dark;
|
return dark;
|
||||||
}
|
}
|
||||||
const dark = initDark();
|
const dark = initDark();
|
||||||
|
|
||||||
const qrcode = signals.createSignal(
|
const qrcode = signals.createSignal(
|
||||||
/** @type {string | null} */ (null),
|
/** @type {string | null} */ (null)
|
||||||
);
|
);
|
||||||
|
|
||||||
function createLastHeightResource() {
|
function createLastHeightResource() {
|
||||||
@@ -2048,7 +2125,7 @@ function main() {
|
|||||||
lastHeight.set(h);
|
lastHeight.set(h);
|
||||||
},
|
},
|
||||||
/** @satisfies {Height} */ (5),
|
/** @satisfies {Height} */ (5),
|
||||||
"height",
|
"height"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
fetchLastHeight();
|
fetchLastHeight();
|
||||||
@@ -2095,10 +2172,10 @@ function main() {
|
|||||||
const owner = signals.getOwner();
|
const owner = signals.getOwner();
|
||||||
|
|
||||||
const chartOption = signals.createSignal(
|
const chartOption = signals.createSignal(
|
||||||
/** @type {ChartOption | null} */ (null),
|
/** @type {ChartOption | null} */ (null)
|
||||||
);
|
);
|
||||||
const simOption = signals.createSignal(
|
const simOption = signals.createSignal(
|
||||||
/** @type {SimulationOption | null} */ (null),
|
/** @type {SimulationOption | null} */ (null)
|
||||||
);
|
);
|
||||||
|
|
||||||
let previousElement = /** @type {HTMLElement | undefined} */ (
|
let previousElement = /** @type {HTMLElement | undefined} */ (
|
||||||
@@ -2137,7 +2214,7 @@ function main() {
|
|||||||
colors,
|
colors,
|
||||||
elements,
|
elements,
|
||||||
lightweightCharts,
|
lightweightCharts,
|
||||||
selected: /** @type {Accessor<ChartOption>} */ (
|
option: /** @type {Accessor<ChartOption>} */ (
|
||||||
chartOption
|
chartOption
|
||||||
),
|
),
|
||||||
signals,
|
signals,
|
||||||
@@ -2145,10 +2222,10 @@ function main() {
|
|||||||
webSockets,
|
webSockets,
|
||||||
vecsResources,
|
vecsResources,
|
||||||
vecIdToIndexes,
|
vecIdToIndexes,
|
||||||
}),
|
})
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
firstTimeLoadingChart = false;
|
firstTimeLoadingChart = false;
|
||||||
@@ -2171,9 +2248,9 @@ function main() {
|
|||||||
vecsResources,
|
vecsResources,
|
||||||
option,
|
option,
|
||||||
vecIdToIndexes,
|
vecIdToIndexes,
|
||||||
}),
|
})
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
firstTimeLoadingTable = false;
|
firstTimeLoadingTable = false;
|
||||||
@@ -2201,10 +2278,10 @@ function main() {
|
|||||||
signals,
|
signals,
|
||||||
utils,
|
utils,
|
||||||
vecsResources,
|
vecsResources,
|
||||||
}),
|
})
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
firstTimeLoadingSimulation = false;
|
firstTimeLoadingSimulation = false;
|
||||||
@@ -2233,55 +2310,52 @@ function main() {
|
|||||||
createMobileSwitchEffect();
|
createMobileSwitchEffect();
|
||||||
|
|
||||||
utils.dom.onFirstIntersection(elements.aside, () =>
|
utils.dom.onFirstIntersection(elements.aside, () =>
|
||||||
signals.runWithOwner(owner, initSelectedFrame),
|
signals.runWithOwner(owner, initSelectedFrame)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
initSelected();
|
initSelected();
|
||||||
|
|
||||||
function initFolders() {
|
function initFolders() {
|
||||||
function initTreeElement() {
|
// async function scrollToSelected() {
|
||||||
|
// if (!options.selected()) throw "Selected should be set by now";
|
||||||
|
// const selectedId = options.selected().id;
|
||||||
|
|
||||||
|
// const path = options.selected().path;
|
||||||
|
|
||||||
|
// let i = 0;
|
||||||
|
// while (i !== path.length) {
|
||||||
|
// try {
|
||||||
|
// const id = path[i];
|
||||||
|
// const details = /** @type {HTMLDetailsElement} */ (
|
||||||
|
// utils.dom.getElementById(id)
|
||||||
|
// );
|
||||||
|
// details.open = true;
|
||||||
|
// i++;
|
||||||
|
// } catch {
|
||||||
|
// await utils.next();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// await utils.next();
|
||||||
|
// await utils.next();
|
||||||
|
|
||||||
|
// utils.dom
|
||||||
|
// .getElementById(`${selectedId}-nav-selector`)
|
||||||
|
// .scrollIntoView({
|
||||||
|
// behavior: "instant",
|
||||||
|
// block: "center",
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
utils.dom.onFirstIntersection(elements.nav, () => {
|
||||||
options.treeElement.set(() => {
|
options.treeElement.set(() => {
|
||||||
const treeElement = window.document.createElement("div");
|
const treeElement = window.document.createElement("div");
|
||||||
treeElement.classList.add("tree");
|
treeElement.classList.add("tree");
|
||||||
elements.nav.append(treeElement);
|
elements.nav.append(treeElement);
|
||||||
return treeElement;
|
return treeElement;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
async function scrollToSelected() {
|
// setTimeout(scrollToSelected, 10);
|
||||||
if (!options.selected()) throw "Selected should be set by now";
|
|
||||||
const selectedId = options.selected().id;
|
|
||||||
|
|
||||||
const path = options.selected().path;
|
|
||||||
|
|
||||||
let i = 0;
|
|
||||||
while (i !== path.length) {
|
|
||||||
try {
|
|
||||||
const id = path[i];
|
|
||||||
const details = /** @type {HTMLDetailsElement} */ (
|
|
||||||
utils.dom.getElementById(id)
|
|
||||||
);
|
|
||||||
details.open = true;
|
|
||||||
i++;
|
|
||||||
} catch {
|
|
||||||
await utils.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await utils.next();
|
|
||||||
|
|
||||||
utils.dom
|
|
||||||
.getElementById(`${selectedId}-nav-selector`)
|
|
||||||
.scrollIntoView({
|
|
||||||
behavior: "instant",
|
|
||||||
block: "center",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.dom.onFirstIntersection(elements.nav, () => {
|
|
||||||
console.log("nav: init");
|
|
||||||
initTreeElement();
|
|
||||||
scrollToSelected();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
initFolders();
|
initFolders();
|
||||||
@@ -2314,7 +2388,7 @@ function main() {
|
|||||||
if (indexes?.length) {
|
if (indexes?.length) {
|
||||||
const maxIndex = Math.min(
|
const maxIndex = Math.min(
|
||||||
(order || indexes).length - 1,
|
(order || indexes).length - 1,
|
||||||
minIndex + RESULTS_PER_PAGE - 1,
|
minIndex + RESULTS_PER_PAGE - 1
|
||||||
);
|
);
|
||||||
|
|
||||||
list = Array(maxIndex - minIndex + 1);
|
list = Array(maxIndex - minIndex + 1);
|
||||||
@@ -2390,7 +2464,7 @@ function main() {
|
|||||||
haystack,
|
haystack,
|
||||||
needle,
|
needle,
|
||||||
undefined,
|
undefined,
|
||||||
infoThresh,
|
infoThresh
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!result?.[0]?.length || !result?.[1]) {
|
if (!result?.[0]?.length || !result?.[1]) {
|
||||||
@@ -2398,7 +2472,7 @@ function main() {
|
|||||||
haystack,
|
haystack,
|
||||||
needle,
|
needle,
|
||||||
outOfOrder,
|
outOfOrder,
|
||||||
infoThresh,
|
infoThresh
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2407,7 +2481,7 @@ function main() {
|
|||||||
haystack,
|
haystack,
|
||||||
needle,
|
needle,
|
||||||
outOfOrder,
|
outOfOrder,
|
||||||
infoThresh,
|
infoThresh
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2416,7 +2490,7 @@ function main() {
|
|||||||
haystack,
|
haystack,
|
||||||
needle,
|
needle,
|
||||||
outOfOrder,
|
outOfOrder,
|
||||||
infoThresh,
|
infoThresh
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2425,7 +2499,7 @@ function main() {
|
|||||||
haystack,
|
haystack,
|
||||||
needle,
|
needle,
|
||||||
undefined,
|
undefined,
|
||||||
infoThresh,
|
infoThresh
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2434,7 +2508,7 @@ function main() {
|
|||||||
haystack,
|
haystack,
|
||||||
needle,
|
needle,
|
||||||
outOfOrder,
|
outOfOrder,
|
||||||
infoThresh,
|
infoThresh
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2517,7 +2591,7 @@ function main() {
|
|||||||
|
|
||||||
shareDiv.hidden = false;
|
shareDiv.hidden = false;
|
||||||
});
|
});
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
initShare();
|
initShare();
|
||||||
@@ -2539,7 +2613,7 @@ function main() {
|
|||||||
localStorage.setItem(barWidthLocalStorageKey, String(width));
|
localStorage.setItem(barWidthLocalStorageKey, String(width));
|
||||||
} else {
|
} else {
|
||||||
elements.main.style.width = elements.style.getPropertyValue(
|
elements.main.style.width = elements.style.getPropertyValue(
|
||||||
"--default-main-width",
|
"--default-main-width"
|
||||||
);
|
);
|
||||||
localStorage.removeItem(barWidthLocalStorageKey);
|
localStorage.removeItem(barWidthLocalStorageKey);
|
||||||
}
|
}
|
||||||
@@ -2575,9 +2649,9 @@ function main() {
|
|||||||
window.addEventListener("mouseleave", setResizeFalse);
|
window.addEventListener("mouseleave", setResizeFalse);
|
||||||
}
|
}
|
||||||
initDesktopResizeBar();
|
initDesktopResizeBar();
|
||||||
}),
|
})
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
main();
|
main();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Height | DateIndex | WeekIndex | DifficultyEpoch | MonthIndex | QuarterIndex | YearIndex | HalvingEpoch | DecadeIndex} ChartableIndex
|
* @typedef {Height | DateIndex | WeekIndex | MonthIndex | QuarterIndex | YearIndex | DecadeIndex} ChartableIndex
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @template {readonly unknown[]} T
|
* @template {readonly unknown[]} T
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
* @property {Color} [color]
|
* @property {Color} [color]
|
||||||
* @property {[Color, Color]} [colors]
|
* @property {[Color, Color]} [colors]
|
||||||
* @property {DeepPartial<BaselineStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [options]
|
* @property {DeepPartial<BaselineStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [options]
|
||||||
* @property {Accessor<BaselineData[]>} [data]
|
* @property {Accessor<BaselineData<number>[]>} [data]
|
||||||
* @typedef {BaseSeriesBlueprint & BaselineSeriesBlueprintSpecific} BaselineSeriesBlueprint
|
* @typedef {BaseSeriesBlueprint & BaselineSeriesBlueprintSpecific} BaselineSeriesBlueprint
|
||||||
*
|
*
|
||||||
* @typedef {Object} CandlestickSeriesBlueprintSpecific
|
* @typedef {Object} CandlestickSeriesBlueprintSpecific
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
* @property {"Line"} [type]
|
* @property {"Line"} [type]
|
||||||
* @property {Color} [color]
|
* @property {Color} [color]
|
||||||
* @property {DeepPartial<LineStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [options]
|
* @property {DeepPartial<LineStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [options]
|
||||||
* @property {Accessor<LineData[]>} [data]
|
* @property {Accessor<LineData<number>[]>} [data]
|
||||||
* @typedef {BaseSeriesBlueprint & LineSeriesBlueprintSpecific} LineSeriesBlueprint
|
* @typedef {BaseSeriesBlueprint & LineSeriesBlueprintSpecific} LineSeriesBlueprint
|
||||||
*
|
*
|
||||||
* @typedef {BaselineSeriesBlueprint | CandlestickSeriesBlueprint | LineSeriesBlueprint} AnySeriesBlueprint
|
* @typedef {BaselineSeriesBlueprint | CandlestickSeriesBlueprint | LineSeriesBlueprint} AnySeriesBlueprint
|
||||||
@@ -1430,7 +1430,7 @@ function createPartialOptions(colors) {
|
|||||||
key: `${fixKey(key)}realized-price`,
|
key: `${fixKey(key)}realized-price`,
|
||||||
name,
|
name,
|
||||||
color,
|
color,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
: createPriceWithRatio({
|
: createPriceWithRatio({
|
||||||
@@ -1512,7 +1512,9 @@ function createPartialOptions(colors) {
|
|||||||
}),
|
}),
|
||||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||||
type: "Baseline",
|
type: "Baseline",
|
||||||
key: `${fixKey(key)}net-realized-profit-and-loss-relative-to-realized-cap`,
|
key: `${fixKey(
|
||||||
|
key
|
||||||
|
)}net-realized-profit-and-loss-relative-to-realized-cap`,
|
||||||
title: useGroupName ? name : "Net",
|
title: useGroupName ? name : "Net",
|
||||||
color: useGroupName ? color : undefined,
|
color: useGroupName ? color : undefined,
|
||||||
options: {
|
options: {
|
||||||
@@ -1581,7 +1583,9 @@ function createPartialOptions(colors) {
|
|||||||
bottom: list.flatMap(({ color, name, key }) => [
|
bottom: list.flatMap(({ color, name, key }) => [
|
||||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||||
type: "Baseline",
|
type: "Baseline",
|
||||||
key: `${fixKey(key)}adjusted-spent-output-profit-ratio`,
|
key: `${fixKey(
|
||||||
|
key
|
||||||
|
)}adjusted-spent-output-profit-ratio`,
|
||||||
title: useGroupName ? name : "asopr",
|
title: useGroupName ? name : "asopr",
|
||||||
color: useGroupName ? color : undefined,
|
color: useGroupName ? color : undefined,
|
||||||
options: {
|
options: {
|
||||||
@@ -1603,7 +1607,7 @@ function createPartialOptions(colors) {
|
|||||||
key: `${fixKey(key)}sell-side-risk-ratio`,
|
key: `${fixKey(key)}sell-side-risk-ratio`,
|
||||||
name: useGroupName ? name : "Risk",
|
name: useGroupName ? name : "Risk",
|
||||||
color: color,
|
color: color,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -1691,7 +1695,9 @@ function createPartialOptions(colors) {
|
|||||||
}),
|
}),
|
||||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||||
type: "Baseline",
|
type: "Baseline",
|
||||||
key: `${fixKey(key)}net-unrealized-profit-and-loss-relative-to-market-cap`,
|
key: `${fixKey(
|
||||||
|
key
|
||||||
|
)}net-unrealized-profit-and-loss-relative-to-market-cap`,
|
||||||
title: useGroupName ? name : "Net",
|
title: useGroupName ? name : "Net",
|
||||||
color: useGroupName ? color : undefined,
|
color: useGroupName ? color : undefined,
|
||||||
options: {
|
options: {
|
||||||
@@ -1868,7 +1874,7 @@ function createPartialOptions(colors) {
|
|||||||
key: `${key}-sma`,
|
key: `${key}-sma`,
|
||||||
name: key,
|
name: key,
|
||||||
color,
|
color,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
...averages.map(({ key, name, color }) =>
|
...averages.map(({ key, name, color }) =>
|
||||||
@@ -1878,7 +1884,7 @@ function createPartialOptions(colors) {
|
|||||||
title: `${name} Market Price Moving Average`,
|
title: `${name} Market Price Moving Average`,
|
||||||
legend: "average",
|
legend: "average",
|
||||||
color,
|
color,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -1969,7 +1975,7 @@ function createPartialOptions(colors) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
.../** @type {const} */ ([
|
.../** @type {const} */ ([
|
||||||
{ name: "2 Year", key: "2y" },
|
{ name: "2 Year", key: "2y" },
|
||||||
@@ -2040,7 +2046,7 @@ function createPartialOptions(colors) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -2056,7 +2062,7 @@ function createPartialOptions(colors) {
|
|||||||
name: `${year}`,
|
name: `${year}`,
|
||||||
color,
|
color,
|
||||||
defaultActive,
|
defaultActive,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
...dcaClasses.map(
|
...dcaClasses.map(
|
||||||
@@ -2083,7 +2089,7 @@ function createPartialOptions(colors) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -2144,10 +2150,10 @@ function createPartialOptions(colors) {
|
|||||||
bottom: [
|
bottom: [
|
||||||
...createAverageSumCumulativeMinMaxPercentilesSeries("fee"),
|
...createAverageSumCumulativeMinMaxPercentilesSeries("fee"),
|
||||||
...createAverageSumCumulativeMinMaxPercentilesSeries(
|
...createAverageSumCumulativeMinMaxPercentilesSeries(
|
||||||
"fee-in-btc",
|
"fee-in-btc"
|
||||||
),
|
),
|
||||||
...createAverageSumCumulativeMinMaxPercentilesSeries(
|
...createAverageSumCumulativeMinMaxPercentilesSeries(
|
||||||
"fee-in-usd",
|
"fee-in-usd"
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -2911,7 +2917,7 @@ export function initOptions({
|
|||||||
const detailsList = [];
|
const detailsList = [];
|
||||||
|
|
||||||
const treeElement = signals.createSignal(
|
const treeElement = signals.createSignal(
|
||||||
/** @type {HTMLDivElement | null} */ (null),
|
/** @type {HTMLDivElement | null} */ (null)
|
||||||
);
|
);
|
||||||
|
|
||||||
/** @type {string[] | undefined} */
|
/** @type {string[] | undefined} */
|
||||||
@@ -3030,7 +3036,7 @@ export function initOptions({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
null,
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
partialTree.forEach((anyPartial, partialIndex) => {
|
partialTree.forEach((anyPartial, partialIndex) => {
|
||||||
@@ -3053,7 +3059,7 @@ export function initOptions({
|
|||||||
|
|
||||||
if ("tree" in anyPartial) {
|
if ("tree" in anyPartial) {
|
||||||
const folderId = utils.stringToId(
|
const folderId = utils.stringToId(
|
||||||
`${(path || []).join(" ")} ${anyPartial.name} folder`,
|
`${(path || []).join(" ")} ${anyPartial.name} folder`
|
||||||
);
|
);
|
||||||
|
|
||||||
/** @type {Omit<OptionsGroup, keyof PartialOptionsGroup>} */
|
/** @type {Omit<OptionsGroup, keyof PartialOptionsGroup>} */
|
||||||
@@ -3068,13 +3074,13 @@ export function initOptions({
|
|||||||
const thisPath = groupAddons.id;
|
const thisPath = groupAddons.id;
|
||||||
|
|
||||||
const passedDetails = signals.createSignal(
|
const passedDetails = signals.createSignal(
|
||||||
/** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null),
|
/** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null)
|
||||||
);
|
);
|
||||||
|
|
||||||
const childOptionsCount = recursiveProcessPartialTree(
|
const childOptionsCount = recursiveProcessPartialTree(
|
||||||
anyPartial.tree,
|
anyPartial.tree,
|
||||||
passedDetails,
|
passedDetails,
|
||||||
[...(path || []), thisPath],
|
[...(path || []), thisPath]
|
||||||
);
|
);
|
||||||
|
|
||||||
listForSum.push(childOptionsCount);
|
listForSum.push(childOptionsCount);
|
||||||
@@ -3106,7 +3112,7 @@ export function initOptions({
|
|||||||
summary.append(supCount);
|
summary.append(supCount);
|
||||||
|
|
||||||
signals.createEffect(childOptionsCount, (childOptionsCount) => {
|
signals.createEffect(childOptionsCount, (childOptionsCount) => {
|
||||||
supCount.innerHTML = childOptionsCount.toLocaleString();
|
supCount.innerHTML = childOptionsCount.toLocaleString("en-us");
|
||||||
});
|
});
|
||||||
|
|
||||||
details.addEventListener("toggle", () => {
|
details.addEventListener("toggle", () => {
|
||||||
@@ -3206,7 +3212,7 @@ export function initOptions({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return signals.createMemo(() =>
|
return signals.createMemo(() =>
|
||||||
listForSum.reduce((acc, s) => acc + s(), 0),
|
listForSum.reduce((acc, s) => acc + s(), 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
recursiveProcessPartialTree(partialOptions, treeElement);
|
recursiveProcessPartialTree(partialOptions, treeElement);
|
||||||
@@ -3233,7 +3239,7 @@ export function initOptions({
|
|||||||
console.log(
|
console.log(
|
||||||
[...m.entries()]
|
[...m.entries()]
|
||||||
.filter(([_, value]) => value > 1)
|
.filter(([_, value]) => value > 1)
|
||||||
.map(([key, _]) => key),
|
.map(([key, _]) => key)
|
||||||
);
|
);
|
||||||
|
|
||||||
throw Error("ID duplicate");
|
throw Error("ID duplicate");
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export function init({
|
|||||||
input.value = value;
|
input.value = value;
|
||||||
stateValue = value;
|
stateValue = value;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
input.addEventListener("input", () => {
|
input.addEventListener("input", () => {
|
||||||
@@ -139,7 +139,7 @@ export function init({
|
|||||||
input.value = value;
|
input.value = value;
|
||||||
stateValue = value;
|
stateValue = value;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
input.addEventListener("change", () => {
|
input.addEventListener("change", () => {
|
||||||
@@ -328,7 +328,7 @@ export function init({
|
|||||||
keyPrefix,
|
keyPrefix,
|
||||||
key: "top-up-freq",
|
key: "top-up-freq",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -356,7 +356,7 @@ export function init({
|
|||||||
keyPrefix,
|
keyPrefix,
|
||||||
key: "swap-freq",
|
key: "swap-freq",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -369,7 +369,7 @@ export function init({
|
|||||||
keyPrefix,
|
keyPrefix,
|
||||||
key: "interval-start",
|
key: "interval-start",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
),
|
),
|
||||||
end: signals.createSignal(/** @type {Date | null} */ (new Date()), {
|
end: signals.createSignal(/** @type {Date | null} */ (new Date()), {
|
||||||
save: {
|
save: {
|
||||||
@@ -391,7 +391,7 @@ export function init({
|
|||||||
};
|
};
|
||||||
|
|
||||||
parametersElement.append(
|
parametersElement.append(
|
||||||
utils.dom.createHeader("Save in Bitcoin").headerElement,
|
utils.dom.createHeader("Save in Bitcoin").headerElement
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -410,7 +410,9 @@ export function init({
|
|||||||
* @param {string} param0.text
|
* @param {string} param0.text
|
||||||
*/
|
*/
|
||||||
function createColoredSpan({ color, text }) {
|
function createColoredSpan({ color, text }) {
|
||||||
return `<span style="color: ${colors[color]()}; font-weight: 500; text-transform: uppercase;
|
return `<span style="color: ${colors[
|
||||||
|
color
|
||||||
|
]()}; font-weight: 500; text-transform: uppercase;
|
||||||
font-size: var(--font-size-sm);">${text}</span>`;
|
font-size: var(--font-size-sm);">${text}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,9 +431,9 @@ export function init({
|
|||||||
title: "Initial Dollar Amount",
|
title: "Initial Dollar Amount",
|
||||||
signal: settings.dollars.initial.amount,
|
signal: settings.dollars.initial.amount,
|
||||||
signals,
|
signals,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
parametersElement.append(
|
parametersElement.append(
|
||||||
@@ -449,9 +451,9 @@ export function init({
|
|||||||
list: frequencies.list,
|
list: frequencies.list,
|
||||||
signal: settings.dollars.topUp.frenquency,
|
signal: settings.dollars.topUp.frenquency,
|
||||||
deep: true,
|
deep: true,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
parametersElement.append(
|
parametersElement.append(
|
||||||
@@ -469,9 +471,9 @@ export function init({
|
|||||||
title: "Top Up Dollar Amount",
|
title: "Top Up Dollar Amount",
|
||||||
signal: settings.dollars.topUp.amount,
|
signal: settings.dollars.topUp.amount,
|
||||||
signals,
|
signals,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
parametersElement.append(
|
parametersElement.append(
|
||||||
@@ -489,9 +491,9 @@ export function init({
|
|||||||
title: "Initial Swap Amount",
|
title: "Initial Swap Amount",
|
||||||
signal: settings.bitcoin.investment.initial,
|
signal: settings.bitcoin.investment.initial,
|
||||||
signals,
|
signals,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
parametersElement.append(
|
parametersElement.append(
|
||||||
@@ -508,9 +510,9 @@ export function init({
|
|||||||
list: frequencies.list,
|
list: frequencies.list,
|
||||||
signal: settings.bitcoin.investment.frequency,
|
signal: settings.bitcoin.investment.frequency,
|
||||||
deep: true,
|
deep: true,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
parametersElement.append(
|
parametersElement.append(
|
||||||
@@ -528,9 +530,9 @@ export function init({
|
|||||||
title: "Bitcoin Recurrent Investment",
|
title: "Bitcoin Recurrent Investment",
|
||||||
signal: settings.bitcoin.investment.recurrent,
|
signal: settings.bitcoin.investment.recurrent,
|
||||||
signals,
|
signals,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
parametersElement.append(
|
parametersElement.append(
|
||||||
@@ -547,9 +549,9 @@ export function init({
|
|||||||
title: "First Simulation Date",
|
title: "First Simulation Date",
|
||||||
signal: settings.interval.start,
|
signal: settings.interval.start,
|
||||||
signals,
|
signals,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
parametersElement.append(
|
parametersElement.append(
|
||||||
@@ -566,9 +568,9 @@ export function init({
|
|||||||
title: "Last Simulation Day",
|
title: "Last Simulation Day",
|
||||||
signal: settings.interval.end,
|
signal: settings.interval.end,
|
||||||
signals,
|
signals,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
parametersElement.append(
|
parametersElement.append(
|
||||||
@@ -589,9 +591,9 @@ export function init({
|
|||||||
step: 0.01,
|
step: 0.01,
|
||||||
signals,
|
signals,
|
||||||
placeholder: "Fees",
|
placeholder: "Fees",
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const p1 = window.document.createElement("p");
|
const p1 = window.document.createElement("p");
|
||||||
@@ -606,94 +608,94 @@ export function init({
|
|||||||
const owner = signals.getOwner();
|
const owner = signals.getOwner();
|
||||||
|
|
||||||
const totalInvestedAmountData = signals.createSignal(
|
const totalInvestedAmountData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const bitcoinValueData = signals.createSignal(
|
const bitcoinValueData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const bitcoinData = signals.createSignal(
|
const bitcoinData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const resultData = signals.createSignal(
|
const resultData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const dollarsLeftData = signals.createSignal(
|
const dollarsLeftData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const totalValueData = signals.createSignal(
|
const totalValueData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const investmentData = signals.createSignal(
|
const investmentData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const bitcoinAddedData = signals.createSignal(
|
const bitcoinAddedData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const averagePricePaidData = signals.createSignal(
|
const averagePricePaidData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const bitcoinPriceData = signals.createSignal(
|
const bitcoinPriceData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const buyCountData = signals.createSignal(
|
const buyCountData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const totalFeesPaidData = signals.createSignal(
|
const totalFeesPaidData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const daysCountData = signals.createSignal(
|
const daysCountData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const profitableDaysRatioData = signals.createSignal(
|
const profitableDaysRatioData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const unprofitableDaysRatioData = signals.createSignal(
|
const unprofitableDaysRatioData = signals.createSignal(
|
||||||
/** @type {LineData<Time>[]} */ ([]),
|
/** @type {LineData<number>[]} */ ([]),
|
||||||
{
|
{
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const index = () => /** @type {DateIndex} */ (0);
|
const index = () => /** @type {DateIndex} */ (0);
|
||||||
@@ -933,8 +935,7 @@ export function init({
|
|||||||
let lastSatsAdded = 0;
|
let lastSatsAdded = 0;
|
||||||
|
|
||||||
range.forEach((date, index) => {
|
range.forEach((date, index) => {
|
||||||
const year = date.getUTCFullYear();
|
const time = date.valueOf() / 1000;
|
||||||
const time = utils.date.toString(date);
|
|
||||||
|
|
||||||
if (topUpFrequency.isTriggerDay(date)) {
|
if (topUpFrequency.isTriggerDay(date)) {
|
||||||
dollars += topUpAmount;
|
dollars += topUpAmount;
|
||||||
@@ -1095,16 +1096,21 @@ export function init({
|
|||||||
p1.innerHTML = `After exchanging ${serInvestedAmount} in the span of ${serDaysCount} days, you would have accumulated ${serSats} Satoshis (${serBitcoin} Bitcoin) worth today ${serBitcoinValue} at an average price of ${serAveragePricePaid} per Bitcoin with a return of investment of ${serRoi}, have ${serDollars} left and paid a total of ${serTotalFeesPaid} in fees.`;
|
p1.innerHTML = `After exchanging ${serInvestedAmount} in the span of ${serDaysCount} days, you would have accumulated ${serSats} Satoshis (${serBitcoin} Bitcoin) worth today ${serBitcoinValue} at an average price of ${serAveragePricePaid} per Bitcoin with a return of investment of ${serRoi}, have ${serDollars} left and paid a total of ${serTotalFeesPaid} in fees.`;
|
||||||
|
|
||||||
const dayDiff = Math.floor(
|
const dayDiff = Math.floor(
|
||||||
utils.date.differenceBetween(new Date(), lastInvestDay),
|
utils.date.differenceBetween(new Date(), lastInvestDay)
|
||||||
);
|
);
|
||||||
const serDailyInvestment = c("emerald", fd(dailyInvestment));
|
const serDailyInvestment = c("emerald", fd(dailyInvestment));
|
||||||
const setLastSatsAdded = c("orange", f(lastSatsAdded));
|
const setLastSatsAdded = c("orange", f(lastSatsAdded));
|
||||||
p2.innerHTML = `You would've last bought ${c("blue", dayDiff ? `${f(dayDiff)} ${dayDiff > 1 ? "days" : "day"} ago` : "today")} and exchanged ${serDailyInvestment} for approximately ${setLastSatsAdded} Satoshis`;
|
p2.innerHTML = `You would've last bought ${c(
|
||||||
|
"blue",
|
||||||
|
dayDiff
|
||||||
|
? `${f(dayDiff)} ${dayDiff > 1 ? "days" : "day"} ago`
|
||||||
|
: "today"
|
||||||
|
)} and exchanged ${serDailyInvestment} for approximately ${setLastSatsAdded} Satoshis`;
|
||||||
|
|
||||||
const serProfitableDaysRatio = c("green", fp(profitableDaysRatio));
|
const serProfitableDaysRatio = c("green", fp(profitableDaysRatio));
|
||||||
const serUnprofitableDaysRatio = c(
|
const serUnprofitableDaysRatio = c(
|
||||||
"red",
|
"red",
|
||||||
fp(unprofitableDaysRatio),
|
fp(unprofitableDaysRatio)
|
||||||
);
|
);
|
||||||
|
|
||||||
p3.innerHTML = `You would've been ${serProfitableDaysRatio} of the time profitable and ${serUnprofitableDaysRatio} of the time unprofitable.`;
|
p3.innerHTML = `You would've been ${serProfitableDaysRatio} of the time profitable and ${serUnprofitableDaysRatio} of the time unprofitable.`;
|
||||||
@@ -1114,7 +1120,7 @@ export function init({
|
|||||||
(lowestAnnual4YReturn) => {
|
(lowestAnnual4YReturn) => {
|
||||||
const serLowestAnnual4YReturn = c(
|
const serLowestAnnual4YReturn = c(
|
||||||
"cyan",
|
"cyan",
|
||||||
`${fp(lowestAnnual4YReturn)}`,
|
`${fp(lowestAnnual4YReturn)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const lowestAnnual4YReturnPercentage = 1 + lowestAnnual4YReturn;
|
const lowestAnnual4YReturnPercentage = 1 + lowestAnnual4YReturn;
|
||||||
@@ -1130,22 +1136,22 @@ export function init({
|
|||||||
const bitcoinValueAfter4y = bitcoinValueReturn(4);
|
const bitcoinValueAfter4y = bitcoinValueReturn(4);
|
||||||
const serBitcoinValueAfter4y = c(
|
const serBitcoinValueAfter4y = c(
|
||||||
"purple",
|
"purple",
|
||||||
fd(bitcoinValueAfter4y),
|
fd(bitcoinValueAfter4y)
|
||||||
);
|
);
|
||||||
const bitcoinValueAfter10y = bitcoinValueReturn(10);
|
const bitcoinValueAfter10y = bitcoinValueReturn(10);
|
||||||
const serBitcoinValueAfter10y = c(
|
const serBitcoinValueAfter10y = c(
|
||||||
"fuchsia",
|
"fuchsia",
|
||||||
fd(bitcoinValueAfter10y),
|
fd(bitcoinValueAfter10y)
|
||||||
);
|
);
|
||||||
const bitcoinValueAfter21y = bitcoinValueReturn(21);
|
const bitcoinValueAfter21y = bitcoinValueReturn(21);
|
||||||
const serBitcoinValueAfter21y = c(
|
const serBitcoinValueAfter21y = c(
|
||||||
"pink",
|
"pink",
|
||||||
fd(bitcoinValueAfter21y),
|
fd(bitcoinValueAfter21y)
|
||||||
);
|
);
|
||||||
|
|
||||||
/** @param {number} v */
|
/** @param {number} v */
|
||||||
p4.innerHTML = `The lowest annual return after 4 years has historically been ${serLowestAnnual4YReturn}.<br/>Using it as the baseline, your Bitcoin would be worth ${serBitcoinValueAfter4y} after 4 years, ${serBitcoinValueAfter10y} after 10 years and ${serBitcoinValueAfter21y} after 21 years.`;
|
p4.innerHTML = `The lowest annual return after 4 years has historically been ${serLowestAnnual4YReturn}.<br/>Using it as the baseline, your Bitcoin would be worth ${serBitcoinValueAfter4y} after 4 years, ${serBitcoinValueAfter10y} after 10 years and ${serBitcoinValueAfter21y} after 21 years.`;
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
totalInvestedAmountData.set((a) => a);
|
totalInvestedAmountData.set((a) => a);
|
||||||
@@ -1163,7 +1169,7 @@ export function init({
|
|||||||
daysCountData.set((a) => a);
|
daysCountData.set((a) => a);
|
||||||
profitableDaysRatioData.set((a) => a);
|
profitableDaysRatioData.set((a) => a);
|
||||||
unprofitableDaysRatioData.set((a) => a);
|
unprofitableDaysRatioData.set((a) => a);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -305,22 +305,25 @@ function createTable({
|
|||||||
return l;
|
return l;
|
||||||
});
|
});
|
||||||
|
|
||||||
signals.createEffect(vec.fetched[fetchedKey].vec, (vec) => {
|
signals.createEffect(
|
||||||
if (!vec) return;
|
() => vec.fetched().get(fetchedKey)?.vec(),
|
||||||
|
(vec) => {
|
||||||
|
if (!vec?.length) return;
|
||||||
|
|
||||||
const thIndex = colIndex() + 1;
|
const thIndex = colIndex() + 1;
|
||||||
|
|
||||||
for (let i = 0; i < rowElements.length; i++) {
|
for (let i = 0; i < rowElements.length; i++) {
|
||||||
const iRev = vec.length - 1 - i;
|
const iRev = vec.length - 1 - i;
|
||||||
const value = vec[iRev];
|
const value = vec[iRev];
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
rowElements[i].childNodes[thIndex].innerHTML =
|
rowElements[i].childNodes[thIndex].innerHTML =
|
||||||
serializeValue({
|
serializeValue({
|
||||||
value,
|
value,
|
||||||
unit,
|
unit,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return () => vecId;
|
return () => vecId;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// File auto-generated, any modifications will be overwritten
|
// File auto-generated, any modifications will be overwritten
|
||||||
//
|
//
|
||||||
|
|
||||||
export const VERSION = "v0.0.48";
|
export const VERSION = "v0.0.52";
|
||||||
|
|
||||||
/** @typedef {0} DateIndex */
|
/** @typedef {0} DateIndex */
|
||||||
/** @typedef {1} DecadeIndex */
|
/** @typedef {1} DecadeIndex */
|
||||||
@@ -1251,6 +1251,7 @@ export function createVecIdToIndexes() {
|
|||||||
"fee-75p": [5],
|
"fee-75p": [5],
|
||||||
"fee-90p": [5],
|
"fee-90p": [5],
|
||||||
"fee-average": [0, 1, 2, 5, 7, 19, 22, 23],
|
"fee-average": [0, 1, 2, 5, 7, 19, 22, 23],
|
||||||
|
"fee-in-btc": [20],
|
||||||
"fee-in-btc-10p": [5],
|
"fee-in-btc-10p": [5],
|
||||||
"fee-in-btc-25p": [5],
|
"fee-in-btc-25p": [5],
|
||||||
"fee-in-btc-75p": [5],
|
"fee-in-btc-75p": [5],
|
||||||
@@ -1260,6 +1261,7 @@ export function createVecIdToIndexes() {
|
|||||||
"fee-in-btc-median": [5],
|
"fee-in-btc-median": [5],
|
||||||
"fee-in-btc-min": [0, 1, 2, 5, 7, 19, 22, 23],
|
"fee-in-btc-min": [0, 1, 2, 5, 7, 19, 22, 23],
|
||||||
"fee-in-btc-sum": [0, 1, 2, 5, 7, 19, 22, 23],
|
"fee-in-btc-sum": [0, 1, 2, 5, 7, 19, 22, 23],
|
||||||
|
"fee-in-usd": [20],
|
||||||
"fee-in-usd-10p": [5],
|
"fee-in-usd-10p": [5],
|
||||||
"fee-in-usd-25p": [5],
|
"fee-in-usd-25p": [5],
|
||||||
"fee-in-usd-75p": [5],
|
"fee-in-usd-75p": [5],
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ sw.addEventListener("install", (event) => {
|
|||||||
|
|
||||||
sw.addEventListener("activate", (event) => {
|
sw.addEventListener("activate", (event) => {
|
||||||
console.log("sw: active");
|
console.log("sw: active");
|
||||||
event.waitUntil(sw.clients.claim());
|
sw.clients.claim();
|
||||||
event.waitUntil(
|
event.waitUntil(
|
||||||
caches
|
caches
|
||||||
.keys()
|
.keys()
|
||||||
@@ -42,6 +42,8 @@ sw.addEventListener("fetch", (event) => {
|
|||||||
return; // let the browser handle it
|
return; // let the browser handle it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cache = caches.open(CACHE_NAME);
|
||||||
|
|
||||||
// 2) NAVIGATION: network‐first on your shell
|
// 2) NAVIGATION: network‐first on your shell
|
||||||
if (req.mode === "navigate") {
|
if (req.mode === "navigate") {
|
||||||
event.respondWith(
|
event.respondWith(
|
||||||
@@ -52,9 +54,7 @@ sw.addEventListener("fetch", (event) => {
|
|||||||
if (response.ok || response.status === 304) {
|
if (response.ok || response.status === 304) {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const clone = response.clone();
|
const clone = response.clone();
|
||||||
caches
|
cache.then((cache) => cache.put("/index.html", clone));
|
||||||
.open(CACHE_NAME)
|
|
||||||
.then((cache) => cache.put("/index.html", clone));
|
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ sw.addEventListener("fetch", (event) => {
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const clone = response.clone();
|
const clone = response.clone();
|
||||||
caches.open(CACHE_NAME).then((cache) => cache.put(req, clone));
|
cache.then((cache) => cache.put(req, clone));
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
|
|||||||
Reference in New Issue
Block a user