mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-09 06:31:57 -07:00
pricer: snapshot
This commit is contained in:
@@ -29,6 +29,7 @@ docker/kibo
|
||||
|
||||
# Types
|
||||
paths.d.ts
|
||||
vecid-to-indexes.d.ts
|
||||
|
||||
# Outputs
|
||||
_outputs
|
||||
|
||||
Generated
+1
@@ -2118,6 +2118,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"storable_vec",
|
||||
"zerocopy 0.8.17",
|
||||
]
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
mod addressindextxoutindex;
|
||||
mod bitcoin;
|
||||
mod feerate;
|
||||
mod ohlc;
|
||||
// mod ohlc;
|
||||
mod unit;
|
||||
|
||||
pub use addressindextxoutindex::*;
|
||||
pub use bitcoin::*;
|
||||
pub use feerate::*;
|
||||
pub use ohlc::*;
|
||||
// pub use ohlc::*;
|
||||
pub use unit::*;
|
||||
|
||||
+9
-1
@@ -8,8 +8,16 @@ color-eyre = { workspace = true }
|
||||
derive_deref = { workspace = true }
|
||||
indexer = { workspace = true }
|
||||
logger = { workspace = true }
|
||||
reqwest = { version = "0.12.12", features = ["blocking", "brotli", "deflate", "gzip", "json", "zstd"] }
|
||||
reqwest = { version = "0.12.12", features = [
|
||||
"blocking",
|
||||
"brotli",
|
||||
"deflate",
|
||||
"gzip",
|
||||
"json",
|
||||
"zstd",
|
||||
] }
|
||||
jiff = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
storable_vec = { workspace = true }
|
||||
zerocopy = { workspace = true }
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{
|
||||
collections::BTreeMap,
|
||||
fs::{self, File},
|
||||
io::BufReader,
|
||||
path::Path,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
@@ -10,17 +10,48 @@ use color_eyre::eyre::{eyre, ContextCompat};
|
||||
use indexer::Timestamp;
|
||||
use logger::info;
|
||||
use serde_json::Value;
|
||||
use storable_vec::STATELESS;
|
||||
|
||||
use crate::{
|
||||
fetchers::retry,
|
||||
structs::{Cents, OHLC},
|
||||
Close, Date, Dollars, High, Low, Open,
|
||||
Close, Date, Dollars, High, Low, Open, Pricer,
|
||||
};
|
||||
|
||||
pub struct Binance;
|
||||
pub struct Binance {
|
||||
path: PathBuf,
|
||||
_1mn: Option<BTreeMap<Timestamp, OHLC>>,
|
||||
_1d: Option<BTreeMap<Date, OHLC>>,
|
||||
har: Option<BTreeMap<Timestamp, OHLC>>,
|
||||
}
|
||||
|
||||
impl Binance {
|
||||
pub fn fetch_1mn_prices() -> color_eyre::Result<BTreeMap<Timestamp, OHLC>> {
|
||||
pub fn init(path: &Path) -> Self {
|
||||
Self {
|
||||
path: path.to_owned(),
|
||||
_1mn: None,
|
||||
_1d: None,
|
||||
har: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_from_1mn(
|
||||
&mut self,
|
||||
timestamp: Timestamp,
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
) -> color_eyre::Result<OHLC> {
|
||||
if self._1mn.is_none() || self._1mn.as_ref().unwrap().last_key_value().unwrap().0 <= ×tamp {
|
||||
self._1mn.replace(Self::fetch_1mn()?);
|
||||
}
|
||||
Pricer::<STATELESS>::find_height_ohlc(
|
||||
&self._1mn.as_ref().unwrap(),
|
||||
timestamp,
|
||||
previous_timestamp,
|
||||
"binance 1mn",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fetch_1mn() -> color_eyre::Result<BTreeMap<Timestamp, OHLC>> {
|
||||
info!("Fetching 1mn prices from Binance...");
|
||||
|
||||
retry(
|
||||
@@ -30,7 +61,20 @@ impl Binance {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fetch_daily_prices() -> color_eyre::Result<BTreeMap<Date, OHLC>> {
|
||||
pub fn get_from_1d(&mut self, date: &Date) -> color_eyre::Result<OHLC> {
|
||||
if self._1d.is_none() || self._1d.as_ref().unwrap().last_key_value().unwrap().0 < date {
|
||||
self._1d.replace(Self::fetch_1d()?);
|
||||
}
|
||||
|
||||
self._1d
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(date)
|
||||
.cloned()
|
||||
.ok_or(color_eyre::eyre::Error::msg("Couldn't find date"))
|
||||
}
|
||||
|
||||
fn fetch_1d() -> color_eyre::Result<BTreeMap<Date, OHLC>> {
|
||||
info!("Fetching daily prices from Kraken...");
|
||||
|
||||
retry(
|
||||
@@ -40,10 +84,28 @@ impl Binance {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn read_har_file(path: &Path) -> color_eyre::Result<BTreeMap<Timestamp, OHLC>> {
|
||||
pub fn get_from_har_binance(
|
||||
&mut self,
|
||||
timestamp: Timestamp,
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
) -> color_eyre::Result<OHLC> {
|
||||
if self.har.is_none() {
|
||||
self.har.replace(self.read_har().unwrap_or_default());
|
||||
}
|
||||
Pricer::<STATELESS>::find_height_ohlc(
|
||||
&self.har.as_ref().unwrap(),
|
||||
timestamp,
|
||||
previous_timestamp,
|
||||
"binance har",
|
||||
)
|
||||
}
|
||||
|
||||
fn read_har(&self) -> color_eyre::Result<BTreeMap<Timestamp, OHLC>> {
|
||||
info!("Reading Binance har file...");
|
||||
|
||||
fs::create_dir_all(&path)?;
|
||||
let path = &self.path;
|
||||
|
||||
fs::create_dir_all(path)?;
|
||||
|
||||
let path_binance_har = path.join("binance.har");
|
||||
|
||||
|
||||
+75
-27
@@ -1,12 +1,21 @@
|
||||
use std::{collections::BTreeMap, str::FromStr};
|
||||
|
||||
use color_eyre::eyre::ContextCompat;
|
||||
use indexer::Height;
|
||||
use logger::info;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::structs::{Date, OHLC};
|
||||
use crate::{
|
||||
fetchers::retry,
|
||||
structs::{Date, OHLC},
|
||||
Cents, Close, Dollars, High, Low, Open,
|
||||
};
|
||||
|
||||
pub struct Kibo;
|
||||
#[derive(Default)]
|
||||
pub struct Kibo {
|
||||
height_to_ohlc_vec: BTreeMap<Height, Vec<OHLC>>,
|
||||
year_to_date_to_ohlc: BTreeMap<u16, BTreeMap<Date, OHLC>>,
|
||||
}
|
||||
|
||||
const KIBO_OFFICIAL_URL: &str = "https://kibo.money/api";
|
||||
const KIBO_OFFICIAL_BACKUP_URL: &str = "https://backup.kibo.money/api";
|
||||
@@ -22,16 +31,32 @@ impl Kibo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fetch_height_prices(chunk_id: HeightMapChunkId) -> color_eyre::Result<Vec<OHLC>> {
|
||||
info!("kibo: fetch height prices");
|
||||
pub fn get_from_height_kibo(&mut self, height: Height) -> color_eyre::Result<OHLC> {
|
||||
#[allow(clippy::map_entry)]
|
||||
if !self.height_to_ohlc_vec.contains_key(&height)
|
||||
|| ((usize::from(height) + self.height_to_ohlc_vec.get(&height).unwrap().len()) <= usize::from(height))
|
||||
{
|
||||
self.height_to_ohlc_vec
|
||||
.insert(height, Self::fetch_height_prices(height)?);
|
||||
}
|
||||
|
||||
self.height_to_ohlc_vec
|
||||
.get(&height)
|
||||
.unwrap()
|
||||
.get(usize::from(height))
|
||||
.cloned()
|
||||
.ok_or(color_eyre::eyre::Error::msg("Couldn't find height in kibo"))
|
||||
}
|
||||
|
||||
fn fetch_height_prices(height: Height) -> color_eyre::Result<Vec<OHLC>> {
|
||||
info!("Fetching Kibo height prices...");
|
||||
|
||||
retry(
|
||||
|try_index| {
|
||||
let base_url = Self::get_base_url(try_index);
|
||||
|
||||
let body: Value =
|
||||
reqwest::blocking::get(format!("{base_url}/height-to-price?chunk={}", chunk_id.to_usize()))?
|
||||
.json()?;
|
||||
reqwest::blocking::get(format!("{base_url}/height-to-price?chunk={}", height))?.json()?;
|
||||
|
||||
let vec = body
|
||||
.as_object()
|
||||
@@ -55,19 +80,41 @@ impl Kibo {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fetch_date_prices(chunk_id: DateMapChunkId) -> color_eyre::Result<BTreeMap<Date, OHLC>> {
|
||||
info!("kibo: fetch date prices");
|
||||
pub fn get_from_date_kibo(&mut self, date: &Date) -> color_eyre::Result<OHLC> {
|
||||
let year = date.year();
|
||||
|
||||
#[allow(clippy::map_entry)]
|
||||
if !self.year_to_date_to_ohlc.contains_key(&year)
|
||||
|| self
|
||||
.year_to_date_to_ohlc
|
||||
.get(&year)
|
||||
.unwrap()
|
||||
.last_key_value()
|
||||
.unwrap()
|
||||
.0
|
||||
< date
|
||||
{
|
||||
self.year_to_date_to_ohlc.insert(year, Self::fetch_date_prices(year)?);
|
||||
}
|
||||
|
||||
self.year_to_date_to_ohlc
|
||||
.get(&year)
|
||||
.unwrap()
|
||||
.get(date)
|
||||
.cloned()
|
||||
.ok_or(color_eyre::eyre::Error::msg("Couldn't find date in kibo"))
|
||||
}
|
||||
|
||||
fn fetch_date_prices(year: u16) -> color_eyre::Result<BTreeMap<Date, OHLC>> {
|
||||
info!("Fetching Kibo date prices...");
|
||||
|
||||
retry(
|
||||
|try_index| {
|
||||
let base_url = Self::get_base_url(try_index);
|
||||
|
||||
let body: Value =
|
||||
reqwest::blocking::get(format!("{base_url}/date-to-price?chunk={}", chunk_id.to_usize()))?
|
||||
.json()?;
|
||||
let body: Value = reqwest::blocking::get(format!("{base_url}/date-to-price?chunk={}", year))?.json()?;
|
||||
|
||||
Ok(body
|
||||
.as_object()
|
||||
body.as_object()
|
||||
.context("Expect to be an object")?
|
||||
.get("dataset")
|
||||
.context("Expect object to have dataset")?
|
||||
@@ -79,10 +126,10 @@ impl Kibo {
|
||||
.context("Expect to be an object")?
|
||||
.iter()
|
||||
.map(|(serialized_date, value)| -> color_eyre::Result<_> {
|
||||
let date = Date::wrap(NaiveDate::from_str(serialized_date)?);
|
||||
let date = Date::from(jiff::civil::Date::from_str(serialized_date).unwrap());
|
||||
Ok((date, Self::value_to_ohlc(value)?))
|
||||
})
|
||||
.collect::<Result<BTreeMap<_, _>, _>>()?)
|
||||
.collect::<Result<BTreeMap<_, _>, _>>()
|
||||
},
|
||||
30,
|
||||
RETRIES,
|
||||
@@ -92,19 +139,20 @@ impl Kibo {
|
||||
fn value_to_ohlc(value: &Value) -> color_eyre::Result<OHLC> {
|
||||
let ohlc = value.as_object().context("Expect as_object to work")?;
|
||||
|
||||
let get_value = |key: &str| -> color_eyre::Result<f32> {
|
||||
Ok(ohlc
|
||||
.get(key)
|
||||
.context("Expect get key to work")?
|
||||
.as_f64()
|
||||
.context("Expect as_f64 to work")? as f32)
|
||||
let get_value = |key: &str| -> color_eyre::Result<_> {
|
||||
Ok(Cents::from(Dollars::from(
|
||||
ohlc.get(key)
|
||||
.context("Expect get key to work")?
|
||||
.as_f64()
|
||||
.context("Expect as_f64 to work")?,
|
||||
)))
|
||||
};
|
||||
|
||||
Ok(OHLC {
|
||||
open: get_value("open")?,
|
||||
high: get_value("high")?,
|
||||
low: get_value("low")?,
|
||||
close: get_value("close")?,
|
||||
})
|
||||
Ok((
|
||||
Open::from(get_value("open")?),
|
||||
High::from(get_value("high")?),
|
||||
Low::from(get_value("low")?),
|
||||
Close::from(get_value("close")?),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,29 @@ use color_eyre::eyre::ContextCompat;
|
||||
use indexer::Timestamp;
|
||||
use logger::info;
|
||||
use serde_json::Value;
|
||||
use storable_vec::STATELESS;
|
||||
|
||||
use crate::{fetchers::retry, structs::Date, Cents, Close, Dollars, High, Low, Open, OHLC};
|
||||
use crate::{fetchers::retry, structs::Date, Cents, Close, Dollars, High, Low, Open, Pricer, OHLC};
|
||||
|
||||
pub struct Kraken;
|
||||
#[derive(Default)]
|
||||
pub struct Kraken {
|
||||
_1mn: Option<BTreeMap<Timestamp, OHLC>>,
|
||||
_1d: Option<BTreeMap<Date, OHLC>>,
|
||||
}
|
||||
|
||||
impl Kraken {
|
||||
pub fn fetch_1mn_prices() -> color_eyre::Result<BTreeMap<Timestamp, OHLC>> {
|
||||
pub fn get_from_1mn(
|
||||
&mut self,
|
||||
timestamp: Timestamp,
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
) -> color_eyre::Result<OHLC> {
|
||||
if self._1mn.is_none() || self._1mn.as_ref().unwrap().last_key_value().unwrap().0 <= ×tamp {
|
||||
self._1mn.replace(Self::fetch_1mn()?);
|
||||
}
|
||||
Pricer::<STATELESS>::find_height_ohlc(&self._1mn.as_ref().unwrap(), timestamp, previous_timestamp, "kraken 1m")
|
||||
}
|
||||
|
||||
fn fetch_1mn() -> color_eyre::Result<BTreeMap<Timestamp, OHLC>> {
|
||||
info!("Fetching 1mn prices from Kraken...");
|
||||
|
||||
retry(
|
||||
@@ -20,7 +36,19 @@ impl Kraken {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fetch_daily_prices() -> color_eyre::Result<BTreeMap<Date, OHLC>> {
|
||||
pub fn get_from_1d(&mut self, date: &Date) -> color_eyre::Result<OHLC> {
|
||||
if self._1d.is_none() || self._1d.as_ref().unwrap().last_key_value().unwrap().0 < date {
|
||||
self._1d.replace(Kraken::fetch_1d()?);
|
||||
}
|
||||
self._1d
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(date)
|
||||
.cloned()
|
||||
.ok_or(color_eyre::eyre::Error::msg("Couldn't find date"))
|
||||
}
|
||||
|
||||
fn fetch_1d() -> color_eyre::Result<BTreeMap<Date, OHLC>> {
|
||||
info!("Fetching daily prices from Kraken...");
|
||||
|
||||
retry(
|
||||
|
||||
@@ -1,5 +1,277 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use color_eyre::eyre::Error;
|
||||
|
||||
mod fetchers;
|
||||
mod structs;
|
||||
|
||||
pub use fetchers::*;
|
||||
use indexer::{Height, Indexer, Timestamp};
|
||||
use storable_vec::{AnyJsonStorableVec, AnyStorableVec, StorableVec, Version, SINGLE_THREAD};
|
||||
pub use structs::*;
|
||||
|
||||
pub struct Pricer<const MODE: u8> {
|
||||
path: PathBuf,
|
||||
binance: Binance,
|
||||
kraken: Kraken,
|
||||
kibo: Kibo,
|
||||
|
||||
pub dateindex_to_close_in_cents: StorableVec<Dateindex, Close<Cents>, MODE>,
|
||||
pub dateindex_to_close_in_dollars: StorableVec<Dateindex, Close<Dollars>, MODE>,
|
||||
pub dateindex_to_high_in_cents: StorableVec<Dateindex, High<Cents>, MODE>,
|
||||
pub dateindex_to_high_in_dollars: StorableVec<Dateindex, High<Dollars>, MODE>,
|
||||
pub dateindex_to_low_in_cents: StorableVec<Dateindex, Low<Cents>, MODE>,
|
||||
pub dateindex_to_low_in_dollars: StorableVec<Dateindex, Low<Dollars>, MODE>,
|
||||
pub dateindex_to_open_in_cents: StorableVec<Dateindex, Open<Cents>, MODE>,
|
||||
pub dateindex_to_open_in_dollars: StorableVec<Dateindex, Open<Dollars>, MODE>,
|
||||
pub height_to_close_in_cents: StorableVec<Height, Close<Cents>, MODE>,
|
||||
pub height_to_close_in_dollars: StorableVec<Height, Close<Dollars>, MODE>,
|
||||
pub height_to_high_in_cents: StorableVec<Height, High<Cents>, MODE>,
|
||||
pub height_to_high_in_dollars: StorableVec<Height, High<Dollars>, MODE>,
|
||||
pub height_to_low_in_cents: StorableVec<Height, Low<Cents>, MODE>,
|
||||
pub height_to_low_in_dollars: StorableVec<Height, Low<Dollars>, MODE>,
|
||||
pub height_to_open_in_cents: StorableVec<Height, Open<Cents>, MODE>,
|
||||
pub height_to_open_in_dollars: StorableVec<Height, Open<Dollars>, MODE>,
|
||||
}
|
||||
|
||||
impl<const MODE: u8> Pricer<MODE> {
|
||||
pub fn import(path: &Path) -> color_eyre::Result<Self> {
|
||||
fs::create_dir_all(path)?;
|
||||
|
||||
Ok(Self {
|
||||
path: path.to_owned(),
|
||||
binance: Binance::init(path),
|
||||
kraken: Kraken::default(),
|
||||
kibo: Kibo::default(),
|
||||
|
||||
// binance_1mn: None,
|
||||
// binance_daily: None,
|
||||
// binance_har: None,
|
||||
// kraken_1mn: None,
|
||||
// kraken_daily: None,
|
||||
// kibo_by_height: BTreeMap::default(),
|
||||
// kibo_by_date: BTreeMap::default(),
|
||||
dateindex_to_close_in_cents: StorableVec::import(
|
||||
&path.join("dateindex_to_close_in_cents"),
|
||||
Version::from(1),
|
||||
)?,
|
||||
dateindex_to_close_in_dollars: StorableVec::import(
|
||||
&path.join("dateindex_to_close_in_dollars"),
|
||||
Version::from(1),
|
||||
)?,
|
||||
dateindex_to_high_in_cents: StorableVec::import(
|
||||
&path.join("dateindex_to_high_in_cents"),
|
||||
Version::from(1),
|
||||
)?,
|
||||
dateindex_to_high_in_dollars: StorableVec::import(
|
||||
&path.join("dateindex_to_high_in_dollars"),
|
||||
Version::from(1),
|
||||
)?,
|
||||
dateindex_to_low_in_cents: StorableVec::import(&path.join("dateindex_to_low_in_cents"), Version::from(1))?,
|
||||
dateindex_to_low_in_dollars: StorableVec::import(
|
||||
&path.join("dateindex_to_low_in_dollars"),
|
||||
Version::from(1),
|
||||
)?,
|
||||
dateindex_to_open_in_cents: StorableVec::import(
|
||||
&path.join("dateindex_to_open_in_cents"),
|
||||
Version::from(1),
|
||||
)?,
|
||||
dateindex_to_open_in_dollars: StorableVec::import(
|
||||
&path.join("dateindex_to_open_in_dollars"),
|
||||
Version::from(1),
|
||||
)?,
|
||||
height_to_close_in_cents: StorableVec::import(&path.join("height_to_close_in_cents"), Version::from(1))?,
|
||||
height_to_close_in_dollars: StorableVec::import(
|
||||
&path.join("height_to_close_in_dollars"),
|
||||
Version::from(1),
|
||||
)?,
|
||||
height_to_high_in_cents: StorableVec::import(&path.join("height_to_high_in_cents"), Version::from(1))?,
|
||||
height_to_high_in_dollars: StorableVec::import(&path.join("height_to_high_in_dollars"), Version::from(1))?,
|
||||
height_to_low_in_cents: StorableVec::import(&path.join("height_to_low_in_cents"), Version::from(1))?,
|
||||
height_to_low_in_dollars: StorableVec::import(&path.join("height_to_low_in_dollars"), Version::from(1))?,
|
||||
height_to_open_in_cents: StorableVec::import(&path.join("height_to_open_in_cents"), Version::from(1))?,
|
||||
height_to_open_in_dollars: StorableVec::import(&path.join("height_to_open_in_dollars"), Version::from(1))?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_if_needed(&mut self, indexer: &mut Indexer<SINGLE_THREAD>) {
|
||||
// TODO: Remove all outdated
|
||||
|
||||
indexer
|
||||
.vecs
|
||||
.height_to_timestamp
|
||||
.iter_from(Height::default(), |v| Ok(()));
|
||||
|
||||
// self.open
|
||||
// .multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.open);
|
||||
|
||||
// self.high
|
||||
// .multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.high);
|
||||
|
||||
// self.low
|
||||
// .multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.low);
|
||||
|
||||
// self.close
|
||||
// .multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.close);
|
||||
}
|
||||
|
||||
fn get_date_ohlc(&mut self, date: Date) -> color_eyre::Result<OHLC> {
|
||||
if self.ohlc.date.is_key_safe(date) {
|
||||
Ok(self.ohlc.date.get_or_import(&date).unwrap().to_owned())
|
||||
} else {
|
||||
let ohlc = self
|
||||
.get_from_daily_kraken(&date)
|
||||
.or_else(|_| self.get_from_daily_binance(&date))
|
||||
.or_else(|_| self.get_from_date_kibo(&date))?;
|
||||
|
||||
self.ohlc.date.insert(date, ohlc);
|
||||
|
||||
Ok(ohlc)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_height_ohlc(
|
||||
&mut self,
|
||||
height: Height,
|
||||
timestamp: Timestamp,
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
) -> color_eyre::Result<OHLC> {
|
||||
if let Some(ohlc) = self.ohlc.height.get_or_import(&height) {
|
||||
return Ok(ohlc);
|
||||
}
|
||||
|
||||
let timestamp = timestamp.to_floored_seconds();
|
||||
|
||||
if previous_timestamp.is_none() && !height.is_first() {
|
||||
panic!("Shouldn't be possible");
|
||||
}
|
||||
|
||||
let previous_timestamp = previous_timestamp.map(|t| t.to_floored_seconds());
|
||||
|
||||
let ohlc = self
|
||||
.get_from_1mn_kraken(timestamp, previous_timestamp)
|
||||
.unwrap_or_else(|_| {
|
||||
self.get_from_1mn_binance(timestamp, previous_timestamp)
|
||||
.unwrap_or_else(|_| {
|
||||
self.get_from_har_binance(timestamp, previous_timestamp, config)
|
||||
.unwrap_or_else(|_| {
|
||||
self.get_from_height_kibo(&height).unwrap_or_else(|_| {
|
||||
let date = timestamp.to_date();
|
||||
|
||||
panic!(
|
||||
"Can't find the price for: height: {height} - date: {date}
|
||||
1mn APIs are limited to the last 16 hours for Binance's and the last 10 hours for Kraken's
|
||||
How to fix this:
|
||||
1. Go to https://www.binance.com/en/trade/BTC_USDT?type=spot
|
||||
2. Select 1mn interval
|
||||
3. Open the inspector/dev tools
|
||||
4. Go to the Network Tab
|
||||
5. Filter URLs by 'uiKlines'
|
||||
6. Go back to the chart and scroll until you pass the date mentioned few lines ago
|
||||
7. Go back to the dev tools
|
||||
8. Export to a har file (if there is no explicit button, click on the cog button)
|
||||
9. Move the file to 'parser/imports/binance.har'
|
||||
"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// self.ohlc.height.insert(height, ohlc);
|
||||
|
||||
Ok(ohlc)
|
||||
}
|
||||
|
||||
fn find_height_ohlc(
|
||||
tree: &BTreeMap<Timestamp, OHLC>,
|
||||
timestamp: Timestamp,
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
name: &str,
|
||||
) -> color_eyre::Result<OHLC> {
|
||||
let previous_ohlc = previous_timestamp.map_or(Some(OHLC::default()), |previous_timestamp| {
|
||||
tree.get(&previous_timestamp).cloned()
|
||||
});
|
||||
|
||||
let last_ohlc = tree.get(×tamp);
|
||||
|
||||
if previous_ohlc.is_none() || last_ohlc.is_none() {
|
||||
return Err(Error::msg(format!("Couldn't find timestamp in {name}")));
|
||||
}
|
||||
|
||||
let previous_ohlc = previous_ohlc.unwrap();
|
||||
|
||||
let mut final_ohlc = (
|
||||
Open::from(previous_ohlc.3),
|
||||
High::from(previous_ohlc.3),
|
||||
Low::from(previous_ohlc.3),
|
||||
previous_ohlc.3,
|
||||
);
|
||||
|
||||
let start = previous_timestamp.unwrap_or(Timestamp::from(0));
|
||||
let end = timestamp;
|
||||
|
||||
// Otherwise it's a re-org
|
||||
if start < end {
|
||||
tree.range(start..=end).skip(1).for_each(|(_, ohlc)| {
|
||||
if ohlc.1 > final_ohlc.1 {
|
||||
final_ohlc.1 = ohlc.1
|
||||
}
|
||||
|
||||
if ohlc.2 < final_ohlc.2 {
|
||||
final_ohlc.2 = ohlc.2
|
||||
}
|
||||
|
||||
final_ohlc.3 = ohlc.3;
|
||||
});
|
||||
}
|
||||
|
||||
Ok(final_ohlc)
|
||||
}
|
||||
|
||||
pub fn as_any_json_vec_slice(&self) -> [&dyn AnyJsonStorableVec; 16] {
|
||||
[
|
||||
&self.dateindex_to_close_in_cents as &dyn AnyJsonStorableVec,
|
||||
&self.dateindex_to_close_in_dollars,
|
||||
&self.dateindex_to_high_in_cents,
|
||||
&self.dateindex_to_high_in_dollars,
|
||||
&self.dateindex_to_low_in_cents,
|
||||
&self.dateindex_to_low_in_dollars,
|
||||
&self.dateindex_to_open_in_cents,
|
||||
&self.dateindex_to_open_in_dollars,
|
||||
&self.height_to_close_in_cents,
|
||||
&self.height_to_close_in_dollars,
|
||||
&self.height_to_high_in_cents,
|
||||
&self.height_to_high_in_dollars,
|
||||
&self.height_to_low_in_cents,
|
||||
&self.height_to_low_in_dollars,
|
||||
&self.height_to_open_in_cents,
|
||||
&self.height_to_open_in_dollars,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn as_mut_any_vec_slice(&mut self) -> [&mut dyn AnyStorableVec; 16] {
|
||||
[
|
||||
&mut self.dateindex_to_close_in_cents as &mut dyn AnyStorableVec,
|
||||
&mut self.dateindex_to_close_in_dollars,
|
||||
&mut self.dateindex_to_high_in_cents,
|
||||
&mut self.dateindex_to_high_in_dollars,
|
||||
&mut self.dateindex_to_low_in_cents,
|
||||
&mut self.dateindex_to_low_in_dollars,
|
||||
&mut self.dateindex_to_open_in_cents,
|
||||
&mut self.dateindex_to_open_in_dollars,
|
||||
&mut self.height_to_close_in_cents,
|
||||
&mut self.height_to_close_in_dollars,
|
||||
&mut self.height_to_high_in_cents,
|
||||
&mut self.height_to_high_in_dollars,
|
||||
&mut self.height_to_low_in_cents,
|
||||
&mut self.height_to_low_in_dollars,
|
||||
&mut self.height_to_open_in_cents,
|
||||
&mut self.height_to_open_in_dollars,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+7
-4
@@ -1,16 +1,19 @@
|
||||
// fn main() {}
|
||||
|
||||
use pricer::{Binance, Kraken};
|
||||
use indexer::Height;
|
||||
use pricer::{Binance, Kibo, Kraken};
|
||||
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
|
||||
logger::init_log(None);
|
||||
|
||||
// dbg!(Binance::fetch_daily_prices());
|
||||
dbg!(Binance::fetch_1d_prices()?);
|
||||
// dbg!(Binance::fetch_1mn_prices());
|
||||
// dbg!(Kraken::fetch_daily_prices());
|
||||
dbg!(Kraken::fetch_1mn_prices());
|
||||
dbg!(Kraken::fetch_1d()?);
|
||||
// dbg!(Kraken::fetch_1mn_prices()?);
|
||||
dbg!(Kibo::fetch_date_prices(2025)?);
|
||||
dbg!(Kibo::fetch_height_prices(Height::from(880_000_u32))?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,316 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use allocative::Allocative;
|
||||
use chrono::Days;
|
||||
use color_eyre::eyre::Error;
|
||||
|
||||
use struct_iterable::Iterable;
|
||||
|
||||
use crate::{
|
||||
parser::price::{Binance, Kibo, Kraken},
|
||||
structs::{
|
||||
Amount, BiMap, Config, Date, DateMap, DateMapChunkId, Height, HeightMapChunkId, MapKey, MapKind, Timestamp,
|
||||
OHLC,
|
||||
},
|
||||
utils::{ONE_MONTH_IN_DAYS, ONE_WEEK_IN_DAYS, ONE_YEAR_IN_DAYS},
|
||||
};
|
||||
|
||||
use super::{AnyDataset, ComputeData, MinInitialStates, RatioDataset};
|
||||
|
||||
#[derive(Allocative, Iterable)]
|
||||
pub struct PriceDatasets {
|
||||
min_initial_states: MinInitialStates,
|
||||
|
||||
kraken_daily: Option<BTreeMap<Date, OHLC>>,
|
||||
kraken_1mn: Option<BTreeMap<u32, OHLC>>,
|
||||
binance_1mn: Option<BTreeMap<u32, OHLC>>,
|
||||
binance_daily: Option<BTreeMap<Date, OHLC>>,
|
||||
binance_har: Option<BTreeMap<u32, OHLC>>,
|
||||
kibo_by_height: BTreeMap<HeightMapChunkId, Vec<OHLC>>,
|
||||
kibo_by_date: BTreeMap<DateMapChunkId, BTreeMap<Date, OHLC>>,
|
||||
|
||||
pub ohlc: BiMap<OHLC>,
|
||||
pub open: BiMap<f32>,
|
||||
pub high: BiMap<f32>,
|
||||
pub low: BiMap<f32>,
|
||||
pub close: BiMap<f32>,
|
||||
}
|
||||
|
||||
impl PriceDatasets {
|
||||
pub fn import(config: &Config) -> color_eyre::Result<Self> {
|
||||
let path_dataset = config.path_datasets();
|
||||
let f = |s: &str| path_dataset.join(s);
|
||||
|
||||
let mut s = Self {
|
||||
min_initial_states: MinInitialStates::default(),
|
||||
|
||||
binance_1mn: None,
|
||||
binance_daily: None,
|
||||
binance_har: None,
|
||||
kraken_1mn: None,
|
||||
kraken_daily: None,
|
||||
kibo_by_height: BTreeMap::default(),
|
||||
kibo_by_date: BTreeMap::default(),
|
||||
|
||||
// ---
|
||||
// Inserted
|
||||
// ---
|
||||
ohlc: BiMap::new_json(1, MapKind::Inserted, &config.path_price()),
|
||||
|
||||
// ---
|
||||
// Computed
|
||||
// ---
|
||||
open_cents: BiMap::new_bin(1, MapKind::Computed, &f("open")),
|
||||
high_cents: BiMap::new_bin(1, MapKind::Computed, &f("high")),
|
||||
low_cents: BiMap::new_bin(1, MapKind::Computed, &f("low")),
|
||||
close: BiMap::new_bin(1, MapKind::Computed, &f("close")),
|
||||
};
|
||||
|
||||
s.min_initial_states
|
||||
.consume(MinInitialStates::compute_from_dataset(&s, config));
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
pub fn compute(&mut self, compute_data: &ComputeData, circulating_supply: &mut BiMap<f64>) {
|
||||
let &ComputeData { dates, heights, .. } = compute_data;
|
||||
|
||||
self.open
|
||||
.multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.open);
|
||||
|
||||
self.high
|
||||
.multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.high);
|
||||
|
||||
self.low
|
||||
.multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.low);
|
||||
|
||||
self.close
|
||||
.multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.close);
|
||||
}
|
||||
|
||||
pub fn get_date_ohlc(&mut self, date: Date) -> color_eyre::Result<OHLC> {
|
||||
if self.ohlc.date.is_key_safe(date) {
|
||||
Ok(self.ohlc.date.get_or_import(&date).unwrap().to_owned())
|
||||
} else {
|
||||
let ohlc = self
|
||||
.get_from_daily_kraken(&date)
|
||||
.or_else(|_| self.get_from_daily_binance(&date))
|
||||
.or_else(|_| self.get_from_date_kibo(&date))?;
|
||||
|
||||
self.ohlc.date.insert(date, ohlc);
|
||||
|
||||
Ok(ohlc)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_from_date_kibo(&mut self, date: &Date) -> color_eyre::Result<OHLC> {
|
||||
let chunk_id = date.to_chunk_id();
|
||||
|
||||
#[allow(clippy::map_entry)]
|
||||
if !self.kibo_by_date.contains_key(&chunk_id)
|
||||
|| self.kibo_by_date.get(&chunk_id).unwrap().last_key_value().unwrap().0 < date
|
||||
{
|
||||
self.kibo_by_date.insert(chunk_id, Kibo::fetch_date_prices(chunk_id)?);
|
||||
}
|
||||
|
||||
self.kibo_by_date
|
||||
.get(&chunk_id)
|
||||
.unwrap()
|
||||
.get(date)
|
||||
.cloned()
|
||||
.ok_or(Error::msg("Couldn't find date in satonomics"))
|
||||
}
|
||||
|
||||
fn get_from_daily_kraken(&mut self, date: &Date) -> color_eyre::Result<OHLC> {
|
||||
if self.kraken_daily.is_none() || self.kraken_daily.as_ref().unwrap().last_key_value().unwrap().0 < date {
|
||||
self.kraken_daily.replace(Kraken::fetch_daily_prices()?);
|
||||
}
|
||||
|
||||
self.kraken_daily
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(date)
|
||||
.cloned()
|
||||
.ok_or(Error::msg("Couldn't find date"))
|
||||
}
|
||||
|
||||
fn get_from_daily_binance(&mut self, date: &Date) -> color_eyre::Result<OHLC> {
|
||||
if self.binance_daily.is_none() || self.binance_daily.as_ref().unwrap().last_key_value().unwrap().0 < date {
|
||||
self.binance_daily.replace(Binance::fetch_daily_prices()?);
|
||||
}
|
||||
|
||||
self.binance_daily
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(date)
|
||||
.cloned()
|
||||
.ok_or(Error::msg("Couldn't find date"))
|
||||
}
|
||||
|
||||
pub fn get_height_ohlc(
|
||||
&mut self,
|
||||
height: Height,
|
||||
timestamp: Timestamp,
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
config: &Config,
|
||||
) -> color_eyre::Result<OHLC> {
|
||||
if let Some(ohlc) = self.ohlc.height.get_or_import(&height) {
|
||||
return Ok(ohlc);
|
||||
}
|
||||
|
||||
let timestamp = timestamp.to_floored_seconds();
|
||||
|
||||
if previous_timestamp.is_none() && !height.is_first() {
|
||||
panic!("Shouldn't be possible");
|
||||
}
|
||||
|
||||
let previous_timestamp = previous_timestamp.map(|t| t.to_floored_seconds());
|
||||
|
||||
let ohlc = self
|
||||
.get_from_1mn_kraken(timestamp, previous_timestamp)
|
||||
.unwrap_or_else(|_| {
|
||||
self.get_from_1mn_binance(timestamp, previous_timestamp)
|
||||
.unwrap_or_else(|_| {
|
||||
self.get_from_har_binance(timestamp, previous_timestamp, config)
|
||||
.unwrap_or_else(|_| {
|
||||
self.get_from_height_kibo(&height).unwrap_or_else(|_| {
|
||||
let date = timestamp.to_date();
|
||||
|
||||
panic!(
|
||||
"Can't find the price for: height: {height} - date: {date}
|
||||
1mn APIs are limited to the last 16 hours for Binance's and the last 10 hours for Kraken's
|
||||
How to fix this:
|
||||
1. Go to https://www.binance.com/en/trade/BTC_USDT?type=spot
|
||||
2. Select 1mn interval
|
||||
3. Open the inspector/dev tools
|
||||
4. Go to the Network Tab
|
||||
5. Filter URLs by 'uiKlines'
|
||||
6. Go back to the chart and scroll until you pass the date mentioned few lines ago
|
||||
7. Go back to the dev tools
|
||||
8. Export to a har file (if there is no explicit button, click on the cog button)
|
||||
9. Move the file to 'parser/imports/binance.har'
|
||||
"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
self.ohlc.height.insert(height, ohlc);
|
||||
|
||||
Ok(ohlc)
|
||||
}
|
||||
|
||||
fn get_from_height_kibo(&mut self, height: &Height) -> color_eyre::Result<OHLC> {
|
||||
let chunk_id = height.to_chunk_id();
|
||||
|
||||
#[allow(clippy::map_entry)]
|
||||
if !self.kibo_by_height.contains_key(&chunk_id)
|
||||
|| ((chunk_id.to_usize() + self.kibo_by_height.get(&chunk_id).unwrap().len()) <= height.to_usize())
|
||||
{
|
||||
self.kibo_by_height
|
||||
.insert(chunk_id, Kibo::fetch_height_prices(chunk_id)?);
|
||||
}
|
||||
|
||||
self.kibo_by_height
|
||||
.get(&chunk_id)
|
||||
.unwrap()
|
||||
.get(height.to_serialized_key().to_usize())
|
||||
.cloned()
|
||||
.ok_or(Error::msg("Couldn't find height in kibo"))
|
||||
}
|
||||
|
||||
fn get_from_1mn_kraken(
|
||||
&mut self,
|
||||
timestamp: Timestamp,
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
) -> color_eyre::Result<OHLC> {
|
||||
if self.kraken_1mn.is_none() || self.kraken_1mn.as_ref().unwrap().last_key_value().unwrap().0 <= ×tamp {
|
||||
self.kraken_1mn.replace(Kraken::fetch_1mn_prices()?);
|
||||
}
|
||||
|
||||
Self::find_height_ohlc(&self.kraken_1mn, timestamp, previous_timestamp, "kraken 1m")
|
||||
}
|
||||
|
||||
fn get_from_1mn_binance(
|
||||
&mut self,
|
||||
timestamp: Timestamp,
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
) -> color_eyre::Result<OHLC> {
|
||||
if self.binance_1mn.is_none() || self.binance_1mn.as_ref().unwrap().last_key_value().unwrap().0 <= ×tamp {
|
||||
self.binance_1mn.replace(Binance::fetch_1mn_prices()?);
|
||||
}
|
||||
|
||||
Self::find_height_ohlc(&self.binance_1mn, timestamp, previous_timestamp, "binance 1m")
|
||||
}
|
||||
|
||||
fn get_from_har_binance(
|
||||
&mut self,
|
||||
timestamp: Timestamp,
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
config: &Config,
|
||||
) -> color_eyre::Result<OHLC> {
|
||||
if self.binance_har.is_none() {
|
||||
self.binance_har
|
||||
.replace(Binance::read_har_file(config).unwrap_or_default());
|
||||
}
|
||||
|
||||
Self::find_height_ohlc(&self.binance_har, timestamp, previous_timestamp, "binance har")
|
||||
}
|
||||
|
||||
fn find_height_ohlc(
|
||||
tree: &Option<BTreeMap<u32, OHLC>>,
|
||||
timestamp: Timestamp,
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
name: &str,
|
||||
) -> color_eyre::Result<OHLC> {
|
||||
let tree = tree.as_ref().unwrap();
|
||||
|
||||
let err = Error::msg(format!("Couldn't find timestamp in {name}"));
|
||||
|
||||
let previous_ohlc = previous_timestamp.map_or(Some(OHLC::default()), |previous_timestamp| {
|
||||
tree.get(&previous_timestamp).cloned()
|
||||
});
|
||||
|
||||
let last_ohlc = tree.get(×tamp);
|
||||
|
||||
if previous_ohlc.is_none() || last_ohlc.is_none() {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let previous_ohlc = previous_ohlc.unwrap();
|
||||
|
||||
let mut final_ohlc = OHLC {
|
||||
open: previous_ohlc.close,
|
||||
high: previous_ohlc.close,
|
||||
low: previous_ohlc.close,
|
||||
close: previous_ohlc.close,
|
||||
};
|
||||
|
||||
let start = previous_timestamp.unwrap_or_default();
|
||||
let end = timestamp;
|
||||
|
||||
// Otherwise it's a re-org
|
||||
if start < end {
|
||||
tree.range(&*start..=&*end).skip(1).for_each(|(_, ohlc)| {
|
||||
if ohlc.high > final_ohlc.high {
|
||||
final_ohlc.high = ohlc.high
|
||||
}
|
||||
|
||||
if ohlc.low < final_ohlc.low {
|
||||
final_ohlc.low = ohlc.low
|
||||
}
|
||||
|
||||
final_ohlc.close = ohlc.close;
|
||||
});
|
||||
}
|
||||
|
||||
Ok(final_ohlc)
|
||||
}
|
||||
}
|
||||
|
||||
impl AnyDataset for PriceDatasets {
|
||||
fn get_min_initial_states(&self) -> &MinInitialStates {
|
||||
&self.min_initial_states
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,22 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
||||
|
||||
use super::Dollars;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Deref, FromBytes, Immutable, IntoBytes, KnownLayout, Serialize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Deref,
|
||||
FromBytes,
|
||||
Immutable,
|
||||
IntoBytes,
|
||||
KnownLayout,
|
||||
Serialize,
|
||||
)]
|
||||
pub struct Cents(u64);
|
||||
|
||||
impl From<Dollars> for Cents {
|
||||
|
||||
@@ -2,7 +2,22 @@ use derive_deref::Deref;
|
||||
use serde::Serialize;
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout, Deref, Serialize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
FromBytes,
|
||||
Immutable,
|
||||
IntoBytes,
|
||||
KnownLayout,
|
||||
Deref,
|
||||
Serialize,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct Close<T>(T);
|
||||
impl<T> From<T> for Close<T> {
|
||||
|
||||
+15
-37
@@ -1,18 +1,17 @@
|
||||
use std::ops::Add;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use indexer::Timestamp;
|
||||
use jiff::{civil::Date as Date_, tz::TimeZone, Span};
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
||||
|
||||
use super::Dateindex;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromBytes, Immutable, IntoBytes, KnownLayout)]
|
||||
pub struct Date(u32);
|
||||
|
||||
impl Date {
|
||||
const INDEX_ZERO: Self = Self(20090103);
|
||||
const INDEX_ZERO_: Date_ = Date_::constant(2009, 1, 3);
|
||||
const INDEX_ONE: Self = Self(20090109);
|
||||
const INDEX_ONE_: Date_ = Date_::constant(2009, 1, 9);
|
||||
pub const INDEX_ZERO: Self = Self(20090103);
|
||||
pub const INDEX_ZERO_: Date_ = Date_::constant(2009, 1, 3);
|
||||
pub const INDEX_ONE: Self = Self(20090109);
|
||||
pub const INDEX_ONE_: Date_ = Date_::constant(2009, 1, 9);
|
||||
|
||||
pub fn year(&self) -> u16 {
|
||||
(self.0 / 1_00_00) as u16
|
||||
@@ -51,33 +50,12 @@ impl From<Timestamp> for Date {
|
||||
}
|
||||
}
|
||||
|
||||
// impl TryFrom<Date> for usize {
|
||||
// type Error = color_eyre::Report;
|
||||
// fn try_from(value: Date) -> Result<Self, Self::Error> {
|
||||
// let value_ = Date_::from(value);
|
||||
// if value_ < Date::INDEX_ZERO_ {
|
||||
// Err(eyre!("Date is too early"))
|
||||
// } else if value == Date::INDEX_ZERO {
|
||||
// Ok(0)
|
||||
// } else if value_ < Date::INDEX_ONE_ {
|
||||
// Err(eyre!("Date is between first and second"))
|
||||
// } else if value == Date::INDEX_ONE {
|
||||
// Ok(1)
|
||||
// } else {
|
||||
// Ok(Date_::from(Date::INDEX_ONE).until(value_)?.get_days() as usize + 1)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl From<usize> for Date {
|
||||
// fn from(value: usize) -> Self {
|
||||
// Self::from(Self::INDEX_ZERO_.checked_add(Span::new().days(value as i64)).unwrap())
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Add<usize> for Date {
|
||||
// type Output = Self;
|
||||
// fn add(self, rhs: usize) -> Self::Output {
|
||||
// Self::from(Date_::from(self).checked_add(Span::new().days(rhs as i64)).unwrap())
|
||||
// }
|
||||
// }
|
||||
impl From<Dateindex> for Date {
|
||||
fn from(value: Dateindex) -> Self {
|
||||
Self::from(
|
||||
Self::INDEX_ZERO_
|
||||
.checked_add(Span::new().days(i64::from(value)))
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::ops::Add;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
||||
|
||||
use super::Date;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromBytes, Immutable, IntoBytes, KnownLayout)]
|
||||
pub struct Dateindex(u16);
|
||||
|
||||
@@ -17,9 +20,33 @@ impl From<usize> for Dateindex {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Dateindex> for i64 {
|
||||
fn from(value: Dateindex) -> Self {
|
||||
value.0 as i64
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for Dateindex {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
Self(self.0 + rhs as u16)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Date> for Dateindex {
|
||||
type Error = color_eyre::Report;
|
||||
fn try_from(value: Date) -> Result<Self, Self::Error> {
|
||||
let value_ = jiff::civil::Date::from(value);
|
||||
if value_ < Date::INDEX_ZERO_ {
|
||||
Err(eyre!("Date is too early"))
|
||||
} else if value == Date::INDEX_ZERO {
|
||||
Ok(Self(0))
|
||||
} else if value_ < Date::INDEX_ONE_ {
|
||||
Err(eyre!("Date is between first and second"))
|
||||
} else if value == Date::INDEX_ONE {
|
||||
Ok(Self(1))
|
||||
} else {
|
||||
Ok(Self(Date::INDEX_ONE_.until(value_)?.get_days() as u16 + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use derive_deref::Deref;
|
||||
use serde::Serialize;
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
||||
|
||||
use super::Cents;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Deref)]
|
||||
#[derive(Debug, Default, Clone, Copy, Deref, FromBytes, Immutable, IntoBytes, KnownLayout, Serialize)]
|
||||
pub struct Dollars(f64);
|
||||
|
||||
impl From<f64> for Dollars {
|
||||
|
||||
@@ -2,7 +2,24 @@ use derive_deref::Deref;
|
||||
use serde::Serialize;
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout, Deref, Serialize)]
|
||||
use super::Close;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Clone,
|
||||
Copy,
|
||||
FromBytes,
|
||||
Immutable,
|
||||
IntoBytes,
|
||||
KnownLayout,
|
||||
Deref,
|
||||
Serialize,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct High<T>(T);
|
||||
impl<T> From<T> for High<T> {
|
||||
@@ -10,3 +27,12 @@ impl<T> From<T> for High<T> {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Close<T>> for High<T>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
fn from(value: Close<T>) -> Self {
|
||||
Self(*value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,24 @@ use derive_deref::Deref;
|
||||
use serde::Serialize;
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout, Deref, Serialize)]
|
||||
use super::Close;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
FromBytes,
|
||||
Immutable,
|
||||
IntoBytes,
|
||||
KnownLayout,
|
||||
Deref,
|
||||
Serialize,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct Low<T>(T);
|
||||
impl<T> From<T> for Low<T> {
|
||||
@@ -10,3 +27,12 @@ impl<T> From<T> for Low<T> {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Close<T>> for Low<T>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
fn from(value: Close<T>) -> Self {
|
||||
Self(*value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,24 @@ use derive_deref::Deref;
|
||||
use serde::Serialize;
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout, Deref, Serialize)]
|
||||
use super::Close;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
FromBytes,
|
||||
Immutable,
|
||||
IntoBytes,
|
||||
KnownLayout,
|
||||
Deref,
|
||||
Serialize,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct Open<T>(T);
|
||||
impl<T> From<T> for Open<T> {
|
||||
@@ -10,3 +27,12 @@ impl<T> From<T> for Open<T> {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Close<T>> for Open<T>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
fn from(value: Close<T>) -> Self {
|
||||
Self(*value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,26 @@ pub enum Index {
|
||||
Txoutindex,
|
||||
}
|
||||
|
||||
impl Index {
|
||||
pub fn all() -> [Self; 13] {
|
||||
[
|
||||
Self::Addressindex,
|
||||
Self::Dateindex,
|
||||
Self::Height,
|
||||
Self::P2PK33index,
|
||||
Self::P2PK65index,
|
||||
Self::P2PKHindex,
|
||||
Self::P2SHindex,
|
||||
Self::P2TRindex,
|
||||
Self::P2WPKHindex,
|
||||
Self::P2WSHindex,
|
||||
Self::Txindex,
|
||||
Self::Txinindex,
|
||||
Self::Txoutindex,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Index {
|
||||
type Error = ();
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::{collections::BTreeMap, fs, io};
|
||||
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use storable_vec::AnyJsonStorableVec;
|
||||
|
||||
use crate::WEBSITE_DEV_PATH;
|
||||
|
||||
use super::index::Index;
|
||||
|
||||
#[derive(Default, Deref, DerefMut)]
|
||||
@@ -32,6 +34,44 @@ impl VecIdToIndexToVec {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_dts_file(&self) -> io::Result<()> {
|
||||
if !fs::exists(WEBSITE_DEV_PATH)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let path = format!("{WEBSITE_DEV_PATH}/scripts/types/vecid-to-indexes.d.ts");
|
||||
|
||||
let mut contents = Index::all()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i_of_i, i)| format!("type {} = {};", i, i_of_i))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
contents += "\n\ninterface VecIdToIndexes {\n";
|
||||
|
||||
self.iter().for_each(|(id, index_to_vec)| {
|
||||
let indexes = index_to_vec
|
||||
.keys()
|
||||
.map(|i| i.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
contents += &format!(
|
||||
" {}: [{indexes}]\n",
|
||||
if id.contains("-") {
|
||||
format!("\"{id}\"")
|
||||
} else {
|
||||
id.to_owned()
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
contents.push('}');
|
||||
|
||||
fs::write(path, contents)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Deref, DerefMut)]
|
||||
|
||||
@@ -16,12 +16,11 @@ use reqwest::StatusCode;
|
||||
use crate::{
|
||||
log_result,
|
||||
traits::{HeaderMapExtended, ModifiedState, ResponseExtended},
|
||||
WEBSITE_DEV_PATH,
|
||||
};
|
||||
|
||||
use super::minify::minify_js;
|
||||
|
||||
const WEBSITE_DEV_PATH: &str = "../website/";
|
||||
|
||||
pub async fn file_handler(headers: HeaderMap, path: extract::Path<String>) -> Response {
|
||||
any_handler(headers, Some(path))
|
||||
}
|
||||
|
||||
+4
-3
@@ -23,10 +23,9 @@ pub struct AppState {
|
||||
computer: &'static Computer<STATELESS>,
|
||||
}
|
||||
|
||||
pub async fn main(indexer: Indexer<STATELESS>, computer: Computer<STATELESS>) -> color_eyre::Result<()> {
|
||||
// pub async fn main(routes: Routes, config: Config) -> color_eyre::Result<()> {
|
||||
// routes.generate_dts_file();
|
||||
pub const WEBSITE_DEV_PATH: &str = "../website/";
|
||||
|
||||
pub async fn main(indexer: Indexer<STATELESS>, computer: Computer<STATELESS>) -> color_eyre::Result<()> {
|
||||
let indexer = Box::leak(Box::new(indexer));
|
||||
let computer = Box::leak(Box::new(computer));
|
||||
let vecs = Box::leak(Box::new(VecIdToIndexToVec::default()));
|
||||
@@ -37,6 +36,8 @@ pub async fn main(indexer: Indexer<STATELESS>, computer: Computer<STATELESS>) ->
|
||||
.into_iter()
|
||||
.for_each(|vec| vecs.insert(vec));
|
||||
|
||||
vecs.generate_dts_file()?;
|
||||
|
||||
let state = AppState {
|
||||
vecs,
|
||||
indexer,
|
||||
|
||||
Reference in New Issue
Block a user