Compare commits
165 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce9b4bc4dd | |||
| 8b12b00114 | |||
| 1775cc1d54 | |||
| e4bd09df24 | |||
| 5e8c7da4df | |||
| c85592eefe | |||
| 05861c9113 | |||
| 3508d1e315 | |||
| e3177b8054 | |||
| 03e3760152 | |||
| 4740610923 | |||
| e28a0cde55 | |||
| 5b855fd835 | |||
| a2f5704581 | |||
| f7aa9424db | |||
| aa8b47a3dd | |||
| 11911c1898 | |||
| 4814c1971d | |||
| be9569f3fb | |||
| 900e72f95a | |||
| d2827f188b | |||
| cf9903b759 | |||
| 23f96461f4 | |||
| 9f2fd26e98 | |||
| 78d837c080 | |||
| 241b9312b7 | |||
| ed70ad7378 | |||
| 00213176d8 | |||
| 406650a45a | |||
| 56750ccf3c | |||
| dfc286b393 | |||
| 49a66f72fc | |||
| 3f237689da | |||
| cf1fb483b3 | |||
| b10f5e3f67 | |||
| c4fc24c513 | |||
| 3ac9c2d95e | |||
| e5ab4dafc0 | |||
| 10ae1911c3 | |||
| 73ebcdf0d6 | |||
| 5347523921 | |||
| 7ef70b953b | |||
| ccaca524fe | |||
| dd51f91cab | |||
| 537d98b41b | |||
| 9c4cadfc04 | |||
| 2001370441 | |||
| cc87b22757 | |||
| c0a65b30ad | |||
| c07e66c086 | |||
| a0cfc1be2b | |||
| 1505454793 | |||
| e1dff66283 | |||
| 5be801a086 | |||
| 94d4b05c29 | |||
| cebb889f7e | |||
| c4ed6ed034 | |||
| ec960bfefa | |||
| 79f689dde1 | |||
| 3b3654df56 | |||
| c66f008f07 | |||
| 37d9498d90 | |||
| 1ff67093db | |||
| daed37ccb8 | |||
| d41d807b4f | |||
| d6fa5c8a55 | |||
| 2dd608dfed | |||
| a98546f605 | |||
| 3567559d4e | |||
| 216476ee45 | |||
| 3fc28c07fb | |||
| 85f6ef063d | |||
| 1e71e2d68f | |||
| b24a29895f | |||
| 0167a2ae59 | |||
| 2c867103ca | |||
| 8c289df336 | |||
| 4489920cbf | |||
| 029a85081b | |||
| 1bc739d07f | |||
| c229e218f6 | |||
| a66f4ad4bd | |||
| 1dd687dab7 | |||
| 50ff6e2745 | |||
| 811dec713b | |||
| 617d6f4bd7 | |||
| 57cd2d6252 | |||
| ec64f8d048 | |||
| ed288a9dba | |||
| 27da0a4102 | |||
| 3c01ba1a76 | |||
| 252c8833ae | |||
| f45fb6efe6 | |||
| 8cc1f8d691 | |||
| bff22b5182 | |||
| d31d47eb32 | |||
| 5fe984c39d | |||
| 7f07b0daa7 | |||
| 5de9757d46 | |||
| f89276d7b8 | |||
| 30ba034206 | |||
| fa1e5aaa7f | |||
| 870c70180f | |||
| 6d35c26b3f | |||
| be4e693a27 | |||
| 5810276156 | |||
| d10ac3f87b | |||
| 9810bc09e9 | |||
| a0a13eb2a8 | |||
| 6e996797b8 | |||
| 663092b501 | |||
| 8ea13544de | |||
| e73daa6214 | |||
| d83a833b4d | |||
| ec3a2f29f0 | |||
| cf92c60a01 | |||
| b7f51b03bc | |||
| 903e69ff77 | |||
| c4167ddaad | |||
| 50bfdb0d68 | |||
| a6cb09ff1c | |||
| e4c9f23476 | |||
| 44e5415d43 | |||
| 1c653693ed | |||
| 39c470ad7a | |||
| 1103e538a5 | |||
| c0cd4cba6f | |||
| b91120e8d4 | |||
| 005774a4c2 | |||
| 16bbfebfba | |||
| 15505cd82d | |||
| 016d80e002 | |||
| 0f3c267a48 | |||
| 589bb02411 | |||
| c0f4ece17b | |||
| c3ae3cb768 | |||
| c9e0f9d985 | |||
| e3431c2fa3 | |||
| 5979b9771e | |||
| aa61832fb2 | |||
| 2ac6e982b1 | |||
| 3204ddcf07 | |||
| c87b1c133c | |||
| 9b275ecdae | |||
| d6fd7de361 | |||
| 49d66a133e | |||
| c559f26d0e | |||
| bbe9f1bad2 | |||
| 7e1fb6472d | |||
| 0ff8d20573 | |||
| 9c1f9448dc | |||
| 43a6081dd6 | |||
| 985e961876 | |||
| 098f6de047 | |||
| 1b0f90fd68 | |||
| 12252f407b | |||
| 3b6e3f47ab | |||
| 6a9ac9b025 | |||
| ae6aa4088b | |||
| c08f431180 | |||
| 123c1f56e9 | |||
| 35ac65a864 | |||
| e9f362cc87 | |||
| 65685c23e1 | |||
| 2f74748cea |
@@ -1,4 +1,4 @@
|
||||
# This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/
|
||||
# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist
|
||||
#
|
||||
# Copyright 2022-2024, axodotdev
|
||||
# SPDX-License-Identifier: MIT or Apache-2.0
|
||||
@@ -47,7 +47,7 @@ on:
|
||||
jobs:
|
||||
# Run 'dist plan' (or host) to determine what tasks we need to do
|
||||
plan:
|
||||
runs-on: "ubuntu-latest"
|
||||
runs-on: "ubuntu-22.04"
|
||||
outputs:
|
||||
val: ${{ steps.plan.outputs.manifest }}
|
||||
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
|
||||
@@ -58,12 +58,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install dist
|
||||
# we specify bash to get pipefail; it guards against the `curl` command
|
||||
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
||||
shell: bash
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.28.0/cargo-dist-installer.sh | sh"
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.29.0/cargo-dist-installer.sh | sh"
|
||||
- name: Cache dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -117,6 +118,7 @@ jobs:
|
||||
git config --global core.longpaths true
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install Rust non-interactively if not already installed
|
||||
if: ${{ matrix.container }}
|
||||
@@ -168,13 +170,14 @@ jobs:
|
||||
needs:
|
||||
- plan
|
||||
- build-local-artifacts
|
||||
runs-on: "ubuntu-latest"
|
||||
runs-on: "ubuntu-22.04"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -218,12 +221,13 @@ jobs:
|
||||
if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
runs-on: "ubuntu-latest"
|
||||
runs-on: "ubuntu-22.04"
|
||||
outputs:
|
||||
val: ${{ steps.host.outputs.manifest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -282,10 +286,11 @@ jobs:
|
||||
# still allowing individual publish jobs to skip themselves (for prereleases).
|
||||
# "host" however must run to completion, no skipping allowed!
|
||||
if: ${{ always() && needs.host.result == 'success' }}
|
||||
runs-on: "ubuntu-latest"
|
||||
runs-on: "ubuntu-22.04"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
# Builds
|
||||
target
|
||||
dist
|
||||
vecid-to-indexes.js
|
||||
|
||||
# Copies
|
||||
*\ copy*
|
||||
@@ -16,3 +18,14 @@ _*
|
||||
|
||||
# Logs
|
||||
.log
|
||||
|
||||
# Environment variables/configs
|
||||
.env
|
||||
|
||||
# Profiling
|
||||
profile.json.gz
|
||||
flamegraph.svg
|
||||
*.trace
|
||||
|
||||
# AI
|
||||
CLAUDE.md
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
<!--
|
||||
# v0.X.Y | WIP
|
||||

|
||||

|
||||
-->
|
||||
|
||||
# v0.X.0 | WIP | A new beginning
|
||||
|
||||

|
||||
|
||||
Full rewrite
|
||||
|
||||
# [kibo-v0.5.0](https://github.com/kibo-money/kibo/tree/eea56d394bf92c62c81da8b78b8c47ea730683f5) | [873199](https://mempool.space/block/0000000000000000000270925aa6a565be92e13164565a3f7994ca1966e48050) - 2024/12/04
|
||||
# [kibo-v0.5.0](https://github.com/bitcoinresearchkit/brk/tree/eea56d394bf92c62c81da8b78b8c47ea730683f5) | [873199](https://mempool.space/block/0000000000000000000270925aa6a565be92e13164565a3f7994ca1966e48050) - 2024/12/04
|
||||
|
||||

|
||||

|
||||
|
||||
## Datasets
|
||||
|
||||
@@ -74,9 +76,9 @@ Full rewrite
|
||||
|
||||
- Moved back to this repo
|
||||
|
||||
# [kibo-v0.4.0](https://github.com/kibo-money/kibo/tree/a64c544815d9ef785e2fc1323582f774f16b9200) | [861950](https://mempool.space/block/00000000000000000000530d0e30ccf7deeace122dcc99f2668a06c6dad83629) - 2024/09/19
|
||||
# [kibo-v0.4.0](https://github.com/bitcoinresearchkit/brk/tree/a64c544815d9ef785e2fc1323582f774f16b9200) | [861950](https://mempool.space/block/00000000000000000000530d0e30ccf7deeace122dcc99f2668a06c6dad83629) - 2024/09/19
|
||||
|
||||

|
||||

|
||||
|
||||
## Brand
|
||||
|
||||
@@ -111,9 +113,9 @@ Full rewrite
|
||||
- Added serving of the website
|
||||
- Improved `Cache-Control` behavior
|
||||
|
||||
# [kibo-v0.3.0](https://github.com/kibo-money/kibo/tree/b68b016091c45b071218fba01bac5b76e8eaf18c) | [853930](https://mempool.space/block/00000000000000000002eb5e9a7950ca2d5d98bd1ed28fc9098aa630d417985d) - 2024/07/26
|
||||
# [satonomics-v0.3.0](https://github.com/bitcoinresearchkit/brk/tree/b68b016091c45b071218fba01bac5b76e8eaf18c) | [853930](https://mempool.space/block/00000000000000000002eb5e9a7950ca2d5d98bd1ed28fc9098aa630d417985d) - 2024/07/26
|
||||
|
||||

|
||||

|
||||
|
||||
## Parser
|
||||
|
||||
@@ -190,9 +192,9 @@ Full rewrite
|
||||
- Only run with a watcher if `cargo watch` is available
|
||||
- Removed id_to_path file in favor for only `paths.d.ts` in `app/src/types`
|
||||
|
||||
# [kibo-v0.2.0](https://github.com/kibo-money/kibo/tree/248187889283597c5dbb806292297453c25e97b8) | [851286](https://mempool.space/block/0000000000000000000281ca7f1bf8c50702bfca168c7af1bdc67c977c1ac8ed) - 2024/07/08
|
||||
# [satonomics-v0.2.0](https://github.com/bitcoinresearchkit/brk/tree/248187889283597c5dbb806292297453c25e97b8) | [851286](https://mempool.space/block/0000000000000000000281ca7f1bf8c50702bfca168c7af1bdc67c977c1ac8ed) - 2024/07/08
|
||||
|
||||

|
||||

|
||||
|
||||
## App
|
||||
|
||||
@@ -226,9 +228,9 @@ Full rewrite
|
||||
|
||||
- Fixed ulimit only being run in Mac OS instead of whenever the program is detected
|
||||
|
||||
# [kibo-v0.1.1](https://github.com/kibo-money/kibo/tree/e55b5195a9de9aea306903c94ed63cb1720fda5f) | [849240](https://mempool.space/block/000000000000000000002b8653988655071c07bb5f7181c038f9326bc86db741) - 2024/06/24
|
||||
# [satonomics-v0.1.1](https://github.com/bitcoinresearchkit/brk/tree/e55b5195a9de9aea306903c94ed63cb1720fda5f) | [849240](https://mempool.space/block/000000000000000000002b8653988655071c07bb5f7181c038f9326bc86db741) - 2024/06/24
|
||||
|
||||

|
||||

|
||||
|
||||
## Parser
|
||||
|
||||
@@ -276,10 +278,10 @@ Full rewrite
|
||||
|
||||
- Deleted old price datasets and their backups
|
||||
|
||||
# [kibo-v0.1.0](https://github.com/kibo-money/kibo/tree/a1a576d088c8f83ed32d48753a7611f70a964574) | [848642](https://mempool.space/block/000000000000000000020be5761d70751252219a9557f55e91ecdfb86c4e026a) - 2024/06/19
|
||||
# [satonomics-v0.1.0](https://github.com/bitcoinresearchkit/brk/tree/a1a576d088c8f83ed32d48753a7611f70a964574) | [848642](https://mempool.space/block/000000000000000000020be5761d70751252219a9557f55e91ecdfb86c4e026a) - 2024/06/19
|
||||
|
||||

|
||||

|
||||
|
||||
# kibo-v0.0.1 | [835444](https://mempool.space/block/000000000000000000009f93907a0dd83c080d5585cc7ec82c076d45f6d7c872) - 2024/03/20
|
||||
# satonomics-v0.0.1 | [835444](https://mempool.space/block/000000000000000000009f93907a0dd83c080d5585cc7ec82c076d45f6d7c872) - 2024/03/20
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -4,55 +4,57 @@ 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.license = "MIT"
|
||||
package.edition = "2024"
|
||||
package.version = "0.0.55"
|
||||
package.version = "0.0.87"
|
||||
package.homepage = "https://bitcoinresearchkit.org"
|
||||
package.repository = "https://github.com/bitcoinresearchkit/brk"
|
||||
package.readme = "README.md"
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[profile.profiling]
|
||||
inherits = "release"
|
||||
debug = true
|
||||
|
||||
[profile.dist]
|
||||
inherits = "release"
|
||||
|
||||
[workspace.dependencies]
|
||||
arc-swap = "1.7.1"
|
||||
axum = "0.8.4"
|
||||
bincode = { version = "2.0.1", features = ["serde"] }
|
||||
bitcoin = { version = "0.32.6", features = ["serde"] }
|
||||
bitcoin = { version = "0.32.7", features = ["serde"] }
|
||||
bitcoincore-rpc = "0.19.0"
|
||||
brk_cli = { version = "0.0.55", path = "crates/brk_cli" }
|
||||
brk_computer = { version = "0.0.55", path = "crates/brk_computer" }
|
||||
brk_core = { version = "0.0.55", path = "crates/brk_core" }
|
||||
brk_exit = { version = "0.0.55", path = "crates/brk_exit" }
|
||||
brk_fetcher = { version = "0.0.55", path = "crates/brk_fetcher" }
|
||||
brk_indexer = { version = "0.0.55", path = "crates/brk_indexer" }
|
||||
brk_logger = { version = "0.0.55", path = "crates/brk_logger" }
|
||||
brk_parser = { version = "0.0.55", path = "crates/brk_parser" }
|
||||
brk_query = { version = "0.0.55", path = "crates/brk_query" }
|
||||
brk_server = { version = "0.0.55", path = "crates/brk_server" }
|
||||
brk_state = { version = "0.0.55", path = "crates/brk_state" }
|
||||
brk_store = { version = "0.0.55", path = "crates/brk_store" }
|
||||
brk_vec = { version = "0.0.55", path = "crates/brk_vec" }
|
||||
brk_bundler = { version = "0.0.87", path = "crates/brk_bundler" }
|
||||
brk_cli = { version = "0.0.87", path = "crates/brk_cli" }
|
||||
brk_computer = { version = "0.0.87", path = "crates/brk_computer" }
|
||||
brk_structs = { version = "0.0.87", path = "crates/brk_structs" }
|
||||
brk_error = { version = "0.0.87", path = "crates/brk_error" }
|
||||
brk_fetcher = { version = "0.0.87", path = "crates/brk_fetcher" }
|
||||
brk_indexer = { version = "0.0.87", path = "crates/brk_indexer" }
|
||||
brk_interface = { version = "0.0.87", path = "crates/brk_interface" }
|
||||
brk_logger = { version = "0.0.87", path = "crates/brk_logger" }
|
||||
brk_mcp = { version = "0.0.87", path = "crates/brk_mcp" }
|
||||
brk_parser = { version = "0.0.87", path = "crates/brk_parser" }
|
||||
brk_server = { version = "0.0.87", path = "crates/brk_server" }
|
||||
brk_store = { version = "0.0.87", path = "crates/brk_store" }
|
||||
vecdb = { version = "0.1.0", features = ["derive"]}
|
||||
# vecdb = { path = "../seqdb/crates/vecdb", features = ["derive"]}
|
||||
byteview = "=0.6.1"
|
||||
clap = { version = "4.5.40", features = ["string"] }
|
||||
clap_derive = "4.5.40"
|
||||
color-eyre = "0.6.5"
|
||||
derive_deref = "1.1.1"
|
||||
fjall = "2.11.0"
|
||||
jiff = "0.2.14"
|
||||
log = { version = "0.4.27" }
|
||||
minreq = { version = "2.13.4", features = ["https", "serde_json"] }
|
||||
fjall = "2.11.2"
|
||||
jiff = "0.2.15"
|
||||
log = "0.4.27"
|
||||
minreq = { version = "2.14.0", features = ["https", "serde_json"] }
|
||||
parking_lot = "0.12.4"
|
||||
rayon = "1.10.0"
|
||||
serde = { version = "1.0.219" }
|
||||
serde = "1.0.219"
|
||||
serde_bytes = "0.11.17"
|
||||
serde_derive = "1.0.219"
|
||||
serde_json = { version = "1.0.140", features = ["float_roundtrip"] }
|
||||
tabled = "0.20.0"
|
||||
tokio = { version = "1.45.1", features = ["rt-multi-thread"] }
|
||||
zerocopy = { version = "0.8.25" }
|
||||
zerocopy-derive = "0.8.25"
|
||||
serde_json = { version = "1.0.142", features = ["float_roundtrip"] }
|
||||
tokio = { version = "1.47.1", features = ["rt-multi-thread"] }
|
||||
zerocopy = "0.8.26"
|
||||
zerocopy-derive = "0.8.26"
|
||||
|
||||
[workspace.metadata.release]
|
||||
shared-version = true
|
||||
@@ -61,20 +63,7 @@ pre-release-commit-message = "release: v{{version}}"
|
||||
tag-message = "release: v{{version}}"
|
||||
|
||||
[workspace.metadata.dist]
|
||||
cargo-dist-version = "0.28.0"
|
||||
cargo-dist-version = "0.29.0"
|
||||
ci = "github"
|
||||
installers = []
|
||||
targets = [
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
"x86_64-apple-darwin",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
]
|
||||
|
||||
[workspace.metadata.dist.github-custom-runners]
|
||||
global = "ubuntu-latest"
|
||||
aarch64-apple-darwin.runner = "macos-14"
|
||||
x86_64-unknown-linux-gnu.runner = "ubuntu-latest"
|
||||
x86_64-unknown-linux-gnu.container = { image = "quay.io/pypa/manylinux_2_28_x86_64", host = "x86_64-unknown-linux-musl" }
|
||||
aarch64-unknown-linux-gnu.runner = "ubuntu-latest"
|
||||
aarch64-unknown-linux-gnu.container = { image = "quay.io/pypa/manylinux_2_28_x86_64", host = "x86_64-unknown-linux-musl" }
|
||||
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"]
|
||||
|
||||
@@ -23,12 +23,6 @@
|
||||
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
|
||||
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
|
||||
</a>
|
||||
<a href="https://bsky.app/profile/bitcoinresearchkit.org">
|
||||
<img src="https://img.shields.io/badge/bluesky-blue?link=https%3A%2F%2Fbsky.app%2Fprofile%2Fbitcoinresearchkit.org" alt="Bluesky" />
|
||||
</a>
|
||||
<a href="https://x.com/brkdotorg">
|
||||
<img src="https://img.shields.io/badge/x.com-black" alt="X" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
The Bitcoin Research Kit is a high-performance toolchain designed to parse, index, compute, serve and visualize data from a Bitcoin node, enabling users to gain deeper insights into the Bitcoin network.
|
||||
@@ -40,10 +34,13 @@ The toolkit can be used in various ways to accommodate as many needs as possible
|
||||
- **[Website](https://bitcoinresearchkit.org)** \
|
||||
Everyone is welcome to visit the official instance and showcase of the suite's capabilities. \
|
||||
It has a wide range of functionalities including charts, tables and simulations which you can visit for free and without the need for an account. \
|
||||
Also available at: [kibo.money](https://kibo.money) // [satonomics.xyz](https://satonomics.xyz)
|
||||
- **[API](https://github.com/bitcoinresearchkit/brk/tree/main/crates/brk_server#endpoints)** \
|
||||
Researchers and developers are free to use BRK's public API with  dataset variants at your disposal. \
|
||||
Also available at: [brekit.org](https://brekit.org) // [kibo.money](https://kibo.money) // [satonomics.xyz](https://satonomics.xyz)
|
||||
- **[API](https://github.com/bitcoinresearchkit/brk/tree/main/crates/brk_server#brk-server)** \
|
||||
Researchers and developers are free to use BRK's public API with  dataset variants at their disposal. \
|
||||
Just like the website, it's entirely free, with no authentication or rate-limiting.
|
||||
- **[AI](https://github.com/bitcoinresearchkit/brk/blob/main/crates/brk_mcp/README.md#brk-mcp)** \
|
||||
LLMs have to possibility to connect to BRK's backend through a [MCP](https://modelcontextprotocol.io/introduction). \
|
||||
It will give them access to the same tools as the API, with no restrictions, and allow you to have your very own data analysts.
|
||||
- **[CLI](https://crates.io/crates/brk_cli)** \
|
||||
Node runners are strongly encouraged to try out and self-host their own instance using BRK's command line interface. \
|
||||
The CLI has multiple cogs available for users to tweak to adapt to all situations with even the possibility for web developers to create their own custom website which could later on be added as an alternative front-end.
|
||||
@@ -57,20 +54,22 @@ In contrast, existing alternatives tend to be either [very costly](https://studi
|
||||
|
||||
## Crates
|
||||
|
||||
- [`brk`](https://crates.io/crates/brk): Wrapper around all other `brk-*` crates
|
||||
- [`brk_cli`](https://crates.io/crates/brk_cli): A standalone command line interface to interact with the Bitcoin Research Kit
|
||||
- [`brk_computer`](https://crates.io/crates/brk_computer): A Bitcoin dataset computer, built on top of brk_indexer
|
||||
- [`brk_core`](https://crates.io/crates/brk_core): The Core (Structs and Errors) of the Bitcoin Research Kit
|
||||
- [`brk_exit`](https://crates.io/crates/brk_exit): An exit blocker built on top of ctrlc
|
||||
- [`brk`](https://crates.io/crates/brk): A wrapper around all other `brk-*` crates
|
||||
- [`brk_bundler`](https://crates.io/crates/brk_bundler): A thin wrapper around [`rolldown`](https://rolldown.rs/)
|
||||
- [`brk_cli`](https://crates.io/crates/brk_cli): A command line interface to run a BRK instance
|
||||
- [`brk_computer`](https://crates.io/crates/brk_computer): A Bitcoin dataset computer built on top of [`brk_indexer`](https://crates.io/crates/brk_indexer)
|
||||
- [`brk_error`](https://crates.io/crates/brk_error): Errors used throughout BRK
|
||||
- [`brk_fetcher`](https://crates.io/crates/brk_fetcher): A Bitcoin price fetcher
|
||||
- [`brk_indexer`](https://crates.io/crates/brk_indexer): A Bitcoin Core indexer built on top of brk_parser
|
||||
- [`brk_logger`](https://crates.io/crates/brk_logger): A clean logger used in the Bitcoin Research Kit.
|
||||
- [`brk_parser`](https://crates.io/crates/brk_parser): A very fast Bitcoin Core block parser and iterator built on top of bitcoin-rust
|
||||
- [`brk_query`](https://crates.io/crates/brk_query): A library that finds requested datasets.
|
||||
- [`brk_server`](https://crates.io/crates/brk_server): A server that serves Bitcoin data and swappable front-ends, built on top of `brk_indexer`, `brk_fetcher` and `brk_computer`
|
||||
- [`brk_state`](https://crates.io/crates/brk_state): Various states used mainly by the computer
|
||||
- [`brk_indexer`](https://crates.io/crates/brk_indexer): A Bitcoin indexer built on top of [`brk_parser`](https://crates.io/crates/brk_parser)
|
||||
- [`brk_interface`](https://crates.io/crates/brk_interface): An interface to find and format data from BRK
|
||||
- [`brk_logger`](https://crates.io/crates/brk_logger): A thin wrapper around [`env_logger`](https://crates.io/crates/env_logger)
|
||||
- [`brk_mcp`](https://crates.io/crates/brk_mcp): A bridge for LLMs to access BRK
|
||||
- [`brk_parser`](https://crates.io/crates/brk_parser): A very fast Bitcoin block parser and iterator built on top of [`bitcoin-rust`](https://crates.io/crates/bitcoin)
|
||||
- [`brk_server`](https://crates.io/crates/brk_server): A server with an API for anything from BRK
|
||||
- [`brk_store`](https://crates.io/crates/brk_store): A thin wrapper around [`fjall`](https://crates.io/crates/fjall)
|
||||
- [`brk_vec`](https://crates.io/crates/brk_vec): A push-only, truncable, compressable, saveable Vec
|
||||
- [`brk_structs`](https://crates.io/crates/brk_structs): Structs used throughout BRK
|
||||
- [`vecdb`](https://crates.io/crates/vecdb): A KISS index-value storage engine
|
||||
- [`vecdb_macros`](https://crates.io/crates/vecdb_macros): Macros for [`vecdb`](https://crates.io/crates/vecdb)
|
||||
|
||||
## Hosting as a service
|
||||
|
||||
@@ -91,7 +90,7 @@ Pricing: `0.01 BTC / month` *or* `0.1 BTC / year`
|
||||
|
||||
Deepest gratitude to the [Open Sats](https://opensats.org/) public charity. Their grant — from December 2024 to the present — has been critical in sustaining this project.
|
||||
|
||||
Heartfelt thanks go out to every donor on [Nostr](https://primal.net/p/npub1jagmm3x39lmwfnrtvxcs9ac7g300y3dusv9lgzhk2e4x5frpxlrqa73v44) and [Geyser.fund](https://geyser.fund/project/brk) whose support has ensured the availability of the [kibo.money](https://kibo.money) public instance.
|
||||
Heartfelt thanks go out to every donor on [Nostr](https://primal.net/p/npub1jagmm3x39lmwfnrtvxcs9ac7g300y3dusv9lgzhk2e4x5frpxlrqa73v44) and [Geyser.fund](https://geyser.fund/project/brk) whose support has ensured the availability of the [bitcoinresearchkit.org](https://bitcoinresearchkit.org) public instance.
|
||||
|
||||
## Donate
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# TODO
|
||||
|
||||
- __crates__
|
||||
- _cli_
|
||||
- check disk space on first launch
|
||||
- add custom path support for config.toml
|
||||
- maybe add bitcoind download and launch support
|
||||
- via: https://github.com/rust-bitcoin/corepc/blob/master/node
|
||||
- test read/write speed, add warning if too low (<2gb/s)
|
||||
- pull latest version and notify is out of date
|
||||
- _computer_
|
||||
- **add rollback of states (in stateful)**
|
||||
- remove configurable format (raw/compressed) and chose sane ones instead
|
||||
- linear reads: compressed (height/date/... + txindex_to_height + txindex_to_version + ...)
|
||||
- random reads: raw (outputindex_to_value + ...)
|
||||
- add prices paid by percentile (percentile cost basis) back
|
||||
- add support for per index computation
|
||||
- fix min feerate which is always ZERO due to coinbase transaction
|
||||
- before computing multiple sources check their length, panic if not equal
|
||||
- add oracle price dataset (https://utxo.live/oracle/UTXOracle.py)
|
||||
- add address counts relative to all datasets
|
||||
- make decade, quarter, year datasets `computed` instead of `eager`
|
||||
- add 6 months (semester) interval datasets to builder
|
||||
- some datasets in `indexes` can probably be removed
|
||||
- add revived/sent supply datasets
|
||||
- add `in-sats` version of all price datasets (average and co)
|
||||
- add `p2pk` group (sum of `p2pk33` and `p2pk65`)
|
||||
- add chopiness datasets
|
||||
- add utxo count, address count, supply data for by reused addresses in groups by address type
|
||||
- add more date ranges (3-6 months and more)
|
||||
- add puell multiple dataset
|
||||
- add pi cycle dataset
|
||||
- add emas of price
|
||||
- add 7d and 30d ema to sell side risk ratio and sopr
|
||||
- don't compute everything for all cohorts as some datasets combinations are irrelevant
|
||||
- addresses/utxos by amount don't need mvrvz for example
|
||||
- add all possible charts from:
|
||||
- https://mainnet.observer
|
||||
- https://glassnode.com
|
||||
- https://checkonchain.com
|
||||
- https://researchbitcoin.net/exciting-update-coming-to-the-bitcoin-lab/
|
||||
- https://mempool.space/research
|
||||
- _indexer_
|
||||
- parse only the needed block number
|
||||
- maybe using https://developer.bitcoin.org/reference/rpc/getblockhash.html
|
||||
- _interface_
|
||||
- create pagination enum
|
||||
- from to
|
||||
- from option<count>
|
||||
- to option<count>
|
||||
- page + option<per page> default 1000 max 1000
|
||||
- from/to/count params don’t cap all combinations
|
||||
- example: from -10,000 count 10, won’t work if underlying vec isn’t 10k or more long
|
||||
- _parser_
|
||||
- save `vec` file instead of `json`
|
||||
- support lock file, process in read only if already opened in write mode
|
||||
- if less than X (10 maybe ?) get block using rpc instead of parsing the block files
|
||||
- _server_
|
||||
- api
|
||||
- add extensions support (.json .csv …)
|
||||
- if format instead of extension then don't download file
|
||||
- add support for https (rustls)
|
||||
- __docs__
|
||||
- _README_
|
||||
- add a comparison table with alternatives
|
||||
- add contribution section where help is needed
|
||||
- documentation/mcp/datasets/different front ends
|
||||
- add faq
|
||||
- __websites__
|
||||
- _default_
|
||||
- explorer
|
||||
- blocks
|
||||
- transactions
|
||||
- addresses
|
||||
- miners
|
||||
- maybe xpubs
|
||||
- charts
|
||||
- improve some names and colors
|
||||
- remove `sum` series when it's a duplicate of the `base` (in subsidy for example)
|
||||
- selected unit sometimes changes when going back end forth
|
||||
- add support for custom charts
|
||||
- separate z-score charts from "realized price" (with their own prices), have 4y, 2y and 1y
|
||||
- price scale format depends on unit, hide digits for sats for example (if/when possible)
|
||||
- table
|
||||
- pagination
|
||||
- exports (.json, .csv,…)
|
||||
- search
|
||||
- datasets add legend, and keywords ?
|
||||
- height/address/txid
|
||||
- api
|
||||
- add api page with interactivity
|
||||
- global
|
||||
- **fix navigation/history**
|
||||
- move share button to footer ?
|
||||
- Use `ichart.createPane()` in wrapper
|
||||
- improve behavior when local storage is unavailable
|
||||
- by having a global state
|
||||
- __global__
|
||||
- check `TODO`s in codebase
|
||||
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 263 KiB |
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 386 KiB |
|
After Width: | Height: | Size: 496 KiB |
|
After Width: | Height: | Size: 564 KiB |
|
After Width: | Height: | Size: 592 KiB |
|
After Width: | Height: | Size: 453 KiB |
|
After Width: | Height: | Size: 526 KiB |
@@ -7,49 +7,53 @@ homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[features]
|
||||
full = [
|
||||
"core",
|
||||
"bundler",
|
||||
"computer",
|
||||
"exit",
|
||||
"error",
|
||||
"fetcher",
|
||||
"indexer",
|
||||
"interface",
|
||||
"logger",
|
||||
"mcp",
|
||||
"parser",
|
||||
"query",
|
||||
"server",
|
||||
"state",
|
||||
"store",
|
||||
"vec",
|
||||
"structs",
|
||||
"vecs",
|
||||
]
|
||||
core = ["brk_core"]
|
||||
bundler = ["brk_bundler"]
|
||||
computer = ["brk_computer"]
|
||||
exit = ["brk_exit"]
|
||||
error = ["brk_error"]
|
||||
fetcher = ["brk_fetcher"]
|
||||
indexer = ["brk_indexer"]
|
||||
interface = ["brk_interface"]
|
||||
logger = ["brk_logger"]
|
||||
mcp = ["brk_mcp"]
|
||||
parser = ["brk_parser"]
|
||||
query = ["brk_query"]
|
||||
server = ["brk_server"]
|
||||
state = ["brk_state"]
|
||||
store = ["brk_store"]
|
||||
vec = ["brk_vec"]
|
||||
structs = ["brk_structs"]
|
||||
vecs = ["vecdb"]
|
||||
|
||||
[dependencies]
|
||||
brk_bundler = { workspace = true, optional = true }
|
||||
brk_cli = { workspace = true }
|
||||
brk_core = { workspace = true, optional = true }
|
||||
brk_computer = { workspace = true, optional = true }
|
||||
brk_exit = { workspace = true, optional = true }
|
||||
brk_error = { workspace = true, optional = true }
|
||||
brk_fetcher = { workspace = true, optional = true }
|
||||
brk_indexer = { workspace = true, optional = true }
|
||||
brk_interface = { workspace = true, optional = true }
|
||||
brk_logger = { workspace = true, optional = true }
|
||||
brk_mcp = { workspace = true, optional = true }
|
||||
brk_parser = { workspace = true, optional = true }
|
||||
brk_query = { workspace = true, optional = true }
|
||||
brk_server = { workspace = true, optional = true }
|
||||
brk_state = { workspace = true, optional = true }
|
||||
brk_store = { workspace = true, optional = true }
|
||||
brk_vec = { workspace = true, optional = true }
|
||||
brk_structs = { workspace = true, optional = true }
|
||||
vecdb = { workspace = true, optional = true }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fn main() {
|
||||
let profile = std::env::var("PROFILE").unwrap_or_default();
|
||||
|
||||
if profile == "release" {
|
||||
println!("cargo:rustc-flag=-C");
|
||||
println!("cargo:rustc-flag=target-cpu=native");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
fn main() {}
|
||||
@@ -0,0 +1 @@
|
||||
sudo cargo flamegraph --profile profiling --root
|
||||
@@ -0,0 +1,2 @@
|
||||
cargo build --profile profiling
|
||||
samply record ../../target/profiling/brk
|
||||
@@ -1,16 +1,23 @@
|
||||
#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
#[cfg(feature = "bundler")]
|
||||
#[doc(inline)]
|
||||
pub use brk_core as core;
|
||||
pub use brk_bundler as bundler;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use brk_cli as cli;
|
||||
|
||||
#[cfg(feature = "structs")]
|
||||
#[doc(inline)]
|
||||
pub use brk_structs as structs;
|
||||
|
||||
#[cfg(feature = "computer")]
|
||||
#[doc(inline)]
|
||||
pub use brk_computer as computer;
|
||||
|
||||
#[cfg(feature = "exit")]
|
||||
#[cfg(feature = "error")]
|
||||
#[doc(inline)]
|
||||
pub use brk_exit as exit;
|
||||
pub use brk_error as error;
|
||||
|
||||
#[cfg(feature = "fetcher")]
|
||||
#[doc(inline)]
|
||||
@@ -24,26 +31,26 @@ pub use brk_indexer as indexer;
|
||||
#[doc(inline)]
|
||||
pub use brk_logger as logger;
|
||||
|
||||
#[cfg(feature = "mcp")]
|
||||
#[doc(inline)]
|
||||
pub use brk_mcp as mcp;
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
#[doc(inline)]
|
||||
pub use brk_parser as parser;
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
#[cfg(feature = "interface")]
|
||||
#[doc(inline)]
|
||||
pub use brk_query as query;
|
||||
pub use brk_interface as interface;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
#[doc(inline)]
|
||||
pub use brk_server as server;
|
||||
|
||||
#[cfg(feature = "state")]
|
||||
#[doc(inline)]
|
||||
pub use brk_state as state;
|
||||
|
||||
#[cfg(feature = "store")]
|
||||
#[doc(inline)]
|
||||
pub use brk_store as store;
|
||||
|
||||
#[cfg(feature = "vec")]
|
||||
#[cfg(feature = "vecs")]
|
||||
#[doc(inline)]
|
||||
pub use brk_vec as vec;
|
||||
pub use vecdb as vecs;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "brk_bundler"
|
||||
description = "A thin wrapper around rolldown"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
log = { workspace = true }
|
||||
notify = "8.2.0"
|
||||
brk_rolldown = "0.1.4"
|
||||
# brk_rolldown = { path = "../../../rolldown/crates/rolldown"}
|
||||
sugar_path = "1.2.0"
|
||||
tokio = { workspace = true }
|
||||
@@ -0,0 +1,8 @@
|
||||
fn main() {
|
||||
let profile = std::env::var("PROFILE").unwrap_or_default();
|
||||
|
||||
if profile == "release" {
|
||||
println!("cargo:rustc-flag=-C");
|
||||
println!("cargo:rustc-flag=target-cpu=native");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
use std::{
|
||||
fs, io,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use brk_rolldown::{Bundler, BundlerOptions, RawMinifyOptions, SourceMapType};
|
||||
use log::error;
|
||||
use notify::{EventKind, RecursiveMode, Watcher};
|
||||
use sugar_path::SugarPath;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub async fn bundle(websites_path: &Path, source_folder: &str, watch: bool) -> io::Result<PathBuf> {
|
||||
let source_path = websites_path.join(source_folder);
|
||||
let dist_path = websites_path.join("dist");
|
||||
|
||||
let _ = fs::remove_dir_all(&dist_path);
|
||||
copy_dir_all(&source_path, &dist_path)?;
|
||||
|
||||
let source_scripts = format!("./{source_folder}/scripts");
|
||||
let source_entry = format!("{source_scripts}/entry.js");
|
||||
|
||||
let absolute_websites_path = websites_path.absolutize();
|
||||
|
||||
let mut bundler = Bundler::new(BundlerOptions {
|
||||
input: Some(vec![source_entry.into()]),
|
||||
dir: Some("./dist/scripts".to_string()),
|
||||
cwd: Some(absolute_websites_path),
|
||||
minify: Some(RawMinifyOptions::Bool(true)),
|
||||
sourcemap: Some(SourceMapType::File),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
bundler.write().await.unwrap();
|
||||
|
||||
let absolute_source_index_path = source_path.join("index.html").absolutize();
|
||||
let absolute_source_index_path_clone = absolute_source_index_path.clone();
|
||||
let absolute_source_path = source_path.absolutize();
|
||||
let absolute_source_path_clone = absolute_source_path.clone();
|
||||
let absolute_source_scripts_path = websites_path.join(source_scripts).absolutize();
|
||||
let absolute_source_sw_path = source_path.join("service-worker.js").absolutize();
|
||||
let absolute_source_sw_path_clone = absolute_source_sw_path.clone();
|
||||
|
||||
let absolute_dist_entry_path = dist_path.join("scripts/entry.js").absolutize();
|
||||
let absolute_dist_index_path = dist_path.join("index.html").absolutize();
|
||||
let absolute_dist_path = dist_path.absolutize();
|
||||
let absolute_dist_path_clone = absolute_dist_path.clone();
|
||||
let absolute_dist_sw_path = dist_path.join("service-worker.js").absolutize();
|
||||
|
||||
let write_index = move || {
|
||||
let mut contents = fs::read_to_string(&absolute_source_index_path).unwrap();
|
||||
|
||||
if let Ok(entry) = fs::read_to_string(absolute_dist_path_clone.join("scripts/entry.js"))
|
||||
&& let Some(start) = entry.find("main")
|
||||
&& let Some(end) = entry.find(".js")
|
||||
{
|
||||
let main_hashed = &entry[start..end];
|
||||
contents = contents.replace("/scripts/main.js", &format!("/scripts/{main_hashed}.js"));
|
||||
}
|
||||
|
||||
let _ = fs::write(&absolute_dist_index_path, contents);
|
||||
};
|
||||
|
||||
let write_sw = move || {
|
||||
let contents = fs::read_to_string(&absolute_source_sw_path)
|
||||
.unwrap()
|
||||
.replace("__VERSION__", &format!("v{VERSION}"));
|
||||
let _ = fs::write(&absolute_dist_sw_path, contents);
|
||||
};
|
||||
|
||||
write_index();
|
||||
write_sw();
|
||||
|
||||
if !watch {
|
||||
return Ok(dist_path);
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
let write_index_clone = write_index.clone();
|
||||
|
||||
let mut entry_watcher = notify::recommended_watcher(
|
||||
move |res: Result<notify::Event, notify::Error>| match res {
|
||||
Ok(_) => write_index_clone(),
|
||||
Err(e) => error!("watch error: {e:?}"),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
entry_watcher
|
||||
.watch(&absolute_dist_entry_path, RecursiveMode::Recursive)
|
||||
.unwrap();
|
||||
|
||||
let mut source_watcher = notify::recommended_watcher(
|
||||
move |res: Result<notify::Event, notify::Error>| match res {
|
||||
Ok(event) => match event.kind {
|
||||
EventKind::Create(_) => event.paths,
|
||||
EventKind::Modify(_) => event.paths,
|
||||
_ => vec![],
|
||||
}
|
||||
.into_iter()
|
||||
.filter(|path| path.starts_with(&absolute_source_path))
|
||||
.filter(|path| !path.starts_with(&absolute_source_scripts_path))
|
||||
.for_each(|source_path| {
|
||||
let suffix = source_path.strip_prefix(&absolute_source_path).unwrap();
|
||||
let dist_path = absolute_dist_path.join(suffix);
|
||||
|
||||
if source_path == absolute_source_index_path_clone {
|
||||
write_index();
|
||||
} else if source_path == absolute_source_sw_path_clone {
|
||||
write_sw();
|
||||
} else {
|
||||
let _ = fs::copy(&source_path, &dist_path);
|
||||
}
|
||||
}),
|
||||
Err(e) => error!("watch error: {e:?}"),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
source_watcher
|
||||
.watch(&absolute_source_path_clone, RecursiveMode::Recursive)
|
||||
.unwrap();
|
||||
|
||||
let watcher =
|
||||
brk_rolldown::Watcher::new(vec![Arc::new(Mutex::new(bundler))], None).unwrap();
|
||||
|
||||
watcher.start().await;
|
||||
});
|
||||
|
||||
Ok(dist_path)
|
||||
}
|
||||
|
||||
fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
fs::create_dir_all(&dst)?;
|
||||
for entry in fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let ty = entry.file_type()?;
|
||||
if ty.is_dir() {
|
||||
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
} else {
|
||||
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,32 +1,33 @@
|
||||
[package]
|
||||
name = "brk_cli"
|
||||
description = "A command line interface to interact with the full Bitcoin Research Kit"
|
||||
description = "A command line interface to run a BRK instance"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
bitcoincore-rpc = { workspace = true }
|
||||
brk_bundler = { workspace = true }
|
||||
brk_computer = { workspace = true }
|
||||
brk_core = { workspace = true }
|
||||
brk_exit = { workspace = true }
|
||||
brk_fetcher = { workspace = true }
|
||||
brk_indexer = { workspace = true }
|
||||
brk_interface = { workspace = true }
|
||||
brk_logger = { workspace = true }
|
||||
brk_parser = { workspace = true }
|
||||
brk_query = { workspace = true }
|
||||
brk_server = { workspace = true }
|
||||
brk_vec = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
clap_derive = { workspace = true }
|
||||
color-eyre = { workspace = true }
|
||||
vecdb = { workspace = true }
|
||||
clap = { version = "4.5.43", features = ["string"] }
|
||||
clap_derive = "4.5.41"
|
||||
color-eyre = "0.6.5"
|
||||
log = { workspace = true }
|
||||
minreq = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tabled = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
toml = "0.8.23"
|
||||
toml = "0.9.5"
|
||||
zip = { version = "4.3.0", default-features = false, features = ["deflate"] }
|
||||
|
||||
[[bin]]
|
||||
name = "brk"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# BRK Cli
|
||||
# BRK CLI
|
||||
|
||||
<p align="left">
|
||||
<a href="https://github.com/bitcoinresearchkit/brk">
|
||||
@@ -23,17 +23,13 @@
|
||||
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
|
||||
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
|
||||
</a>
|
||||
<a href="https://bsky.app/profile/bitcoinresearchkit.org">
|
||||
<img src="https://img.shields.io/badge/bluesky-blue?link=https%3A%2F%2Fbsky.app%2Fprofile%2Fbitcoinresearchkit.org" alt="Bluesky" />
|
||||
</a>
|
||||
<a href="https://x.com/brkdotorg">
|
||||
<img src="https://img.shields.io/badge/x.com-black" alt="X" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
A command line interface to interact with the full Bitcoin Research Kit. It's built on top of every other create and gives the possility to use BRK using the terminal instead of Rust.
|
||||
A command line interface to run a Bitcoin Research Kit instance.
|
||||
|
||||
It has 2 commandes for now (other than `help` and `version`) which are `run` and `query`. The former is used to run the processing (indexer + computer) and/or the server. The latter uses `brk_query` as its backend just like to server to be able to get datasets via the terminal instead of the API. Both commands are very costumizable by having all the parameters of their Rust counterparts ([`run`](https://github.com/bitcoinresearchkit/brk/blob/main/crates/brk_cli/src/run.rs#L91-L147), [`query`](https://github.com/bitcoinresearchkit/brk/blob/main/crates/brk_query/src/params.rs)).
|
||||
It's very customizable with all parameters from the underlying tools (crates) used inside.
|
||||
|
||||
Run `brk -h` for more information.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -54,22 +50,24 @@ To be determined
|
||||
- [Bitcoin](https://bitcoin.org/en/full-node)
|
||||
- [Rust](https://www.rust-lang.org/tools/install)
|
||||
- Unix based operating system (Mac OS or Linux)
|
||||
- Ubuntu users need to install `open-ssl` via `sudo apt install libssl-dev pkg-config`
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Ubuntu users need to install `open-ssl` via `sudo apt install libssl-dev pkg-config`
|
||||
|
||||
## Download
|
||||
|
||||
### Binaries
|
||||
|
||||
You can find a pre-built binary for your operating system on the releases page ([link](https://github.com/bitcoinresearchkit/brk/releases/latest)).
|
||||
You can find a pre-built binary for your operating system in the [releases page](https://github.com/bitcoinresearchkit/brk/releases/latest).
|
||||
|
||||
### Cargo
|
||||
|
||||
```bash
|
||||
# Install
|
||||
cargo install brk # or `cargo install brk_cli`, the result is the same
|
||||
cargo install brk --locked # or `cargo install brk_cli`, the result is the same
|
||||
|
||||
# Update
|
||||
cargo install brk # or `cargo install-update -a` if you have `cargo-update` installed
|
||||
cargo install brk --locked # or `cargo install-update -a` if you have `cargo-update` installed
|
||||
```
|
||||
|
||||
### Source
|
||||
@@ -82,10 +80,11 @@ cargo run -r
|
||||
|
||||
## Usage
|
||||
|
||||
Run `brk -h` to view each available command and their respective description.
|
||||
Run `brk -h` to view each available parameter and their respective description.
|
||||
|
||||
`-h` works also for commands, which mean that `brk run -h` will explain all the parameters of `brk run` for example.
|
||||
> [!TIP]
|
||||
> Every parameter set will be saved at `~/.brk/config.toml`, which allows you to simply run `brk` next time.
|
||||
|
||||
Every parameter set for `brk run` will be saved at `~/.brk/config.toml`, which will allow you to simply run `brk run` next time.
|
||||
## Tunnel
|
||||
|
||||
Then the easiest to let others access your server is to use `cloudflared` which will also cache requests. For more information go to: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/
|
||||
The easiest way to let others access your server is to use `cloudflared` which will also cache requests. For more information see [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) documentation.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fn main() {
|
||||
let profile = std::env::var("PROFILE").unwrap_or_default();
|
||||
|
||||
if profile == "release" {
|
||||
println!("cargo:rustc-flag=-C");
|
||||
println!("cargo:rustc-flag=target-cpu=native");
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::{fs, io, path::Path};
|
||||
|
||||
use brk_query::{Index, Query};
|
||||
use brk_interface::{Index, Interface};
|
||||
use brk_server::VERSION;
|
||||
|
||||
use crate::{VERSION, Website};
|
||||
use crate::website::Website;
|
||||
|
||||
const SCRIPTS: &str = "scripts";
|
||||
|
||||
@@ -11,7 +12,7 @@ pub trait Bridge {
|
||||
fn generate_bridge_file(&self, website: Website, websites_path: &Path) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl Bridge for Query<'static> {
|
||||
impl Bridge for Interface<'static> {
|
||||
fn generate_bridge_file(&self, website: Website, websites_path: &Path) -> io::Result<()> {
|
||||
if website.is_none() {
|
||||
return Ok(());
|
||||
@@ -36,10 +37,9 @@ impl Bridge for Query<'static> {
|
||||
// File auto-generated, any modifications will be overwritten
|
||||
//
|
||||
|
||||
export const VERSION = \"v{}\";
|
||||
export const VERSION = \"v{VERSION}\";
|
||||
|
||||
",
|
||||
VERSION
|
||||
"
|
||||
);
|
||||
|
||||
contents += &indexes
|
||||
@@ -53,7 +53,7 @@ export const VERSION = \"v{}\";
|
||||
.join("\n");
|
||||
|
||||
contents += &format!(
|
||||
"\n\n/** @typedef {{{}}} Index */",
|
||||
"\n\n/** @typedef {{{}}} Index */\n",
|
||||
indexes
|
||||
.iter()
|
||||
.map(|i| i.to_string())
|
||||
@@ -61,12 +61,37 @@ export const VERSION = \"v{}\";
|
||||
.join(" | ")
|
||||
);
|
||||
|
||||
contents += "\n\nexport function createVecIdToIndexes() {\n";
|
||||
contents += "
|
||||
/** @typedef {ReturnType<typeof createIndexes>} Indexes */
|
||||
|
||||
contents += " return /** @type {const} */ ({\n";
|
||||
export function createIndexes() {
|
||||
return {
|
||||
";
|
||||
|
||||
self.vec_trees
|
||||
.id_to_index_to_vec
|
||||
contents += &indexes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i_of_i, i)| {
|
||||
let lowered = i.to_string().to_lowercase();
|
||||
format!(" {lowered}: /** @satisfies {{{i}}} */ ({i_of_i}),",)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
contents += " };\n}\n";
|
||||
|
||||
contents += "
|
||||
/** @typedef {ReturnType<typeof createVecIdToIndexes>} VecIdToIndexes
|
||||
/** @typedef {keyof VecIdToIndexes} VecId */
|
||||
|
||||
/**
|
||||
* @returns {Record<any, number[]>}
|
||||
*/
|
||||
export function createVecIdToIndexes() {
|
||||
return {
|
||||
";
|
||||
|
||||
self.id_to_index_to_vec()
|
||||
.iter()
|
||||
.for_each(|(id, index_to_vec)| {
|
||||
let indexes = index_to_vec
|
||||
@@ -79,11 +104,7 @@ export const VERSION = \"v{}\";
|
||||
contents += &format!(" \"{id}\": [{indexes}],\n");
|
||||
});
|
||||
|
||||
contents += " });\n";
|
||||
contents.push('}');
|
||||
|
||||
contents += "\n/** @typedef {ReturnType<typeof createVecIdToIndexes>} VecIdToIndexes */";
|
||||
contents += "\n/** @typedef {keyof VecIdToIndexes} VecId */\n";
|
||||
contents += " };\n}\n";
|
||||
|
||||
fs::write(path, contents)
|
||||
}
|
||||
@@ -1,113 +1,22 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
thread::sleep,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use bitcoincore_rpc::{self, Auth, Client, RpcApi};
|
||||
use brk_computer::Computer;
|
||||
use brk_core::{default_bitcoin_path, default_brk_path, default_on_error, dot_brk_path};
|
||||
use brk_exit::Exit;
|
||||
use bitcoincore_rpc::{self, Auth, Client};
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_server::{Server, Website};
|
||||
use brk_vec::{Computation, Format};
|
||||
use clap_derive::{Parser, ValueEnum};
|
||||
use clap::Parser;
|
||||
use clap_derive::Parser;
|
||||
use color_eyre::eyre::eyre;
|
||||
use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
||||
let config = RunConfig::import(Some(config))?;
|
||||
use crate::{default_bitcoin_path, default_brk_path, dot_brk_path, website::Website};
|
||||
|
||||
let rpc = config.rpc()?;
|
||||
|
||||
let exit = Exit::new();
|
||||
|
||||
let parser = brk_parser::Parser::new(config.blocksdir(), rpc);
|
||||
|
||||
let format = config.format();
|
||||
|
||||
let mut indexer = Indexer::new(&config.outputsdir(), config.check_collisions())?;
|
||||
indexer.import_stores()?;
|
||||
indexer.import_vecs()?;
|
||||
|
||||
let wait_for_synced_node = || -> color_eyre::Result<()> {
|
||||
let is_synced = || -> color_eyre::Result<bool> {
|
||||
let info = rpc.get_blockchain_info()?;
|
||||
Ok(info.headers == info.blocks)
|
||||
};
|
||||
|
||||
if !is_synced()? {
|
||||
info!("Waiting for node to be synced...");
|
||||
while !is_synced()? {
|
||||
sleep(Duration::from_secs(1))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut computer = Computer::new(&config.outputsdir(), config.fetcher(), format);
|
||||
computer.import_stores(&indexer)?;
|
||||
computer.import_vecs(&indexer, config.computation())?;
|
||||
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()?
|
||||
.block_on(async {
|
||||
let server = if config.serve() {
|
||||
let served_indexer = indexer.clone();
|
||||
let served_computer = computer.clone();
|
||||
|
||||
let server = Server::new(served_indexer, served_computer, config.website())?;
|
||||
|
||||
let opt = Some(tokio::spawn(async move {
|
||||
server.serve().await.unwrap();
|
||||
}));
|
||||
|
||||
sleep(Duration::from_secs(1));
|
||||
|
||||
opt
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if config.process() {
|
||||
loop {
|
||||
wait_for_synced_node()?;
|
||||
|
||||
let block_count = rpc.get_block_count()?;
|
||||
|
||||
info!("{} blocks found.", block_count + 1);
|
||||
|
||||
let starting_indexes = indexer.index(&parser, rpc, &exit)?;
|
||||
|
||||
computer.compute(&mut indexer, starting_indexes, &exit)?;
|
||||
|
||||
if let Some(delay) = config.delay() {
|
||||
sleep(Duration::from_secs(delay))
|
||||
}
|
||||
|
||||
info!("Waiting for new blocks...");
|
||||
|
||||
while block_count == rpc.get_block_count()? {
|
||||
sleep(Duration::from_secs(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(handle) = server {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
const DOWNLOADS: &str = "downloads";
|
||||
|
||||
#[derive(Parser, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||
pub struct RunConfig {
|
||||
#[command(version, about)]
|
||||
pub struct Config {
|
||||
/// Bitcoin main directory path, defaults: ~/.bitcoin, ~/Library/Application\ Support/Bitcoin, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "PATH")]
|
||||
@@ -123,26 +32,16 @@ pub struct RunConfig {
|
||||
#[arg(long, value_name = "PATH")]
|
||||
brkdir: Option<String>,
|
||||
|
||||
/// Activated services, default: all, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short, long)]
|
||||
services: Option<Services>,
|
||||
|
||||
/// 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)]
|
||||
computation: Option<Computation>,
|
||||
|
||||
/// Format of computed datasets, `compressed` to save disk space (experimental), `raw` to prioritize speed, default: `raw`, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short, long)]
|
||||
format: Option<Format>,
|
||||
|
||||
/// Activate fetching prices from exchanges APIs and the computation of all related datasets, default: true, saved
|
||||
/// Activate fetching prices from BRK's API and the computation of all price related datasets, default: true, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short = 'F', long, value_name = "BOOL")]
|
||||
fetch: Option<bool>,
|
||||
|
||||
/// Activate fetching prices from exchanges APIs if `fetch` is also set to `true`, default: true, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "BOOL")]
|
||||
exchanges: Option<bool>,
|
||||
|
||||
/// Website served by the server (if active), default: default, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short, long)]
|
||||
@@ -178,14 +77,26 @@ pub struct RunConfig {
|
||||
#[arg(long, value_name = "SECONDS")]
|
||||
delay: Option<u64>,
|
||||
|
||||
/// Activate the Model Context Protocol (MCP) endpoint to give LLMs access to BRK (experimental), default: true, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "BOOL")]
|
||||
mcp: Option<bool>,
|
||||
|
||||
/// DEV: Activate watching the selected website's folder for changes, default: false, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "BOOL")]
|
||||
watch: Option<bool>,
|
||||
|
||||
/// DEV: Activate checking address hashes for collisions when indexing, default: false, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "BOOL")]
|
||||
check_collisions: Option<bool>,
|
||||
}
|
||||
|
||||
impl RunConfig {
|
||||
pub fn import(config_args: Option<RunConfig>) -> color_eyre::Result<Self> {
|
||||
impl Config {
|
||||
pub fn import() -> color_eyre::Result<Self> {
|
||||
let config_args = Some(Config::parse());
|
||||
|
||||
let path = dot_brk_path();
|
||||
|
||||
let _ = fs::create_dir_all(&path);
|
||||
@@ -207,20 +118,12 @@ impl RunConfig {
|
||||
config_saved.brkdir = Some(brkdir);
|
||||
}
|
||||
|
||||
if let Some(services) = config_args.services.take() {
|
||||
config_saved.services = Some(services);
|
||||
}
|
||||
|
||||
if let Some(computation) = config_args.computation.take() {
|
||||
config_saved.computation = Some(computation);
|
||||
}
|
||||
|
||||
if let Some(fetch) = config_args.fetch.take() {
|
||||
config_saved.fetch = Some(fetch);
|
||||
}
|
||||
|
||||
if let Some(format) = config_args.format.take() {
|
||||
config_saved.format = Some(format);
|
||||
if let Some(exchanges) = config_args.exchanges.take() {
|
||||
config_saved.exchanges = Some(exchanges);
|
||||
}
|
||||
|
||||
if let Some(website) = config_args.website.take() {
|
||||
@@ -255,7 +158,15 @@ impl RunConfig {
|
||||
config_saved.check_collisions = Some(check_collisions);
|
||||
}
|
||||
|
||||
if config_args != RunConfig::default() {
|
||||
if let Some(mcp) = config_args.mcp.take() {
|
||||
config_saved.mcp = Some(mcp);
|
||||
}
|
||||
|
||||
if let Some(watch) = config_args.watch.take() {
|
||||
config_saved.watch = Some(watch);
|
||||
}
|
||||
|
||||
if config_args != Config::default() {
|
||||
dbg!(config_args);
|
||||
panic!("Didn't consume the full config")
|
||||
}
|
||||
@@ -267,19 +178,6 @@ impl RunConfig {
|
||||
|
||||
config.write(&path)?;
|
||||
|
||||
// info!("Configuration {{");
|
||||
// info!(" bitcoindir: {:?}", config.bitcoindir);
|
||||
// info!(" brkdir: {:?}", config.brkdir);
|
||||
// info!(" services: {:?}", config.services);
|
||||
// info!(" website: {:?}", config.website);
|
||||
// info!(" rpcconnect: {:?}", config.rpcconnect);
|
||||
// info!(" rpcport: {:?}", config.rpcport);
|
||||
// info!(" rpccookiefile: {:?}", config.rpccookiefile);
|
||||
// info!(" rpcuser: {:?}", config.rpcuser);
|
||||
// info!(" rpcpassword: {:?}", config.rpcpassword);
|
||||
// info!(" delay: {:?}", config.delay);
|
||||
// info!("}}");
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
@@ -307,7 +205,9 @@ impl RunConfig {
|
||||
|
||||
if self.rpc_auth().is_err() {
|
||||
println!(
|
||||
"No way found to authenticate the RPC client, please either set --rpccookiefile or --rpcuser and --rpcpassword.\nRun the program with '-h' for help."
|
||||
"Unsuccessful authentication with the RPC client.
|
||||
First make sure that `bitcoind` is running. If it is then please either set --rpccookiefile or --rpcuser and --rpcpassword as the default values seemed to have failed.
|
||||
Finally, you can run the program with '-h' for help."
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
@@ -315,7 +215,7 @@ impl RunConfig {
|
||||
|
||||
fn read(path: &Path) -> Self {
|
||||
fs::read_to_string(path).map_or_else(
|
||||
|_| RunConfig::default(),
|
||||
|_| Config::default(),
|
||||
|contents| toml::from_str(&contents).unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
@@ -381,22 +281,12 @@ impl RunConfig {
|
||||
.map_or_else(default_brk_path, |s| Self::fix_user_path(s.as_ref()))
|
||||
}
|
||||
|
||||
pub fn outputsdir(&self) -> PathBuf {
|
||||
self.brkdir().join("outputs")
|
||||
}
|
||||
|
||||
pub fn harsdir(&self) -> PathBuf {
|
||||
self.outputsdir().join("hars")
|
||||
self.brkdir().join("hars")
|
||||
}
|
||||
|
||||
pub fn process(&self) -> bool {
|
||||
self.services
|
||||
.is_none_or(|m| m == Services::All || m == Services::Processor)
|
||||
}
|
||||
|
||||
pub fn serve(&self) -> bool {
|
||||
self.services
|
||||
.is_none_or(|m| m == Services::All || m == Services::Server)
|
||||
pub fn downloads_dir(&self) -> PathBuf {
|
||||
dot_brk_path().join(DOWNLOADS)
|
||||
}
|
||||
|
||||
fn path_cookiefile(&self) -> PathBuf {
|
||||
@@ -432,41 +322,35 @@ impl RunConfig {
|
||||
self.fetch.is_none_or(|b| b)
|
||||
}
|
||||
|
||||
pub fn exchanges(&self) -> bool {
|
||||
self.exchanges.is_none_or(|b| b)
|
||||
}
|
||||
|
||||
pub fn fetcher(&self) -> Option<Fetcher> {
|
||||
self.fetch()
|
||||
.then(|| Fetcher::import(Some(self.harsdir().as_path())).unwrap())
|
||||
}
|
||||
|
||||
pub fn computation(&self) -> Computation {
|
||||
self.computation.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn format(&self) -> Format {
|
||||
self.format.unwrap_or_default()
|
||||
.then(|| Fetcher::import(self.exchanges(), Some(self.harsdir().as_path())).unwrap())
|
||||
}
|
||||
|
||||
pub fn check_collisions(&self) -> bool {
|
||||
self.check_collisions.is_some_and(|b| b)
|
||||
}
|
||||
|
||||
pub fn mcp(&self) -> bool {
|
||||
self.mcp.is_none_or(|b| b)
|
||||
}
|
||||
|
||||
pub fn watch(&self) -> bool {
|
||||
self.watch.is_some_and(|b| b)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Default,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Parser,
|
||||
ValueEnum,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
)]
|
||||
pub enum Services {
|
||||
#[default]
|
||||
All,
|
||||
Processor,
|
||||
Server,
|
||||
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()),
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,28 @@
|
||||
use std::{fs, thread};
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
use brk_core::{dot_brk_log_path, dot_brk_path};
|
||||
use brk_query::Params as QueryArgs;
|
||||
use clap::Parser;
|
||||
use clap_derive::{Parser, Subcommand};
|
||||
use query::query;
|
||||
use run::{RunConfig, run};
|
||||
use std::{
|
||||
fs,
|
||||
io::Cursor,
|
||||
path::Path,
|
||||
thread::{self, sleep},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
mod query;
|
||||
mod run;
|
||||
use bitcoincore_rpc::{self, RpcApi};
|
||||
use brk_bundler::bundle;
|
||||
use brk_computer::Computer;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_interface::Interface;
|
||||
use brk_server::{Server, VERSION};
|
||||
use log::info;
|
||||
use vecdb::Exit;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version, about)]
|
||||
#[command(propagate_version = true)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
mod bridge;
|
||||
mod config;
|
||||
mod paths;
|
||||
mod website;
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
enum Commands {
|
||||
/// Run the indexer, computer and server, use `run -h` for more information
|
||||
Run(RunConfig),
|
||||
/// Query generated datasets via the `run` command in a similar fashion as the server's API, use `query -h` for more information
|
||||
Query(QueryArgs),
|
||||
}
|
||||
use crate::{bridge::Bridge, config::Config, paths::*};
|
||||
|
||||
pub fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
@@ -33,14 +31,124 @@ pub fn main() -> color_eyre::Result<()> {
|
||||
|
||||
brk_logger::init(Some(&dot_brk_log_path()));
|
||||
|
||||
let cli = Cli::parse();
|
||||
|
||||
thread::Builder::new()
|
||||
.stack_size(128 * 1024 * 1024)
|
||||
.spawn(|| match cli.command {
|
||||
Commands::Run(args) => run(args),
|
||||
Commands::Query(args) => query(args),
|
||||
})?
|
||||
.stack_size(256 * 1024 * 1024)
|
||||
.spawn(run)?
|
||||
.join()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn run() -> color_eyre::Result<()> {
|
||||
let config = Config::import()?;
|
||||
|
||||
let rpc = config.rpc()?;
|
||||
|
||||
let exit = Exit::new();
|
||||
exit.set_ctrlc_handler();
|
||||
|
||||
let parser = brk_parser::Parser::new(config.blocksdir(), config.brkdir(), rpc);
|
||||
|
||||
let mut indexer = Indexer::forced_import(&config.brkdir())?;
|
||||
|
||||
let wait_for_synced_node = |rpc_client: &bitcoincore_rpc::Client| -> color_eyre::Result<()> {
|
||||
let is_synced = || -> color_eyre::Result<bool> {
|
||||
let info = rpc_client.get_blockchain_info()?;
|
||||
Ok(info.headers == info.blocks)
|
||||
};
|
||||
|
||||
if !is_synced()? {
|
||||
info!("Waiting for node to be synced...");
|
||||
while !is_synced()? {
|
||||
sleep(Duration::from_secs(1))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut computer = Computer::forced_import(&config.brkdir(), &indexer, config.fetcher())?;
|
||||
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()?
|
||||
.block_on(async {
|
||||
let interface = Interface::build(&indexer, &computer);
|
||||
|
||||
let website = config.website();
|
||||
|
||||
let downloads_path = config.downloads_dir();
|
||||
|
||||
let bundle_path = if website.is_some() {
|
||||
let websites_dev_path = Path::new("../../websites");
|
||||
|
||||
let websites_path = if fs::exists(websites_dev_path)? {
|
||||
websites_dev_path.to_path_buf()
|
||||
} else {
|
||||
let downloaded_websites_path =
|
||||
downloads_path.join(format!("brk-{VERSION}")).join("websites");
|
||||
|
||||
if !fs::exists(&downloaded_websites_path)? {
|
||||
info!("Downloading websites from Github...");
|
||||
|
||||
let url = format!(
|
||||
"https://github.com/bitcoinresearchkit/brk/archive/refs/tags/v{VERSION}.zip",
|
||||
);
|
||||
|
||||
let response = minreq::get(url).send()?;
|
||||
let bytes = response.as_bytes();
|
||||
let cursor = Cursor::new(bytes);
|
||||
|
||||
let mut zip = zip::ZipArchive::new(cursor).unwrap();
|
||||
|
||||
zip.extract(downloads_path).unwrap();
|
||||
}
|
||||
|
||||
downloaded_websites_path
|
||||
};
|
||||
|
||||
interface.generate_bridge_file(website, websites_path.as_path())?;
|
||||
|
||||
let watch = config.watch();
|
||||
|
||||
Some(bundle(&websites_path, website.to_folder_name(), watch).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let server = Server::new(
|
||||
interface,
|
||||
bundle_path,
|
||||
);
|
||||
|
||||
let mcp = config.mcp();
|
||||
|
||||
tokio::spawn(async move {
|
||||
server.serve(mcp).await.unwrap();
|
||||
});
|
||||
|
||||
sleep(Duration::from_secs(1));
|
||||
|
||||
loop {
|
||||
wait_for_synced_node(rpc)?;
|
||||
|
||||
let block_count = rpc.get_block_count()?;
|
||||
|
||||
info!("{} blocks found.", block_count + 1);
|
||||
|
||||
let starting_indexes =
|
||||
indexer.index(&parser, rpc, &exit, config.check_collisions()).unwrap();
|
||||
|
||||
computer.compute(&indexer, starting_indexes, &exit).unwrap();
|
||||
|
||||
if let Some(delay) = config.delay() {
|
||||
sleep(Duration::from_secs(delay))
|
||||
}
|
||||
|
||||
info!("Waiting for new blocks...");
|
||||
|
||||
while block_count == rpc.get_block_count()? {
|
||||
sleep(Duration::from_secs(1))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
use brk_computer::Computer;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_query::{Index, Output, Params as QueryParams, Query, Tabled, Value};
|
||||
use tabled::settings::Style;
|
||||
|
||||
use crate::run::RunConfig;
|
||||
|
||||
pub fn query(params: QueryParams) -> color_eyre::Result<()> {
|
||||
let config = RunConfig::import(None)?;
|
||||
|
||||
let format = config.format();
|
||||
|
||||
let mut indexer = Indexer::new(&config.outputsdir(), config.check_collisions())?;
|
||||
indexer.import_vecs()?;
|
||||
|
||||
let mut computer = Computer::new(&config.outputsdir(), config.fetcher(), format);
|
||||
computer.import_vecs(&indexer, config.computation())?;
|
||||
|
||||
let query = Query::build(&indexer, &computer);
|
||||
|
||||
let index = Index::try_from(params.index.as_str())?;
|
||||
let ids = params.values.iter().map(|s| s.as_str()).collect::<Vec<_>>();
|
||||
let from = params.from();
|
||||
let to = params.to();
|
||||
let format = params.format();
|
||||
|
||||
let res = query.search_and_format(index, &ids, from, to, format)?;
|
||||
|
||||
if format.is_some() {
|
||||
println!("{}", res);
|
||||
} else {
|
||||
println!(
|
||||
"{}",
|
||||
match res {
|
||||
Output::Json(v) => match v {
|
||||
Value::Single(v) => v.to_string().replace("\"", ""),
|
||||
v => {
|
||||
let v = match v {
|
||||
Value::Single(_) => unreachable!("Already processed"),
|
||||
Value::List(v) => vec![v],
|
||||
Value::Matrix(v) => v,
|
||||
};
|
||||
let mut table =
|
||||
v.to_table(ids.iter().map(|id| id.to_string()).collect::<Vec<_>>());
|
||||
table.with(Style::psql());
|
||||
table.to_string()
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -17,7 +17,7 @@ impl Website {
|
||||
!self.is_none()
|
||||
}
|
||||
|
||||
pub fn to_folder_name(&self) -> &str {
|
||||
pub fn to_folder_name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Custom => "custom",
|
||||
Self::Default => "default",
|
||||
@@ -1,25 +1,29 @@
|
||||
[package]
|
||||
name = "brk_computer"
|
||||
description = "A Bitcoin dataset computer, built on top of brk_indexer"
|
||||
description = "A Bitcoin dataset computer built on top of brk_indexer"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
bitcoin = { workspace = true }
|
||||
bitcoincore-rpc = { workspace = true }
|
||||
brk_core = { workspace = true }
|
||||
brk_exit = { workspace = true }
|
||||
brk_structs = { workspace = true }
|
||||
brk_error = { workspace = true }
|
||||
brk_fetcher = { workspace = true }
|
||||
brk_indexer = { workspace = true }
|
||||
brk_logger = { workspace = true }
|
||||
brk_parser = { workspace = true }
|
||||
brk_state = { workspace = true }
|
||||
brk_vec = { workspace = true }
|
||||
color-eyre = { workspace = true }
|
||||
fjall = { workspace = true }
|
||||
jiff = { workspace = true }
|
||||
vecdb = { workspace = true }
|
||||
derive_deref = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rayon = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
zerocopy = { workspace = true }
|
||||
zerocopy-derive = { workspace = true }
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["zerocopy"]
|
||||
|
||||
@@ -23,12 +23,6 @@
|
||||
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
|
||||
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
|
||||
</a>
|
||||
<a href="https://bsky.app/profile/bitcoinresearchkit.org">
|
||||
<img src="https://img.shields.io/badge/bluesky-blue?link=https%3A%2F%2Fbsky.app%2Fprofile%2Fbitcoinresearchkit.org" alt="Bluesky" />
|
||||
</a>
|
||||
<a href="https://x.com/brkdotorg">
|
||||
<img src="https://img.shields.io/badge/x.com-black" alt="X" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
A dataset computer, built on top of `brk_indexer` and `brk_fetcher`. It computes any dataset you can think of and if it doesn't feel free to create an issue.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fn main() {
|
||||
let profile = std::env::var("PROFILE").unwrap_or_default();
|
||||
|
||||
if profile == "release" {
|
||||
println!("cargo:rustc-flag=-C");
|
||||
println!("cargo:rustc-flag=target-cpu=native");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
use std::{
|
||||
path::Path,
|
||||
thread::{self, sleep},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use brk_computer::Computer;
|
||||
use brk_error::Result;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_parser::Parser;
|
||||
use vecdb::Exit;
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
brk_logger::init(Some(Path::new(".log")));
|
||||
|
||||
let bitcoin_dir = Path::new(&std::env::var("HOME").unwrap())
|
||||
.join("Library")
|
||||
.join("Application Support")
|
||||
.join("Bitcoin");
|
||||
// let bitcoin_dir = Path::new("/Volumes/WD_BLACK/bitcoin");
|
||||
|
||||
let rpc = Box::leak(Box::new(bitcoincore_rpc::Client::new(
|
||||
"http://localhost:8332",
|
||||
bitcoincore_rpc::Auth::CookieFile(bitcoin_dir.join(".cookie")),
|
||||
)?));
|
||||
let exit = Exit::new();
|
||||
exit.set_ctrlc_handler();
|
||||
|
||||
// Can't increase main thread's stack size, thus we need to use another thread
|
||||
thread::Builder::new()
|
||||
.stack_size(256 * 1024 * 1024)
|
||||
.spawn(move || -> Result<()> {
|
||||
let outputs_dir = Path::new(&std::env::var("HOME").unwrap()).join(".brk");
|
||||
// let outputs_dir = Path::new("../../_outputs");
|
||||
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), outputs_dir.to_path_buf(), rpc);
|
||||
|
||||
let mut indexer = Indexer::forced_import(&outputs_dir)?;
|
||||
|
||||
let fetcher = Fetcher::import(true, None)?;
|
||||
|
||||
let mut computer = Computer::forced_import(&outputs_dir, &indexer, Some(fetcher))?;
|
||||
|
||||
loop {
|
||||
let i = Instant::now();
|
||||
let starting_indexes = indexer.index(&parser, rpc, &exit, true)?;
|
||||
computer.compute(&indexer, starting_indexes, &exit)?;
|
||||
dbg!(i.elapsed());
|
||||
sleep(Duration::from_secs(10));
|
||||
}
|
||||
})?
|
||||
.join()
|
||||
.unwrap()
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
use std::{path::Path, thread};
|
||||
|
||||
use brk_computer::Computer;
|
||||
use brk_core::{default_bitcoin_path, default_brk_path};
|
||||
use brk_exit::Exit;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_parser::Parser;
|
||||
use brk_vec::{Computation, Format};
|
||||
|
||||
pub fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
|
||||
brk_logger::init(Some(Path::new(".log")));
|
||||
|
||||
let bitcoin_dir = default_bitcoin_path();
|
||||
|
||||
let rpc = Box::leak(Box::new(bitcoincore_rpc::Client::new(
|
||||
"http://localhost:8332",
|
||||
bitcoincore_rpc::Auth::CookieFile(bitcoin_dir.join(".cookie")),
|
||||
)?));
|
||||
let exit = Exit::new();
|
||||
|
||||
// Can't increase main thread's stack programatically, thus we need to use another thread
|
||||
thread::Builder::new()
|
||||
.stack_size(32 * 1024 * 1024)
|
||||
.spawn(move || -> color_eyre::Result<()> {
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), rpc);
|
||||
|
||||
let _outputs_dir = default_brk_path().join("outputs");
|
||||
let outputs_dir = _outputs_dir.as_path();
|
||||
// let outputs_dir = Path::new("../../_outputs");
|
||||
|
||||
let format = Format::Raw;
|
||||
|
||||
let mut indexer = Indexer::new(outputs_dir, true)?;
|
||||
indexer.import_stores()?;
|
||||
indexer.import_vecs()?;
|
||||
|
||||
let fetcher = Fetcher::import(None)?;
|
||||
|
||||
let mut computer = Computer::new(outputs_dir, Some(fetcher), format);
|
||||
computer.import_stores(&indexer)?;
|
||||
computer.import_vecs(&indexer, Computation::Lazy)?;
|
||||
|
||||
let starting_indexes = indexer.index(&parser, rpc, &exit)?;
|
||||
|
||||
computer.compute(&mut indexer, starting_indexes, &exit)?;
|
||||
|
||||
Ok(())
|
||||
})?
|
||||
.join()
|
||||
.unwrap()
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
cargo build --profile profiling
|
||||
flamegraph -- ../../target/profiling/examples/main
|
||||
@@ -0,0 +1,2 @@
|
||||
cargo build --example main --profile profiling
|
||||
samply record ../../target/profiling/examples/main
|
||||
@@ -1,16 +1,20 @@
|
||||
use std::{fs, path::Path};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{
|
||||
CheckedSub, DifficultyEpoch, HalvingEpoch, Height, StoredU32, StoredU64, StoredUsize,
|
||||
Timestamp, Version, Weight,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, AnyIterableVec, Computation, EagerVec, Format};
|
||||
use brk_structs::{
|
||||
CheckedSub, DifficultyEpoch, HalvingEpoch, Height, StoredU32, StoredU64, Timestamp, Version,
|
||||
Weight,
|
||||
};
|
||||
use vecdb::{
|
||||
AnyCollectableVec, Computation, Database, EagerVec, Exit, Format, PAGE_SIZE, VecIterator,
|
||||
};
|
||||
|
||||
use crate::grouped::Source;
|
||||
|
||||
use super::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, StorableVecGeneatorOptions},
|
||||
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, VecBuilderOptions},
|
||||
indexes,
|
||||
};
|
||||
|
||||
@@ -18,6 +22,8 @@ const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
db: Database,
|
||||
|
||||
pub height_to_interval: EagerVec<Height, Timestamp>,
|
||||
pub height_to_vbytes: EagerVec<Height, StoredU64>,
|
||||
pub difficultyepoch_to_timestamp: EagerVec<DifficultyEpoch, Timestamp>,
|
||||
@@ -25,104 +31,112 @@ pub struct Vecs {
|
||||
pub timeindexes_to_timestamp: ComputedVecsFromDateIndex<Timestamp>,
|
||||
pub indexes_to_block_count: ComputedVecsFromHeight<StoredU32>,
|
||||
pub indexes_to_block_interval: ComputedVecsFromHeight<Timestamp>,
|
||||
pub indexes_to_block_size: ComputedVecsFromHeight<StoredUsize>,
|
||||
pub indexes_to_block_size: ComputedVecsFromHeight<StoredU64>,
|
||||
pub indexes_to_block_vbytes: ComputedVecsFromHeight<StoredU64>,
|
||||
pub indexes_to_block_weight: ComputedVecsFromHeight<Weight>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
parent: &Path,
|
||||
version: Version,
|
||||
_computation: Computation,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
) -> color_eyre::Result<Self> {
|
||||
fs::create_dir_all(path)?;
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let db = Database::open(&parent.join("blocks"))?;
|
||||
db.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
|
||||
Ok(Self {
|
||||
height_to_interval: EagerVec::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"interval",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
timeindexes_to_timestamp: ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"timestamp",
|
||||
true,
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_first(),
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_first(),
|
||||
)?,
|
||||
indexes_to_block_interval: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"block_interval",
|
||||
false,
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_percentiles()
|
||||
.add_minmax()
|
||||
.add_average(),
|
||||
)?,
|
||||
indexes_to_block_count: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"block_count",
|
||||
true,
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_block_weight: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"block_weight",
|
||||
false,
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_block_size: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"block_size",
|
||||
false,
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
height_to_vbytes: EagerVec::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"vbytes",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
indexes_to_block_vbytes: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"block_vbytes",
|
||||
false,
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
difficultyepoch_to_timestamp: EagerVec::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"timestamp",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
halvingepoch_to_timestamp: EagerVec::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"timestamp",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -132,7 +146,19 @@ impl Vecs {
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
) -> Result<()> {
|
||||
self.compute_(indexer, indexes, starting_indexes, exit)?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.timeindexes_to_timestamp.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
@@ -144,7 +170,8 @@ impl Vecs {
|
||||
&indexes.dateindex_to_date,
|
||||
|(di, d, ..)| (di, Timestamp::from(d)),
|
||||
exit,
|
||||
)
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -154,23 +181,20 @@ impl Vecs {
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, indexer, _, starting_indexes, exit| {
|
||||
let indexer_vecs = indexer.vecs();
|
||||
|
||||
v.compute_range(
|
||||
starting_indexes.height,
|
||||
&indexer_vecs.height_to_weight,
|
||||
&indexer.vecs.height_to_weight,
|
||||
|h| (h, StoredU32::from(1_u32)),
|
||||
exit,
|
||||
)
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
let indexer_vecs = indexer.vecs();
|
||||
|
||||
let mut height_to_timestamp_iter = indexer_vecs.height_to_timestamp.iter();
|
||||
let mut height_to_timestamp_iter = indexer.vecs.height_to_timestamp.iter();
|
||||
self.height_to_interval.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer_vecs.height_to_timestamp,
|
||||
&indexer.vecs.height_to_timestamp,
|
||||
|(height, timestamp, ..)| {
|
||||
let interval = height.decremented().map_or(Timestamp::ZERO, |prev_h| {
|
||||
let prev_timestamp = height_to_timestamp_iter.unwrap_get_inner(prev_h);
|
||||
@@ -194,19 +218,19 @@ impl Vecs {
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&indexer_vecs.height_to_weight),
|
||||
Some(&indexer.vecs.height_to_weight),
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_size.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&indexer_vecs.height_to_total_size),
|
||||
Some(&indexer.vecs.height_to_total_size),
|
||||
)?;
|
||||
|
||||
self.height_to_vbytes.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer_vecs.height_to_weight,
|
||||
&indexer.vecs.height_to_weight,
|
||||
|(h, w, ..)| {
|
||||
(
|
||||
h,
|
||||
@@ -223,7 +247,7 @@ impl Vecs {
|
||||
Some(&self.height_to_vbytes),
|
||||
)?;
|
||||
|
||||
let mut height_to_timestamp_iter = indexer_vecs.height_to_timestamp.iter();
|
||||
let mut height_to_timestamp_iter = indexer.vecs.height_to_timestamp.iter();
|
||||
|
||||
self.difficultyepoch_to_timestamp.compute_transform(
|
||||
starting_indexes.difficultyepoch,
|
||||
@@ -0,0 +1,767 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, CheckedSub, Dollars, StoredF64, Version};
|
||||
use vecdb::{AnyCollectableVec, Computation, Database, Exit, Format, PAGE_SIZE, VecIterator};
|
||||
|
||||
use super::{
|
||||
Indexes,
|
||||
grouped::{
|
||||
ComputedRatioVecsFromDateIndex, ComputedValueVecsFromHeight, ComputedVecsFromHeight,
|
||||
Source, VecBuilderOptions,
|
||||
},
|
||||
indexes, price, stateful, transactions,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
db: Database,
|
||||
|
||||
pub indexes_to_coinblocks_created: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_coinblocks_stored: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_liveliness: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_vaultedness: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_activity_to_vaultedness_ratio: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_vaulted_supply: ComputedValueVecsFromHeight,
|
||||
pub indexes_to_active_supply: ComputedValueVecsFromHeight,
|
||||
pub indexes_to_thermo_cap: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_investor_cap: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_vaulted_cap: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_active_cap: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_vaulted_price: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_vaulted_price_ratio: ComputedRatioVecsFromDateIndex,
|
||||
pub indexes_to_active_price: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_active_price_ratio: ComputedRatioVecsFromDateIndex,
|
||||
pub indexes_to_true_market_mean: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_true_market_mean_ratio: ComputedRatioVecsFromDateIndex,
|
||||
pub indexes_to_cointime_value_destroyed: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_cointime_value_created: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_cointime_value_stored: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_cointime_price: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_cointime_cap: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_cointime_price_ratio: ComputedRatioVecsFromDateIndex,
|
||||
// pub indexes_to_thermo_cap_relative_to_investor_cap: ComputedValueVecsFromHeight,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
parent: &Path,
|
||||
version: Version,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
) -> Result<Self> {
|
||||
let db = Database::open(&parent.join("cointime"))?;
|
||||
db.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
|
||||
let compute_dollars = price.is_some();
|
||||
|
||||
Ok(Self {
|
||||
indexes_to_coinblocks_created: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"coinblocks_created",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_coinblocks_stored: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"coinblocks_stored",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_liveliness: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"liveliness",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaultedness: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"vaultedness",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_activity_to_vaultedness_ratio: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"activity_to_vaultedness_ratio",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_supply: ComputedValueVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"vaulted_supply",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
compute_dollars,
|
||||
indexes,
|
||||
)?,
|
||||
indexes_to_active_supply: ComputedValueVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"active_supply",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
compute_dollars,
|
||||
indexes,
|
||||
)?,
|
||||
indexes_to_thermo_cap: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"thermo_cap",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_investor_cap: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"investor_cap",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_cap: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"vaulted_cap",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_active_cap: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"active_cap",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_price: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"vaulted_price",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
&db,
|
||||
"vaulted_price",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
true,
|
||||
)?,
|
||||
indexes_to_active_price: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"active_price",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_active_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
&db,
|
||||
"active_price",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
true,
|
||||
)?,
|
||||
indexes_to_true_market_mean: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"true_market_mean",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_true_market_mean_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
&db,
|
||||
"true_market_mean",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
true,
|
||||
)?,
|
||||
indexes_to_cointime_value_destroyed: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"cointime_value_destroyed",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_cointime_value_created: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"cointime_value_created",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_cointime_value_stored: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"cointime_value_stored",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_cointime_price: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"cointime_price",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_cointime_cap: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"cointime_cap",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_cointime_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
&db,
|
||||
"cointime_price",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
true,
|
||||
)?,
|
||||
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
price: Option<&price::Vecs>,
|
||||
transactions: &transactions::Vecs,
|
||||
stateful: &stateful::Vecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
price,
|
||||
transactions,
|
||||
stateful,
|
||||
exit,
|
||||
)?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
price: Option<&price::Vecs>,
|
||||
transactions: &transactions::Vecs,
|
||||
stateful: &stateful::Vecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let circulating_supply = &stateful.utxo_cohorts.all.1.height_to_supply;
|
||||
|
||||
self.indexes_to_coinblocks_created.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
circulating_supply,
|
||||
|(i, v, ..)| (i, StoredF64::from(Bitcoin::from(v))),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
let indexes_to_coinblocks_destroyed =
|
||||
&stateful.utxo_cohorts.all.1.indexes_to_coinblocks_destroyed;
|
||||
|
||||
self.indexes_to_coinblocks_stored.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
let mut coinblocks_destroyed_iter = indexes_to_coinblocks_destroyed
|
||||
.height
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.into_iter();
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
self.indexes_to_coinblocks_created.height.as_ref().unwrap(),
|
||||
|(i, created, ..)| {
|
||||
let destroyed = coinblocks_destroyed_iter.unwrap_get_inner(i);
|
||||
(i, created.checked_sub(destroyed).unwrap())
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_liveliness.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
indexes_to_coinblocks_destroyed
|
||||
.height_extra
|
||||
.unwrap_cumulative(),
|
||||
self.indexes_to_coinblocks_created
|
||||
.height_extra
|
||||
.unwrap_cumulative(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
let liveliness = &self.indexes_to_liveliness;
|
||||
|
||||
self.indexes_to_vaultedness.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
liveliness.height.as_ref().unwrap(),
|
||||
|(i, v, ..)| (i, StoredF64::from(1.0).checked_sub(v).unwrap()),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
let vaultedness = &self.indexes_to_vaultedness;
|
||||
|
||||
self.indexes_to_activity_to_vaultedness_ratio.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
liveliness.height.as_ref().unwrap(),
|
||||
vaultedness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_vaulted_supply.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
circulating_supply,
|
||||
vaultedness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_active_supply.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
circulating_supply,
|
||||
liveliness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
if let Some(price) = price {
|
||||
let realized_cap = stateful
|
||||
.utxo_cohorts
|
||||
.all
|
||||
.1
|
||||
.height_to_realized_cap
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
let realized_price = stateful
|
||||
.utxo_cohorts
|
||||
.all
|
||||
.1
|
||||
.indexes_to_realized_price
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.height
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
self.indexes_to_thermo_cap.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
transactions
|
||||
.indexes_to_subsidy
|
||||
.dollars
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.height_extra
|
||||
.unwrap_cumulative(),
|
||||
|(i, v, ..)| (i, v),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_investor_cap.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_subtract(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
self.indexes_to_thermo_cap.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_vaulted_cap.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
self.indexes_to_vaultedness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_active_cap.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
self.indexes_to_liveliness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_vaulted_price.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
realized_price,
|
||||
self.indexes_to_vaultedness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_vaulted_price_ratio.compute_rest(
|
||||
indexer,
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.indexes_to_vaulted_price.dateindex.unwrap_last()),
|
||||
)?;
|
||||
|
||||
self.indexes_to_active_price.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
realized_price,
|
||||
self.indexes_to_liveliness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_active_price_ratio.compute_rest(
|
||||
indexer,
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.indexes_to_active_price.dateindex.unwrap_last()),
|
||||
)?;
|
||||
|
||||
self.indexes_to_true_market_mean.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
self.indexes_to_investor_cap.height.as_ref().unwrap(),
|
||||
self.indexes_to_active_supply
|
||||
.bitcoin
|
||||
.height
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_true_market_mean_ratio.compute_rest(
|
||||
indexer,
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.indexes_to_true_market_mean.dateindex.unwrap_last()),
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_value_destroyed.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
// TODO: Another example when the callback should be applied to each index, instead of to base then merging from more granular to less
|
||||
// The price taken won't be correct for time based indexes
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&price.chainindexes_to_close.height,
|
||||
indexes_to_coinblocks_destroyed.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_value_created.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&price.chainindexes_to_close.height,
|
||||
self.indexes_to_coinblocks_created.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_value_stored.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&price.chainindexes_to_close.height,
|
||||
self.indexes_to_coinblocks_stored.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_price.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
self.indexes_to_cointime_value_destroyed
|
||||
.height_extra
|
||||
.unwrap_cumulative(),
|
||||
self.indexes_to_coinblocks_stored
|
||||
.height_extra
|
||||
.unwrap_cumulative(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_cap.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
self.indexes_to_cointime_price.height.as_ref().unwrap(),
|
||||
circulating_supply,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_price_ratio.compute_rest(
|
||||
indexer,
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.indexes_to_cointime_price.dateindex.unwrap_last()),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.indexes_to_coinblocks_created.vecs(),
|
||||
self.indexes_to_coinblocks_stored.vecs(),
|
||||
self.indexes_to_liveliness.vecs(),
|
||||
self.indexes_to_vaultedness.vecs(),
|
||||
self.indexes_to_activity_to_vaultedness_ratio.vecs(),
|
||||
self.indexes_to_vaulted_supply.vecs(),
|
||||
self.indexes_to_active_supply.vecs(),
|
||||
self.indexes_to_thermo_cap.vecs(),
|
||||
self.indexes_to_investor_cap.vecs(),
|
||||
self.indexes_to_vaulted_cap.vecs(),
|
||||
self.indexes_to_active_cap.vecs(),
|
||||
self.indexes_to_vaulted_price.vecs(),
|
||||
self.indexes_to_vaulted_price_ratio.vecs(),
|
||||
self.indexes_to_active_price.vecs(),
|
||||
self.indexes_to_active_price_ratio.vecs(),
|
||||
self.indexes_to_true_market_mean.vecs(),
|
||||
self.indexes_to_true_market_mean_ratio.vecs(),
|
||||
self.indexes_to_cointime_price.vecs(),
|
||||
self.indexes_to_cointime_cap.vecs(),
|
||||
self.indexes_to_cointime_price_ratio.vecs(),
|
||||
self.indexes_to_cointime_value_destroyed.vecs(),
|
||||
self.indexes_to_cointime_value_created.vecs(),
|
||||
self.indexes_to_cointime_value_stored.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{StoredU16, Version};
|
||||
use vecdb::{AnyCollectableVec, AnyVec, Computation, Database, Exit, Format};
|
||||
|
||||
use crate::grouped::Source;
|
||||
|
||||
use super::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecsFromHeight, VecBuilderOptions},
|
||||
indexes,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
db: Database,
|
||||
|
||||
pub constant_0: ComputedVecsFromHeight<StoredU16>,
|
||||
pub constant_1: ComputedVecsFromHeight<StoredU16>,
|
||||
pub constant_50: ComputedVecsFromHeight<StoredU16>,
|
||||
pub constant_100: ComputedVecsFromHeight<StoredU16>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
parent: &Path,
|
||||
version: Version,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let db = Database::open(&parent.join("constants"))?;
|
||||
|
||||
Ok(Self {
|
||||
constant_0: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"constant_0",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_1: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"constant_1",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_50: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"constant_50",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_100: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"constant_100",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_(indexer, indexes, starting_indexes, exit)?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.constant_0.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredU16::new(0)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_1.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredU16::new(1)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_50.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredU16::new(50)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_100.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredU16::new(100)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.constant_0.vecs(),
|
||||
self.constant_1.vecs(),
|
||||
self.constant_50.vecs(),
|
||||
self.constant_100.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{DateIndex, Height, OHLCCents, Version};
|
||||
use vecdb::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, Database, Exit, GenericStoredVec,
|
||||
RawVec, StoredIndex, VecIterator,
|
||||
};
|
||||
|
||||
use super::{Indexes, indexes};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
db: Database,
|
||||
fetcher: Fetcher,
|
||||
|
||||
pub dateindex_to_ohlc_in_cents: RawVec<DateIndex, OHLCCents>,
|
||||
pub height_to_ohlc_in_cents: RawVec<Height, OHLCCents>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(parent: &Path, fetcher: Fetcher, version: Version) -> Result<Self> {
|
||||
let db = Database::open(&parent.join("fetched"))?;
|
||||
|
||||
Ok(Self {
|
||||
fetcher,
|
||||
|
||||
dateindex_to_ohlc_in_cents: RawVec::forced_import(
|
||||
&db,
|
||||
"ohlc_in_cents",
|
||||
version + Version::ZERO,
|
||||
)?,
|
||||
height_to_ohlc_in_cents: RawVec::forced_import(
|
||||
&db,
|
||||
"ohlc_in_cents",
|
||||
version + Version::ZERO,
|
||||
)?,
|
||||
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_(indexer, indexes, starting_indexes, exit)?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let height_to_timestamp = &indexer.vecs.height_to_timestamp;
|
||||
let index = starting_indexes
|
||||
.height
|
||||
.min(Height::from(self.height_to_ohlc_in_cents.len()));
|
||||
height_to_timestamp
|
||||
.iter_at(index)
|
||||
.try_for_each(|(i, v)| -> Result<()> {
|
||||
let v = v.into_owned();
|
||||
self.height_to_ohlc_in_cents.forced_push_at(
|
||||
i,
|
||||
self.fetcher
|
||||
.get_height(
|
||||
i,
|
||||
v,
|
||||
i.decremented().map(|prev_i| {
|
||||
height_to_timestamp.into_iter().unwrap_get_inner(prev_i)
|
||||
}),
|
||||
)
|
||||
.unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.height_to_ohlc_in_cents.safe_flush(exit)?;
|
||||
|
||||
let index = starting_indexes
|
||||
.dateindex
|
||||
.min(DateIndex::from(self.dateindex_to_ohlc_in_cents.len()));
|
||||
let mut prev = None;
|
||||
indexes
|
||||
.dateindex_to_date
|
||||
.iter_at(index)
|
||||
.try_for_each(|(i, v)| -> Result<()> {
|
||||
let d = v.into_owned();
|
||||
if prev.is_none() {
|
||||
let i = i.unwrap_to_usize();
|
||||
prev.replace(if i > 0 {
|
||||
self.dateindex_to_ohlc_in_cents
|
||||
.into_iter()
|
||||
.unwrap_get_inner_(i - 1)
|
||||
} else {
|
||||
OHLCCents::default()
|
||||
});
|
||||
}
|
||||
|
||||
let ohlc = if i.unwrap_to_usize() + 100 >= self.dateindex_to_ohlc_in_cents.len()
|
||||
&& let Ok(mut ohlc) = self.fetcher.get_date(d)
|
||||
{
|
||||
let prev_open = *prev.as_ref().unwrap().close;
|
||||
*ohlc.open = prev_open;
|
||||
*ohlc.high = (*ohlc.high).max(prev_open);
|
||||
*ohlc.low = (*ohlc.low).min(prev_open);
|
||||
ohlc
|
||||
} else {
|
||||
prev.clone().unwrap()
|
||||
};
|
||||
|
||||
prev.replace(ohlc.clone());
|
||||
|
||||
self.dateindex_to_ohlc_in_cents
|
||||
.forced_push_at(i, ohlc, exit)?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
self.dateindex_to_ohlc_in_cents.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
vec![
|
||||
&self.dateindex_to_ohlc_in_cents as &dyn AnyCollectableVec,
|
||||
&self.height_to_ohlc_in_cents,
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_structs::Version;
|
||||
use vecdb::{
|
||||
AnyBoxedIterableVec, AnyCloneableIterableVec, AnyCollectableVec, AnyIterableVec, Computation,
|
||||
ComputedVec, ComputedVecFrom2, Database, Exit, Format, FromCoarserIndex, StoredIndex,
|
||||
};
|
||||
|
||||
use crate::grouped::{EagerVecBuilder, VecBuilderOptions};
|
||||
|
||||
use super::ComputedType;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecBuilder<I, T, S1I, S2T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: ComputedType,
|
||||
S2T: ComputedType,
|
||||
{
|
||||
pub first: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub average: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub sum: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub max: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub min: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub last: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub cumulative: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<I, T, S1I, S2T> ComputedVecBuilder<I, T, S1I, S2T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: ComputedType + 'static,
|
||||
S1I: StoredIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: ComputedType,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
format: Format,
|
||||
computation: Computation,
|
||||
source: Option<AnyBoxedIterableVec<S1I, T>>,
|
||||
source_extra: &EagerVecBuilder<S1I, T>,
|
||||
len_source: AnyBoxedIterableVec<I, S2T>,
|
||||
options: ComputedVecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
let only_one_active = options.is_only_one_active();
|
||||
|
||||
let suffix = |s: &str| format!("{name}_{s}");
|
||||
|
||||
let maybe_suffix = |s: &str| {
|
||||
if only_one_active {
|
||||
name.to_string()
|
||||
} else {
|
||||
suffix(s)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
first: options.first.then(|| {
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
db,
|
||||
&maybe_suffix("first"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
source_extra
|
||||
.first
|
||||
.as_ref()
|
||||
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
source
|
||||
.next_at(S1I::min_from(i))
|
||||
.map(|(_, cow)| cow.into_owned())
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
last: options.last.then(|| {
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
source_extra.last.as_ref().map_or_else(
|
||||
|| {
|
||||
source
|
||||
.as_ref()
|
||||
.unwrap_or_else(|| {
|
||||
dbg!(db, name, I::to_string());
|
||||
panic!()
|
||||
})
|
||||
.clone()
|
||||
},
|
||||
|v| v.clone(),
|
||||
),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
source
|
||||
.next_at(S1I::max_from(i, source.len()))
|
||||
.map(|(_, cow)| cow.into_owned())
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
min: options.min.then(|| {
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
db,
|
||||
&maybe_suffix("min"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
source_extra
|
||||
.min
|
||||
.as_ref()
|
||||
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
S1I::inclusive_range_from(i, source.len())
|
||||
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
|
||||
.min()
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
max: options.max.then(|| {
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
db,
|
||||
&maybe_suffix("max"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
source_extra
|
||||
.max
|
||||
.as_ref()
|
||||
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
S1I::inclusive_range_from(i, source.len())
|
||||
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
|
||||
.max()
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
average: options.average.then(|| {
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
db,
|
||||
&maybe_suffix("average"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
source_extra
|
||||
.average
|
||||
.as_ref()
|
||||
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
let vec = S1I::inclusive_range_from(i, source.len())
|
||||
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
|
||||
.collect::<Vec<_>>();
|
||||
if vec.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let mut sum = T::from(0);
|
||||
let len = vec.len();
|
||||
vec.into_iter().for_each(|v| sum += v);
|
||||
Some(sum / len)
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
sum: options.sum.then(|| {
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
db,
|
||||
&(if !options.last && !options.average && !options.min && !options.max {
|
||||
name.to_string()
|
||||
} else {
|
||||
maybe_suffix("sum")
|
||||
}),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
source_extra
|
||||
.sum
|
||||
.as_ref()
|
||||
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
let vec = S1I::inclusive_range_from(i, source.len())
|
||||
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
|
||||
.collect::<Vec<_>>();
|
||||
if vec.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let mut sum = T::from(0);
|
||||
vec.into_iter().for_each(|v| sum += v);
|
||||
Some(sum)
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
cumulative: options.cumulative.then(|| {
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
db,
|
||||
&suffix("cumulative"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
source_extra.cumulative.as_ref().unwrap().boxed_clone(),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
source
|
||||
.next_at(S1I::max_from(i, source.len()))
|
||||
.map(|(_, cow)| cow.into_owned())
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_if_necessary<T2>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
len_source: &impl AnyIterableVec<I, T2>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
first.compute_if_necessary(max_from, len_source, exit)?;
|
||||
}
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
last.compute_if_necessary(max_from, len_source, exit)?;
|
||||
}
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
min.compute_if_necessary(max_from, len_source, exit)?;
|
||||
}
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
max.compute_if_necessary(max_from, len_source, exit)?;
|
||||
}
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
average.compute_if_necessary(max_from, len_source, exit)?;
|
||||
}
|
||||
if let Some(sum) = self.sum.as_mut() {
|
||||
sum.compute_if_necessary(max_from, len_source, exit)?;
|
||||
}
|
||||
if let Some(cumulative) = self.cumulative.as_mut() {
|
||||
cumulative.compute_if_necessary(max_from, len_source, exit)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn starting_index(&self, max_from: I) -> I {
|
||||
max_from.min(I::from(
|
||||
self.vecs().into_iter().map(|v| v.len()).min().unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn unwrap_first(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.first.as_ref().unwrap()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn unwrap_average(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.average.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_sum(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.sum.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_max(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.max.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_min(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.min.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_last(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.last.as_ref().unwrap()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn unwrap_cumulative(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.cumulative.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
let mut v: Vec<&dyn AnyCollectableVec> = vec![];
|
||||
|
||||
if let Some(first) = self.first.as_ref() {
|
||||
v.push(first.as_ref());
|
||||
}
|
||||
if let Some(last) = self.last.as_ref() {
|
||||
v.push(last.as_ref());
|
||||
}
|
||||
if let Some(min) = self.min.as_ref() {
|
||||
v.push(min.as_ref());
|
||||
}
|
||||
if let Some(max) = self.max.as_ref() {
|
||||
v.push(max.as_ref());
|
||||
}
|
||||
if let Some(average) = self.average.as_ref() {
|
||||
v.push(average.as_ref());
|
||||
}
|
||||
if let Some(sum) = self.sum.as_ref() {
|
||||
v.push(sum.as_ref());
|
||||
}
|
||||
if let Some(cumulative) = self.cumulative.as_ref() {
|
||||
v.push(cumulative.as_ref());
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct ComputedVecBuilderOptions {
|
||||
average: bool,
|
||||
sum: bool,
|
||||
max: bool,
|
||||
min: bool,
|
||||
first: bool,
|
||||
last: bool,
|
||||
cumulative: bool,
|
||||
}
|
||||
|
||||
impl From<VecBuilderOptions> for ComputedVecBuilderOptions {
|
||||
fn from(value: VecBuilderOptions) -> Self {
|
||||
Self {
|
||||
average: value.average(),
|
||||
sum: value.sum(),
|
||||
max: value.max(),
|
||||
min: value.min(),
|
||||
first: value.first(),
|
||||
last: value.last(),
|
||||
cumulative: value.cumulative(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputedVecBuilderOptions {
|
||||
pub fn add_first(mut self) -> Self {
|
||||
self.first = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_last(mut self) -> Self {
|
||||
self.last = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_min(mut self) -> Self {
|
||||
self.min = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_max(mut self) -> Self {
|
||||
self.max = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_average(mut self) -> Self {
|
||||
self.average = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_sum(mut self) -> Self {
|
||||
self.sum = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_cumulative(mut self) -> Self {
|
||||
self.cumulative = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_min(mut self) -> Self {
|
||||
self.min = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_max(mut self) -> Self {
|
||||
self.max = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_average(mut self) -> Self {
|
||||
self.average = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_sum(mut self) -> Self {
|
||||
self.sum = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_cumulative(mut self) -> Self {
|
||||
self.cumulative = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_minmax(mut self) -> Self {
|
||||
self.min = true;
|
||||
self.max = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_only_one_active(&self) -> bool {
|
||||
[
|
||||
self.average,
|
||||
self.sum,
|
||||
self.max,
|
||||
self.min,
|
||||
self.first,
|
||||
self.last,
|
||||
self.cumulative,
|
||||
]
|
||||
.iter()
|
||||
.filter(|b| **b)
|
||||
.count()
|
||||
== 1
|
||||
}
|
||||
|
||||
pub fn copy_self_extra(&self) -> Self {
|
||||
Self {
|
||||
cumulative: self.cumulative,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{CheckedSub, Result, StoredUsize, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format, StoredIndex, StoredType};
|
||||
use color_eyre::eyre::ContextCompat;
|
||||
use brk_error::{Error, Result};
|
||||
use brk_structs::{CheckedSub, StoredU64, Version};
|
||||
use vecdb::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, Database, EagerVec, Exit, Format,
|
||||
GenericStoredVec, StoredIndex, StoredRaw,
|
||||
};
|
||||
|
||||
use crate::utils::get_percentile;
|
||||
|
||||
use super::ComputedType;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ComputedVecBuilder<I, T>
|
||||
pub struct EagerVecBuilder<I, T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: ComputedType,
|
||||
@@ -31,30 +31,20 @@ where
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<I, T> ComputedVecBuilder<I, T>
|
||||
impl<I, T> EagerVecBuilder<I, T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: ComputedType,
|
||||
{
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
let only_one_active = options.is_only_one_active();
|
||||
|
||||
let prefix = |s: &str| format!("{s}_{name}");
|
||||
|
||||
let maybe_prefix = |s: &str| {
|
||||
if only_one_active {
|
||||
name.to_string()
|
||||
} else {
|
||||
prefix(s)
|
||||
}
|
||||
};
|
||||
|
||||
let suffix = |s: &str| format!("{name}_{s}");
|
||||
|
||||
let maybe_suffix = |s: &str| {
|
||||
@@ -69,8 +59,8 @@ where
|
||||
first: options.first.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&maybe_prefix("first"),
|
||||
db,
|
||||
&maybe_suffix("first"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
@@ -79,13 +69,13 @@ where
|
||||
}),
|
||||
last: options.last.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(path, name, version + Version::ZERO, format).unwrap(),
|
||||
EagerVec::forced_import(db, name, version + Version::ZERO, format).unwrap(),
|
||||
)
|
||||
}),
|
||||
min: options.min.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
db,
|
||||
&maybe_suffix("min"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -96,7 +86,7 @@ where
|
||||
max: options.max.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
db,
|
||||
&maybe_suffix("max"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -107,7 +97,7 @@ where
|
||||
median: options.median.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
db,
|
||||
&maybe_suffix("median"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -118,7 +108,7 @@ where
|
||||
average: options.average.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
db,
|
||||
&maybe_suffix("average"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -129,8 +119,12 @@ where
|
||||
sum: options.sum.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&maybe_suffix("sum"),
|
||||
db,
|
||||
&(if !options.last && !options.average && !options.min && !options.max {
|
||||
name.to_string()
|
||||
} else {
|
||||
maybe_suffix("sum")
|
||||
}),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
@@ -140,8 +134,8 @@ where
|
||||
cumulative: options.cumulative.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&prefix("cumulative"),
|
||||
db,
|
||||
&suffix("cumulative"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
@@ -151,7 +145,7 @@ where
|
||||
_90p: options._90p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
db,
|
||||
&maybe_suffix("90p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -162,7 +156,7 @@ where
|
||||
_75p: options._75p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
db,
|
||||
&maybe_suffix("75p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -173,7 +167,7 @@ where
|
||||
_25p: options._25p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
db,
|
||||
&maybe_suffix("25p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -184,7 +178,7 @@ where
|
||||
_10p: options._10p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
db,
|
||||
&maybe_suffix("10p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -207,7 +201,7 @@ where
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
self.validate_computed_version_or_reset_file(source.version())?;
|
||||
self.validate_computed_version_or_reset(source.version())?;
|
||||
|
||||
let index = self.starting_index(max_from);
|
||||
|
||||
@@ -217,8 +211,9 @@ where
|
||||
cumulative_vec.iter().unwrap_get_inner(index)
|
||||
});
|
||||
source.iter_at(index).try_for_each(|(i, v)| -> Result<()> {
|
||||
cumulative = cumulative.clone() + v.into_inner();
|
||||
cumulative_vec.forced_push_at(i, cumulative.clone(), exit)
|
||||
cumulative += v.into_owned();
|
||||
cumulative_vec.forced_push_at(i, cumulative, exit)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
@@ -231,13 +226,13 @@ where
|
||||
max_from: I,
|
||||
source: &impl AnyIterableVec<I2, T>,
|
||||
first_indexes: &impl AnyIterableVec<I, I2>,
|
||||
count_indexes: &impl AnyIterableVec<I, StoredUsize>,
|
||||
count_indexes: &impl AnyIterableVec<I, StoredU64>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
I2: StoredIndex + StoredType + CheckedSub<I2>,
|
||||
I2: StoredIndex + StoredRaw + CheckedSub<I2>,
|
||||
{
|
||||
self.validate_computed_version_or_reset_file(
|
||||
self.validate_computed_version_or_reset(
|
||||
source.version() + first_indexes.version() + count_indexes.version(),
|
||||
)?;
|
||||
|
||||
@@ -256,10 +251,10 @@ where
|
||||
|
||||
first_indexes
|
||||
.iter_at(index)
|
||||
.try_for_each(|(i, first_index)| -> Result<()> {
|
||||
let first_index = first_index.into_inner();
|
||||
.try_for_each(|(index, first_index)| -> Result<()> {
|
||||
let first_index = first_index.into_owned();
|
||||
|
||||
let count_index = count_indexes_iter.unwrap_get_inner(i);
|
||||
let count_index = count_indexes_iter.unwrap_get_inner(index);
|
||||
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
let f = source_iter
|
||||
@@ -269,7 +264,7 @@ where
|
||||
}
|
||||
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
let count_index = *count_index;
|
||||
let count_index = *count_index as usize;
|
||||
if count_index == 0 {
|
||||
panic!("should compute last if count can be 0")
|
||||
}
|
||||
@@ -280,7 +275,7 @@ where
|
||||
// dbg!(first_index, count_index, last_index);
|
||||
// })
|
||||
// .unwrap()
|
||||
// .into_inner();
|
||||
// .into_owned();
|
||||
last.forced_push_at(index, v, exit)?;
|
||||
}
|
||||
|
||||
@@ -299,8 +294,8 @@ where
|
||||
if needs_values {
|
||||
source_iter.set(first_index);
|
||||
let mut values = (&mut source_iter)
|
||||
.take(*count_index)
|
||||
.map(|(_, v)| v.into_inner())
|
||||
.take(*count_index as usize)
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if needs_sorted {
|
||||
@@ -308,14 +303,14 @@ where
|
||||
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
max.forced_push_at(
|
||||
i,
|
||||
values
|
||||
index,
|
||||
*values
|
||||
.last()
|
||||
.context("expect some")
|
||||
.ok_or(Error::Str("expect some"))
|
||||
.inspect_err(|_| {
|
||||
dbg!(
|
||||
&values,
|
||||
max.path(),
|
||||
max.name(),
|
||||
first_indexes.name(),
|
||||
first_index,
|
||||
count_indexes.name(),
|
||||
@@ -324,34 +319,33 @@ where
|
||||
source.name()
|
||||
);
|
||||
})
|
||||
.unwrap()
|
||||
.clone(),
|
||||
.unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some(_90p) = self._90p.as_mut() {
|
||||
_90p.forced_push_at(i, get_percentile(&values, 0.90), exit)?;
|
||||
_90p.forced_push_at(index, get_percentile(&values, 0.90), exit)?;
|
||||
}
|
||||
|
||||
if let Some(_75p) = self._75p.as_mut() {
|
||||
_75p.forced_push_at(i, get_percentile(&values, 0.75), exit)?;
|
||||
_75p.forced_push_at(index, get_percentile(&values, 0.75), exit)?;
|
||||
}
|
||||
|
||||
if let Some(median) = self.median.as_mut() {
|
||||
median.forced_push_at(i, get_percentile(&values, 0.50), exit)?;
|
||||
median.forced_push_at(index, get_percentile(&values, 0.50), exit)?;
|
||||
}
|
||||
|
||||
if let Some(_25p) = self._25p.as_mut() {
|
||||
_25p.forced_push_at(i, get_percentile(&values, 0.25), exit)?;
|
||||
_25p.forced_push_at(index, get_percentile(&values, 0.25), exit)?;
|
||||
}
|
||||
|
||||
if let Some(_10p) = self._10p.as_mut() {
|
||||
_10p.forced_push_at(i, get_percentile(&values, 0.10), exit)?;
|
||||
_10p.forced_push_at(index, get_percentile(&values, 0.10), exit)?;
|
||||
}
|
||||
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
min.forced_push_at(i, values.first().unwrap().clone(), exit)?;
|
||||
min.forced_push_at(index, *values.first().unwrap(), exit)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,19 +354,19 @@ where
|
||||
let sum = values.into_iter().fold(T::from(0), |a, b| a + b);
|
||||
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
let avg = sum.clone() / len;
|
||||
average.forced_push_at(i, avg, exit)?;
|
||||
let avg = sum / len;
|
||||
average.forced_push_at(index, avg, exit)?;
|
||||
}
|
||||
|
||||
if needs_sum_or_cumulative {
|
||||
if let Some(sum_vec) = self.sum.as_mut() {
|
||||
sum_vec.forced_push_at(i, sum.clone(), exit)?;
|
||||
sum_vec.forced_push_at(index, sum, exit)?;
|
||||
}
|
||||
|
||||
if let Some(cumulative_vec) = self.cumulative.as_mut() {
|
||||
let t = cumulative.as_ref().unwrap().clone() + sum;
|
||||
cumulative.replace(t.clone());
|
||||
cumulative_vec.forced_push_at(i, t, exit)?;
|
||||
let t = cumulative.unwrap() + sum;
|
||||
cumulative.replace(t);
|
||||
cumulative_vec.forced_push_at(index, t, exit)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -390,13 +384,13 @@ where
|
||||
pub fn from_aligned<I2>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
source: &ComputedVecBuilder<I2, T>,
|
||||
source: &EagerVecBuilder<I2, T>,
|
||||
first_indexes: &impl AnyIterableVec<I, I2>,
|
||||
count_indexes: &impl AnyIterableVec<I, StoredUsize>,
|
||||
count_indexes: &impl AnyIterableVec<I, StoredU64>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
I2: StoredIndex + StoredType + CheckedSub<I2>,
|
||||
I2: StoredIndex + StoredRaw + CheckedSub<I2>,
|
||||
{
|
||||
if self._90p.is_some()
|
||||
|| self._75p.is_some()
|
||||
@@ -407,7 +401,7 @@ where
|
||||
panic!("unsupported");
|
||||
}
|
||||
|
||||
self.validate_computed_version_or_reset_file(
|
||||
self.validate_computed_version_or_reset(
|
||||
VERSION + first_indexes.version() + count_indexes.version(),
|
||||
)?;
|
||||
|
||||
@@ -430,10 +424,10 @@ where
|
||||
|
||||
first_indexes
|
||||
.iter_at(index)
|
||||
.try_for_each(|(i, first_index, ..)| -> Result<()> {
|
||||
let first_index = first_index.into_inner();
|
||||
.try_for_each(|(index, first_index, ..)| -> Result<()> {
|
||||
let first_index = first_index.into_owned();
|
||||
|
||||
let count_index = count_indexes_iter.unwrap_get_inner(i);
|
||||
let count_index = count_indexes_iter.unwrap_get_inner(index);
|
||||
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
let v = source_first_iter
|
||||
@@ -444,7 +438,7 @@ where
|
||||
}
|
||||
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
let count_index = *count_index;
|
||||
let count_index = *count_index as usize;
|
||||
if count_index == 0 {
|
||||
panic!("should compute last if count can be 0")
|
||||
}
|
||||
@@ -468,22 +462,22 @@ where
|
||||
let source_max_iter = source_max_iter.as_mut().unwrap();
|
||||
source_max_iter.set(first_index);
|
||||
let mut values = source_max_iter
|
||||
.take(*count_index)
|
||||
.map(|(_, v)| v.into_inner())
|
||||
.take(*count_index as usize)
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
values.sort_unstable();
|
||||
max.forced_push_at(i, values.last().unwrap().clone(), exit)?;
|
||||
max.forced_push_at(index, *values.last().unwrap(), exit)?;
|
||||
}
|
||||
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
let source_min_iter = source_min_iter.as_mut().unwrap();
|
||||
source_min_iter.set(first_index);
|
||||
let mut values = source_min_iter
|
||||
.take(*count_index)
|
||||
.map(|(_, v)| v.into_inner())
|
||||
.take(*count_index as usize)
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
values.sort_unstable();
|
||||
min.forced_push_at(i, values.first().unwrap().clone(), exit)?;
|
||||
min.forced_push_at(index, *values.first().unwrap(), exit)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,8 +486,8 @@ where
|
||||
let source_average_iter = source_average_iter.as_mut().unwrap();
|
||||
source_average_iter.set(first_index);
|
||||
let values = source_average_iter
|
||||
.take(*count_index)
|
||||
.map(|(_, v)| v.into_inner())
|
||||
.take(*count_index as usize)
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let len = values.len();
|
||||
@@ -501,27 +495,27 @@ where
|
||||
// TODO: Multiply by count then divide by cumulative
|
||||
// Right now it's not 100% accurate as there could be more or less elements in the lower timeframe (28 days vs 31 days in a month for example)
|
||||
let avg = cumulative / len;
|
||||
average.forced_push_at(i, avg, exit)?;
|
||||
average.forced_push_at(index, avg, exit)?;
|
||||
}
|
||||
|
||||
if needs_sum_or_cumulative {
|
||||
let source_sum_iter = source_sum_iter.as_mut().unwrap();
|
||||
source_sum_iter.set(first_index);
|
||||
let values = source_sum_iter
|
||||
.take(*count_index)
|
||||
.map(|(_, v)| v.into_inner())
|
||||
.take(*count_index as usize)
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let sum = values.into_iter().fold(T::from(0), |a, b| a + b);
|
||||
|
||||
if let Some(sum_vec) = self.sum.as_mut() {
|
||||
sum_vec.forced_push_at(i, sum.clone(), exit)?;
|
||||
sum_vec.forced_push_at(index, sum, exit)?;
|
||||
}
|
||||
|
||||
if let Some(cumulative_vec) = self.cumulative.as_mut() {
|
||||
let t = cumulative.as_ref().unwrap().clone() + sum;
|
||||
cumulative.replace(t.clone());
|
||||
cumulative_vec.forced_push_at(i, t, exit)?;
|
||||
let t = cumulative.unwrap() + sum;
|
||||
cumulative.replace(t);
|
||||
cumulative_vec.forced_push_at(index, t, exit)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -669,42 +663,42 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_computed_version_or_reset_file(&mut self, version: Version) -> Result<()> {
|
||||
pub fn validate_computed_version_or_reset(&mut self, version: Version) -> Result<()> {
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
first.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
first.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
last.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
last.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
min.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
min.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
max.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
max.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(median) = self.median.as_mut() {
|
||||
median.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
median.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
average.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
average.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(sum) = self.sum.as_mut() {
|
||||
sum.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
sum.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(cumulative) = self.cumulative.as_mut() {
|
||||
cumulative.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
cumulative.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_90p) = self._90p.as_mut() {
|
||||
_90p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
_90p.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_75p) = self._75p.as_mut() {
|
||||
_75p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
_75p.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_25p) = self._25p.as_mut() {
|
||||
_25p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
_25p.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_10p) = self._10p.as_mut() {
|
||||
_10p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
_10p.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -712,7 +706,7 @@ where
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct StorableVecGeneatorOptions {
|
||||
pub struct VecBuilderOptions {
|
||||
average: bool,
|
||||
sum: bool,
|
||||
max: bool,
|
||||
@@ -727,7 +721,55 @@ pub struct StorableVecGeneatorOptions {
|
||||
cumulative: bool,
|
||||
}
|
||||
|
||||
impl StorableVecGeneatorOptions {
|
||||
impl VecBuilderOptions {
|
||||
pub fn average(&self) -> bool {
|
||||
self.average
|
||||
}
|
||||
|
||||
pub fn sum(&self) -> bool {
|
||||
self.sum
|
||||
}
|
||||
|
||||
pub fn max(&self) -> bool {
|
||||
self.max
|
||||
}
|
||||
|
||||
pub fn _90p(&self) -> bool {
|
||||
self._90p
|
||||
}
|
||||
|
||||
pub fn _75p(&self) -> bool {
|
||||
self._75p
|
||||
}
|
||||
|
||||
pub fn median(&self) -> bool {
|
||||
self.median
|
||||
}
|
||||
|
||||
pub fn _25p(&self) -> bool {
|
||||
self._25p
|
||||
}
|
||||
|
||||
pub fn _10p(&self) -> bool {
|
||||
self._10p
|
||||
}
|
||||
|
||||
pub fn min(&self) -> bool {
|
||||
self.min
|
||||
}
|
||||
|
||||
pub fn first(&self) -> bool {
|
||||
self.first
|
||||
}
|
||||
|
||||
pub fn last(&self) -> bool {
|
||||
self.last
|
||||
}
|
||||
|
||||
pub fn cumulative(&self) -> bool {
|
||||
self.cumulative
|
||||
}
|
||||
|
||||
pub fn add_first(mut self) -> Self {
|
||||
self.first = true;
|
||||
self
|
||||
@@ -0,0 +1,23 @@
|
||||
use std::ops::{Add, AddAssign, Div};
|
||||
|
||||
use vecdb::StoredCompressed;
|
||||
|
||||
pub trait ComputedType
|
||||
where
|
||||
Self: StoredCompressed
|
||||
+ From<usize>
|
||||
+ Div<usize, Output = Self>
|
||||
+ Add<Output = Self>
|
||||
+ AddAssign
|
||||
+ Ord,
|
||||
{
|
||||
}
|
||||
impl<T> ComputedType for T where
|
||||
T: StoredCompressed
|
||||
+ From<usize>
|
||||
+ Div<usize, Output = Self>
|
||||
+ Add<Output = Self>
|
||||
+ AddAssign
|
||||
+ Ord
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{
|
||||
DateIndex, DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use vecdb::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, AnyIterableVec, Computation, Database, EagerVec,
|
||||
Exit, Format,
|
||||
};
|
||||
|
||||
use crate::{Indexes, grouped::ComputedVecBuilder, indexes};
|
||||
|
||||
use super::{ComputedType, EagerVecBuilder, Source, VecBuilderOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecsFromDateIndex<T>
|
||||
where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub dateindex: Option<EagerVec<DateIndex, T>>,
|
||||
pub dateindex_extra: EagerVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
|
||||
pub semesterindex: ComputedVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T, DateIndex, YearIndex>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedVecsFromDateIndex<T>
|
||||
where
|
||||
T: ComputedType + 'static,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
source: Source<DateIndex, T>,
|
||||
version: Version,
|
||||
format: Format,
|
||||
computation: Computation,
|
||||
indexes: &indexes::Vecs,
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
let dateindex = source.is_compute().then(|| {
|
||||
EagerVec::forced_import(db, name, version + VERSION + Version::ZERO, format).unwrap()
|
||||
});
|
||||
|
||||
let dateindex_extra = EagerVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options.copy_self_extra(),
|
||||
)?;
|
||||
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
let dateindex_source = source.vec().or(dateindex.as_ref().map(|v| v.boxed_clone()));
|
||||
|
||||
Ok(Self {
|
||||
weekindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.weekindex_to_weekindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.monthindex_to_monthindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.quarterindex_to_quarterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
semesterindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.semesterindex_to_semesterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.yearindex_to_yearindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.decadeindex_to_decadeindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
dateindex,
|
||||
dateindex_extra,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all<F>(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut EagerVec<DateIndex, T>,
|
||||
&Indexer,
|
||||
&indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
self.dateindex.as_mut().unwrap(),
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let dateindex: Option<&EagerVec<DateIndex, T>> = None;
|
||||
self.compute_rest(indexes, starting_indexes, exit, dateindex)
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
dateindex: Option<&impl AnyIterableVec<DateIndex, T>>,
|
||||
) -> Result<()> {
|
||||
if let Some(dateindex) = dateindex {
|
||||
self.dateindex_extra
|
||||
.extend(starting_indexes.dateindex, dateindex, exit)?;
|
||||
} else {
|
||||
let dateindex = self.dateindex.as_ref().unwrap();
|
||||
|
||||
self.dateindex_extra
|
||||
.extend(starting_indexes.dateindex, dateindex, exit)?;
|
||||
}
|
||||
|
||||
self.weekindex.compute_if_necessary(
|
||||
starting_indexes.weekindex,
|
||||
&indexes.weekindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.compute_if_necessary(
|
||||
starting_indexes.monthindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex.compute_if_necessary(
|
||||
starting_indexes.quarterindex,
|
||||
&indexes.quarterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.semesterindex.compute_if_necessary(
|
||||
starting_indexes.semesterindex,
|
||||
&indexes.semesterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.compute_if_necessary(
|
||||
starting_indexes.yearindex,
|
||||
&indexes.yearindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.compute_if_necessary(
|
||||
starting_indexes.decadeindex,
|
||||
&indexes.decadeindex_to_yearindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.dateindex
|
||||
.as_ref()
|
||||
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
|
||||
self.dateindex_extra.vecs(),
|
||||
self.weekindex.vecs(),
|
||||
self.monthindex.vecs(),
|
||||
self.quarterindex.vecs(),
|
||||
self.semesterindex.vecs(),
|
||||
self.yearindex.vecs(),
|
||||
self.decadeindex.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
use std::path::Path;
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_core::{
|
||||
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, Result, Version,
|
||||
WeekIndex, YearIndex,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format};
|
||||
use brk_structs::{
|
||||
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, SemesterIndex,
|
||||
Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use vecdb::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, AnyIterableVec, Computation, Database, EagerVec,
|
||||
Exit, Format,
|
||||
};
|
||||
|
||||
use crate::vecs::{Indexes, indexes};
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecBuilder, Source},
|
||||
indexes,
|
||||
};
|
||||
|
||||
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
|
||||
use super::{ComputedType, EagerVecBuilder, VecBuilderOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecsFromHeight<T>
|
||||
@@ -18,46 +24,50 @@ where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub height: Option<EagerVec<Height, T>>,
|
||||
pub height_extra: ComputedVecBuilder<Height, T>,
|
||||
pub dateindex: ComputedVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T>,
|
||||
pub difficultyepoch: ComputedVecBuilder<DifficultyEpoch, T>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T>,
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T>,
|
||||
pub height_extra: EagerVecBuilder<Height, T>,
|
||||
pub dateindex: EagerVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
|
||||
pub difficultyepoch: EagerVecBuilder<DifficultyEpoch, T>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
|
||||
pub semesterindex: ComputedVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T, DateIndex, YearIndex>,
|
||||
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedVecsFromHeight<T>
|
||||
where
|
||||
T: ComputedType + Ord + From<f64>,
|
||||
T: ComputedType + Ord + From<f64> + 'static,
|
||||
f64: From<T>,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
source: Source<Height, T>,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let height = compute_source.then(|| {
|
||||
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format).unwrap()
|
||||
computation: Computation,
|
||||
indexes: &indexes::Vecs,
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
let height = source.is_compute().then(|| {
|
||||
EagerVec::forced_import(db, name, version + VERSION + Version::ZERO, format).unwrap()
|
||||
});
|
||||
|
||||
let height_extra = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
let height_extra = EagerVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options.copy_self_extra(),
|
||||
)?;
|
||||
|
||||
let dateindex = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
let dateindex = EagerVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -67,47 +77,78 @@ where
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
Ok(Self {
|
||||
weekindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.weekindex_to_weekindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.monthindex_to_monthindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.quarterindex_to_quarterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
semesterindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.semesterindex_to_semesterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.yearindex_to_yearindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.decadeindex_to_decadeindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(db, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
height,
|
||||
height_extra,
|
||||
dateindex,
|
||||
weekindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
difficultyepoch: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(path, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
difficultyepoch: EagerVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -123,7 +164,7 @@ where
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
) -> Result<()>
|
||||
where
|
||||
F: FnMut(&mut EagerVec<Height, T>, &Indexer, &indexes::Vecs, &Indexes, &Exit) -> Result<()>,
|
||||
{
|
||||
@@ -144,9 +185,9 @@ where
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
height: Option<&impl AnyIterableVec<Height, T>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
if let Some(height) = height {
|
||||
height_vec: Option<&impl AnyIterableVec<Height, T>>,
|
||||
) -> Result<()> {
|
||||
if let Some(height) = height_vec {
|
||||
self.height_extra
|
||||
.extend(starting_indexes.height, height, exit)?;
|
||||
|
||||
@@ -188,42 +229,38 @@ where
|
||||
)?;
|
||||
}
|
||||
|
||||
self.weekindex.from_aligned(
|
||||
self.weekindex.compute_if_necessary(
|
||||
starting_indexes.weekindex,
|
||||
&self.dateindex,
|
||||
&indexes.weekindex_to_first_dateindex,
|
||||
&indexes.weekindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.from_aligned(
|
||||
self.monthindex.compute_if_necessary(
|
||||
starting_indexes.monthindex,
|
||||
&self.dateindex,
|
||||
&indexes.monthindex_to_first_dateindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex.from_aligned(
|
||||
self.quarterindex.compute_if_necessary(
|
||||
starting_indexes.quarterindex,
|
||||
&self.monthindex,
|
||||
&indexes.quarterindex_to_first_monthindex,
|
||||
&indexes.quarterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.from_aligned(
|
||||
self.semesterindex.compute_if_necessary(
|
||||
starting_indexes.semesterindex,
|
||||
&indexes.semesterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.compute_if_necessary(
|
||||
starting_indexes.yearindex,
|
||||
&self.monthindex,
|
||||
&indexes.yearindex_to_first_monthindex,
|
||||
&indexes.yearindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.from_aligned(
|
||||
self.decadeindex.compute_if_necessary(
|
||||
starting_indexes.decadeindex,
|
||||
&self.yearindex,
|
||||
&indexes.decadeindex_to_first_yearindex,
|
||||
&indexes.decadeindex_to_yearindex_count,
|
||||
exit,
|
||||
)?;
|
||||
@@ -242,6 +279,7 @@ where
|
||||
self.difficultyepoch.vecs(),
|
||||
self.monthindex.vecs(),
|
||||
self.quarterindex.vecs(),
|
||||
self.semesterindex.vecs(),
|
||||
self.yearindex.vecs(),
|
||||
// self.halvingepoch.vecs(),
|
||||
self.decadeindex.vecs(),
|
||||
@@ -1,13 +1,12 @@
|
||||
use std::path::Path;
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_core::{DifficultyEpoch, Height, Result, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, EagerVec, Format};
|
||||
use brk_structs::{DifficultyEpoch, Height, Version};
|
||||
use vecdb::{AnyCollectableVec, Database, EagerVec, Exit, Format};
|
||||
|
||||
use crate::vecs::{Indexes, indexes};
|
||||
use crate::{Indexes, indexes};
|
||||
|
||||
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
|
||||
use super::{ComputedType, EagerVecBuilder, VecBuilderOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecsFromHeightStrict<T>
|
||||
@@ -15,8 +14,8 @@ where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub height: EagerVec<Height, T>,
|
||||
pub height_extra: ComputedVecBuilder<Height, T>,
|
||||
pub difficultyepoch: ComputedVecBuilder<DifficultyEpoch, T>,
|
||||
pub height_extra: EagerVecBuilder<Height, T>,
|
||||
pub difficultyepoch: EagerVecBuilder<DifficultyEpoch, T>,
|
||||
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
|
||||
}
|
||||
|
||||
@@ -28,17 +27,16 @@ where
|
||||
f64: From<T>,
|
||||
{
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let height =
|
||||
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format)?;
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
let height = EagerVec::forced_import(db, name, version + VERSION + Version::ZERO, format)?;
|
||||
|
||||
let height_extra = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
let height_extra = EagerVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -50,14 +48,14 @@ where
|
||||
Ok(Self {
|
||||
height,
|
||||
height_extra,
|
||||
difficultyepoch: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
difficultyepoch: EagerVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(path, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(db, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,7 +66,7 @@ where
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
) -> Result<()>
|
||||
where
|
||||
F: FnMut(&mut EagerVec<Height, T>, &Indexer, &indexes::Vecs, &Indexes, &Exit) -> Result<()>,
|
||||
{
|
||||
@@ -1,18 +1,21 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{
|
||||
Bitcoin, DateIndex, DecadeIndex, DifficultyEpoch, Dollars, Height, MonthIndex, QuarterIndex,
|
||||
Result, Sats, TxIndex, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{
|
||||
AnyCollectableVec, AnyVec, CollectableVec, EagerVec, Format, StoredIndex, VecIterator,
|
||||
use brk_structs::{
|
||||
Bitcoin, DateIndex, DecadeIndex, DifficultyEpoch, Dollars, Height, MonthIndex, QuarterIndex,
|
||||
Sats, SemesterIndex, TxIndex, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use vecdb::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, AnyVec, CollectableVec, Computation, Database,
|
||||
EagerVec, Exit, Format, GenericStoredVec, StoredIndex, VecIterator,
|
||||
};
|
||||
|
||||
use crate::vecs::{Indexes, fetched, indexes};
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecBuilder, Source},
|
||||
indexes, price,
|
||||
};
|
||||
|
||||
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
|
||||
use super::{ComputedType, EagerVecBuilder, VecBuilderOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecsFromTxindex<T>
|
||||
@@ -20,41 +23,45 @@ where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub txindex: Option<Box<EagerVec<TxIndex, T>>>,
|
||||
pub height: ComputedVecBuilder<Height, T>,
|
||||
pub dateindex: ComputedVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T>,
|
||||
pub difficultyepoch: ComputedVecBuilder<DifficultyEpoch, T>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T>,
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T>,
|
||||
pub height: EagerVecBuilder<Height, T>,
|
||||
pub dateindex: EagerVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
|
||||
pub difficultyepoch: EagerVecBuilder<DifficultyEpoch, T>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
|
||||
pub semesterindex: ComputedVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T, DateIndex, YearIndex>,
|
||||
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedVecsFromTxindex<T>
|
||||
where
|
||||
T: ComputedType + Ord + From<f64>,
|
||||
T: ComputedType + Ord + From<f64> + 'static,
|
||||
f64: From<T>,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
source: Source<TxIndex, T>,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let txindex = compute_source.then(|| {
|
||||
computation: Computation,
|
||||
indexes: &indexes::Vecs,
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
let txindex = source.is_compute().then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format)
|
||||
EagerVec::forced_import(db, name, version + VERSION + Version::ZERO, format)
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let height = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
let height = EagerVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -63,59 +70,93 @@ where
|
||||
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
let dateindex = EagerVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
txindex,
|
||||
height,
|
||||
dateindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
weekindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
difficultyepoch: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
computation,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.weekindex_to_weekindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.monthindex_to_monthindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.quarterindex_to_quarterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
semesterindex: ComputedVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.semesterindex_to_semesterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.yearindex_to_yearindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(path, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.decadeindex_to_decadeindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
|
||||
txindex,
|
||||
height,
|
||||
dateindex,
|
||||
difficultyepoch: EagerVecBuilder::forced_import(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(db, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -127,7 +168,7 @@ where
|
||||
// starting_indexes: &Indexes,
|
||||
// exit: &Exit,
|
||||
// mut compute: F,
|
||||
// ) -> color_eyre::Result<()>
|
||||
// ) -> Result<()>
|
||||
// where
|
||||
// F: FnMut(
|
||||
// &mut EagerVec<TxIndex, T>,
|
||||
@@ -163,7 +204,7 @@ where
|
||||
self.height.compute(
|
||||
starting_indexes.height,
|
||||
txindex,
|
||||
&indexer.vecs().height_to_first_txindex,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
&indexes.height_to_txindex_count,
|
||||
exit,
|
||||
)?;
|
||||
@@ -173,7 +214,7 @@ where
|
||||
self.height.compute(
|
||||
starting_indexes.height,
|
||||
txindex,
|
||||
&indexer.vecs().height_to_first_txindex,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
&indexes.height_to_txindex_count,
|
||||
exit,
|
||||
)?;
|
||||
@@ -196,42 +237,38 @@ where
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.weekindex.from_aligned(
|
||||
self.weekindex.compute_if_necessary(
|
||||
starting_indexes.weekindex,
|
||||
&self.dateindex,
|
||||
&indexes.weekindex_to_first_dateindex,
|
||||
&indexes.weekindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.from_aligned(
|
||||
self.monthindex.compute_if_necessary(
|
||||
starting_indexes.monthindex,
|
||||
&self.dateindex,
|
||||
&indexes.monthindex_to_first_dateindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex.from_aligned(
|
||||
self.quarterindex.compute_if_necessary(
|
||||
starting_indexes.quarterindex,
|
||||
&self.monthindex,
|
||||
&indexes.quarterindex_to_first_monthindex,
|
||||
&indexes.quarterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.from_aligned(
|
||||
self.semesterindex.compute_if_necessary(
|
||||
starting_indexes.semesterindex,
|
||||
&indexes.semesterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.compute_if_necessary(
|
||||
starting_indexes.yearindex,
|
||||
&self.monthindex,
|
||||
&indexes.yearindex_to_first_monthindex,
|
||||
&indexes.yearindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.from_aligned(
|
||||
self.decadeindex.compute_if_necessary(
|
||||
starting_indexes.decadeindex,
|
||||
&self.yearindex,
|
||||
&indexes.decadeindex_to_first_yearindex,
|
||||
&indexes.decadeindex_to_yearindex_count,
|
||||
exit,
|
||||
)?;
|
||||
@@ -258,6 +295,7 @@ where
|
||||
self.difficultyepoch.vecs(),
|
||||
self.monthindex.vecs(),
|
||||
self.quarterindex.vecs(),
|
||||
self.semesterindex.vecs(),
|
||||
self.yearindex.vecs(),
|
||||
// self.halvingepoch.vecs(),
|
||||
self.decadeindex.vecs(),
|
||||
@@ -285,11 +323,11 @@ impl ComputedVecsFromTxindex<Bitcoin> {
|
||||
};
|
||||
|
||||
self.height
|
||||
.validate_computed_version_or_reset_file(txindex_version)?;
|
||||
.validate_computed_version_or_reset(txindex_version)?;
|
||||
|
||||
let starting_index = self.height.starting_index(starting_indexes.height);
|
||||
|
||||
(starting_index.unwrap_to_usize()..indexer.vecs().height_to_weight.len())
|
||||
(starting_index.unwrap_to_usize()..indexer.vecs.height_to_weight.len())
|
||||
.map(Height::from)
|
||||
.try_for_each(|height| -> Result<()> {
|
||||
if let Some(first) = self.height.first.as_mut() {
|
||||
@@ -455,7 +493,7 @@ impl ComputedVecsFromTxindex<Dollars> {
|
||||
exit: &Exit,
|
||||
bitcoin: &ComputedVecsFromTxindex<Bitcoin>,
|
||||
txindex: Option<&impl CollectableVec<TxIndex, Dollars>>,
|
||||
fetched: &fetched::Vecs,
|
||||
price: &price::Vecs,
|
||||
) -> Result<()> {
|
||||
let txindex_version = if let Some(txindex) = txindex {
|
||||
txindex.version()
|
||||
@@ -464,13 +502,13 @@ impl ComputedVecsFromTxindex<Dollars> {
|
||||
};
|
||||
|
||||
self.height
|
||||
.validate_computed_version_or_reset_file(txindex_version)?;
|
||||
.validate_computed_version_or_reset(txindex_version)?;
|
||||
|
||||
let starting_index = self.height.starting_index(starting_indexes.height);
|
||||
|
||||
let mut close_iter = fetched.chainindexes_to_close.height.into_iter();
|
||||
let mut close_iter = price.chainindexes_to_close.height.into_iter();
|
||||
|
||||
(starting_index.unwrap_to_usize()..indexer.vecs().height_to_weight.len())
|
||||
(starting_index.unwrap_to_usize()..indexer.vecs.height_to_weight.len())
|
||||
.map(Height::from)
|
||||
.try_for_each(|height| -> Result<()> {
|
||||
let price = *close_iter.unwrap_get_inner(height);
|
||||
@@ -1,22 +1,26 @@
|
||||
mod builder;
|
||||
mod builder_computed;
|
||||
mod builder_eager;
|
||||
mod computed;
|
||||
mod from_dateindex;
|
||||
mod from_height;
|
||||
mod from_height_strict;
|
||||
mod from_txindex;
|
||||
mod ratio_from_dateindex;
|
||||
mod r#type;
|
||||
mod source;
|
||||
mod value_from_dateindex;
|
||||
mod value_from_height;
|
||||
mod value_from_txindex;
|
||||
mod value_height;
|
||||
|
||||
pub use builder::*;
|
||||
pub use builder_computed::*;
|
||||
pub use builder_eager::*;
|
||||
use computed::*;
|
||||
pub use from_dateindex::*;
|
||||
pub use from_height::*;
|
||||
pub use from_height_strict::*;
|
||||
pub use from_txindex::*;
|
||||
pub use ratio_from_dateindex::*;
|
||||
use r#type::*;
|
||||
pub use source::*;
|
||||
pub use value_from_dateindex::*;
|
||||
pub use value_from_height::*;
|
||||
pub use value_from_txindex::*;
|
||||
@@ -0,0 +1,51 @@
|
||||
use vecdb::AnyBoxedIterableVec;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Source<I, T> {
|
||||
Compute,
|
||||
None,
|
||||
Vec(AnyBoxedIterableVec<I, T>),
|
||||
}
|
||||
|
||||
impl<I, T> Source<I, T> {
|
||||
pub fn is_compute(&self) -> bool {
|
||||
matches!(self, Self::Compute)
|
||||
}
|
||||
|
||||
pub fn is_none(&self) -> bool {
|
||||
matches!(self, Self::None)
|
||||
}
|
||||
|
||||
pub fn is_vec(&self) -> bool {
|
||||
matches!(self, Self::Vec(_))
|
||||
}
|
||||
|
||||
pub fn vec(self) -> Option<AnyBoxedIterableVec<I, T>> {
|
||||
match self {
|
||||
Self::Vec(v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> From<bool> for Source<I, T> {
|
||||
fn from(value: bool) -> Self {
|
||||
if value { Self::Compute } else { Self::None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> From<AnyBoxedIterableVec<I, T>> for Source<I, T> {
|
||||
fn from(value: AnyBoxedIterableVec<I, T>) -> Self {
|
||||
Self::Vec(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> From<Option<AnyBoxedIterableVec<I, T>>> for Source<I, T> {
|
||||
fn from(value: Option<AnyBoxedIterableVec<I, T>>) -> Self {
|
||||
if let Some(v) = value {
|
||||
Self::Vec(v)
|
||||
} else {
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,18 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Bitcoin, DateIndex, Dollars, Result, Sats, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, CollectableVec, EagerVec, Format, StoredVec};
|
||||
use brk_structs::{Bitcoin, DateIndex, Dollars, Sats, Version};
|
||||
use vecdb::{
|
||||
AnyCollectableVec, CollectableVec, Computation, Database, EagerVec, Exit, Format, StoredVec,
|
||||
};
|
||||
|
||||
use crate::vecs::{Indexes, fetched, grouped::ComputedVecsFromDateIndex, indexes};
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::ComputedVecsFromDateIndex,
|
||||
indexes, price,
|
||||
traits::{ComputeFromBitcoin, ComputeFromSats},
|
||||
};
|
||||
|
||||
use super::StorableVecGeneatorOptions;
|
||||
use super::{Source, VecBuilderOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedValueVecsFromDateIndex {
|
||||
@@ -19,39 +24,48 @@ pub struct ComputedValueVecsFromDateIndex {
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl ComputedValueVecsFromDateIndex {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
source: Source<DateIndex, Sats>,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
computation: Computation,
|
||||
options: VecBuilderOptions,
|
||||
compute_dollars: bool,
|
||||
) -> color_eyre::Result<Self> {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sats: ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
db,
|
||||
name,
|
||||
compute_source,
|
||||
source,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
bitcoin: ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
db,
|
||||
&format!("{name}_in_btc"),
|
||||
true,
|
||||
Source::Compute,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
dollars: compute_dollars.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
db,
|
||||
&format!("{name}_in_usd"),
|
||||
true,
|
||||
Source::Compute,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)
|
||||
.unwrap()
|
||||
@@ -63,11 +77,11 @@ impl ComputedValueVecsFromDateIndex {
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
) -> Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut EagerVec<DateIndex, Sats>,
|
||||
@@ -86,7 +100,7 @@ impl ComputedValueVecsFromDateIndex {
|
||||
)?;
|
||||
|
||||
let dateindex: Option<&StoredVec<DateIndex, Sats>> = None;
|
||||
self.compute_rest(indexer, indexes, fetched, starting_indexes, exit, dateindex)?;
|
||||
self.compute_rest(indexer, indexes, price, starting_indexes, exit, dateindex)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -95,11 +109,11 @@ impl ComputedValueVecsFromDateIndex {
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
dateindex: Option<&impl CollectableVec<DateIndex, Sats>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
) -> Result<()> {
|
||||
if let Some(dateindex) = dateindex {
|
||||
self.sats
|
||||
.compute_rest(indexes, starting_indexes, exit, Some(dateindex))?;
|
||||
@@ -135,7 +149,7 @@ impl ComputedValueVecsFromDateIndex {
|
||||
}
|
||||
|
||||
let dateindex_to_bitcoin = self.bitcoin.dateindex.as_ref().unwrap();
|
||||
let dateindex_to_close = fetched
|
||||
let dateindex_to_close = price
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.timeindexes_to_close
|
||||
@@ -1,13 +1,18 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Bitcoin, Dollars, Height, Result, Sats, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, CollectableVec, EagerVec, Format, StoredVec};
|
||||
use brk_structs::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{
|
||||
AnyCollectableVec, CollectableVec, Computation, Database, EagerVec, Exit, Format, StoredVec,
|
||||
};
|
||||
|
||||
use crate::vecs::{Indexes, fetched, indexes};
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::Source,
|
||||
indexes, price,
|
||||
traits::{ComputeFromBitcoin, ComputeFromSats},
|
||||
};
|
||||
|
||||
use super::{ComputedVecsFromHeight, StorableVecGeneatorOptions};
|
||||
use super::{ComputedVecsFromHeight, VecBuilderOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedValueVecsFromHeight {
|
||||
@@ -19,39 +24,48 @@ pub struct ComputedValueVecsFromHeight {
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl ComputedValueVecsFromHeight {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
source: Source<Height, Sats>,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
computation: Computation,
|
||||
options: VecBuilderOptions,
|
||||
compute_dollars: bool,
|
||||
) -> color_eyre::Result<Self> {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sats: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
db,
|
||||
name,
|
||||
compute_source,
|
||||
source,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
bitcoin: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
db,
|
||||
&format!("{name}_in_btc"),
|
||||
true,
|
||||
Source::Compute,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
dollars: compute_dollars.then(|| {
|
||||
ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
db,
|
||||
&format!("{name}_in_usd"),
|
||||
true,
|
||||
Source::Compute,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)
|
||||
.unwrap()
|
||||
@@ -63,11 +77,11 @@ impl ComputedValueVecsFromHeight {
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
) -> Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut EagerVec<Height, Sats>,
|
||||
@@ -86,7 +100,7 @@ impl ComputedValueVecsFromHeight {
|
||||
)?;
|
||||
|
||||
let height: Option<&StoredVec<Height, Sats>> = None;
|
||||
self.compute_rest(indexer, indexes, fetched, starting_indexes, exit, height)?;
|
||||
self.compute_rest(indexer, indexes, price, starting_indexes, exit, height)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -95,11 +109,11 @@ impl ComputedValueVecsFromHeight {
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
height: Option<&impl CollectableVec<Height, Sats>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
) -> Result<()> {
|
||||
if let Some(height) = height {
|
||||
self.sats
|
||||
.compute_rest(indexes, starting_indexes, exit, Some(height))?;
|
||||
@@ -135,7 +149,7 @@ impl ComputedValueVecsFromHeight {
|
||||
}
|
||||
|
||||
let height_to_bitcoin = self.bitcoin.height.as_ref().unwrap();
|
||||
let height_to_close = &fetched.as_ref().unwrap().chainindexes_to_close.height;
|
||||
let height_to_close = &price.as_ref().unwrap().chainindexes_to_close.height;
|
||||
|
||||
if let Some(dollars) = self.dollars.as_mut() {
|
||||
dollars.compute_all(
|
||||
@@ -1,16 +1,14 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Bitcoin, Close, Dollars, Height, Sats, TxIndex, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{
|
||||
AnyCollectableVec, BoxedAnyIterableVec, CloneableAnyIterableVec, CollectableVec, Computation,
|
||||
ComputedVecFrom3, Format, LazyVecFrom1, StoredIndex, StoredVec,
|
||||
use brk_structs::{Bitcoin, Close, Dollars, Height, Sats, TxIndex, Version};
|
||||
use vecdb::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, CollectableVec, Computation, ComputedVecFrom3,
|
||||
Database, Exit, Format, LazyVecFrom1, StoredIndex, StoredVec,
|
||||
};
|
||||
|
||||
use crate::vecs::{Indexes, fetched, indexes};
|
||||
use crate::{Indexes, grouped::Source, indexes, price};
|
||||
|
||||
use super::{ComputedVecsFromTxindex, StorableVecGeneatorOptions};
|
||||
use super::{ComputedVecsFromTxindex, VecBuilderOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedValueVecsFromTxindex {
|
||||
@@ -38,76 +36,81 @@ const VERSION: Version = Version::ZERO;
|
||||
impl ComputedValueVecsFromTxindex {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
indexes: &indexes::Vecs,
|
||||
source: Option<BoxedAnyIterableVec<TxIndex, Sats>>,
|
||||
source: Source<TxIndex, Sats>,
|
||||
version: Version,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let compute_source = source.is_none();
|
||||
let compute_dollars = fetched.is_some();
|
||||
price: Option<&price::Vecs>,
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
let compute_dollars = price.is_some();
|
||||
|
||||
let name_in_btc = format!("{name}_in_btc");
|
||||
let name_in_usd = format!("{name}_in_usd");
|
||||
|
||||
let sats = ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
db,
|
||||
name,
|
||||
compute_source,
|
||||
source.clone(),
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?;
|
||||
|
||||
let source_vec = source.vec();
|
||||
|
||||
let bitcoin_txindex = LazyVecFrom1::init(
|
||||
&name_in_btc,
|
||||
version + VERSION,
|
||||
source.map_or_else(|| sats.txindex.as_ref().unwrap().boxed_clone(), |s| s),
|
||||
source_vec.map_or_else(|| sats.txindex.as_ref().unwrap().boxed_clone(), |s| s),
|
||||
|txindex: TxIndex, iter| {
|
||||
iter.next_at(txindex.unwrap_to_usize()).map(|(_, value)| {
|
||||
let sats = value.into_inner();
|
||||
let sats = value.into_owned();
|
||||
Bitcoin::from(sats)
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let bitcoin = ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
db,
|
||||
&name_in_btc,
|
||||
false,
|
||||
Source::None,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?;
|
||||
|
||||
let dollars_txindex = fetched.map(|fetched| {
|
||||
let dollars_txindex = price.map(|price| {
|
||||
ComputedVecFrom3::forced_import_or_init_from_3(
|
||||
computation,
|
||||
path,
|
||||
db,
|
||||
&name_in_usd,
|
||||
version + VERSION,
|
||||
format,
|
||||
bitcoin_txindex.boxed_clone(),
|
||||
indexes.txindex_to_height.boxed_clone(),
|
||||
fetched.chainindexes_to_close.height.boxed_clone(),
|
||||
price.chainindexes_to_close.height.boxed_clone(),
|
||||
|txindex: TxIndex,
|
||||
txindex_to_btc_iter,
|
||||
txindex_to_height_iter,
|
||||
height_to_close_iter| {
|
||||
let txindex = txindex.unwrap_to_usize();
|
||||
txindex_to_btc_iter.next_at(txindex).and_then(|(_, value)| {
|
||||
let btc = value.into_inner();
|
||||
let btc = value.into_owned();
|
||||
txindex_to_height_iter
|
||||
.next_at(txindex)
|
||||
.and_then(|(_, value)| {
|
||||
let height = value.into_inner();
|
||||
let height = value.into_owned();
|
||||
height_to_close_iter
|
||||
.next_at(height.unwrap_to_usize())
|
||||
.map(|(_, close)| *close.into_inner() * btc)
|
||||
.map(|(_, close)| *close.into_owned() * btc)
|
||||
})
|
||||
})
|
||||
},
|
||||
@@ -122,11 +125,13 @@ impl ComputedValueVecsFromTxindex {
|
||||
dollars_txindex,
|
||||
dollars: compute_dollars.then(|| {
|
||||
ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
db,
|
||||
&name_in_usd,
|
||||
false,
|
||||
Source::None,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)
|
||||
.unwrap()
|
||||
@@ -138,11 +143,11 @@ impl ComputedValueVecsFromTxindex {
|
||||
// &mut self,
|
||||
// indexer: &Indexer,
|
||||
// indexes: &indexes::Vecs,
|
||||
// fetched: Option<&marketprice::Vecs>,
|
||||
// price: Option<&marketprice::Vecs>,
|
||||
// starting_indexes: &Indexes,
|
||||
// exit: &Exit,
|
||||
// mut compute: F,
|
||||
// ) -> color_eyre::Result<()>
|
||||
// ) -> Result<()>
|
||||
// where
|
||||
// F: FnMut(
|
||||
// &mut EagerVec<TxIndex, Sats>,
|
||||
@@ -180,8 +185,8 @@ impl ComputedValueVecsFromTxindex {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
txindex: Option<&impl CollectableVec<TxIndex, Sats>>,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
) -> color_eyre::Result<()> {
|
||||
price: Option<&price::Vecs>,
|
||||
) -> Result<()> {
|
||||
if let Some(txindex) = txindex {
|
||||
self.sats
|
||||
.compute_rest(indexer, indexes, starting_indexes, exit, Some(txindex))?;
|
||||
@@ -205,7 +210,7 @@ impl ComputedValueVecsFromTxindex {
|
||||
|
||||
dollars_txindex.compute_if_necessary(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs().txindex_to_txid,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -216,7 +221,7 @@ impl ComputedValueVecsFromTxindex {
|
||||
exit,
|
||||
&self.bitcoin,
|
||||
Some(dollars_txindex),
|
||||
fetched.as_ref().unwrap(),
|
||||
price.as_ref().unwrap(),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Bitcoin, Dollars, Height, Result, Sats, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, CollectableVec, EagerVec, Format, StoredVec};
|
||||
use brk_structs::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{AnyCollectableVec, CollectableVec, Database, EagerVec, Exit, Format, StoredVec};
|
||||
|
||||
use crate::vecs::{Indexes, fetched, indexes};
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::Source,
|
||||
indexes, price,
|
||||
traits::{ComputeFromBitcoin, ComputeFromSats},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedHeightValueVecs {
|
||||
@@ -18,27 +21,27 @@ const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl ComputedHeightValueVecs {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
source: Source<Height, Sats>,
|
||||
version: Version,
|
||||
format: Format,
|
||||
compute_dollars: bool,
|
||||
) -> color_eyre::Result<Self> {
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sats: compute_source.then(|| {
|
||||
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format)
|
||||
sats: source.is_compute().then(|| {
|
||||
EagerVec::forced_import(db, name, version + VERSION + Version::ZERO, format)
|
||||
.unwrap()
|
||||
}),
|
||||
bitcoin: EagerVec::forced_import(
|
||||
path,
|
||||
db,
|
||||
&format!("{name}_in_btc"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
dollars: compute_dollars.then(|| {
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
db,
|
||||
&format!("{name}_in_usd"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -52,11 +55,11 @@ impl ComputedHeightValueVecs {
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
) -> Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut EagerVec<Height, Sats>,
|
||||
@@ -75,18 +78,18 @@ impl ComputedHeightValueVecs {
|
||||
)?;
|
||||
|
||||
let height: Option<&StoredVec<Height, Sats>> = None;
|
||||
self.compute_rest(fetched, starting_indexes, exit, height)?;
|
||||
self.compute_rest(price, starting_indexes, exit, height)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
height: Option<&impl CollectableVec<Height, Sats>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
) -> Result<()> {
|
||||
if let Some(height) = height {
|
||||
self.bitcoin
|
||||
.compute_from_sats(starting_indexes.height, height, exit)?;
|
||||
@@ -99,7 +102,7 @@ impl ComputedHeightValueVecs {
|
||||
}
|
||||
|
||||
let height_to_bitcoin = &self.bitcoin;
|
||||
let height_to_close = &fetched.as_ref().unwrap().chainindexes_to_close.height;
|
||||
let height_to_close = &price.as_ref().unwrap().chainindexes_to_close.height;
|
||||
|
||||
if let Some(dollars) = self.dollars.as_mut() {
|
||||
dollars.compute_from_bitcoin(
|
||||
@@ -1,104 +1,247 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![doc = "\n## Example\n\n```rust"]
|
||||
#![doc = include_str!("../examples/main.rs")]
|
||||
#![doc = include_str!("../examples/computer.rs")]
|
||||
#![doc = "```"]
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::Version;
|
||||
use brk_exit::Exit;
|
||||
use brk_error::Result;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, Computation, Format};
|
||||
|
||||
mod stores;
|
||||
mod utils;
|
||||
mod vecs;
|
||||
|
||||
use brk_structs::Version;
|
||||
use log::info;
|
||||
use stores::Stores;
|
||||
use vecs::Vecs;
|
||||
use vecdb::{AnyCollectableVec, Computation, Exit, Format};
|
||||
|
||||
mod blocks;
|
||||
mod cointime;
|
||||
mod constants;
|
||||
mod fetched;
|
||||
mod grouped;
|
||||
mod indexes;
|
||||
mod market;
|
||||
mod mining;
|
||||
mod price;
|
||||
mod stateful;
|
||||
mod states;
|
||||
mod traits;
|
||||
mod transactions;
|
||||
mod utils;
|
||||
|
||||
use indexes::Indexes;
|
||||
|
||||
use states::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Computer {
|
||||
path: PathBuf,
|
||||
fetcher: Option<Fetcher>,
|
||||
vecs: Option<Vecs>,
|
||||
stores: Option<Stores>,
|
||||
format: Format,
|
||||
pub indexes: indexes::Vecs,
|
||||
pub constants: constants::Vecs,
|
||||
pub blocks: blocks::Vecs,
|
||||
pub mining: mining::Vecs,
|
||||
pub market: market::Vecs,
|
||||
pub price: Option<price::Vecs>,
|
||||
pub transactions: transactions::Vecs,
|
||||
pub stateful: stateful::Vecs,
|
||||
pub fetched: Option<fetched::Vecs>,
|
||||
pub cointime: cointime::Vecs,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE;
|
||||
|
||||
impl Computer {
|
||||
pub fn new(outputs_dir: &Path, fetcher: Option<Fetcher>, format: Format) -> Self {
|
||||
Self {
|
||||
path: outputs_dir.to_owned(),
|
||||
fetcher,
|
||||
vecs: None,
|
||||
stores: None,
|
||||
format,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import_vecs(
|
||||
&mut self,
|
||||
/// Do NOT import multiple times or things will break !!!
|
||||
pub fn forced_import(
|
||||
outputs_path: &Path,
|
||||
indexer: &Indexer,
|
||||
computation: Computation,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.vecs = Some(Vecs::import(
|
||||
// TODO: Give self.path, join inside import
|
||||
&self.path.join("vecs/computed"),
|
||||
fetcher: Option<Fetcher>,
|
||||
) -> Result<Self> {
|
||||
let computed_path = outputs_path.join("computed");
|
||||
|
||||
let computation = Computation::Lazy;
|
||||
let format = Format::Compressed;
|
||||
|
||||
let indexes = indexes::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
indexer,
|
||||
self.fetcher.is_some(),
|
||||
computation,
|
||||
self.format,
|
||||
)?);
|
||||
Ok(())
|
||||
format,
|
||||
)?;
|
||||
|
||||
let fetched = fetcher.map(|fetcher| {
|
||||
fetched::Vecs::forced_import(outputs_path, fetcher, VERSION + Version::ZERO).unwrap()
|
||||
});
|
||||
|
||||
let price = fetched.is_some().then(|| {
|
||||
price::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
blocks: blocks::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
)?,
|
||||
mining: mining::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
)?,
|
||||
constants: constants::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
)?,
|
||||
market: market::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
)?,
|
||||
stateful: stateful::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
price.as_ref(),
|
||||
&computed_path.join("states"),
|
||||
)?,
|
||||
transactions: transactions::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
indexer,
|
||||
&indexes,
|
||||
computation,
|
||||
format,
|
||||
price.as_ref(),
|
||||
)?,
|
||||
cointime: cointime::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
price.as_ref(),
|
||||
)?,
|
||||
indexes,
|
||||
fetched,
|
||||
price,
|
||||
})
|
||||
}
|
||||
|
||||
/// Do NOT import multiple times or things will break !!!
|
||||
/// Clone struct instead
|
||||
pub fn import_stores(&mut self, indexer: &Indexer) -> color_eyre::Result<()> {
|
||||
self.stores = Some(Stores::import(
|
||||
// TODO: Give self.path, join inside import
|
||||
&self.path.join("stores"),
|
||||
VERSION + Version::ZERO,
|
||||
indexer.keyspace(),
|
||||
)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Computer {
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexer: &Indexer,
|
||||
starting_indexes: brk_indexer::Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
info!("Computing...");
|
||||
self.vecs
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.compute(indexer, starting_indexes, self.fetcher.as_mut(), exit)
|
||||
) -> Result<()> {
|
||||
info!("Computing indexes...");
|
||||
let mut starting_indexes = self.indexes.compute(indexer, starting_indexes, exit)?;
|
||||
|
||||
info!("Computing constants...");
|
||||
self.constants
|
||||
.compute(indexer, &self.indexes, &starting_indexes, exit)?;
|
||||
|
||||
info!("Computing blocks...");
|
||||
self.blocks
|
||||
.compute(indexer, &self.indexes, &starting_indexes, exit)?;
|
||||
|
||||
info!("Computing mining...");
|
||||
self.mining
|
||||
.compute(indexer, &self.indexes, &starting_indexes, exit)?;
|
||||
|
||||
if let Some(fetched) = self.fetched.as_mut() {
|
||||
info!("Computing fetched...");
|
||||
fetched.compute(indexer, &self.indexes, &starting_indexes, exit)?;
|
||||
|
||||
info!("Computing prices...");
|
||||
self.price.as_mut().unwrap().compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
&starting_indexes,
|
||||
fetched,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
info!("Computing transactions...");
|
||||
self.transactions.compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
&starting_indexes,
|
||||
self.price.as_ref(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
if let Some(price) = self.price.as_ref() {
|
||||
info!("Computing market...");
|
||||
self.market.compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
price,
|
||||
&mut self.transactions,
|
||||
&starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
info!("Computing stateful...");
|
||||
self.stateful.compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
&self.transactions,
|
||||
self.price.as_ref(),
|
||||
&self.market,
|
||||
&mut starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.cointime.compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
&starting_indexes,
|
||||
self.price.as_ref(),
|
||||
&self.transactions,
|
||||
&self.stateful,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
// pub fn vecs(&self) -> &Vecs {
|
||||
self.vecs.as_ref().unwrap().vecs()
|
||||
[
|
||||
self.constants.vecs(),
|
||||
self.indexes.vecs(),
|
||||
self.blocks.vecs(),
|
||||
self.mining.vecs(),
|
||||
self.market.vecs(),
|
||||
self.transactions.vecs(),
|
||||
self.stateful.vecs(),
|
||||
self.cointime.vecs(),
|
||||
self.fetched.as_ref().map_or(vec![], |v| v.vecs()),
|
||||
self.price.as_ref().map_or(vec![], |v| v.vecs()),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
// pub fn mut_vecs(&mut self) -> &mut Vecs {
|
||||
// self.vecs.as_mut().unwrap()
|
||||
// }
|
||||
|
||||
pub fn stores(&self) -> &Stores {
|
||||
self.stores.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn mut_stores(&mut self) -> &mut Stores {
|
||||
self.stores.as_mut().unwrap()
|
||||
pub fn static_clone(&self) -> &'static Self {
|
||||
Box::leak(Box::new(self.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use std::{fs, path::Path};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{DifficultyEpoch, HalvingEpoch, StoredF64, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, Computation, Format, VecIterator};
|
||||
use brk_structs::{DifficultyEpoch, HalvingEpoch, StoredF64, Version};
|
||||
use vecdb::{AnyCollectableVec, Computation, Database, Exit, Format, PAGE_SIZE, VecIterator};
|
||||
|
||||
use crate::grouped::Source;
|
||||
|
||||
use super::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, StorableVecGeneatorOptions},
|
||||
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, VecBuilderOptions},
|
||||
indexes,
|
||||
};
|
||||
|
||||
@@ -15,6 +17,8 @@ const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
db: Database,
|
||||
|
||||
pub indexes_to_difficulty: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_difficultyepoch: ComputedVecsFromDateIndex<DifficultyEpoch>,
|
||||
pub indexes_to_halvingepoch: ComputedVecsFromDateIndex<HalvingEpoch>,
|
||||
@@ -22,38 +26,48 @@ pub struct Vecs {
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
parent: &Path,
|
||||
version: Version,
|
||||
_computation: Computation,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
) -> color_eyre::Result<Self> {
|
||||
fs::create_dir_all(path)?;
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let db = Database::open(&parent.join("mining"))?;
|
||||
db.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
|
||||
Ok(Self {
|
||||
indexes_to_difficulty: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"difficulty",
|
||||
false,
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_difficultyepoch: ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"difficultyepoch",
|
||||
true,
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_halvingepoch: ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
&db,
|
||||
"halvingepoch",
|
||||
true,
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,7 +77,19 @@ impl Vecs {
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
) -> Result<()> {
|
||||
self.compute_(indexer, indexes, starting_indexes, exit)?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let mut height_to_difficultyepoch_iter = indexes.height_to_difficultyepoch.into_iter();
|
||||
self.indexes_to_difficultyepoch.compute_all(
|
||||
indexer,
|
||||
@@ -84,7 +110,8 @@ impl Vecs {
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -108,7 +135,8 @@ impl Vecs {
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -116,7 +144,7 @@ impl Vecs {
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&indexer.vecs().height_to_difficulty),
|
||||
Some(&indexer.vecs.height_to_difficulty),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -0,0 +1,265 @@
|
||||
use std::{ops::Deref, path::Path};
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, DateIndex, Dollars, Height, StoredU64, Version};
|
||||
use vecdb::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, Computation, Database, EagerVec, Exit,
|
||||
Format, GenericStoredVec, VecIterator,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecsFromHeight, Source, VecBuilderOptions},
|
||||
indexes, market, price,
|
||||
stateful::{
|
||||
common,
|
||||
r#trait::{CohortVecs, DynCohortVecs},
|
||||
},
|
||||
states::AddressCohortState,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
starting_height: Height,
|
||||
|
||||
pub state: Option<AddressCohortState>,
|
||||
|
||||
pub inner: common::Vecs,
|
||||
|
||||
pub height_to_address_count: EagerVec<Height, StoredU64>,
|
||||
pub indexes_to_address_count: ComputedVecsFromHeight<StoredU64>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
cohort_name: Option<&str>,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: Option<&Path>,
|
||||
compute_relative_to_all: bool,
|
||||
) -> Result<Self> {
|
||||
let compute_dollars = price.is_some();
|
||||
|
||||
let suffix = |s: &str| cohort_name.map_or(s.to_string(), |name| format!("{name}_{s}"));
|
||||
|
||||
Ok(Self {
|
||||
starting_height: Height::ZERO,
|
||||
state: states_path.map(|states_path| {
|
||||
AddressCohortState::default_and_import(
|
||||
states_path,
|
||||
cohort_name.unwrap_or_default(),
|
||||
compute_dollars,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
height_to_address_count: EagerVec::forced_import(
|
||||
db,
|
||||
&suffix("address_count"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
indexes_to_address_count: ComputedVecsFromHeight::forced_import(
|
||||
db,
|
||||
&suffix("address_count"),
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
inner: common::Vecs::forced_import(
|
||||
db,
|
||||
cohort_name,
|
||||
computation,
|
||||
format,
|
||||
version,
|
||||
indexes,
|
||||
price,
|
||||
compute_relative_to_all,
|
||||
false,
|
||||
false,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for Vecs {
|
||||
fn starting_height(&self) -> Height {
|
||||
[
|
||||
self.state.as_ref().map_or(Height::MAX, |state| {
|
||||
state.height().map_or(Height::MAX, |h| h.incremented())
|
||||
}),
|
||||
self.height_to_address_count.len().into(),
|
||||
self.inner.starting_height(),
|
||||
]
|
||||
.into_iter()
|
||||
.min()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn init(&mut self, starting_height: Height) {
|
||||
if starting_height > self.starting_height() {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
self.starting_height = starting_height;
|
||||
|
||||
if let Some(prev_height) = starting_height.decremented() {
|
||||
self.state.as_mut().unwrap().address_count = *self
|
||||
.height_to_address_count
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height);
|
||||
}
|
||||
|
||||
self.inner.init(
|
||||
&mut self.starting_height,
|
||||
&mut self.state.as_mut().unwrap().inner,
|
||||
);
|
||||
}
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.height_to_address_count
|
||||
.validate_computed_version_or_reset(
|
||||
base_version + self.height_to_address_count.inner_version(),
|
||||
)?;
|
||||
|
||||
self.inner.validate_computed_versions(base_version)
|
||||
}
|
||||
|
||||
fn forced_pushed_at(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
if self.starting_height > height {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.height_to_address_count.forced_push_at(
|
||||
height,
|
||||
self.state.as_ref().unwrap().address_count.into(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.inner
|
||||
.forced_pushed_at(height, exit, &self.state.as_ref().unwrap().inner)
|
||||
}
|
||||
|
||||
fn compute_then_force_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Option<Dollars>,
|
||||
dateindex: Option<DateIndex>,
|
||||
date_price: Option<Option<Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_then_force_push_unrealized_states(
|
||||
height,
|
||||
height_price,
|
||||
dateindex,
|
||||
date_price,
|
||||
exit,
|
||||
&self.state.as_ref().unwrap().inner,
|
||||
)
|
||||
}
|
||||
|
||||
fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
self.height_to_address_count.safe_flush(exit)?;
|
||||
|
||||
self.inner
|
||||
.safe_flush_stateful_vecs(height, exit, &mut self.state.as_mut().unwrap().inner)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.indexes_to_address_count.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.height_to_address_count),
|
||||
)?;
|
||||
|
||||
self.inner
|
||||
.compute_rest_part1(indexer, indexes, price, starting_indexes, exit)
|
||||
}
|
||||
|
||||
fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.inner.vecs(),
|
||||
self.indexes_to_address_count.vecs(),
|
||||
vec![&self.height_to_address_count],
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
|
||||
impl CohortVecs for Vecs {
|
||||
fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.height_to_address_count.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
others
|
||||
.iter()
|
||||
.map(|v| &v.height_to_address_count)
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice(),
|
||||
exit,
|
||||
)?;
|
||||
self.inner.compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| &v.inner).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
market: &market::Vecs,
|
||||
height_to_supply: &impl AnyIterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl AnyIterableVec<DateIndex, Bitcoin>,
|
||||
height_to_realized_cap: Option<&impl AnyIterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_rest_part2(
|
||||
indexer,
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
market,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Vecs {
|
||||
type Target = common::Vecs;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,595 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{
|
||||
AddressGroups, Bitcoin, ByAmountRange, ByGreatEqualAmount, ByLowerThanAmount, DateIndex,
|
||||
Dollars, GroupFilter, Height, Version,
|
||||
};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use vecdb::{AnyIterableVec, Computation, Database, Exit, Format};
|
||||
|
||||
use crate::{
|
||||
Indexes, indexes, market, price,
|
||||
stateful::{
|
||||
address_cohort,
|
||||
r#trait::{CohortVecs, DynCohortVecs},
|
||||
},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::new(0);
|
||||
|
||||
#[derive(Clone, Deref, DerefMut)]
|
||||
pub struct Vecs(AddressGroups<(GroupFilter, address_cohort::Vecs)>);
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
_computation: Computation,
|
||||
format: Format,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: &Path,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(
|
||||
AddressGroups {
|
||||
amount_range: ByAmountRange {
|
||||
_0sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_with_0sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_1sat_to_10sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_1sat_under_10sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_10sats_to_100sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_10sats_under_100sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_100sats_to_1k_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_100sats_under_1k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_1k_sats_to_10k_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_1k_sats_under_10k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_10k_sats_to_100k_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_10k_sats_under_100k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_100k_sats_to_1m_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_100k_sats_under_1m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_1m_sats_to_10m_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_1m_sats_under_10m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_10m_sats_to_1btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_10m_sats_under_1btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_1btc_to_10btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_1btc_under_10btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_10btc_to_100btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_10btc_under_100btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_100btc_to_1k_btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_100btc_under_1k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_1k_btc_to_10k_btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_1k_btc_under_10k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_10k_btc_to_100k_btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_10k_btc_under_100k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_100k_btc_or_more: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_100k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
},
|
||||
lt_amount: ByLowerThanAmount {
|
||||
_10sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_10sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_100sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1k_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_1k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10k_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_10k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100k_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_100k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1m_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_1m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10m_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_10m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_1btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_10btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_100btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1k_btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_1k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10k_btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_10k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100k_btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_under_100k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
},
|
||||
ge_amount: ByGreatEqualAmount {
|
||||
_1sat: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_1sat"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_10sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_100sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1k_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_1k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10k_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_10k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100k_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_100k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1m_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_1m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10m_sats: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_10m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_1btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_10btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_100btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1k_btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_1k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10k_btc: address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Some("addrs_above_10k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn compute_overlapping_vecs(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let by_size_range = self.0.amount_range.as_vec();
|
||||
|
||||
[
|
||||
self.0
|
||||
.ge_amount
|
||||
.as_mut_vec()
|
||||
.into_iter()
|
||||
.map(|(filter, vecs)| {
|
||||
(
|
||||
vecs,
|
||||
by_size_range
|
||||
.into_iter()
|
||||
.filter(|(other, _)| filter.includes(other))
|
||||
.map(|(_, v)| v)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
self.0
|
||||
.lt_amount
|
||||
.as_mut_vec()
|
||||
.into_iter()
|
||||
.map(|(filter, vecs)| {
|
||||
(
|
||||
vecs,
|
||||
by_size_range
|
||||
.into_iter()
|
||||
.filter(|(other, _)| filter.includes(other))
|
||||
.map(|(_, v)| v)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.try_for_each(|(vecs, stateful)| {
|
||||
vecs.compute_from_stateful(starting_indexes, &stateful, exit)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.as_mut_vecs().into_iter().try_for_each(|(_, v)| {
|
||||
v.compute_rest_part1(indexer, indexes, price, starting_indexes, exit)
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
market: &market::Vecs,
|
||||
height_to_supply: &impl AnyIterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl AnyIterableVec<DateIndex, Bitcoin>,
|
||||
height_to_realized_cap: Option<&impl AnyIterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.0.as_boxed_mut_vecs().into_iter().try_for_each(|v| {
|
||||
v.into_iter().try_for_each(|(_, v)| {
|
||||
v.compute_rest_part2(
|
||||
indexer,
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
market,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
self.as_mut_separate_vecs()
|
||||
.into_iter()
|
||||
.try_for_each(|(_, v)| v.safe_flush_stateful_vecs(height, exit))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use brk_structs::{ByAddressType, Height};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use vecdb::VecIterator;
|
||||
|
||||
use crate::stateful::addresstype_to_height_to_addresscount::AddressTypeToHeightToAddressCount;
|
||||
|
||||
#[derive(Debug, Default, Deref, DerefMut)]
|
||||
pub struct AddressTypeToAddressCount(ByAddressType<u64>);
|
||||
|
||||
impl From<(&AddressTypeToHeightToAddressCount, Height)> for AddressTypeToAddressCount {
|
||||
fn from((groups, starting_height): (&AddressTypeToHeightToAddressCount, Height)) -> Self {
|
||||
if let Some(prev_height) = starting_height.decremented() {
|
||||
Self(ByAddressType {
|
||||
p2pk65: groups
|
||||
.p2pk65
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height)
|
||||
.into(),
|
||||
p2pk33: groups
|
||||
.p2pk33
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height)
|
||||
.into(),
|
||||
p2pkh: groups
|
||||
.p2pkh
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height)
|
||||
.into(),
|
||||
p2sh: groups.p2sh.into_iter().unwrap_get_inner(prev_height).into(),
|
||||
p2wpkh: groups
|
||||
.p2wpkh
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height)
|
||||
.into(),
|
||||
p2wsh: groups
|
||||
.p2wsh
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height)
|
||||
.into(),
|
||||
p2tr: groups.p2tr.into_iter().unwrap_get_inner(prev_height).into(),
|
||||
p2a: groups.p2a.into_iter().unwrap_get_inner(prev_height).into(),
|
||||
})
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use brk_error::Result;
|
||||
use brk_structs::{ByAddressType, Height, StoredU64};
|
||||
use vecdb::{EagerVec, Exit, GenericStoredVec};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::stateful::addresstype_to_addresscount::AddressTypeToAddressCount;
|
||||
|
||||
#[derive(Debug, Clone, Deref, DerefMut)]
|
||||
pub struct AddressTypeToHeightToAddressCount(ByAddressType<EagerVec<Height, StoredU64>>);
|
||||
|
||||
impl From<ByAddressType<EagerVec<Height, StoredU64>>> for AddressTypeToHeightToAddressCount {
|
||||
fn from(value: ByAddressType<EagerVec<Height, StoredU64>>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressTypeToHeightToAddressCount {
|
||||
pub fn forced_push_at(
|
||||
&mut self,
|
||||
height: Height,
|
||||
addresstype_to_usize: &AddressTypeToAddressCount,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.p2pk65
|
||||
.forced_push_at(height, addresstype_to_usize.p2pk65.into(), exit)?;
|
||||
self.p2pk33
|
||||
.forced_push_at(height, addresstype_to_usize.p2pk33.into(), exit)?;
|
||||
self.p2pkh
|
||||
.forced_push_at(height, addresstype_to_usize.p2pkh.into(), exit)?;
|
||||
self.p2sh
|
||||
.forced_push_at(height, addresstype_to_usize.p2sh.into(), exit)?;
|
||||
self.p2wpkh
|
||||
.forced_push_at(height, addresstype_to_usize.p2wpkh.into(), exit)?;
|
||||
self.p2wsh
|
||||
.forced_push_at(height, addresstype_to_usize.p2wsh.into(), exit)?;
|
||||
self.p2tr
|
||||
.forced_push_at(height, addresstype_to_usize.p2tr.into(), exit)?;
|
||||
self.p2a
|
||||
.forced_push_at(height, addresstype_to_usize.p2a.into(), exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
use brk_error::Result;
|
||||
use brk_structs::{ByAddressType, StoredU64};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use vecdb::{AnyCollectableVec, Exit};
|
||||
|
||||
use crate::{
|
||||
Indexes, grouped::ComputedVecsFromHeight, indexes,
|
||||
stateful::addresstype_to_height_to_addresscount::AddressTypeToHeightToAddressCount,
|
||||
};
|
||||
|
||||
#[derive(Clone, Deref, DerefMut)]
|
||||
pub struct AddressTypeToIndexesToAddressCount(ByAddressType<ComputedVecsFromHeight<StoredU64>>);
|
||||
|
||||
impl From<ByAddressType<ComputedVecsFromHeight<StoredU64>>> for AddressTypeToIndexesToAddressCount {
|
||||
fn from(value: ByAddressType<ComputedVecsFromHeight<StoredU64>>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressTypeToIndexesToAddressCount {
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
// height: Height,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
addresstype_to_height_to_addresscount: &AddressTypeToHeightToAddressCount,
|
||||
) -> Result<()> {
|
||||
self.p2pk65.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2pk65),
|
||||
)?;
|
||||
self.p2pk33.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2pk33),
|
||||
)?;
|
||||
self.p2pkh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2pkh),
|
||||
)?;
|
||||
self.p2sh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2sh),
|
||||
)?;
|
||||
self.p2wpkh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2wpkh),
|
||||
)?;
|
||||
self.p2wsh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2wsh),
|
||||
)?;
|
||||
self.p2tr.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2tr),
|
||||
)?;
|
||||
self.p2a.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2a),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
//
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
self.0
|
||||
.as_typed_vec()
|
||||
.into_iter()
|
||||
.flat_map(|(_, v)| v.vecs())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use std::{collections::BTreeMap, mem};
|
||||
|
||||
use brk_structs::TypeIndex;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use super::ByAddressType;
|
||||
|
||||
#[derive(Debug, Deref, DerefMut)]
|
||||
pub struct AddressTypeToTypeIndexTree<T>(ByAddressType<BTreeMap<TypeIndex, T>>);
|
||||
|
||||
impl<T> AddressTypeToTypeIndexTree<T> {
|
||||
pub fn merge(mut self, mut other: Self) -> Self {
|
||||
Self::merge_(&mut self.p2pk65, &mut other.p2pk65);
|
||||
Self::merge_(&mut self.p2pk33, &mut other.p2pk33);
|
||||
Self::merge_(&mut self.p2pkh, &mut other.p2pkh);
|
||||
Self::merge_(&mut self.p2sh, &mut other.p2sh);
|
||||
Self::merge_(&mut self.p2wpkh, &mut other.p2wpkh);
|
||||
Self::merge_(&mut self.p2wsh, &mut other.p2wsh);
|
||||
Self::merge_(&mut self.p2tr, &mut other.p2tr);
|
||||
Self::merge_(&mut self.p2a, &mut other.p2a);
|
||||
self
|
||||
}
|
||||
|
||||
fn merge_(own: &mut BTreeMap<TypeIndex, T>, other: &mut BTreeMap<TypeIndex, T>) {
|
||||
if own.len() >= other.len() {
|
||||
own.append(other);
|
||||
} else {
|
||||
other.append(own);
|
||||
mem::swap(own, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for AddressTypeToTypeIndexTree<T> {
|
||||
fn default() -> Self {
|
||||
Self(ByAddressType {
|
||||
p2pk65: BTreeMap::default(),
|
||||
p2pk33: BTreeMap::default(),
|
||||
p2pkh: BTreeMap::default(),
|
||||
p2sh: BTreeMap::default(),
|
||||
p2wpkh: BTreeMap::default(),
|
||||
p2wsh: BTreeMap::default(),
|
||||
p2tr: BTreeMap::default(),
|
||||
p2a: BTreeMap::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
use std::mem;
|
||||
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use super::ByAddressType;
|
||||
|
||||
#[derive(Debug, Deref, DerefMut)]
|
||||
pub struct AddressTypeToVec<T>(ByAddressType<Vec<T>>);
|
||||
|
||||
impl<T> AddressTypeToVec<T> {
|
||||
pub fn merge(mut self, mut other: Self) -> Self {
|
||||
Self::merge_(&mut self.p2pk65, &mut other.p2pk65);
|
||||
Self::merge_(&mut self.p2pk33, &mut other.p2pk33);
|
||||
Self::merge_(&mut self.p2pkh, &mut other.p2pkh);
|
||||
Self::merge_(&mut self.p2sh, &mut other.p2sh);
|
||||
Self::merge_(&mut self.p2wpkh, &mut other.p2wpkh);
|
||||
Self::merge_(&mut self.p2wsh, &mut other.p2wsh);
|
||||
Self::merge_(&mut self.p2tr, &mut other.p2tr);
|
||||
Self::merge_(&mut self.p2a, &mut other.p2a);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn merge_mut(&mut self, mut other: Self) {
|
||||
Self::merge_(&mut self.p2pk65, &mut other.p2pk65);
|
||||
Self::merge_(&mut self.p2pk33, &mut other.p2pk33);
|
||||
Self::merge_(&mut self.p2pkh, &mut other.p2pkh);
|
||||
Self::merge_(&mut self.p2sh, &mut other.p2sh);
|
||||
Self::merge_(&mut self.p2wpkh, &mut other.p2wpkh);
|
||||
Self::merge_(&mut self.p2wsh, &mut other.p2wsh);
|
||||
Self::merge_(&mut self.p2tr, &mut other.p2tr);
|
||||
Self::merge_(&mut self.p2a, &mut other.p2a);
|
||||
}
|
||||
|
||||
fn merge_(own: &mut Vec<T>, other: &mut Vec<T>) {
|
||||
if own.len() >= other.len() {
|
||||
own.append(other);
|
||||
} else {
|
||||
other.append(own);
|
||||
mem::swap(own, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for AddressTypeToVec<T> {
|
||||
fn default() -> Self {
|
||||
Self(ByAddressType {
|
||||
p2pk65: vec![],
|
||||
p2pk33: vec![],
|
||||
p2pkh: vec![],
|
||||
p2sh: vec![],
|
||||
p2wpkh: vec![],
|
||||
p2wsh: vec![],
|
||||
p2tr: vec![],
|
||||
p2a: vec![],
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use brk_structs::Height;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::stateful::AddressTypeToVec;
|
||||
|
||||
#[derive(Debug, Default, Deref, DerefMut)]
|
||||
pub struct HeightToAddressTypeToVec<T>(pub BTreeMap<Height, AddressTypeToVec<T>>);
|
||||
@@ -0,0 +1,49 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use vecdb::{CompressedVec, RawVec, StoredCompressed, StoredIndex, StoredRaw};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RangeMap<I, T>(BTreeMap<I, T>);
|
||||
|
||||
impl<I, T> RangeMap<I, T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: StoredIndex,
|
||||
{
|
||||
pub fn get(&self, key: I) -> Option<&T> {
|
||||
self.0.range(..=key).next_back().map(|(&min, value)| {
|
||||
if min > key {
|
||||
unreachable!()
|
||||
}
|
||||
value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> From<&RawVec<I, T>> for RangeMap<T, I>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: StoredIndex + StoredRaw,
|
||||
{
|
||||
fn from(vec: &RawVec<I, T>) -> Self {
|
||||
Self(
|
||||
vec.into_iter()
|
||||
.map(|(i, v)| (v.into_owned(), i))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> From<&CompressedVec<I, T>> for RangeMap<T, I>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: StoredIndex + StoredCompressed,
|
||||
{
|
||||
fn from(vec: &CompressedVec<I, T>) -> Self {
|
||||
Self(
|
||||
vec.into_iter()
|
||||
.map(|(i, v)| (v.into_owned(), i))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, DateIndex, Dollars, Height, Version};
|
||||
use vecdb::{AnyCollectableVec, AnyIterableVec, Exit};
|
||||
|
||||
use crate::{Indexes, indexes, market, price};
|
||||
|
||||
pub trait DynCohortVecs: Send + Sync {
|
||||
fn starting_height(&self) -> Height;
|
||||
|
||||
fn init(&mut self, starting_height: Height);
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()>;
|
||||
|
||||
fn forced_pushed_at(&mut self, height: Height, exit: &Exit) -> Result<()>;
|
||||
|
||||
fn compute_then_force_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Option<Dollars>,
|
||||
dateindex: Option<DateIndex>,
|
||||
date_price: Option<Option<Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
|
||||
fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()>;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
|
||||
fn vecs(&self) -> Vec<&dyn AnyCollectableVec>;
|
||||
}
|
||||
|
||||
pub trait CohortVecs: DynCohortVecs {
|
||||
fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
market: &market::Vecs,
|
||||
height_to_supply: &impl AnyIterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl AnyIterableVec<DateIndex, Bitcoin>,
|
||||
height_to_realized_cap: Option<&impl AnyIterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
use std::{ops::Deref, path::Path};
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, DateIndex, Dollars, Height, Version};
|
||||
use vecdb::{AnyCollectableVec, AnyIterableVec, Computation, Database, Exit, Format};
|
||||
|
||||
use crate::{
|
||||
Indexes, UTXOCohortState, indexes, market, price,
|
||||
stateful::{
|
||||
common,
|
||||
r#trait::{CohortVecs, DynCohortVecs},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
starting_height: Height,
|
||||
|
||||
pub state: Option<UTXOCohortState>,
|
||||
|
||||
inner: common::Vecs,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
cohort_name: Option<&str>,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: Option<&Path>,
|
||||
compute_relative_to_all: bool,
|
||||
ratio_extended: bool,
|
||||
compute_adjusted: bool,
|
||||
) -> Result<Self> {
|
||||
let compute_dollars = price.is_some();
|
||||
|
||||
Ok(Self {
|
||||
starting_height: Height::ZERO,
|
||||
|
||||
state: states_path.map(|states_path| {
|
||||
UTXOCohortState::default_and_import(
|
||||
states_path,
|
||||
cohort_name.unwrap_or_default(),
|
||||
compute_dollars,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
|
||||
inner: common::Vecs::forced_import(
|
||||
db,
|
||||
cohort_name,
|
||||
computation,
|
||||
format,
|
||||
version,
|
||||
indexes,
|
||||
price,
|
||||
compute_relative_to_all,
|
||||
ratio_extended,
|
||||
compute_adjusted,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for Vecs {
|
||||
fn starting_height(&self) -> Height {
|
||||
[
|
||||
self.state.as_ref().map_or(Height::MAX, |state| {
|
||||
state.height().map_or(Height::MAX, |h| h.incremented())
|
||||
}),
|
||||
self.inner.starting_height(),
|
||||
]
|
||||
.into_iter()
|
||||
.min()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn init(&mut self, starting_height: Height) {
|
||||
if starting_height > self.starting_height() {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
self.starting_height = starting_height;
|
||||
|
||||
self.inner
|
||||
.init(&mut self.starting_height, self.state.as_mut().unwrap());
|
||||
}
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.inner.validate_computed_versions(base_version)
|
||||
}
|
||||
|
||||
fn forced_pushed_at(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
if self.starting_height > height {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.inner
|
||||
.forced_pushed_at(height, exit, self.state.as_ref().unwrap())
|
||||
}
|
||||
|
||||
fn compute_then_force_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Option<Dollars>,
|
||||
dateindex: Option<DateIndex>,
|
||||
date_price: Option<Option<Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_then_force_push_unrealized_states(
|
||||
height,
|
||||
height_price,
|
||||
dateindex,
|
||||
date_price,
|
||||
exit,
|
||||
self.state.as_mut().unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
self.inner
|
||||
.safe_flush_stateful_vecs(height, exit, self.state.as_mut().unwrap())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner
|
||||
.compute_rest_part1(indexer, indexes, price, starting_indexes, exit)
|
||||
}
|
||||
|
||||
fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
self.inner.vecs()
|
||||
}
|
||||
}
|
||||
|
||||
impl CohortVecs for Vecs {
|
||||
fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| &v.inner).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
market: &market::Vecs,
|
||||
height_to_supply: &impl AnyIterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl AnyIterableVec<DateIndex, Bitcoin>,
|
||||
height_to_realized_cap: Option<&impl AnyIterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_rest_part2(
|
||||
indexer,
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
market,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Vecs {
|
||||
type Target = common::Vecs;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
use brk_structs::{EmptyAddressData, EmptyAddressIndex, LoadedAddressData, LoadedAddressIndex};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WithAddressDataSource<T> {
|
||||
New(T),
|
||||
FromLoadedAddressDataVec((LoadedAddressIndex, T)),
|
||||
FromEmptyAddressDataVec((EmptyAddressIndex, T)),
|
||||
}
|
||||
|
||||
impl<T> WithAddressDataSource<T> {
|
||||
pub fn is_new(&self) -> bool {
|
||||
matches!(self, Self::New(_))
|
||||
}
|
||||
|
||||
pub fn is_from_emptyaddressdata(&self) -> bool {
|
||||
matches!(self, Self::FromEmptyAddressDataVec(_))
|
||||
}
|
||||
|
||||
pub fn deref_mut(&mut self) -> &mut T {
|
||||
match self {
|
||||
Self::New(v) => v,
|
||||
Self::FromLoadedAddressDataVec((_, v)) => v,
|
||||
Self::FromEmptyAddressDataVec((_, v)) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WithAddressDataSource<EmptyAddressData>> for WithAddressDataSource<LoadedAddressData> {
|
||||
fn from(value: WithAddressDataSource<EmptyAddressData>) -> Self {
|
||||
match value {
|
||||
WithAddressDataSource::New(v) => Self::New(v.into()),
|
||||
WithAddressDataSource::FromLoadedAddressDataVec((i, v)) => {
|
||||
Self::FromLoadedAddressDataVec((i, v.into()))
|
||||
}
|
||||
WithAddressDataSource::FromEmptyAddressDataVec((i, v)) => {
|
||||
Self::FromEmptyAddressDataVec((i, v.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WithAddressDataSource<LoadedAddressData>> for WithAddressDataSource<EmptyAddressData> {
|
||||
fn from(value: WithAddressDataSource<LoadedAddressData>) -> Self {
|
||||
match value {
|
||||
WithAddressDataSource::New(v) => Self::New(v.into()),
|
||||
WithAddressDataSource::FromLoadedAddressDataVec((i, v)) => {
|
||||
Self::FromLoadedAddressDataVec((i, v.into()))
|
||||
}
|
||||
WithAddressDataSource::FromEmptyAddressDataVec((i, v)) => {
|
||||
Self::FromEmptyAddressDataVec((i, v.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::ops::{Add, AddAssign, SubAssign};
|
||||
|
||||
use brk_core::{Dollars, Timestamp};
|
||||
use brk_structs::{Dollars, Timestamp};
|
||||
|
||||
use super::SupplyState;
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_structs::{Dollars, Height, LoadedAddressData, Sats};
|
||||
|
||||
use crate::SupplyState;
|
||||
|
||||
use super::CohortState;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AddressCohortState {
|
||||
pub address_count: u64,
|
||||
pub inner: CohortState,
|
||||
}
|
||||
|
||||
impl AddressCohortState {
|
||||
pub fn default_and_import(path: &Path, name: &str, compute_dollars: bool) -> Result<Self> {
|
||||
Ok(Self {
|
||||
address_count: 0,
|
||||
inner: CohortState::default_and_import(path, name, compute_dollars)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<Height> {
|
||||
self.inner.height()
|
||||
}
|
||||
|
||||
pub fn reset_price_to_amount(&mut self) -> Result<()> {
|
||||
self.inner.reset_price_to_amount()
|
||||
}
|
||||
|
||||
pub fn reset_single_iteration_values(&mut self) {
|
||||
self.inner.reset_single_iteration_values();
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn send(
|
||||
&mut self,
|
||||
addressdata: &mut LoadedAddressData,
|
||||
value: Sats,
|
||||
current_price: Option<Dollars>,
|
||||
prev_price: Option<Dollars>,
|
||||
blocks_old: usize,
|
||||
days_old: f64,
|
||||
older_than_hour: bool,
|
||||
) -> Result<()> {
|
||||
let compute_price = current_price.is_some();
|
||||
|
||||
let prev_realized_price = compute_price.then(|| addressdata.realized_price());
|
||||
let prev_supply_state = SupplyState {
|
||||
utxos: addressdata.outputs_len as u64,
|
||||
value: addressdata.amount(),
|
||||
};
|
||||
|
||||
addressdata.send(value, prev_price)?;
|
||||
|
||||
let supply_state = SupplyState {
|
||||
utxos: addressdata.outputs_len as u64,
|
||||
value: addressdata.amount(),
|
||||
};
|
||||
|
||||
self.inner.send_(
|
||||
&SupplyState { utxos: 1, value },
|
||||
current_price,
|
||||
prev_price,
|
||||
blocks_old,
|
||||
days_old,
|
||||
older_than_hour,
|
||||
compute_price.then(|| (addressdata.realized_price(), &supply_state)),
|
||||
prev_realized_price.map(|prev_price| (prev_price, &prev_supply_state)),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn receive(
|
||||
&mut self,
|
||||
address_data: &mut LoadedAddressData,
|
||||
value: Sats,
|
||||
price: Option<Dollars>,
|
||||
) {
|
||||
let compute_price = price.is_some();
|
||||
|
||||
let prev_realized_price = compute_price.then(|| address_data.realized_price());
|
||||
let prev_supply_state = SupplyState {
|
||||
utxos: address_data.outputs_len as u64,
|
||||
value: address_data.amount(),
|
||||
};
|
||||
|
||||
address_data.receive(value, price);
|
||||
|
||||
let supply_state = SupplyState {
|
||||
utxos: address_data.outputs_len as u64,
|
||||
value: address_data.amount(),
|
||||
};
|
||||
|
||||
self.inner.receive_(
|
||||
&SupplyState { utxos: 1, value },
|
||||
price,
|
||||
compute_price.then(|| (address_data.realized_price(), &supply_state)),
|
||||
prev_realized_price.map(|prev_price| (prev_price, &prev_supply_state)),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn add(&mut self, addressdata: &LoadedAddressData) {
|
||||
self.address_count += 1;
|
||||
self.inner.increment_(
|
||||
&addressdata.into(),
|
||||
addressdata.realized_cap,
|
||||
addressdata.realized_price(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn subtract(&mut self, addressdata: &LoadedAddressData) {
|
||||
self.address_count = self.address_count.checked_sub(1).unwrap();
|
||||
self.inner.decrement_(
|
||||
&addressdata.into(),
|
||||
addressdata.realized_cap,
|
||||
addressdata.realized_price(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn commit(&mut self, height: Height) -> Result<()> {
|
||||
self.inner.commit(height)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
use std::{cmp::Ordering, path::Path};
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_structs::{CheckedSub, Dollars, Height, Sats};
|
||||
|
||||
use crate::{PriceToAmount, RealizedState, SupplyState, UnrealizedState};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CohortState {
|
||||
pub supply: SupplyState,
|
||||
pub realized: Option<RealizedState>,
|
||||
pub satblocks_destroyed: Sats,
|
||||
pub satdays_destroyed: Sats,
|
||||
|
||||
price_to_amount: PriceToAmount,
|
||||
}
|
||||
|
||||
impl CohortState {
|
||||
pub fn default_and_import(path: &Path, name: &str, compute_dollars: bool) -> Result<Self> {
|
||||
Ok(Self {
|
||||
supply: SupplyState::default(),
|
||||
realized: compute_dollars.then_some(RealizedState::NAN),
|
||||
satblocks_destroyed: Sats::ZERO,
|
||||
satdays_destroyed: Sats::ZERO,
|
||||
price_to_amount: PriceToAmount::forced_import(path, name),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<Height> {
|
||||
self.price_to_amount.height()
|
||||
}
|
||||
|
||||
pub fn reset_price_to_amount(&mut self) -> Result<()> {
|
||||
self.price_to_amount.reset()
|
||||
}
|
||||
|
||||
pub fn price_to_amount_first_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.price_to_amount.first_key_value()
|
||||
}
|
||||
|
||||
pub fn price_to_amount_last_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.price_to_amount.last_key_value()
|
||||
}
|
||||
|
||||
pub fn reset_single_iteration_values(&mut self) {
|
||||
self.satdays_destroyed = Sats::ZERO;
|
||||
self.satblocks_destroyed = Sats::ZERO;
|
||||
if let Some(realized) = self.realized.as_mut() {
|
||||
realized.reset_single_iteration_values();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, supply_state: &SupplyState, price: Option<Dollars>) {
|
||||
self.supply += supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO
|
||||
&& let Some(realized) = self.realized.as_mut()
|
||||
{
|
||||
let price = price.unwrap();
|
||||
realized.increment(supply_state, price);
|
||||
self.price_to_amount.increment(price, supply_state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment_(
|
||||
&mut self,
|
||||
supply_state: &SupplyState,
|
||||
realized_cap: Dollars,
|
||||
realized_price: Dollars,
|
||||
) {
|
||||
self.supply += supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO
|
||||
&& let Some(realized) = self.realized.as_mut()
|
||||
{
|
||||
realized.increment_(realized_cap);
|
||||
self.price_to_amount.increment(realized_price, supply_state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, supply_state: &SupplyState, price: Option<Dollars>) {
|
||||
self.supply -= supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO
|
||||
&& let Some(realized) = self.realized.as_mut()
|
||||
{
|
||||
let price = price.unwrap();
|
||||
realized.decrement(supply_state, price);
|
||||
self.price_to_amount.decrement(price, supply_state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrement_(
|
||||
&mut self,
|
||||
supply_state: &SupplyState,
|
||||
realized_cap: Dollars,
|
||||
realized_price: Dollars,
|
||||
) {
|
||||
self.supply -= supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO
|
||||
&& let Some(realized) = self.realized.as_mut()
|
||||
{
|
||||
realized.decrement_(realized_cap);
|
||||
self.price_to_amount.decrement(realized_price, supply_state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive(&mut self, supply_state: &SupplyState, price: Option<Dollars>) {
|
||||
self.receive_(
|
||||
supply_state,
|
||||
price,
|
||||
price.map(|price| (price, supply_state)),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn receive_(
|
||||
&mut self,
|
||||
supply_state: &SupplyState,
|
||||
price: Option<Dollars>,
|
||||
price_to_amount_increment: Option<(Dollars, &SupplyState)>,
|
||||
price_to_amount_decrement: Option<(Dollars, &SupplyState)>,
|
||||
) {
|
||||
self.supply += supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO
|
||||
&& let Some(realized) = self.realized.as_mut()
|
||||
{
|
||||
let price = price.unwrap();
|
||||
realized.receive(supply_state, price);
|
||||
|
||||
if let Some((price, supply)) = price_to_amount_increment
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.increment(price, supply);
|
||||
}
|
||||
|
||||
if let Some((price, supply)) = price_to_amount_decrement
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.decrement(price, supply);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(
|
||||
&mut self,
|
||||
supply_state: &SupplyState,
|
||||
current_price: Option<Dollars>,
|
||||
prev_price: Option<Dollars>,
|
||||
blocks_old: usize,
|
||||
days_old: f64,
|
||||
older_than_hour: bool,
|
||||
) {
|
||||
self.send_(
|
||||
supply_state,
|
||||
current_price,
|
||||
prev_price,
|
||||
blocks_old,
|
||||
days_old,
|
||||
older_than_hour,
|
||||
None,
|
||||
prev_price.map(|prev_price| (prev_price, supply_state)),
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn send_(
|
||||
&mut self,
|
||||
supply_state: &SupplyState,
|
||||
current_price: Option<Dollars>,
|
||||
prev_price: Option<Dollars>,
|
||||
blocks_old: usize,
|
||||
days_old: f64,
|
||||
older_than_hour: bool,
|
||||
price_to_amount_increment: Option<(Dollars, &SupplyState)>,
|
||||
price_to_amount_decrement: Option<(Dollars, &SupplyState)>,
|
||||
) {
|
||||
if supply_state.utxos == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
self.supply -= supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO {
|
||||
self.satblocks_destroyed += supply_state.value * blocks_old;
|
||||
|
||||
self.satdays_destroyed +=
|
||||
Sats::from((u64::from(supply_state.value) as f64 * days_old).floor() as u64);
|
||||
|
||||
if let Some(realized) = self.realized.as_mut() {
|
||||
let current_price = current_price.unwrap();
|
||||
let prev_price = prev_price.unwrap();
|
||||
realized.send(supply_state, current_price, prev_price, older_than_hour);
|
||||
if let Some((price, supply)) = price_to_amount_increment
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.increment(price, supply);
|
||||
}
|
||||
if let Some((price, supply)) = price_to_amount_decrement
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.decrement(price, supply);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_unrealized_states(
|
||||
&self,
|
||||
height_price: Dollars,
|
||||
date_price: Option<Dollars>,
|
||||
) -> (UnrealizedState, Option<UnrealizedState>) {
|
||||
if self.price_to_amount.is_empty() {
|
||||
return (
|
||||
UnrealizedState::NAN,
|
||||
date_price.map(|_| UnrealizedState::NAN),
|
||||
);
|
||||
}
|
||||
|
||||
let mut height_unrealized_state = UnrealizedState::ZERO;
|
||||
let mut date_unrealized_state = date_price.map(|_| UnrealizedState::ZERO);
|
||||
|
||||
let update_state =
|
||||
|price: Dollars, current_price: Dollars, sats: Sats, state: &mut UnrealizedState| {
|
||||
match price.cmp(¤t_price) {
|
||||
Ordering::Less => {
|
||||
state.supply_in_profit += sats;
|
||||
if price > Dollars::ZERO && current_price > Dollars::ZERO {
|
||||
let diff = current_price.checked_sub(price).unwrap();
|
||||
// Add back once in a while to verify, but generally not needed
|
||||
// if diff <= Dollars::ZERO {
|
||||
// dbg!(price, current_price, diff, sats);
|
||||
// panic!();
|
||||
// }
|
||||
state.unrealized_profit += diff * sats;
|
||||
}
|
||||
}
|
||||
Ordering::Greater => {
|
||||
state.supply_in_loss += sats;
|
||||
if price > Dollars::ZERO && current_price > Dollars::ZERO {
|
||||
let diff = price.checked_sub(current_price).unwrap();
|
||||
// Add back once in a while to verify, but generally not needed
|
||||
// if diff <= Dollars::ZERO {
|
||||
// dbg!(price, current_price, diff, sats);
|
||||
// panic!();
|
||||
// }
|
||||
state.unrealized_loss += diff * sats;
|
||||
}
|
||||
}
|
||||
Ordering::Equal => {
|
||||
state.supply_even += sats;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.price_to_amount.iter().for_each(|(&price, &sats)| {
|
||||
update_state(price, height_price, sats, &mut height_unrealized_state);
|
||||
|
||||
if let Some(date_price) = date_price {
|
||||
update_state(
|
||||
price,
|
||||
date_price,
|
||||
sats,
|
||||
date_unrealized_state.as_mut().unwrap(),
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
(height_unrealized_state, date_unrealized_state)
|
||||
}
|
||||
|
||||
pub fn commit(&mut self, height: Height) -> Result<()> {
|
||||
self.price_to_amount.flush(height)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
mod address;
|
||||
mod common;
|
||||
mod utxo;
|
||||
|
||||
pub use address::*;
|
||||
pub use common::*;
|
||||
pub use utxo::*;
|
||||
@@ -0,0 +1,28 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_structs::Height;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use super::CohortState;
|
||||
|
||||
#[derive(Clone, Deref, DerefMut)]
|
||||
pub struct UTXOCohortState(CohortState);
|
||||
|
||||
impl UTXOCohortState {
|
||||
pub fn default_and_import(path: &Path, name: &str, compute_dollars: bool) -> Result<Self> {
|
||||
Ok(Self(CohortState::default_and_import(
|
||||
path,
|
||||
name,
|
||||
compute_dollars,
|
||||
)?))
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<Height> {
|
||||
self.0.height()
|
||||
}
|
||||
|
||||
pub fn reset_price_to_amount(&mut self) -> Result<()> {
|
||||
self.0.reset_price_to_amount()
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,15 @@
|
||||
mod block;
|
||||
mod cohort;
|
||||
mod outputs;
|
||||
mod realized;
|
||||
// mod hot;
|
||||
mod cohorts;
|
||||
mod price_to_amount;
|
||||
mod realized;
|
||||
mod supply;
|
||||
mod transacted;
|
||||
mod unrealized;
|
||||
|
||||
pub use block::*;
|
||||
pub use cohort::*;
|
||||
pub use outputs::*;
|
||||
pub use realized::*;
|
||||
pub use unrealized::*;
|
||||
// pub use hot::*;
|
||||
pub use cohorts::*;
|
||||
pub use price_to_amount::*;
|
||||
pub use realized::*;
|
||||
pub use supply::*;
|
||||
pub use transacted::*;
|
||||
pub use unrealized::*;
|
||||
@@ -0,0 +1,160 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs,
|
||||
io::{Cursor, Read},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use brk_error::{Error, Result};
|
||||
use brk_structs::{Dollars, Height, Sats};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zerocopy::{FromBytes, IntoBytes};
|
||||
|
||||
use crate::states::SupplyState;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PriceToAmount {
|
||||
pathbuf: PathBuf,
|
||||
height: Option<Height>,
|
||||
state: State,
|
||||
}
|
||||
|
||||
impl PriceToAmount {
|
||||
pub fn forced_import(path: &Path, name: &str) -> Self {
|
||||
Self::import(path, name).unwrap_or_else(|_| {
|
||||
// dbg!(e);
|
||||
Self {
|
||||
pathbuf: Self::path_(path, name),
|
||||
height: None,
|
||||
state: State::default(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn import(path: &Path, name: &str) -> Result<Self> {
|
||||
let path = Self::path_(path, name);
|
||||
fs::create_dir_all(&path)?;
|
||||
|
||||
let state = State::deserialize(&fs::read(Self::path_state_(&path))?)?;
|
||||
|
||||
Ok(Self {
|
||||
height: Height::try_from(Self::path_height_(&path).as_path()).ok(),
|
||||
pathbuf: path,
|
||||
state,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Dollars, &Sats)> {
|
||||
self.state.iter()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.state.is_empty()
|
||||
}
|
||||
|
||||
pub fn first_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.state.first_key_value()
|
||||
}
|
||||
|
||||
pub fn last_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.state.last_key_value()
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, price: Dollars, supply_state: &SupplyState) {
|
||||
*self.state.entry(price).or_default() += supply_state.value;
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, price: Dollars, supply_state: &SupplyState) {
|
||||
if let Some(amount) = self.state.get_mut(&price) {
|
||||
*amount -= supply_state.value;
|
||||
if *amount == Sats::ZERO {
|
||||
self.state.remove(&price);
|
||||
}
|
||||
} else {
|
||||
dbg!(&self.state, price, &self.pathbuf);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Result<()> {
|
||||
self.state.clear();
|
||||
self.height = None;
|
||||
fs::remove_dir_all(&self.pathbuf)?;
|
||||
fs::create_dir_all(&self.pathbuf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flush(&mut self, height: Height) -> Result<()> {
|
||||
self.height = Some(height);
|
||||
height.write(&self.path_height())?;
|
||||
fs::write(self.path_state(), self.state.serialize())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<Height> {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn path_(path: &Path, name: &str) -> PathBuf {
|
||||
path.join(format!("{name}_price_to_amount"))
|
||||
}
|
||||
|
||||
fn path_state(&self) -> PathBuf {
|
||||
Self::path_state_(&self.pathbuf)
|
||||
}
|
||||
fn path_state_(path: &Path) -> PathBuf {
|
||||
path.join("state")
|
||||
}
|
||||
|
||||
fn path_height(&self) -> PathBuf {
|
||||
Self::path_height_(&self.pathbuf)
|
||||
}
|
||||
fn path_height_(path: &Path) -> PathBuf {
|
||||
path.join("height")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Deref, DerefMut, Serialize, Deserialize)]
|
||||
struct State(BTreeMap<Dollars, Sats>);
|
||||
|
||||
impl State {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let len = self.len();
|
||||
|
||||
let mut buffer = Vec::with_capacity(8 + len * 16);
|
||||
|
||||
buffer.extend(len.as_bytes());
|
||||
|
||||
self.iter().for_each(|(key, value)| {
|
||||
buffer.extend(key.as_bytes());
|
||||
buffer.extend(value.as_bytes());
|
||||
});
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
fn deserialize(data: &[u8]) -> Result<Self> {
|
||||
let mut cursor = Cursor::new(data);
|
||||
let mut buffer = [0u8; 8];
|
||||
|
||||
cursor
|
||||
.read_exact(&mut buffer)
|
||||
.map_err(|_| Error::Str("Failed to read entry count"))?;
|
||||
let entry_count = usize::read_from_bytes(&buffer)?;
|
||||
|
||||
let mut map = BTreeMap::new();
|
||||
|
||||
for _ in 0..entry_count {
|
||||
cursor.read_exact(&mut buffer)?;
|
||||
let key = Dollars::read_from_bytes(&buffer)?;
|
||||
|
||||
cursor.read_exact(&mut buffer)?;
|
||||
let value = Sats::read_from_bytes(&buffer)?;
|
||||
|
||||
map.insert(key, value);
|
||||
}
|
||||
|
||||
Ok(Self(map))
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use brk_core::{CheckedSub, Dollars};
|
||||
use brk_structs::{CheckedSub, Dollars};
|
||||
|
||||
use super::SupplyState;
|
||||
|
||||
@@ -42,6 +42,10 @@ impl RealizedState {
|
||||
return;
|
||||
}
|
||||
|
||||
self.increment_(price * supply_state.value)
|
||||
}
|
||||
|
||||
pub fn increment_(&mut self, realized_cap: Dollars) {
|
||||
if self.cap == Dollars::NAN {
|
||||
self.cap = Dollars::ZERO;
|
||||
self.profit = Dollars::ZERO;
|
||||
@@ -52,13 +56,15 @@ impl RealizedState {
|
||||
self.adj_value_destroyed = Dollars::ZERO;
|
||||
}
|
||||
|
||||
let value = price * supply_state.value;
|
||||
self.cap += value;
|
||||
self.cap += realized_cap;
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, supply_state: &SupplyState, price: Dollars) {
|
||||
let value = price * supply_state.value;
|
||||
self.cap = self.cap.checked_sub(value).unwrap();
|
||||
self.decrement_(price * supply_state.value);
|
||||
}
|
||||
|
||||
pub fn decrement_(&mut self, realized_cap: Dollars) {
|
||||
self.cap = self.cap.checked_sub(realized_cap).unwrap();
|
||||
}
|
||||
|
||||
pub fn receive(&mut self, supply_state: &SupplyState, current_price: Dollars) {
|
||||
@@ -1,12 +1,12 @@
|
||||
use std::ops::{Add, AddAssign, SubAssign};
|
||||
|
||||
use brk_core::{CheckedSub, Sats};
|
||||
use brk_structs::{CheckedSub, LoadedAddressData, Sats};
|
||||
use serde::Serialize;
|
||||
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
||||
|
||||
#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, KnownLayout, Serialize)]
|
||||
pub struct SupplyState {
|
||||
pub utxos: usize,
|
||||
pub utxos: u64,
|
||||
pub value: Sats,
|
||||
}
|
||||
|
||||
@@ -39,3 +39,12 @@ impl SubAssign<&SupplyState> for SupplyState {
|
||||
self.value = self.value.checked_sub(rhs.value).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&LoadedAddressData> for SupplyState {
|
||||
fn from(value: &LoadedAddressData) -> Self {
|
||||
Self {
|
||||
utxos: value.outputs_len as u64,
|
||||
value: value.amount(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use std::ops::{Add, AddAssign};
|
||||
|
||||
use brk_structs::{ByAmountRange, GroupedByType, OutputType, Sats};
|
||||
|
||||
use super::SupplyState;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Transacted {
|
||||
pub spendable_supply: SupplyState,
|
||||
pub by_type: GroupedByType<SupplyState>,
|
||||
pub by_size_group: ByAmountRange<SupplyState>,
|
||||
}
|
||||
|
||||
impl Transacted {
|
||||
#[allow(clippy::inconsistent_digit_grouping)]
|
||||
pub fn iterate(&mut self, value: Sats, _type: OutputType) {
|
||||
let supply = SupplyState { utxos: 1, value };
|
||||
|
||||
*self.by_type.get_mut(_type) += &supply;
|
||||
|
||||
if _type.is_unspendable() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.spendable_supply += &supply;
|
||||
|
||||
*self.by_size_group.get_mut(value) += &supply;
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Transacted {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
spendable_supply: self.spendable_supply + rhs.spendable_supply,
|
||||
by_type: self.by_type + rhs.by_type,
|
||||
by_size_group: self.by_size_group + rhs.by_size_group,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Transacted {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.by_size_group += rhs.by_size_group;
|
||||
self.spendable_supply += &rhs.spendable_supply;
|
||||
self.by_type += rhs.by_type;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use brk_core::{Dollars, Sats};
|
||||
use brk_structs::{Dollars, Sats};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct UnrealizedState {
|
||||
@@ -1,34 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::Version;
|
||||
use fjall::TransactionalKeyspace;
|
||||
|
||||
const _VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Stores {
|
||||
// pub address_to_utxos_received: Store<AddressIndexOutputIndex, Unit>,
|
||||
// pub address_to_utxos_spent: Store<AddressIndexOutputIndex, Unit>,
|
||||
}
|
||||
|
||||
impl Stores {
|
||||
pub fn import(_: &Path, _: Version, _: &TransactionalKeyspace) -> color_eyre::Result<Self> {
|
||||
// let address_to_utxos_received = Store::import(
|
||||
// keyspace.clone(),
|
||||
// path,
|
||||
// "address_to_utxos_received",
|
||||
// version + VERSION + Version::ZERO,
|
||||
// )?;
|
||||
// let address_to_utxos_spent = Store::import(
|
||||
// keyspace.clone(),
|
||||
// path,
|
||||
// "address_to_utxos_spent",
|
||||
// version + VERSION + Version::ZERO,
|
||||
// )?;
|
||||
|
||||
Ok(Self {
|
||||
// address_to_utxos_received,
|
||||
// address_to_utxos_spent,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
use brk_error::Result;
|
||||
use brk_structs::{Bitcoin, CheckedSub, Close, Date, DateIndex, Dollars, Sats, StoredF32};
|
||||
use vecdb::{
|
||||
AnyIterableVec, AnyStoredVec, AnyVec, EagerVec, Exit, GenericStoredVec, StoredIndex,
|
||||
VecIterator, Version,
|
||||
};
|
||||
|
||||
const DCA_AMOUNT: Dollars = Dollars::mint(100.0);
|
||||
|
||||
pub trait ComputeDCAStackViaLen {
|
||||
fn compute_dca_stack_via_len(
|
||||
&mut self,
|
||||
max_from: DateIndex,
|
||||
closes: &impl AnyIterableVec<DateIndex, Close<Dollars>>,
|
||||
len: usize,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
|
||||
fn compute_dca_stack_via_from(
|
||||
&mut self,
|
||||
max_from: DateIndex,
|
||||
closes: &impl AnyIterableVec<DateIndex, Close<Dollars>>,
|
||||
from: DateIndex,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
}
|
||||
impl ComputeDCAStackViaLen for EagerVec<DateIndex, Sats> {
|
||||
fn compute_dca_stack_via_len(
|
||||
&mut self,
|
||||
max_from: DateIndex,
|
||||
closes: &impl AnyIterableVec<DateIndex, Close<Dollars>>,
|
||||
len: usize,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + closes.version(),
|
||||
)?;
|
||||
|
||||
let mut other_iter = closes.iter();
|
||||
let mut prev = None;
|
||||
|
||||
let index = max_from.min(DateIndex::from(self.len()));
|
||||
closes.iter_at(index).try_for_each(|(i, closes)| {
|
||||
let price = *closes.into_owned();
|
||||
let i_usize = i.unwrap_to_usize();
|
||||
if prev.is_none() {
|
||||
if i_usize == 0 {
|
||||
prev.replace(Sats::ZERO);
|
||||
} else {
|
||||
prev.replace(self.into_iter().unwrap_get_inner_(i_usize - 1));
|
||||
}
|
||||
}
|
||||
|
||||
let mut stack = Sats::ZERO;
|
||||
|
||||
if price != Dollars::ZERO {
|
||||
stack = prev.unwrap() + Sats::from(Bitcoin::from(DCA_AMOUNT / price));
|
||||
|
||||
if i_usize >= len {
|
||||
let prev_price = *other_iter.unwrap_get_inner_(i_usize - len);
|
||||
if prev_price != Dollars::ZERO {
|
||||
stack = stack
|
||||
.checked_sub(Sats::from(Bitcoin::from(DCA_AMOUNT / prev_price)))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prev.replace(stack);
|
||||
|
||||
self.forced_push_at(i, stack, exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_dca_stack_via_from(
|
||||
&mut self,
|
||||
max_from: DateIndex,
|
||||
closes: &impl AnyIterableVec<DateIndex, Close<Dollars>>,
|
||||
from: DateIndex,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + closes.version(),
|
||||
)?;
|
||||
|
||||
let mut prev = None;
|
||||
|
||||
let index = max_from.min(DateIndex::from(self.len()));
|
||||
closes.iter_at(index).try_for_each(|(i, closes)| {
|
||||
let price = *closes.into_owned();
|
||||
let i_usize = i.unwrap_to_usize();
|
||||
if prev.is_none() {
|
||||
if i_usize == 0 {
|
||||
prev.replace(Sats::ZERO);
|
||||
} else {
|
||||
prev.replace(self.into_iter().unwrap_get_inner_(i_usize - 1));
|
||||
}
|
||||
}
|
||||
|
||||
let mut stack = Sats::ZERO;
|
||||
|
||||
if price != Dollars::ZERO && i >= from {
|
||||
stack = prev.unwrap() + Sats::from(Bitcoin::from(DCA_AMOUNT / price));
|
||||
}
|
||||
|
||||
prev.replace(stack);
|
||||
|
||||
self.forced_push_at(i, stack, exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ComputeDCAAveragePriceViaLen {
|
||||
fn compute_dca_avg_price_via_len(
|
||||
&mut self,
|
||||
max_from: DateIndex,
|
||||
stacks: &impl AnyIterableVec<DateIndex, Sats>,
|
||||
len: usize,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
|
||||
fn compute_dca_avg_price_via_from(
|
||||
&mut self,
|
||||
max_from: DateIndex,
|
||||
stacks: &impl AnyIterableVec<DateIndex, Sats>,
|
||||
from: DateIndex,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
}
|
||||
impl ComputeDCAAveragePriceViaLen for EagerVec<DateIndex, Dollars> {
|
||||
fn compute_dca_avg_price_via_len(
|
||||
&mut self,
|
||||
max_from: DateIndex,
|
||||
stacks: &impl AnyIterableVec<DateIndex, Sats>,
|
||||
len: usize,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ONE + self.inner_version() + stacks.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(DateIndex::from(self.len()));
|
||||
|
||||
let first_price_date = DateIndex::try_from(Date::new(2010, 7, 12)).unwrap();
|
||||
|
||||
stacks.iter_at(index).try_for_each(|(i, stack)| {
|
||||
let stack = stack.into_owned();
|
||||
let mut avg_price = Dollars::from(f64::NAN);
|
||||
if i > first_price_date {
|
||||
avg_price = DCA_AMOUNT
|
||||
* len
|
||||
.min(i.unwrap_to_usize() + 1)
|
||||
.min(i.checked_sub(first_price_date).unwrap().unwrap_to_usize() + 1)
|
||||
/ Bitcoin::from(stack);
|
||||
}
|
||||
self.forced_push_at(i, avg_price, exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_dca_avg_price_via_from(
|
||||
&mut self,
|
||||
max_from: DateIndex,
|
||||
stacks: &impl AnyIterableVec<DateIndex, Sats>,
|
||||
from: DateIndex,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + stacks.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(DateIndex::from(self.len()));
|
||||
|
||||
let from_usize = from.unwrap_to_usize();
|
||||
|
||||
stacks.iter_at(index).try_for_each(|(i, stack)| {
|
||||
let stack = stack.into_owned();
|
||||
let mut avg_price = Dollars::from(f64::NAN);
|
||||
if i >= from {
|
||||
avg_price =
|
||||
DCA_AMOUNT * (i.unwrap_to_usize() + 1 - from_usize) / Bitcoin::from(stack);
|
||||
}
|
||||
self.forced_push_at(i, avg_price, exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ComputeFromSats<I> {
|
||||
fn compute_from_sats(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
sats: &impl AnyIterableVec<I, Sats>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
}
|
||||
impl<I> ComputeFromSats<I> for EagerVec<I, Bitcoin>
|
||||
where
|
||||
I: StoredIndex,
|
||||
{
|
||||
fn compute_from_sats(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
sats: &impl AnyIterableVec<I, Sats>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + sats.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(I::from(self.len()));
|
||||
sats.iter_at(index).try_for_each(|(i, sats)| {
|
||||
let (i, v) = (i, Bitcoin::from(sats.into_owned()));
|
||||
self.forced_push_at(i, v, exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ComputeFromBitcoin<I> {
|
||||
fn compute_from_bitcoin(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
bitcoin: &impl AnyIterableVec<I, Bitcoin>,
|
||||
price: &impl AnyIterableVec<I, Close<Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
}
|
||||
impl<I> ComputeFromBitcoin<I> for EagerVec<I, Dollars>
|
||||
where
|
||||
I: StoredIndex,
|
||||
{
|
||||
fn compute_from_bitcoin(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
bitcoin: &impl AnyIterableVec<I, Bitcoin>,
|
||||
price: &impl AnyIterableVec<I, Close<Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + bitcoin.version(),
|
||||
)?;
|
||||
|
||||
let mut price_iter = price.iter();
|
||||
let index = max_from.min(I::from(self.len()));
|
||||
bitcoin.iter_at(index).try_for_each(|(i, bitcoin)| {
|
||||
let dollars = price_iter.unwrap_get_inner(i);
|
||||
let (i, v) = (i, *dollars * bitcoin.into_owned());
|
||||
self.forced_push_at(i, v, exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ComputeDrawdown<I> {
|
||||
fn compute_drawdown(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
close: &impl AnyIterableVec<I, Close<Dollars>>,
|
||||
ath: &impl AnyIterableVec<I, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
}
|
||||
impl<I> ComputeDrawdown<I> for EagerVec<I, StoredF32>
|
||||
where
|
||||
I: StoredIndex,
|
||||
{
|
||||
fn compute_drawdown(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
close: &impl AnyIterableVec<I, Close<Dollars>>,
|
||||
ath: &impl AnyIterableVec<I, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + ath.version() + close.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(I::from(self.len()));
|
||||
let mut close_iter = close.iter();
|
||||
ath.iter_at(index).try_for_each(|(i, ath)| {
|
||||
let ath = ath.into_owned();
|
||||
if ath == Dollars::ZERO {
|
||||
self.forced_push_at(i, StoredF32::default(), exit)
|
||||
} else {
|
||||
let close = *close_iter.unwrap_get_inner(i);
|
||||
let drawdown = StoredF32::from((*ath - *close) / *ath * -100.0);
|
||||
self.forced_push_at(i, drawdown, exit)
|
||||
}
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use brk_core::{StoredU8, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, AnyVec, Computation, Format};
|
||||
|
||||
use super::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecsFromHeight, StorableVecGeneatorOptions},
|
||||
indexes,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
pub _0: ComputedVecsFromHeight<StoredU8>,
|
||||
pub _1: ComputedVecsFromHeight<StoredU8>,
|
||||
pub _50: ComputedVecsFromHeight<StoredU8>,
|
||||
pub _100: ComputedVecsFromHeight<StoredU8>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
version: Version,
|
||||
_computation: Computation,
|
||||
format: Format,
|
||||
) -> color_eyre::Result<Self> {
|
||||
fs::create_dir_all(path)?;
|
||||
|
||||
Ok(Self {
|
||||
_0: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"0",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
_1: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"1",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
_50: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"50",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
_100: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"100",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
self._0.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredU8::new(0)),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self._1.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredU8::new(1)),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self._50.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredU8::new(50)),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self._100.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredU8::new(100)),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self._0.vecs(),
|
||||
self._1.vecs(),
|
||||
self._50.vecs(),
|
||||
self._100.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{
|
||||
DateIndex, DecadeIndex, MonthIndex, QuarterIndex, Result, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format};
|
||||
|
||||
use crate::vecs::{Indexes, indexes};
|
||||
|
||||
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecsFromDateIndex<T>
|
||||
where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub dateindex: Option<EagerVec<DateIndex, T>>,
|
||||
pub dateindex_extra: ComputedVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T>,
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedVecsFromDateIndex<T>
|
||||
where
|
||||
T: ComputedType,
|
||||
{
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let dateindex = compute_source.then(|| {
|
||||
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format).unwrap()
|
||||
});
|
||||
|
||||
let dateindex_extra = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options.copy_self_extra(),
|
||||
)?;
|
||||
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
Ok(Self {
|
||||
dateindex,
|
||||
dateindex_extra,
|
||||
weekindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all<F>(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut EagerVec<DateIndex, T>,
|
||||
&Indexer,
|
||||
&indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
self.dateindex.as_mut().unwrap(),
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let dateindex: Option<&EagerVec<DateIndex, T>> = None;
|
||||
self.compute_rest(indexes, starting_indexes, exit, dateindex)
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
dateindex: Option<&impl AnyIterableVec<DateIndex, T>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
if let Some(dateindex) = dateindex {
|
||||
self.dateindex_extra
|
||||
.extend(starting_indexes.dateindex, dateindex, exit)?;
|
||||
|
||||
self.weekindex.compute(
|
||||
starting_indexes.weekindex,
|
||||
dateindex,
|
||||
&indexes.weekindex_to_first_dateindex,
|
||||
&indexes.weekindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.compute(
|
||||
starting_indexes.monthindex,
|
||||
dateindex,
|
||||
&indexes.monthindex_to_first_dateindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
} else {
|
||||
let dateindex = self.dateindex.as_ref().unwrap();
|
||||
|
||||
self.dateindex_extra
|
||||
.extend(starting_indexes.dateindex, dateindex, exit)?;
|
||||
|
||||
self.weekindex.compute(
|
||||
starting_indexes.weekindex,
|
||||
dateindex,
|
||||
&indexes.weekindex_to_first_dateindex,
|
||||
&indexes.weekindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.compute(
|
||||
starting_indexes.monthindex,
|
||||
dateindex,
|
||||
&indexes.monthindex_to_first_dateindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
self.quarterindex.from_aligned(
|
||||
starting_indexes.quarterindex,
|
||||
&self.monthindex,
|
||||
&indexes.quarterindex_to_first_monthindex,
|
||||
&indexes.quarterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.from_aligned(
|
||||
starting_indexes.yearindex,
|
||||
&self.monthindex,
|
||||
&indexes.yearindex_to_first_monthindex,
|
||||
&indexes.yearindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.from_aligned(
|
||||
starting_indexes.decadeindex,
|
||||
&self.yearindex,
|
||||
&indexes.decadeindex_to_first_yearindex,
|
||||
&indexes.decadeindex_to_yearindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.dateindex
|
||||
.as_ref()
|
||||
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
|
||||
self.dateindex_extra.vecs(),
|
||||
self.weekindex.vecs(),
|
||||
self.monthindex.vecs(),
|
||||
self.quarterindex.vecs(),
|
||||
self.yearindex.vecs(),
|
||||
self.decadeindex.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
use std::ops::{Add, Div};
|
||||
|
||||
use brk_vec::StoredType;
|
||||
|
||||
pub trait ComputedType
|
||||
where
|
||||
Self: StoredType + From<usize> + Div<usize, Output = Self> + Add<Output = Self> + Ord,
|
||||
{
|
||||
}
|
||||
impl<T> ComputedType for T where
|
||||
T: StoredType + From<usize> + Div<usize, Output = Self> + Add<Output = Self> + Ord
|
||||
{
|
||||
}
|
||||