mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot
This commit is contained in:
@@ -55,7 +55,7 @@ const _MS_PER_DAY = 86400000;
|
||||
const _MS_PER_WEEK = 7 * _MS_PER_DAY;
|
||||
const _EPOCH_MS = 1230768000000;
|
||||
const _DATE_INDEXES = new Set([
|
||||
'minute1', 'minute5', 'minute10', 'minute30',
|
||||
'minute10', 'minute30',
|
||||
'hour1', 'hour4', 'hour12',
|
||||
'day1', 'day3', 'week1',
|
||||
'month1', 'month3', 'month6',
|
||||
@@ -73,8 +73,6 @@ const _addMonths = (months) => new Date(2009, months, 1);
|
||||
*/
|
||||
function indexToDate(index, i) {{
|
||||
switch (index) {{
|
||||
case 'minute1': return new Date(_EPOCH_MS + i * 60000);
|
||||
case 'minute5': return new Date(_EPOCH_MS + i * 300000);
|
||||
case 'minute10': return new Date(_EPOCH_MS + i * 600000);
|
||||
case 'minute30': return new Date(_EPOCH_MS + i * 1800000);
|
||||
case 'hour1': return new Date(_EPOCH_MS + i * 3600000);
|
||||
@@ -102,8 +100,6 @@ function indexToDate(index, i) {{
|
||||
function dateToIndex(index, d) {{
|
||||
const ms = d.getTime();
|
||||
switch (index) {{
|
||||
case 'minute1': return Math.floor((ms - _EPOCH_MS) / 60000);
|
||||
case 'minute5': return Math.floor((ms - _EPOCH_MS) / 300000);
|
||||
case 'minute10': return Math.floor((ms - _EPOCH_MS) / 600000);
|
||||
case 'minute30': return Math.floor((ms - _EPOCH_MS) / 1800000);
|
||||
case 'hour1': return Math.floor((ms - _EPOCH_MS) / 3600000);
|
||||
|
||||
@@ -136,7 +136,7 @@ _GENESIS = date(2009, 1, 3) # day1 0, week1 0
|
||||
_DAY_ONE = date(2009, 1, 9) # day1 1 (6 day gap after genesis)
|
||||
_EPOCH = datetime(2009, 1, 1, tzinfo=timezone.utc)
|
||||
_DATE_INDEXES = frozenset([
|
||||
'minute1', 'minute5', 'minute10', 'minute30',
|
||||
'minute10', 'minute30',
|
||||
'hour1', 'hour4', 'hour12',
|
||||
'day1', 'day3', 'week1',
|
||||
'month1', 'month3', 'month6',
|
||||
@@ -145,11 +145,7 @@ _DATE_INDEXES = frozenset([
|
||||
|
||||
def _index_to_date(index: str, i: int) -> Union[date, datetime]:
|
||||
"""Convert an index value to a date/datetime for date-based indexes."""
|
||||
if index == 'minute1':
|
||||
return _EPOCH + timedelta(minutes=i)
|
||||
elif index == 'minute5':
|
||||
return _EPOCH + timedelta(minutes=i * 5)
|
||||
elif index == 'minute10':
|
||||
if index == 'minute10':
|
||||
return _EPOCH + timedelta(minutes=i * 10)
|
||||
elif index == 'minute30':
|
||||
return _EPOCH + timedelta(minutes=i * 30)
|
||||
@@ -187,13 +183,13 @@ def _date_to_index(index: str, d: Union[date, datetime]) -> int:
|
||||
Returns the floor index (latest index whose date is <= the given date).
|
||||
For sub-day indexes (minute*, hour*), a plain date is treated as midnight UTC.
|
||||
"""
|
||||
if index in ('minute1', 'minute5', 'minute10', 'minute30', 'hour1', 'hour4', 'hour12'):
|
||||
if index in ('minute10', 'minute30', 'hour1', 'hour4', 'hour12'):
|
||||
if isinstance(d, datetime):
|
||||
dt = d if d.tzinfo else d.replace(tzinfo=timezone.utc)
|
||||
else:
|
||||
dt = datetime(d.year, d.month, d.day, tzinfo=timezone.utc)
|
||||
secs = int((dt - _EPOCH).total_seconds())
|
||||
div = {{'minute1': 60, 'minute5': 300, 'minute10': 600, 'minute30': 1800,
|
||||
div = {{'minute10': 600, 'minute30': 1800,
|
||||
'hour1': 3600, 'hour4': 14400, 'hour12': 43200}}
|
||||
return secs // div[index]
|
||||
dd = d.date() if isinstance(d, datetime) else d
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,8 +24,6 @@ pub const DB_NAME: &str = "blocks";
|
||||
|
||||
pub(crate) const TARGET_BLOCKS_PER_DAY_F64: f64 = 144.0;
|
||||
pub(crate) const TARGET_BLOCKS_PER_DAY_F32: f32 = 144.0;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MINUTE1: u64 = 0;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MINUTE5: u64 = 0;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MINUTE10: u64 = 1;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MINUTE30: u64 = 3;
|
||||
pub(crate) const TARGET_BLOCKS_PER_HOUR1: u64 = 6;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Date, Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1,
|
||||
Minute10, Minute30, Minute5, Month1, Month3, Month6, Timestamp, Week1, Year1, Year10,
|
||||
Date, Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4,
|
||||
Minute10, Minute30, Month1, Month3, Month6, Timestamp, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{EagerVec, Exit, LazyVecFrom1, PcoVec, Rw, StorageMode};
|
||||
@@ -19,7 +19,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
|
||||
/// Per-period timestamp indexes.
|
||||
///
|
||||
/// Time-based periods (minute1–year10) are lazy: `idx.to_timestamp()` is a pure
|
||||
/// Time-based periods (minute10–year10) are lazy: `idx.to_timestamp()` is a pure
|
||||
/// function of the index, so no storage or decompression is needed.
|
||||
/// Epoch-based periods (halvingepoch, difficultyepoch) are eager: their timestamps
|
||||
/// come from block data via `compute_indirect`.
|
||||
@@ -28,8 +28,6 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub struct TimestampIndexes<M: StorageMode = Rw>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
LazyVecFrom1<Minute1, Timestamp, Minute1, Height>,
|
||||
LazyVecFrom1<Minute5, Timestamp, Minute5, Height>,
|
||||
LazyVecFrom1<Minute10, Timestamp, Minute10, Height>,
|
||||
LazyVecFrom1<Minute30, Timestamp, Minute30, Height>,
|
||||
LazyVecFrom1<Hour1, Timestamp, Hour1, Height>,
|
||||
|
||||
@@ -867,6 +867,30 @@ impl RealizedBase {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.lower_price_band.usd.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.realized_price.usd.height,
|
||||
&self.investor_price.usd.height,
|
||||
|(i, rp, ip, ..)| {
|
||||
let rp = f64::from(rp);
|
||||
let ip = f64::from(ip);
|
||||
(i, Dollars::from(rp * rp / ip))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.upper_price_band.usd.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.investor_price.usd.height,
|
||||
&self.realized_price.usd.height,
|
||||
|(i, ip, rp, ..)| {
|
||||
let ip = f64::from(ip);
|
||||
let rp = f64::from(rp);
|
||||
(i, Dollars::from(ip * ip / rp))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.realized_cap_30d_delta.height.compute_rolling_change(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, Sats, StoredF32, StoredF64, Version};
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
use vecdb::{Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{
|
||||
ComputedFromHeightLast,
|
||||
ComputedFromHeightLast, LazyFromHeightLast,
|
||||
NegPercentageDollarsF32, PercentageDollarsF32, PercentageSatsF64,
|
||||
StoredF32Identity,
|
||||
};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase};
|
||||
@@ -24,7 +25,7 @@ pub struct RelativeBase<M: StorageMode = Rw> {
|
||||
pub unrealized_loss_rel_to_market_cap: ComputedFromHeightLast<StoredF32, M>,
|
||||
pub neg_unrealized_loss_rel_to_market_cap: ComputedFromHeightLast<StoredF32, M>,
|
||||
pub net_unrealized_pnl_rel_to_market_cap: ComputedFromHeightLast<StoredF32, M>,
|
||||
pub nupl: ComputedFromHeightLast<StoredF32, M>,
|
||||
pub nupl: LazyFromHeightLast<StoredF32, StoredF32>,
|
||||
|
||||
// === Invested Capital in Profit/Loss as % of Realized Cap ===
|
||||
pub invested_capital_in_profit_pct: ComputedFromHeightLast<StoredF32, M>,
|
||||
@@ -36,6 +37,17 @@ impl RelativeBase {
|
||||
let v1 = Version::ONE;
|
||||
let v2 = Version::new(2);
|
||||
|
||||
let net_unrealized_pnl_rel_to_market_cap = ComputedFromHeightLast::forced_import(
|
||||
cfg.db, &cfg.name("net_unrealized_pnl_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
|
||||
)?;
|
||||
|
||||
let nupl = LazyFromHeightLast::from_computed::<StoredF32Identity>(
|
||||
&cfg.name("nupl"),
|
||||
cfg.version + v2,
|
||||
net_unrealized_pnl_rel_to_market_cap.height.read_only_boxed_clone(),
|
||||
&net_unrealized_pnl_rel_to_market_cap,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
supply_in_profit_rel_to_own_supply: ComputedFromHeightLast::forced_import(
|
||||
cfg.db, &cfg.name("supply_in_profit_rel_to_own_supply"), cfg.version + v1, cfg.indexes,
|
||||
@@ -52,12 +64,8 @@ impl RelativeBase {
|
||||
neg_unrealized_loss_rel_to_market_cap: ComputedFromHeightLast::forced_import(
|
||||
cfg.db, &cfg.name("neg_unrealized_loss_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
|
||||
)?,
|
||||
net_unrealized_pnl_rel_to_market_cap: ComputedFromHeightLast::forced_import(
|
||||
cfg.db, &cfg.name("net_unrealized_pnl_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
|
||||
)?,
|
||||
nupl: ComputedFromHeightLast::forced_import(
|
||||
cfg.db, &cfg.name("nupl"), cfg.version + v2, cfg.indexes,
|
||||
)?,
|
||||
net_unrealized_pnl_rel_to_market_cap,
|
||||
nupl,
|
||||
invested_capital_in_profit_pct: ComputedFromHeightLast::forced_import(
|
||||
cfg.db, &cfg.name("invested_capital_in_profit_pct"), cfg.version, cfg.indexes,
|
||||
)?,
|
||||
@@ -100,10 +108,6 @@ impl RelativeBase {
|
||||
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
|
||||
max_from, &unrealized.net_unrealized_pnl.height, market_cap, exit,
|
||||
)?;
|
||||
self.nupl
|
||||
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
|
||||
max_from, &unrealized.net_unrealized_pnl.height, market_cap, exit,
|
||||
)?;
|
||||
self.invested_capital_in_profit_pct
|
||||
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
|
||||
max_from, &unrealized.invested_capital_in_profit.height, &realized.realized_cap.height, exit,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, Year10, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4,
|
||||
Minute1, Minute10, Minute30, Minute5, Month1, Month3, Month6, StoredU64, Version, Week1,
|
||||
Minute10, Minute30, Month1, Month3, Month6, StoredU64, Version, Week1,
|
||||
Year1,
|
||||
};
|
||||
use vecdb::{Database, EagerVec, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
@@ -11,8 +11,6 @@ use brk_error::Result;
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub identity: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub minute1: M::Stored<EagerVec<PcoVec<Height, Minute1>>>,
|
||||
pub minute5: M::Stored<EagerVec<PcoVec<Height, Minute5>>>,
|
||||
pub minute10: M::Stored<EagerVec<PcoVec<Height, Minute10>>>,
|
||||
pub minute30: M::Stored<EagerVec<PcoVec<Height, Minute30>>>,
|
||||
pub hour1: M::Stored<EagerVec<PcoVec<Height, Hour1>>>,
|
||||
@@ -35,8 +33,6 @@ impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "height", version)?,
|
||||
minute1: EagerVec::forced_import(db, "minute1", version)?,
|
||||
minute5: EagerVec::forced_import(db, "minute5", version)?,
|
||||
minute10: EagerVec::forced_import(db, "minute10", version)?,
|
||||
minute30: EagerVec::forced_import(db, "minute30", version)?,
|
||||
hour1: EagerVec::forced_import(db, "hour1", version)?,
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Minute1, Version};
|
||||
use vecdb::{Database, EagerVec, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub identity: M::Stored<EagerVec<PcoVec<Minute1, Minute1>>>,
|
||||
pub first_height: M::Stored<EagerVec<PcoVec<Minute1, Height>>>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "minute1", version)?,
|
||||
first_height: EagerVec::forced_import(db, "minute1_first_height", version)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Minute5, Version};
|
||||
use vecdb::{Database, EagerVec, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub identity: M::Stored<EagerVec<PcoVec<Minute5, Minute5>>>,
|
||||
pub first_height: M::Stored<EagerVec<PcoVec<Minute5, Height>>>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "minute5", version)?,
|
||||
first_height: EagerVec::forced_import(db, "minute5_first_height", version)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,8 @@ mod height;
|
||||
mod hour1;
|
||||
mod hour12;
|
||||
mod hour4;
|
||||
mod minute1;
|
||||
mod minute10;
|
||||
mod minute30;
|
||||
mod minute5;
|
||||
mod month1;
|
||||
mod month3;
|
||||
mod month6;
|
||||
@@ -27,7 +25,7 @@ use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Date, Day1, Day3, Hour1, Hour4, Hour12, Indexes, Minute1, Minute5, Minute10, Minute30, Month1,
|
||||
Date, Day1, Day3, Hour1, Hour4, Hour12, Indexes, Minute10, Minute30, Month1,
|
||||
Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use vecdb::{Database, Exit, PAGE_SIZE, ReadableVec, Rw, StorageMode};
|
||||
@@ -44,8 +42,6 @@ pub use height::Vecs as HeightVecs;
|
||||
pub use hour1::Vecs as Hour1Vecs;
|
||||
pub use hour4::Vecs as Hour4Vecs;
|
||||
pub use hour12::Vecs as Hour12Vecs;
|
||||
pub use minute1::Vecs as Minute1Vecs;
|
||||
pub use minute5::Vecs as Minute5Vecs;
|
||||
pub use minute10::Vecs as Minute10Vecs;
|
||||
pub use minute30::Vecs as Minute30Vecs;
|
||||
pub use month1::Vecs as Month1Vecs;
|
||||
@@ -68,8 +64,6 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub height: HeightVecs<M>,
|
||||
pub difficultyepoch: DifficultyEpochVecs<M>,
|
||||
pub halvingepoch: HalvingEpochVecs<M>,
|
||||
pub minute1: Minute1Vecs<M>,
|
||||
pub minute5: Minute5Vecs<M>,
|
||||
pub minute10: Minute10Vecs<M>,
|
||||
pub minute30: Minute30Vecs<M>,
|
||||
pub hour1: Hour1Vecs<M>,
|
||||
@@ -104,8 +98,6 @@ impl Vecs {
|
||||
height: HeightVecs::forced_import(&db, version)?,
|
||||
difficultyepoch: DifficultyEpochVecs::forced_import(&db, version)?,
|
||||
halvingepoch: HalvingEpochVecs::forced_import(&db, version)?,
|
||||
minute1: Minute1Vecs::forced_import(&db, version)?,
|
||||
minute5: Minute5Vecs::forced_import(&db, version)?,
|
||||
minute10: Minute10Vecs::forced_import(&db, version)?,
|
||||
minute30: Minute30Vecs::forced_import(&db, version)?,
|
||||
hour1: Hour1Vecs::forced_import(&db, version)?,
|
||||
@@ -190,22 +182,6 @@ impl Vecs {
|
||||
|
||||
// --- Timestamp-based height → period mappings ---
|
||||
|
||||
// Minute1
|
||||
self.height.minute1.compute_transform(
|
||||
starting_indexes.height,
|
||||
&blocks_time.timestamp_monotonic,
|
||||
|(h, ts, _)| (h, Minute1::from_timestamp(ts)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Minute5
|
||||
self.height.minute5.compute_transform(
|
||||
starting_indexes.height,
|
||||
&blocks_time.timestamp_monotonic,
|
||||
|(h, ts, _)| (h, Minute5::from_timestamp(ts)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Minute10
|
||||
self.height.minute10.compute_transform(
|
||||
starting_indexes.height,
|
||||
@@ -376,16 +352,6 @@ impl Vecs {
|
||||
|
||||
// --- Starting values from height → period mappings ---
|
||||
|
||||
let starting_minute1 = self
|
||||
.height
|
||||
.minute1
|
||||
.collect_one(decremented_starting_height)
|
||||
.unwrap_or_default();
|
||||
let starting_minute5 = self
|
||||
.height
|
||||
.minute5
|
||||
.collect_one(decremented_starting_height)
|
||||
.unwrap_or_default();
|
||||
let starting_minute10 = self
|
||||
.height
|
||||
.minute10
|
||||
@@ -449,30 +415,6 @@ impl Vecs {
|
||||
|
||||
// --- Compute period-level vecs (first_height + identity) ---
|
||||
|
||||
// Minute1
|
||||
self.minute1.first_height.compute_first_per_index(
|
||||
starting_indexes.height,
|
||||
&self.height.minute1,
|
||||
exit,
|
||||
)?;
|
||||
self.minute1.identity.compute_from_index(
|
||||
starting_minute1,
|
||||
&self.minute1.first_height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Minute5
|
||||
self.minute5.first_height.compute_first_per_index(
|
||||
starting_indexes.height,
|
||||
&self.height.minute5,
|
||||
exit,
|
||||
)?;
|
||||
self.minute5.identity.compute_from_index(
|
||||
starting_minute5,
|
||||
&self.minute5.first_height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Minute10
|
||||
self.minute10.first_height.compute_first_per_index(
|
||||
starting_indexes.height,
|
||||
@@ -669,8 +611,6 @@ impl Vecs {
|
||||
|
||||
Ok(ComputeIndexes::new(
|
||||
starting_indexes,
|
||||
starting_minute1,
|
||||
starting_minute5,
|
||||
starting_minute10,
|
||||
starting_minute30,
|
||||
starting_hour1,
|
||||
|
||||
@@ -7,7 +7,7 @@ use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Minute1, Minute5,
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12,
|
||||
Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
@@ -18,7 +18,7 @@ use vecdb::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, indexes, indexes_from,
|
||||
ComputeIndexes, indexes, indexes_apply, indexes_from,
|
||||
internal::{ComputedVecValue, Indexes, NumericValue},
|
||||
};
|
||||
|
||||
@@ -27,8 +27,6 @@ use crate::{
|
||||
pub struct EagerIndexes<T, M: StorageMode = Rw>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute5, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute10, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute30, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Hour1, T>>>,
|
||||
@@ -86,23 +84,7 @@ where
|
||||
};
|
||||
}
|
||||
|
||||
period!(minute1);
|
||||
period!(minute5);
|
||||
period!(minute10);
|
||||
period!(minute30);
|
||||
period!(hour1);
|
||||
period!(hour4);
|
||||
period!(hour12);
|
||||
period!(day1);
|
||||
period!(day3);
|
||||
period!(week1);
|
||||
period!(month1);
|
||||
period!(month3);
|
||||
period!(month6);
|
||||
period!(year1);
|
||||
period!(year10);
|
||||
period!(halvingepoch);
|
||||
period!(difficultyepoch);
|
||||
indexes_apply!(period);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -131,23 +113,7 @@ where
|
||||
};
|
||||
}
|
||||
|
||||
period!(minute1);
|
||||
period!(minute5);
|
||||
period!(minute10);
|
||||
period!(minute30);
|
||||
period!(hour1);
|
||||
period!(hour4);
|
||||
period!(hour12);
|
||||
period!(day1);
|
||||
period!(day3);
|
||||
period!(week1);
|
||||
period!(month1);
|
||||
period!(month3);
|
||||
period!(month6);
|
||||
period!(year1);
|
||||
period!(year10);
|
||||
period!(halvingepoch);
|
||||
period!(difficultyepoch);
|
||||
indexes_apply!(period);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -176,23 +142,7 @@ where
|
||||
};
|
||||
}
|
||||
|
||||
period!(minute1);
|
||||
period!(minute5);
|
||||
period!(minute10);
|
||||
period!(minute30);
|
||||
period!(hour1);
|
||||
period!(hour4);
|
||||
period!(hour12);
|
||||
period!(day1);
|
||||
period!(day3);
|
||||
period!(week1);
|
||||
period!(month1);
|
||||
period!(month3);
|
||||
period!(month6);
|
||||
period!(year1);
|
||||
period!(year10);
|
||||
period!(halvingepoch);
|
||||
period!(difficultyepoch);
|
||||
indexes_apply!(period);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10,
|
||||
Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4,
|
||||
Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
@@ -18,8 +18,6 @@ where
|
||||
T: VecValue + Formattable + Serialize + JsonSchema,
|
||||
{
|
||||
pub height: LazyVecFrom1<Height, T, Height, Height>,
|
||||
pub minute1: LazyVecFrom1<Minute1, T, Minute1, Minute1>,
|
||||
pub minute5: LazyVecFrom1<Minute5, T, Minute5, Minute5>,
|
||||
pub minute10: LazyVecFrom1<Minute10, T, Minute10, Minute10>,
|
||||
pub minute30: LazyVecFrom1<Minute30, T, Minute30, Minute30>,
|
||||
pub hour1: LazyVecFrom1<Hour1, T, Hour1, Hour1>,
|
||||
@@ -42,8 +40,6 @@ impl<T: VecValue + Formattable + Serialize + JsonSchema> ConstantVecs<T> {
|
||||
pub(crate) fn new<F>(name: &str, version: Version, indexes: &indexes::Vecs) -> Self
|
||||
where
|
||||
F: UnaryTransform<Height, T>
|
||||
+ UnaryTransform<Minute1, T>
|
||||
+ UnaryTransform<Minute5, T>
|
||||
+ UnaryTransform<Minute10, T>
|
||||
+ UnaryTransform<Minute30, T>
|
||||
+ UnaryTransform<Hour1, T>
|
||||
@@ -76,8 +72,6 @@ impl<T: VecValue + Formattable + Serialize + JsonSchema> ConstantVecs<T> {
|
||||
version,
|
||||
indexes.height.identity.read_only_boxed_clone(),
|
||||
),
|
||||
minute1: period!(minute1, Minute1),
|
||||
minute5: period!(minute5, Minute5),
|
||||
minute10: period!(minute10, Minute10),
|
||||
minute30: period!(minute30, Minute30),
|
||||
hour1: period!(hour1, Hour1),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, FromCoarserIndex, HalvingEpoch, Height, Hour1, Hour4, Hour12,
|
||||
Minute1, Minute5, Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
@@ -21,8 +21,6 @@ use crate::{
|
||||
pub struct ComputedHeightDerivedLast<T>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
LazyAggVec<Minute1, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Minute5, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Minute10, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Minute30, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Hour1, Option<T>, Height, Height, T>,
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::marker::PhantomData;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Minute1, Minute5,
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12,
|
||||
Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
@@ -60,8 +60,6 @@ where
|
||||
pub struct LazyHeightDerivedLast<T, S1T = T>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
LazyTransformLast<Minute1, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Minute5, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Minute10, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Minute30, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Hour1, Option<T>, Option<S1T>>,
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
//! Base generic struct with 17 type parameters — one per time period/epoch index.
|
||||
//! Base generic struct with 15 type parameters — one per time period/epoch index.
|
||||
//!
|
||||
//! Foundation for all per-index types. Replaces the repetitive 17-field pattern
|
||||
//! Foundation for all per-index types. Replaces the repetitive 15-field pattern
|
||||
//! found throughout height_derived types.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct Indexes<M1, M5, M10, M30, H1, H4, H12, D1, D3, W1, Mo1, Mo3, Mo6, Y1, Y10, HE, DE> {
|
||||
pub minute1: M1,
|
||||
pub minute5: M5,
|
||||
pub struct Indexes<M10, M30, H1, H4, H12, D1, D3, W1, Mo1, Mo3, Mo6, Y1, Y10, HE, DE> {
|
||||
pub minute10: M10,
|
||||
pub minute30: M30,
|
||||
pub hour1: H1,
|
||||
@@ -38,8 +36,6 @@ pub struct Indexes<M1, M5, M10, M30, H1, H4, H12, D1, D3, W1, Mo1, Mo3, Mo6, Y1,
|
||||
macro_rules! indexes_from {
|
||||
($period:ident, $epoch:ident) => {
|
||||
$crate::internal::Indexes {
|
||||
minute1: $period!(minute1),
|
||||
minute5: $period!(minute5),
|
||||
minute10: $period!(minute10),
|
||||
minute30: $period!(minute30),
|
||||
hour1: $period!(hour1),
|
||||
@@ -63,29 +59,28 @@ macro_rules! indexes_from {
|
||||
};
|
||||
}
|
||||
|
||||
/// Helper macro to apply a function/macro to each field of an `Indexes` value.
|
||||
/// Imperative counterpart to `indexes_from!` — calls `$period!(field)` for each
|
||||
/// period field and `$epoch!(field)` for each epoch field.
|
||||
#[macro_export]
|
||||
macro_rules! indexes_map {
|
||||
($indexes:expr, |$field:ident| $body:expr) => {{
|
||||
let src = $indexes;
|
||||
$crate::internal::Indexes {
|
||||
minute1: { let $field = src.minute1; $body },
|
||||
minute5: { let $field = src.minute5; $body },
|
||||
minute10: { let $field = src.minute10; $body },
|
||||
minute30: { let $field = src.minute30; $body },
|
||||
hour1: { let $field = src.hour1; $body },
|
||||
hour4: { let $field = src.hour4; $body },
|
||||
hour12: { let $field = src.hour12; $body },
|
||||
day1: { let $field = src.day1; $body },
|
||||
day3: { let $field = src.day3; $body },
|
||||
week1: { let $field = src.week1; $body },
|
||||
month1: { let $field = src.month1; $body },
|
||||
month3: { let $field = src.month3; $body },
|
||||
month6: { let $field = src.month6; $body },
|
||||
year1: { let $field = src.year1; $body },
|
||||
year10: { let $field = src.year10; $body },
|
||||
halvingepoch: { let $field = src.halvingepoch; $body },
|
||||
difficultyepoch: { let $field = src.difficultyepoch; $body },
|
||||
}
|
||||
}};
|
||||
macro_rules! indexes_apply {
|
||||
($period:ident, $epoch:ident) => {
|
||||
$period!(minute10);
|
||||
$period!(minute30);
|
||||
$period!(hour1);
|
||||
$period!(hour4);
|
||||
$period!(hour12);
|
||||
$period!(day1);
|
||||
$period!(day3);
|
||||
$period!(week1);
|
||||
$period!(month1);
|
||||
$period!(month3);
|
||||
$period!(month6);
|
||||
$period!(year1);
|
||||
$period!(year10);
|
||||
$epoch!(halvingepoch);
|
||||
$epoch!(difficultyepoch);
|
||||
};
|
||||
($m:ident) => {
|
||||
$crate::indexes_apply!($m, $m)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour4, Hour12, Minute1, Minute5, Minute10,
|
||||
Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour4, Hour12,
|
||||
Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
@@ -22,8 +22,6 @@ use crate::{
|
||||
pub struct LazyEagerIndexes<T, S>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
LazyVecFrom1<Minute1, T, Minute1, S>,
|
||||
LazyVecFrom1<Minute5, T, Minute5, S>,
|
||||
LazyVecFrom1<Minute10, T, Minute10, S>,
|
||||
LazyVecFrom1<Minute30, T, Minute30, S>,
|
||||
LazyVecFrom1<Hour1, T, Hour1, S>,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10,
|
||||
Minute30, Minute5, Month1, Month3, Month6, StoredU64, Week1, Year1, Year10,
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4,
|
||||
Minute10, Minute30, Month1, Month3, Month6, StoredU64, Week1, Year1, Year10,
|
||||
};
|
||||
use vecdb::UnaryTransform;
|
||||
|
||||
use crate::blocks::{
|
||||
TARGET_BLOCKS_PER_DAY, TARGET_BLOCKS_PER_DAY3, TARGET_BLOCKS_PER_DECADE,
|
||||
TARGET_BLOCKS_PER_HALVING, TARGET_BLOCKS_PER_HOUR1, TARGET_BLOCKS_PER_HOUR12,
|
||||
TARGET_BLOCKS_PER_HOUR4, TARGET_BLOCKS_PER_MINUTE1, TARGET_BLOCKS_PER_MINUTE10,
|
||||
TARGET_BLOCKS_PER_MINUTE30, TARGET_BLOCKS_PER_MINUTE5, TARGET_BLOCKS_PER_MONTH,
|
||||
TARGET_BLOCKS_PER_HOUR4, TARGET_BLOCKS_PER_MINUTE10,
|
||||
TARGET_BLOCKS_PER_MINUTE30, TARGET_BLOCKS_PER_MONTH,
|
||||
TARGET_BLOCKS_PER_QUARTER, TARGET_BLOCKS_PER_SEMESTER, TARGET_BLOCKS_PER_WEEK,
|
||||
TARGET_BLOCKS_PER_YEAR,
|
||||
};
|
||||
@@ -22,20 +22,6 @@ impl UnaryTransform<Height, StoredU64> for BlockCountTarget {
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Minute1, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Minute1) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_MINUTE1)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Minute5, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Minute5) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_MINUTE5)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Minute10, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Minute10) -> StoredU64 {
|
||||
|
||||
@@ -3,7 +3,7 @@ use brk_types::StoredU16;
|
||||
use vecdb::{Exit, ReadableVec, VecIndex};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, prices};
|
||||
use crate::{ComputeIndexes, prices, traits::ComputeDrawdown};
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn compute(
|
||||
@@ -63,6 +63,13 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.price_drawdown.height.compute_drawdown(
|
||||
starting_indexes.height,
|
||||
&prices.price.usd.height,
|
||||
&self.price_ath.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ impl Vecs {
|
||||
|(h, close, low, high, ..)| {
|
||||
let range = *high - *low;
|
||||
let stoch = if range == 0.0 {
|
||||
StoredF32::from(50.0)
|
||||
StoredF32::NAN
|
||||
} else {
|
||||
StoredF32::from(((*close - *low) / range * 100.0) as f32)
|
||||
};
|
||||
|
||||
@@ -72,7 +72,7 @@ pub(super) fn compute(
|
||||
.map(|((r, mn), mx)| {
|
||||
let range = mx - mn;
|
||||
if range == 0.0 {
|
||||
50.0
|
||||
f32::NAN
|
||||
} else {
|
||||
(r - mn) / range * 100.0
|
||||
}
|
||||
|
||||
@@ -26,11 +26,7 @@ pub(super) fn compute_ema(source: &[f32], period: usize) -> Vec<f32> {
|
||||
for (i, &val) in source.iter().enumerate() {
|
||||
if i < period {
|
||||
sum += val;
|
||||
if i == period - 1 {
|
||||
result.push(sum / period as f32);
|
||||
} else {
|
||||
result.push(val);
|
||||
}
|
||||
result.push(sum / (i + 1) as f32);
|
||||
} else {
|
||||
let prev = result[i - 1];
|
||||
result.push(val * k + prev * (1.0 - k));
|
||||
|
||||
@@ -102,18 +102,18 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Choppiness index: 100 * log10(tr_2w_sum / (price_2w_max - price_2w_min)) / log10(14)
|
||||
let log10n = 14.0f32.log10();
|
||||
self.price_2w_choppiness_index.height.compute_transform3(
|
||||
self.price_2w_choppiness_index.height.compute_transform4(
|
||||
starting_indexes.height,
|
||||
&self.price_true_range_2w_sum.height,
|
||||
&self.price_2w_max.usd.height,
|
||||
&self.price_2w_min.usd.height,
|
||||
|(h, tr_sum, max, min, ..)| {
|
||||
&blocks.count.height_2w_ago,
|
||||
|(h, tr_sum, max, min, window_start, ..)| {
|
||||
let range = *max - *min;
|
||||
let ci = if range > 0.0 {
|
||||
let n = (h.to_usize() - window_start.to_usize() + 1) as f32;
|
||||
let ci = if range > 0.0 && n > 1.0 {
|
||||
StoredF32::from(
|
||||
100.0 * (*tr_sum / range as f32).log10() / log10n,
|
||||
100.0 * (*tr_sum / range as f32).log10() / n.log10(),
|
||||
)
|
||||
} else {
|
||||
StoredF32::NAN
|
||||
|
||||
@@ -2,7 +2,7 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Cents, Close, Day1, Day3, DifficultyEpoch, HalvingEpoch, High, Hour1, Hour4, Hour12, Low,
|
||||
Minute1, Minute5, Minute10, Minute30, Month1, Month3, Month6, OHLCCents, Open, Version, Week1,
|
||||
Minute10, Minute30, Month1, Month3, Month6, OHLCCents, Open, Version, Week1,
|
||||
Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
@@ -14,7 +14,7 @@ use vecdb::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, indexes_from,
|
||||
ComputeIndexes, indexes_apply, indexes_from,
|
||||
internal::{ComputedHeightDerivedLast, EagerIndexes, Indexes},
|
||||
};
|
||||
|
||||
@@ -25,8 +25,6 @@ use crate::{
|
||||
pub struct OhlcVecs<T, M: StorageMode = Rw>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Minute1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Minute5, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Minute10, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Minute30, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Hour1, T>>>,
|
||||
@@ -132,23 +130,7 @@ impl OhlcVecs<OHLCCents> {
|
||||
};
|
||||
}
|
||||
|
||||
period!(minute1);
|
||||
period!(minute5);
|
||||
period!(minute10);
|
||||
period!(minute30);
|
||||
period!(hour1);
|
||||
period!(hour4);
|
||||
period!(hour12);
|
||||
period!(day1);
|
||||
period!(day3);
|
||||
period!(week1);
|
||||
period!(month1);
|
||||
period!(month3);
|
||||
period!(month6);
|
||||
period!(year1);
|
||||
period!(year10);
|
||||
epoch!(halvingepoch);
|
||||
epoch!(difficultyepoch);
|
||||
indexes_apply!(period, epoch);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -161,8 +143,6 @@ impl OhlcVecs<OHLCCents> {
|
||||
pub struct LazyOhlcVecs<T, S>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
LazyVecFrom1<Minute1, T, Minute1, S>,
|
||||
LazyVecFrom1<Minute5, T, Minute5, S>,
|
||||
LazyVecFrom1<Minute10, T, Minute10, S>,
|
||||
LazyVecFrom1<Minute30, T, Minute30, S>,
|
||||
LazyVecFrom1<Hour1, T, Hour1, S>,
|
||||
|
||||
@@ -9,13 +9,13 @@ use crate::PairOutputIndex;
|
||||
|
||||
use super::{
|
||||
Date, Day1, Day3, Year10, DifficultyEpoch, EmptyAddressIndex, EmptyOutputIndex, HalvingEpoch,
|
||||
Height, Hour1, Hour4, Hour12, FundedAddressIndex, Minute1, Minute5, Minute10, Minute30,
|
||||
Height, Hour1, Hour4, Hour12, FundedAddressIndex, Minute10, Minute30,
|
||||
Month1, OpReturnIndex, P2AAddressIndex, P2MSOutputIndex,
|
||||
P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2TRAddressIndex,
|
||||
P2WPKHAddressIndex, P2WSHAddressIndex, Month3, Month6, Timestamp, TxInIndex, TxIndex,
|
||||
TxOutIndex, UnknownOutputIndex, Week1, Year1,
|
||||
timestamp::INDEX_EPOCH,
|
||||
minute1::MINUTE1_INTERVAL, minute5::MINUTE5_INTERVAL, minute10::MINUTE10_INTERVAL,
|
||||
minute10::MINUTE10_INTERVAL,
|
||||
minute30::MINUTE30_INTERVAL, hour1::HOUR1_INTERVAL, hour4::HOUR4_INTERVAL,
|
||||
hour12::HOUR12_INTERVAL,
|
||||
};
|
||||
@@ -26,8 +26,6 @@ use super::{
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[schemars(example = Index::Day1)]
|
||||
pub enum Index {
|
||||
Minute1,
|
||||
Minute5,
|
||||
Minute10,
|
||||
Minute30,
|
||||
Hour1,
|
||||
@@ -65,10 +63,8 @@ pub enum Index {
|
||||
}
|
||||
|
||||
impl Index {
|
||||
pub const fn all() -> [Self; 36] {
|
||||
pub const fn all() -> [Self; 34] {
|
||||
[
|
||||
Self::Minute1,
|
||||
Self::Minute5,
|
||||
Self::Minute10,
|
||||
Self::Minute30,
|
||||
Self::Hour1,
|
||||
@@ -108,8 +104,6 @@ impl Index {
|
||||
|
||||
pub fn possible_values(&self) -> &'static [&'static str] {
|
||||
match self {
|
||||
Self::Minute1 => Minute1::to_possible_strings(),
|
||||
Self::Minute5 => Minute5::to_possible_strings(),
|
||||
Self::Minute10 => Minute10::to_possible_strings(),
|
||||
Self::Minute30 => Minute30::to_possible_strings(),
|
||||
Self::Hour1 => Hour1::to_possible_strings(),
|
||||
@@ -157,8 +151,6 @@ impl Index {
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Minute1 => <Minute1 as PrintableIndex>::to_string(),
|
||||
Self::Minute5 => <Minute5 as PrintableIndex>::to_string(),
|
||||
Self::Minute10 => <Minute10 as PrintableIndex>::to_string(),
|
||||
Self::Minute30 => <Minute30 as PrintableIndex>::to_string(),
|
||||
Self::Hour1 => <Hour1 as PrintableIndex>::to_string(),
|
||||
@@ -209,9 +201,7 @@ impl Index {
|
||||
pub const fn is_date_based(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Minute1
|
||||
| Self::Minute5
|
||||
| Self::Minute10
|
||||
Self::Minute10
|
||||
| Self::Minute30
|
||||
| Self::Hour1
|
||||
| Self::Hour4
|
||||
@@ -231,8 +221,6 @@ impl Index {
|
||||
/// Returns None for non-time-based indexes.
|
||||
pub fn index_to_timestamp(&self, i: usize) -> Option<Timestamp> {
|
||||
let interval = match self {
|
||||
Self::Minute1 => MINUTE1_INTERVAL,
|
||||
Self::Minute5 => MINUTE5_INTERVAL,
|
||||
Self::Minute10 => MINUTE10_INTERVAL,
|
||||
Self::Minute30 => MINUTE30_INTERVAL,
|
||||
Self::Hour1 => HOUR1_INTERVAL,
|
||||
@@ -269,8 +257,6 @@ impl Index {
|
||||
/// Returns None for non-date-based indexes.
|
||||
pub fn timestamp_to_index(&self, ts: Timestamp) -> Option<usize> {
|
||||
let interval = match self {
|
||||
Self::Minute1 => MINUTE1_INTERVAL,
|
||||
Self::Minute5 => MINUTE5_INTERVAL,
|
||||
Self::Minute10 => MINUTE10_INTERVAL,
|
||||
Self::Minute30 => MINUTE30_INTERVAL,
|
||||
Self::Hour1 => HOUR1_INTERVAL,
|
||||
|
||||
@@ -2,7 +2,7 @@ use derive_more::{Deref, DerefMut};
|
||||
|
||||
use crate::{
|
||||
Day1, Day3, Year10, DifficultyEpoch, EmptyOutputIndex, HalvingEpoch, Height,
|
||||
Hour1, Hour4, Hour12, Minute1, Minute5, Minute10, Minute30, Month1,
|
||||
Hour1, Hour4, Hour12, Minute10, Minute30, Month1,
|
||||
OpReturnIndex, OutputType, P2AAddressIndex, P2MSOutputIndex, P2PK33AddressIndex,
|
||||
P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex,
|
||||
P2WSHAddressIndex, Month3, Month6, TxInIndex, TxIndex, TxOutIndex, TypeIndex,
|
||||
@@ -74,8 +74,6 @@ pub struct ComputeIndexes {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
indexes: Indexes,
|
||||
pub minute1: Minute1,
|
||||
pub minute5: Minute5,
|
||||
pub minute10: Minute10,
|
||||
pub minute30: Minute30,
|
||||
pub hour1: Hour1,
|
||||
@@ -97,8 +95,6 @@ impl ComputeIndexes {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
indexes: Indexes,
|
||||
minute1: Minute1,
|
||||
minute5: Minute5,
|
||||
minute10: Minute10,
|
||||
minute30: Minute30,
|
||||
hour1: Hour1,
|
||||
@@ -117,8 +113,6 @@ impl ComputeIndexes {
|
||||
) -> Self {
|
||||
Self {
|
||||
indexes,
|
||||
minute1,
|
||||
minute5,
|
||||
minute10,
|
||||
minute30,
|
||||
hour1,
|
||||
|
||||
@@ -96,8 +96,6 @@ mod day3;
|
||||
mod hour1;
|
||||
mod hour12;
|
||||
mod hour4;
|
||||
mod minute1;
|
||||
mod minute5;
|
||||
mod minute10;
|
||||
mod minute30;
|
||||
mod month1;
|
||||
@@ -285,8 +283,6 @@ pub use day3::*;
|
||||
pub use hour1::*;
|
||||
pub use hour12::*;
|
||||
pub use hour4::*;
|
||||
pub use minute1::*;
|
||||
pub use minute5::*;
|
||||
pub use minute10::*;
|
||||
pub use minute30::*;
|
||||
pub use month1::*;
|
||||
|
||||
@@ -529,13 +529,6 @@ mod tests {
|
||||
assert_eq!(Index::Hour1.timestamp_to_index(ts), Some(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timestamp_to_index_minute5() {
|
||||
// INDEX_EPOCH + 15 minutes (= 3 * 5min intervals)
|
||||
let ts = Timestamp::new(1230768000 + 900);
|
||||
assert_eq!(Index::Minute5.timestamp_to_index(ts), Some(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timestamp_to_index_non_date_returns_none() {
|
||||
let ts = Timestamp::new(1230768000);
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
use std::ops::Add;
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
|
||||
|
||||
use super::{Timestamp, INDEX_EPOCH};
|
||||
|
||||
pub const MINUTE1_INTERVAL: u32 = 60;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Pco,
|
||||
JsonSchema,
|
||||
)]
|
||||
pub struct Minute1(u32);
|
||||
|
||||
impl Minute1 {
|
||||
pub fn from_timestamp(ts: Timestamp) -> Self {
|
||||
Self((*ts - INDEX_EPOCH) / MINUTE1_INTERVAL)
|
||||
}
|
||||
|
||||
pub fn to_timestamp(&self) -> Timestamp {
|
||||
Timestamp::new(INDEX_EPOCH + self.0 * MINUTE1_INTERVAL)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Minute1> for usize {
|
||||
#[inline]
|
||||
fn from(value: Minute1) -> Self {
|
||||
value.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Minute1 {
|
||||
#[inline]
|
||||
fn from(value: usize) -> Self {
|
||||
Self(value as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for Minute1 {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
Self(self.0 + rhs as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckedSub for Minute1 {
|
||||
fn checked_sub(self, rhs: Self) -> Option<Self> {
|
||||
self.0.checked_sub(rhs.0).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableIndex for Minute1 {
|
||||
fn to_string() -> &'static str {
|
||||
"minute1"
|
||||
}
|
||||
|
||||
fn to_possible_strings() -> &'static [&'static str] {
|
||||
&["1mn", "minute1"]
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Minute1 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut buf = itoa::Buffer::new();
|
||||
let str = buf.format(self.0);
|
||||
f.write_str(str)
|
||||
}
|
||||
}
|
||||
|
||||
impl Formattable for Minute1 {
|
||||
#[inline(always)]
|
||||
fn fmt_csv(&self, f: &mut String) -> std::fmt::Result {
|
||||
use std::fmt::Write;
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
use std::ops::Add;
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
|
||||
|
||||
use super::{Timestamp, INDEX_EPOCH};
|
||||
|
||||
pub const MINUTE5_INTERVAL: u32 = 300;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Pco,
|
||||
JsonSchema,
|
||||
)]
|
||||
pub struct Minute5(u32);
|
||||
|
||||
impl Minute5 {
|
||||
pub fn from_timestamp(ts: Timestamp) -> Self {
|
||||
Self((*ts - INDEX_EPOCH) / MINUTE5_INTERVAL)
|
||||
}
|
||||
|
||||
pub fn to_timestamp(&self) -> Timestamp {
|
||||
Timestamp::new(INDEX_EPOCH + self.0 * MINUTE5_INTERVAL)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Minute5> for usize {
|
||||
#[inline]
|
||||
fn from(value: Minute5) -> Self {
|
||||
value.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Minute5 {
|
||||
#[inline]
|
||||
fn from(value: usize) -> Self {
|
||||
Self(value as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for Minute5 {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
Self(self.0 + rhs as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckedSub for Minute5 {
|
||||
fn checked_sub(self, rhs: Self) -> Option<Self> {
|
||||
self.0.checked_sub(rhs.0).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableIndex for Minute5 {
|
||||
fn to_string() -> &'static str {
|
||||
"minute5"
|
||||
}
|
||||
|
||||
fn to_possible_strings() -> &'static [&'static str] {
|
||||
&["5mn", "minute5"]
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Minute5 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut buf = itoa::Buffer::new();
|
||||
let str = buf.format(self.0);
|
||||
f.write_str(str)
|
||||
}
|
||||
}
|
||||
|
||||
impl Formattable for Minute5 {
|
||||
#[inline(always)]
|
||||
fn fmt_csv(&self, f: &mut String) -> std::fmt::Result {
|
||||
use std::fmt::Write;
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -67,20 +67,6 @@ def hour1_metric():
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def minute5_metric():
|
||||
"""DateMetricData with minute5 (sub-daily)."""
|
||||
return DateMetricData(
|
||||
version=1,
|
||||
index="minute5",
|
||||
total=500000,
|
||||
start=0,
|
||||
end=3,
|
||||
stamp="2024-01-01T00:00:00Z",
|
||||
data=[1, 2, 3],
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def week1_metric():
|
||||
"""DateMetricData with week1."""
|
||||
@@ -150,9 +136,6 @@ class TestIsDateBased:
|
||||
def test_hour1(self, hour1_metric):
|
||||
assert hour1_metric.is_date_based is True
|
||||
|
||||
def test_minute5(self, minute5_metric):
|
||||
assert minute5_metric.is_date_based is True
|
||||
|
||||
def test_week1(self, week1_metric):
|
||||
assert week1_metric.is_date_based is True
|
||||
|
||||
@@ -304,13 +287,6 @@ class TestIndexToDate:
|
||||
assert dates[1] == datetime(2009, 1, 1, 1, 0, 0, tzinfo=timezone.utc)
|
||||
assert dates[2] == datetime(2009, 1, 1, 2, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
def test_minute5_returns_datetime(self, minute5_metric):
|
||||
dates = minute5_metric.dates()
|
||||
assert isinstance(dates[0], datetime)
|
||||
assert dates[0] == datetime(2009, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
|
||||
assert dates[1] == datetime(2009, 1, 1, 0, 5, 0, tzinfo=timezone.utc)
|
||||
assert dates[2] == datetime(2009, 1, 1, 0, 10, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
# ============ _date_to_index conversions ============
|
||||
|
||||
@@ -359,13 +335,6 @@ class TestDateToIndex:
|
||||
assert _date_to_index("hour1", epoch + timedelta(hours=1)) == 1
|
||||
assert _date_to_index("hour1", epoch + timedelta(hours=24)) == 24
|
||||
|
||||
def test_minute5_with_datetime(self):
|
||||
from brk_client import _date_to_index
|
||||
epoch = datetime(2009, 1, 1, tzinfo=timezone.utc)
|
||||
assert _date_to_index("minute5", epoch) == 0
|
||||
assert _date_to_index("minute5", epoch + timedelta(minutes=5)) == 1
|
||||
assert _date_to_index("minute5", epoch + timedelta(minutes=12)) == 2 # floor
|
||||
|
||||
def test_hour1_with_plain_date(self):
|
||||
"""Plain date is treated as midnight UTC for sub-daily."""
|
||||
from brk_client import _date_to_index
|
||||
|
||||
@@ -118,7 +118,7 @@ const ALL_GROUPS = [
|
||||
{
|
||||
label: "Time",
|
||||
items: [
|
||||
"1mn", "5mn", "10mn", "30mn",
|
||||
"10mn", "30mn",
|
||||
"1h", "4h", "12h",
|
||||
"1d", "3d", "1w",
|
||||
"1m", "3m", "6m",
|
||||
|
||||
@@ -21,7 +21,7 @@ export const serdeBool = {
|
||||
|
||||
export const INDEX_LABEL = /** @type {const} */ ({
|
||||
height: "blk",
|
||||
minute1: "1mn", minute5: "5mn", minute10: "10mn", minute30: "30mn",
|
||||
minute10: "10mn", minute30: "30mn",
|
||||
hour1: "1h", hour4: "4h", hour12: "12h",
|
||||
day1: "1d", day3: "3d", week1: "1w",
|
||||
month1: "1m", month3: "3m", month6: "6m",
|
||||
|
||||
Reference in New Issue
Block a user