global: snapshot

This commit is contained in:
nym21
2026-03-01 12:46:07 +01:00
parent e10013fd2c
commit 7bf0220f25
35 changed files with 1450 additions and 2044 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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 (minute1year10) are lazy: `idx.to_timestamp()` is a pure
/// Time-based periods (minute10year10) 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>,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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)?,

View File

@@ -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)?,
})
}
}

View File

@@ -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)?,
})
}
}

View File

@@ -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,

View File

@@ -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(())
}

View File

@@ -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),

View File

@@ -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>,

View File

@@ -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>>,

View File

@@ -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)
};
}

View File

@@ -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>,

View File

@@ -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 {

View File

@@ -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(())
}
}

View File

@@ -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)
};

View File

@@ -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
}

View File

@@ -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));

View File

@@ -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

View File

@@ -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>,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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::*;

View File

@@ -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);

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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",

View File

@@ -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",