mirror of
https://github.com/webclinic017/drift.git
synced 2026-07-27 18:57:55 +00:00
feat(Data): added daily_glassnode DataCollection (#99)
This commit is contained in:
committed by
GitHub
parent
442915f847
commit
867269df2b
@@ -130,4 +130,5 @@ dmypy.json
|
||||
lightning/lightning_logs/
|
||||
|
||||
results.csv
|
||||
predictions.csv
|
||||
wandb/
|
||||
|
||||
@@ -1,5 +1,34 @@
|
||||
# Financial time series prediction models
|
||||
# Financial time series prediction
|
||||
|
||||
And end-to-end pipeline to train predictive Machine Learning models on financial (non-stationary, regime changing) time series.
|
||||
|
||||
## Why?
|
||||
|
||||
Machine learning on financial time series require a fundamentally different approach than used in other ML domains. The data is non-stationary, where the patterns frequently change, and it's extremely important to not to leak out-of-sample data into the training set objective evaluation is important.
|
||||
|
||||
There are very few open-source end-to-end machine learning pipelines that can be effectively used to train and evaluate ML models on financial time series. Among them are[qlib](https://github.com/microsoft/qlib), [AlphaPy](https://github.com/ScottfreeLLC/AlphaPy).
|
||||
|
||||
This repo is different to them in a couple of angles:
|
||||
- Feature extraction and selection is an important, pre-built step in the pipeline. Training models on
|
||||
- Training and evaluation is done in a [walk-forward manner](https://en.wikipedia.org/wiki/Walk_forward_optimization). We argue that that one or two train/test split is not adoquate to evaluate an ML model's performance in a non-stationary, regime changing environment. The walk-forward methodology enables us to evaluate the model's performance on almost the whole time series.
|
||||
- The walk-forward training/evaluation methodology enables "online" (ever-changing) models, that adapt to the market environment. You can specify how frequently would you like to re-train the models.
|
||||
|
||||
|
||||
This project is inspired partially by [Marcos Lopez de Prado's Advances in Financial Machine Learning](https://www.wiley.com/en-us/Advances+in+Financial+Machine+Learning-p-9781119482086) and [The Alpha Scientist's blogposts](https://alphascientist.com/).
|
||||
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Use the conda environment file attached!:)
|
||||
Use the conda environment file attached!:)
|
||||
|
||||
|
||||
## Pipeline components
|
||||
|
||||
- Feature extraction
|
||||
- Dimensionality reduction
|
||||
- Feature selection
|
||||
- Training Level-1 models
|
||||
- Training Level-2 (Ensemble) model
|
||||
- Evaluation of models
|
||||
- Cross-sectional portfolio construction [IN PROGRESS]
|
||||
@@ -1,5 +1,3 @@
|
||||
from models.model_map import model_names_classification, model_names_regression
|
||||
|
||||
|
||||
def get_default_level_1_daily_config() -> tuple[dict, dict, dict]:
|
||||
|
||||
@@ -19,12 +17,13 @@ def get_default_level_1_daily_config() -> tuple[dict, dict, dict]:
|
||||
data_config = dict(
|
||||
assets = ['hourly_crypto'],
|
||||
other_assets = [],
|
||||
# exogenous_data = [],
|
||||
exogenous_data = [],
|
||||
load_non_target_asset= True,
|
||||
log_returns= True,
|
||||
forecasting_horizon = 1,
|
||||
own_features = ['level_2', 'date_days'],
|
||||
other_features = ['single_mom'],
|
||||
exogenous_features = ['fracdiff'],
|
||||
index_column= 'int',
|
||||
method= 'classification',
|
||||
no_of_classes= 'three-balanced'
|
||||
@@ -59,12 +58,13 @@ def get_default_level_2_hourly_config() -> tuple[dict, dict, dict]:
|
||||
data_config = dict(
|
||||
assets = ['hourly_crypto'],
|
||||
other_assets = [],
|
||||
# exogenous_data = [],
|
||||
exogenous_data = [],
|
||||
load_non_target_asset= True,
|
||||
log_returns= True,
|
||||
forecasting_horizon = 1,
|
||||
own_features = ['level_2', 'date_days', 'lags_up_to_5'],
|
||||
other_features = ['level_2'],
|
||||
exogenous_features = ['fracdiff'],
|
||||
index_column= 'int',
|
||||
method= 'classification',
|
||||
no_of_classes= 'three-balanced'
|
||||
@@ -101,12 +101,13 @@ def get_default_level_2_daily_config() -> tuple[dict, dict, dict]:
|
||||
data_config = dict(
|
||||
assets = ['daily_crypto'],
|
||||
other_assets = ['daily_etf'],
|
||||
# exogenous_data = [],
|
||||
exogenous_data = ['daily_glassnode'],
|
||||
load_non_target_asset= True,
|
||||
log_returns= True,
|
||||
forecasting_horizon = 1,
|
||||
own_features = ['level_2', 'date_days', 'fracdiff'],
|
||||
other_features = ['level_2', 'fracdiff'],
|
||||
exogenous_features = ['fracdiff'],
|
||||
index_column= 'int',
|
||||
method= 'classification',
|
||||
no_of_classes= 'three-balanced'
|
||||
@@ -125,17 +126,3 @@ def get_default_level_2_daily_config() -> tuple[dict, dict, dict]:
|
||||
return model_config, training_config, data_config
|
||||
|
||||
|
||||
def validate_config(model_config:dict, training_config:dict, data_config:dict):
|
||||
# We need to make sure there's only one output from the pipeline
|
||||
# If level-2 model is there, we need more than one level-1 models to train
|
||||
if model_config["level_2_model"] is not None: assert len(model_config["level_1_models"]) > 0
|
||||
# If there's no level-2 model, we need to have only one level-1 model
|
||||
if model_config["level_2_model"] is None: assert len(model_config["level_1_models"]) == 1
|
||||
|
||||
def get_model_name(model_config:dict) -> str:
|
||||
if model_config["level_2_model"] is not None:
|
||||
return model_config["level_2_model"][0]
|
||||
elif len(model_config["level_1_models"]) == 1:
|
||||
return model_config["level_1_models"][0][0]
|
||||
else:
|
||||
raise Exception("No model name found")
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
from utils.helpers import flatten
|
||||
from feature_extractors.feature_extractor_presets import presets as feature_extractor_presets
|
||||
from models.model_map import model_map
|
||||
from data_loader.collections import data_collections
|
||||
|
||||
def preprocess_config(model_config:dict, training_config:dict, data_config:dict) -> tuple[dict, dict, dict]:
|
||||
model_config = __preprocess_model_config(model_config, data_config['method'])
|
||||
data_config = __preprocess_feature_extractors_config(data_config)
|
||||
data_config = __preprocess_data_collections_config(data_config)
|
||||
|
||||
validate_config(model_config, training_config, data_config)
|
||||
return model_config, training_config, data_config
|
||||
|
||||
def __preprocess_feature_extractors_config(data_dict: dict) -> dict:
|
||||
data_dict = data_dict.copy()
|
||||
keys = ['own_features', 'other_features', 'exogenous_features']
|
||||
for key in keys:
|
||||
preset_names = data_dict[key]
|
||||
data_dict[key] = flatten([feature_extractor_presets[preset_name] for preset_name in preset_names])
|
||||
return data_dict
|
||||
|
||||
def __preprocess_model_config(model_config:dict, method:str) -> dict:
|
||||
model_config['level_1_models'] = [(model_name, model_map[method + '_models'][model_name]) for model_name in model_config['level_1_models']]
|
||||
if model_config['level_2_model'] is not None:
|
||||
model_config['level_2_model'] = (model_config['level_2_model'], model_map[method + '_models'][model_config['level_2_model']])
|
||||
|
||||
return model_config
|
||||
|
||||
def __preprocess_data_collections_config(data_dict: dict) -> dict:
|
||||
data_dict = data_dict.copy()
|
||||
keys = ['assets', 'other_assets', 'exogenous_data']
|
||||
for key in keys:
|
||||
preset_names = data_dict[key]
|
||||
data_dict[key] = flatten([data_collections[preset_name] for preset_name in preset_names])
|
||||
return data_dict
|
||||
|
||||
|
||||
def validate_config(model_config:dict, training_config:dict, data_config:dict):
|
||||
# We need to make sure there's only one output from the pipeline
|
||||
# If level-2 model is there, we need more than one level-1 models to train
|
||||
if model_config["level_2_model"] is not None: assert len(model_config["level_1_models"]) > 0
|
||||
# If there's no level-2 model, we need to have only one level-1 model
|
||||
if model_config["level_2_model"] is None: assert len(model_config["level_1_models"]) == 1
|
||||
|
||||
def get_model_name(model_config:dict) -> str:
|
||||
if model_config["level_2_model"] is not None:
|
||||
return model_config["level_2_model"][0]
|
||||
elif len(model_config["level_1_models"]) == 1:
|
||||
return model_config["level_1_models"][0][0]
|
||||
else:
|
||||
raise Exception("No model name found")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,382 @@
|
||||
time,close
|
||||
2021-12-16,0.8795999999999999
|
||||
2021-12-15,0.8729
|
||||
2021-12-14,0.8724
|
||||
2021-12-13,0.8636
|
||||
2021-12-12,0.8543999999999999
|
||||
2021-12-11,0.8449
|
||||
2021-12-10,0.8482
|
||||
2021-12-09,0.0
|
||||
2021-12-08,0.0
|
||||
2021-12-07,1.1388
|
||||
2021-12-06,1.1293
|
||||
2021-12-05,1.1533
|
||||
2021-12-04,1.1755
|
||||
2021-12-03,1.1336
|
||||
2021-12-02,1.1024
|
||||
2021-12-01,1.0802
|
||||
2021-11-30,1.0655
|
||||
2021-11-29,1.0434999999999999
|
||||
2021-11-28,1.047
|
||||
2021-11-27,1.0207
|
||||
2021-11-26,1.0084
|
||||
2021-11-25,1.0076
|
||||
2021-11-24,1.0088
|
||||
2021-11-23,0.9963
|
||||
2021-11-22,0.9928
|
||||
2021-11-21,1.0422
|
||||
2021-11-20,1.0314
|
||||
2021-11-19,1.1879
|
||||
2021-11-18,1.187
|
||||
2021-11-17,1.2176
|
||||
2021-11-16,1.0144
|
||||
2021-11-15,0.9781
|
||||
2021-11-14,1.0138
|
||||
2021-11-13,0.9940000000000001
|
||||
2021-11-12,0.9986
|
||||
2021-11-11,0.9871
|
||||
2021-11-10,0.9823999999999999
|
||||
2021-11-09,0.9833
|
||||
2021-11-08,0.9948
|
||||
2021-11-07,0.9512
|
||||
2021-11-06,0.8983
|
||||
2021-11-05,0.8861
|
||||
2021-11-04,0.8774
|
||||
2021-11-03,0.8637
|
||||
2021-11-02,0.0
|
||||
2021-11-01,0.9593
|
||||
2021-10-31,0.9077
|
||||
2021-10-30,0.8991
|
||||
2021-10-29,0.8952
|
||||
2021-10-28,0.8762000000000001
|
||||
2021-10-27,0.8439
|
||||
2021-10-26,0.879
|
||||
2021-10-25,0.8533
|
||||
2021-10-24,0.8595999999999999
|
||||
2021-10-23,0.8653
|
||||
2021-10-22,0.8526
|
||||
2021-10-21,0.8739
|
||||
2021-10-20,0.9177
|
||||
2021-10-19,0.9172
|
||||
2021-10-18,0.9220999999999999
|
||||
2021-10-17,0.9041
|
||||
2021-10-16,0.8881
|
||||
2021-10-15,0.8955
|
||||
2021-10-14,0.9701000000000001
|
||||
2021-10-13,0.9198999999999999
|
||||
2021-10-12,0.9270999999999999
|
||||
2021-10-11,0.8776
|
||||
2021-10-10,0.8748
|
||||
2021-10-09,0.8765999999999999
|
||||
2021-10-08,0.8915000000000001
|
||||
2021-10-07,0.8675
|
||||
2021-10-06,0.8964
|
||||
2021-10-05,0.9904000000000001
|
||||
2021-10-04,0.9732999999999999
|
||||
2021-10-03,0.879
|
||||
2021-10-02,0.8571
|
||||
2021-10-01,0.8472
|
||||
2021-09-30,0.8946999999999999
|
||||
2021-09-29,0.8713
|
||||
2021-09-28,0.8776
|
||||
2021-09-27,0.8773000000000001
|
||||
2021-09-26,0.8647
|
||||
2021-09-25,0.8326
|
||||
2021-09-24,0.8373999999999999
|
||||
2021-09-23,0.8356999999999999
|
||||
2021-09-22,0.85
|
||||
2021-09-21,0.9041
|
||||
2021-09-20,0.8842
|
||||
2021-09-19,0.8845999999999999
|
||||
2021-09-18,0.8371
|
||||
2021-09-17,0.8814
|
||||
2021-09-16,0.8885
|
||||
2021-09-15,0.9269
|
||||
2021-09-14,0.9279999999999999
|
||||
2021-09-13,0.9225
|
||||
2021-09-12,0.9187000000000001
|
||||
2021-09-11,0.8898999999999999
|
||||
2021-09-10,0.9041
|
||||
2021-09-09,0.9037000000000001
|
||||
2021-09-08,0.9134
|
||||
2021-09-07,0.9484999999999999
|
||||
2021-09-06,0.9122
|
||||
2021-09-05,0.9264
|
||||
2021-09-04,0.9113
|
||||
2021-09-03,0.9046
|
||||
2021-09-02,0.8981999999999999
|
||||
2021-09-01,0.9420000000000001
|
||||
2021-08-31,0.9173
|
||||
2021-08-30,0.9163
|
||||
2021-08-29,0.9037000000000001
|
||||
2021-08-28,0.9018999999999999
|
||||
2021-08-27,0.9018999999999999
|
||||
2021-08-26,0.9079
|
||||
2021-08-25,0.9124
|
||||
2021-08-24,1.5045
|
||||
2021-08-23,1.5569
|
||||
2021-08-22,1.6386
|
||||
2021-08-21,1.4488999999999999
|
||||
2021-08-20,1.466
|
||||
2021-08-19,1.3649
|
||||
2021-08-18,1.26
|
||||
2021-08-17,1.2416
|
||||
2021-08-16,1.2707
|
||||
2021-08-15,1.244
|
||||
2021-08-14,1.1973
|
||||
2021-08-13,1.4963
|
||||
2021-08-12,1.4847
|
||||
2021-08-11,1.1527
|
||||
2021-08-10,1.1872
|
||||
2021-08-09,1.1473
|
||||
2021-08-08,1.0987
|
||||
2021-08-07,1.0685
|
||||
2021-08-06,1.0138
|
||||
2021-08-05,0.9619
|
||||
2021-08-04,0.9329999999999999
|
||||
2021-08-03,0.9301
|
||||
2021-08-02,0.9140999999999999
|
||||
2021-08-01,0.9267
|
||||
2021-07-31,0.9177
|
||||
2021-07-30,0.9086
|
||||
2021-07-29,0.8987999999999999
|
||||
2021-07-28,0.9384999999999999
|
||||
2021-07-27,0.95
|
||||
2021-07-26,0.8615
|
||||
2021-07-25,0.8438
|
||||
2021-07-24,0.8440000000000001
|
||||
2021-07-23,0.8375
|
||||
2021-07-22,0.8714
|
||||
2021-07-21,0.92
|
||||
2021-07-20,0.8661
|
||||
2021-07-19,0.9068
|
||||
2021-07-18,0.8329000000000001
|
||||
2021-07-17,0.8168000000000001
|
||||
2021-07-16,0.8255
|
||||
2021-07-15,0.8371
|
||||
2021-07-14,0.8390000000000001
|
||||
2021-07-13,0.8759
|
||||
2021-07-12,0.8804000000000001
|
||||
2021-07-11,0.8748
|
||||
2021-07-10,0.8674
|
||||
2021-07-09,0.8967
|
||||
2021-07-08,0.9144
|
||||
2021-07-07,0.943
|
||||
2021-07-06,0.9534
|
||||
2021-07-05,0.9512999999999999
|
||||
2021-07-04,0.9781
|
||||
2021-07-03,0.9768000000000001
|
||||
2021-07-02,0.9854
|
||||
2021-07-01,1.0090000000000001
|
||||
2021-06-30,1.0228
|
||||
2021-06-29,1.0228
|
||||
2021-06-28,1.0141
|
||||
2021-06-27,1.0595999999999999
|
||||
2021-06-26,1.0454
|
||||
2021-06-25,1.0627
|
||||
2021-06-24,0.9639
|
||||
2021-06-23,0.9954000000000001
|
||||
2021-06-22,1.0141
|
||||
2021-06-21,1.0025
|
||||
2021-06-20,0.9837
|
||||
2021-06-19,0.8994
|
||||
2021-06-18,0.95
|
||||
2021-06-17,1.0978
|
||||
2021-06-16,1.0972
|
||||
2021-06-15,1.0170000000000001
|
||||
2021-06-14,1.0031
|
||||
2021-06-13,1.0198
|
||||
2021-06-12,0.9825
|
||||
2021-06-11,0.9799
|
||||
2021-06-10,0.9479000000000001
|
||||
2021-06-09,0.9732
|
||||
2021-06-08,1.0314
|
||||
2021-06-07,1.0959999999999999
|
||||
2021-06-06,1.0289
|
||||
2021-06-05,1.0524
|
||||
2021-06-04,1.0344
|
||||
2021-06-03,1.0434
|
||||
2021-06-02,1.0015
|
||||
2021-06-01,1.0675
|
||||
2021-05-31,1.0747
|
||||
2021-05-30,1.1406999999999998
|
||||
2021-05-29,1.2490999999999999
|
||||
2021-05-28,1.1978
|
||||
2021-05-27,1.1871
|
||||
2021-05-26,1.1447
|
||||
2021-05-25,1.2105
|
||||
2021-05-24,1.2407
|
||||
2021-05-23,1.3881999999999999
|
||||
2021-05-22,1.4287
|
||||
2021-05-21,1.4112
|
||||
2021-05-20,1.1401999999999999
|
||||
2021-05-19,1.2304000000000002
|
||||
2021-05-18,1.2313
|
||||
2021-05-17,1.153
|
||||
2021-05-16,1.4169999999999998
|
||||
2021-05-15,1.1312
|
||||
2021-05-14,0.9767
|
||||
2021-05-13,0.9294
|
||||
2021-05-12,0.9333
|
||||
2021-05-11,0.8891
|
||||
2021-05-10,0.8926000000000001
|
||||
2021-05-09,0.9045000000000001
|
||||
2021-05-08,0.8761
|
||||
2021-05-07,0.8675
|
||||
2021-05-06,0.8242
|
||||
2021-05-05,0.8428
|
||||
2021-05-04,0.8452
|
||||
2021-05-03,0.828
|
||||
2021-05-02,0.7959
|
||||
2021-05-01,0.7912
|
||||
2021-04-30,0.784
|
||||
2021-04-29,0.7778
|
||||
2021-04-28,0.7753
|
||||
2021-04-27,0.7866
|
||||
2021-04-26,0.8046
|
||||
2021-04-25,0.8358
|
||||
2021-04-24,0.9231
|
||||
2021-04-23,0.8405
|
||||
2021-04-22,0.8654999999999999
|
||||
2021-04-21,0.7313
|
||||
2021-04-20,0.7206
|
||||
2021-04-19,0.7331
|
||||
2021-04-18,0.7186
|
||||
2021-04-17,0.6662
|
||||
2021-04-16,0.7728
|
||||
2021-04-15,0.7504000000000001
|
||||
2021-04-14,0.7820999999999999
|
||||
2021-04-13,0.8443
|
||||
2021-04-12,0.8729
|
||||
2021-04-11,0.8606999999999999
|
||||
2021-04-10,0.7369
|
||||
2021-04-09,0.7369
|
||||
2021-04-08,0.7369
|
||||
2021-04-07,0.7228
|
||||
2021-04-06,0.7833
|
||||
2021-04-05,0.8011
|
||||
2021-04-04,0.7822
|
||||
2021-04-03,0.7947
|
||||
2021-04-02,0.8273999999999999
|
||||
2021-04-01,0.8203
|
||||
2021-03-31,0.8454
|
||||
2021-03-30,0.8462999999999999
|
||||
2021-03-29,0.8434999999999999
|
||||
2021-03-28,0.8072
|
||||
2021-03-27,0.7955
|
||||
2021-03-26,0.7966
|
||||
2021-03-25,0.8306
|
||||
2021-03-24,0.8779
|
||||
2021-03-23,0.7995
|
||||
2021-03-22,0.8499
|
||||
2021-03-21,0.841
|
||||
2021-03-20,0.8640000000000001
|
||||
2021-03-19,0.9335
|
||||
2021-03-18,1.1472
|
||||
2021-03-17,1.2143000000000002
|
||||
2021-03-16,1.0395999999999999
|
||||
2021-03-15,1.0244
|
||||
2021-03-14,1.0282
|
||||
2021-03-13,1.0471
|
||||
2021-03-12,1.0207
|
||||
2021-03-11,1.02
|
||||
2021-03-10,1.0114
|
||||
2021-03-09,0.9897
|
||||
2021-03-08,1.0117
|
||||
2021-03-07,1.0278
|
||||
2021-03-06,1.0441
|
||||
2021-03-05,1.0454
|
||||
2021-03-04,1.0964
|
||||
2021-03-03,1.0458
|
||||
2021-03-02,1.0773000000000001
|
||||
2021-03-01,1.0701
|
||||
2021-02-28,1.1184
|
||||
2021-02-27,1.1171
|
||||
2021-02-26,1.1195
|
||||
2021-02-25,1.1301999999999999
|
||||
2021-02-24,1.1287
|
||||
2021-02-23,1.2639
|
||||
2021-02-22,1.2473999999999998
|
||||
2021-02-21,1.1508
|
||||
2021-02-20,1.0979
|
||||
2021-02-19,1.1478
|
||||
2021-02-18,1.1699
|
||||
2021-02-17,1.2989
|
||||
2021-02-16,1.2006999999999999
|
||||
2021-02-15,1.1735
|
||||
2021-02-14,1.1683
|
||||
2021-02-13,1.1798
|
||||
2021-02-12,1.188
|
||||
2021-02-11,1.185
|
||||
2021-02-10,1.1858
|
||||
2021-02-09,1.7429
|
||||
2021-02-08,1.716
|
||||
2021-02-07,1.4555
|
||||
2021-02-06,1.4212
|
||||
2021-02-05,1.4105
|
||||
2021-02-04,1.3122
|
||||
2021-02-03,1.3109
|
||||
2021-02-02,1.2405
|
||||
2021-02-01,1.218
|
||||
2021-01-31,1.246
|
||||
2021-01-30,1.2211
|
||||
2021-01-29,1.2284
|
||||
2021-01-28,1.3671
|
||||
2021-01-27,1.2609000000000001
|
||||
2021-01-26,1.1601000000000001
|
||||
2021-01-25,1.1153
|
||||
2021-01-24,1.1540000000000001
|
||||
2021-01-23,1.1540000000000001
|
||||
2021-01-22,1.1536
|
||||
2021-01-21,1.331
|
||||
2021-01-20,1.2878999999999998
|
||||
2021-01-19,1.3485
|
||||
2021-01-18,1.3819
|
||||
2021-01-17,1.3386000000000002
|
||||
2021-01-16,1.3733000000000002
|
||||
2021-01-15,1.5174
|
||||
2021-01-14,1.4821
|
||||
2021-01-13,1.5258
|
||||
2021-01-12,1.39
|
||||
2021-01-11,1.4380000000000002
|
||||
2021-01-10,1.3259999999999998
|
||||
2021-01-09,1.4450999999999998
|
||||
2021-01-08,1.3609
|
||||
2021-01-07,1.3241999999999998
|
||||
2021-01-06,1.2539
|
||||
2021-01-05,1.1691
|
||||
2021-01-04,1.0951
|
||||
2021-01-03,1.0772
|
||||
2021-01-02,1.0979999999999999
|
||||
2021-01-01,0.9168999999999999
|
||||
2020-12-31,0.9837
|
||||
2020-12-30,1.0149
|
||||
2020-12-29,0.9595
|
||||
2020-12-28,0.9007
|
||||
2020-12-27,0.9164
|
||||
2020-12-26,0.9679000000000001
|
||||
2020-12-25,0.8793000000000001
|
||||
2020-12-24,0.8935
|
||||
2020-12-23,1.0022
|
||||
2020-12-22,0.8944
|
||||
2020-12-21,0.8917
|
||||
2020-12-20,0.8898999999999999
|
||||
2020-12-19,0.8859
|
||||
2020-12-18,0.8345999999999999
|
||||
2020-12-17,0.8049
|
||||
2020-12-16,0.84
|
||||
2020-12-15,0.736
|
||||
2020-12-14,0.7301000000000001
|
||||
2020-12-13,0.7378
|
||||
2020-12-12,0.705
|
||||
2020-12-11,0.7308
|
||||
2020-12-10,0.7559999999999999
|
||||
2020-12-09,0.737
|
||||
2020-12-08,0.7735
|
||||
2020-12-07,0.8061
|
||||
2020-12-06,0.8161
|
||||
2020-12-05,0.8008
|
||||
2020-12-04,0.7881
|
||||
2020-12-03,0.8159000000000001
|
||||
2020-12-02,0.8195
|
||||
2020-12-01,0.8462000000000001
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+37
-32
@@ -1,43 +1,48 @@
|
||||
from utils.types import Path, FileName, DataSource, DataCollection
|
||||
from utils.helpers import flatten
|
||||
|
||||
daily_etf = ["GLD", "IEF", "QQQ", "SPY", "TLT"]
|
||||
daily_etf = list(zip(["data/daily_etf"] * len(daily_etf), daily_etf))
|
||||
def transform_to_data_collection(path: str, file_names: list[str]) -> DataCollection:
|
||||
return list(zip([path] * len(file_names), file_names))
|
||||
|
||||
daily_crypto = ["ADA_USD",
|
||||
"BCH_USD",
|
||||
"BNB_USD",
|
||||
"BTC_USD",
|
||||
"DOT_USD",
|
||||
"ETC_USD",
|
||||
"ETH_USD",
|
||||
"FIL_USD",
|
||||
"LTC_USD",
|
||||
"SOL_USD",
|
||||
"THETA_USD",
|
||||
"TRX_USD",
|
||||
"UNI_USD",
|
||||
"XLM_USD",
|
||||
"XRP_USD",
|
||||
"XTZ_USD"]
|
||||
daily_crypto = list(zip(["data/daily_crypto"] * len(daily_crypto), daily_crypto))
|
||||
__daily_etf = ["GLD", "IEF", "QQQ", "SPY", "TLT"]
|
||||
|
||||
__daily_crypto = ["ADA_USD", "BCH_USD", "BNB_USD", "BTC_USD", "DOT_USD", "ETC_USD", "ETH_USD", "FIL_USD", "LTC_USD", "SOL_USD", "THETA_USD", "TRX_USD", "UNI_USD", "XLM_USD", "XRP_USD", "XTZ_USD"]
|
||||
|
||||
hourly_crypto = ["BTC_USD", "DASH_USD", "ETC_USD", "ETH_USD", "LTC_USD", "TRX_USD", "XLM_USD", "XMR_USD", "XRP_USD"]
|
||||
hourly_crypto = list(zip(["data/hourly_crypto"] * len(hourly_crypto), hourly_crypto))
|
||||
__hourly_crypto = ["BTC_USD", "DASH_USD", "ETC_USD", "ETH_USD", "LTC_USD", "TRX_USD", "XLM_USD", "XMR_USD", "XRP_USD"]
|
||||
|
||||
__daily_glassnode = ['rhodl_ratio',
|
||||
# 'cvdd',
|
||||
'nvt_ratio',
|
||||
'nvt_signal',
|
||||
'velocity',
|
||||
'supply_adjusted_cdd',
|
||||
'binary_cdd',
|
||||
'supply_adjusted_dormancy',
|
||||
'puell_multiple',
|
||||
'asopr',
|
||||
'reserve_risk',
|
||||
'sopr',
|
||||
'cdd',
|
||||
'asol',
|
||||
'msol',
|
||||
'dormancy',
|
||||
'liveliness',
|
||||
'relative_unrealized_profit',
|
||||
'relative_unrealized_loss',
|
||||
'nupl',
|
||||
'sth_nupl',
|
||||
'lth_nupl',
|
||||
'ssr',
|
||||
'bvin',
|
||||
# 'hash_rate'
|
||||
]
|
||||
|
||||
|
||||
data_collections = dict(
|
||||
daily_crypto = daily_crypto,
|
||||
daily_etf = daily_etf,
|
||||
hourly_crypto = hourly_crypto
|
||||
daily_only_btc = transform_to_data_collection("data/daily_crypto", ['BTC_USD']),
|
||||
daily_crypto = transform_to_data_collection("data/daily_crypto", __daily_crypto),
|
||||
daily_etf = transform_to_data_collection("data/daily_etf", __daily_etf),
|
||||
hourly_crypto = transform_to_data_collection("data/hourly_crypto", __hourly_crypto),
|
||||
daily_glassnode =transform_to_data_collection("data/daily_glassnode", __daily_glassnode),
|
||||
)
|
||||
|
||||
def preprocess_data_collections_config(data_dict: dict) -> dict:
|
||||
data_dict = data_dict.copy()
|
||||
keys = ['assets', 'other_assets']
|
||||
# keys = ['assets', 'other_assets', 'exogenous_data']
|
||||
for key in keys:
|
||||
preset_names = data_dict[key]
|
||||
data_dict[key] = flatten([data_collections[preset_name] for preset_name in preset_names])
|
||||
return data_dict
|
||||
|
||||
+22
-13
@@ -1,7 +1,7 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from utils.types import DataSource, FeatureExtractor
|
||||
from utils.helpers import deduplicate_indexes
|
||||
from utils.helpers import deduplicate_indexes, drop_columns_if_exist
|
||||
from data_loader.collections import DataCollection
|
||||
from typing import Literal
|
||||
import ray
|
||||
@@ -9,13 +9,14 @@ import os
|
||||
|
||||
def load_data(assets: DataCollection,
|
||||
other_assets: DataCollection,
|
||||
# exogenous_data: DataCollection,
|
||||
target_asset: str,
|
||||
exogenous_data: DataCollection,
|
||||
target_asset: DataSource,
|
||||
load_non_target_asset: bool,
|
||||
log_returns: bool,
|
||||
forecasting_horizon: int,
|
||||
own_features: list[tuple[str, FeatureExtractor, list[int]]],
|
||||
other_features: list[tuple[str, FeatureExtractor, list[int]]],
|
||||
exogenous_features: list[tuple[str, FeatureExtractor, list[int]]],
|
||||
index_column: Literal['date', 'int'],
|
||||
method: Literal['regression', 'classification'],
|
||||
no_of_classes: Literal['two', 'three-balanced', 'three-imbalanced'],
|
||||
@@ -29,25 +30,36 @@ def load_data(assets: DataCollection,
|
||||
- Series `forward_returns` with the target asset returns shifted by 1 day
|
||||
"""
|
||||
|
||||
target_file = [f for f in assets if f[1].startswith(target_asset)]
|
||||
other_files = [f for f in assets if load_non_target_asset == True and f[1].startswith(target_asset) == False]
|
||||
target_file = [f for f in assets if f[1].startswith(target_asset[1])]
|
||||
assert len(target_file) == 1, "There should be exactly one target file"
|
||||
other_files = [f for f in assets if load_non_target_asset == True and f[1].startswith(target_asset[1]) == False]
|
||||
files = target_file + other_files + other_assets
|
||||
def is_target_asset(target_asset: str, file: str): return file.split('.')[0].startswith(target_asset)
|
||||
futures = [__load_df.remote(
|
||||
asset_futures = [__load_df.remote(
|
||||
data_source=data_source,
|
||||
prefix=data_source[1],
|
||||
returns='log_returns' if log_returns else 'returns',
|
||||
feature_extractors=own_features if is_target_asset(target_asset[1], data_source[1]) else other_features,
|
||||
narrow_format=narrow_format,
|
||||
) for data_source in files]
|
||||
dfs = ray.get(futures)
|
||||
asset_dfs = ray.get(asset_futures)
|
||||
|
||||
exogenous_futures = [__load_df.remote(
|
||||
data_source=data_source,
|
||||
prefix=data_source[1],
|
||||
returns='returns',
|
||||
feature_extractors=exogenous_features,
|
||||
narrow_format=narrow_format,
|
||||
) for data_source in exogenous_data]
|
||||
exogenous_dfs = ray.get(exogenous_futures)
|
||||
|
||||
dfs = asset_dfs + exogenous_dfs
|
||||
dfs = [deduplicate_indexes(df) for df in dfs]
|
||||
longest_df = max(dfs, key=lambda df: df.shape[0])
|
||||
if narrow_format:
|
||||
dfs = pd.concat(dfs, axis=0).fillna(0.)
|
||||
dfs = pd.concat([df.sort_index().reindex(longest_df.index) for df in dfs], axis=0).fillna(0.)
|
||||
else:
|
||||
dfs = pd.concat([df.reindex(longest_df.index) for df in dfs], axis=1).fillna(0.)
|
||||
dfs = pd.concat([df.sort_index().reindex(longest_df.index) for df in dfs], axis=1).fillna(0.)
|
||||
|
||||
dfs.index = pd.DatetimeIndex(dfs.index)
|
||||
|
||||
@@ -93,10 +105,7 @@ def __load_df(data_source: DataSource,
|
||||
df = __apply_feature_extractors(df, log_returns=True if returns == 'log_returns' else False, feature_extractors = feature_extractors)
|
||||
|
||||
df = df.replace([np.inf, -np.inf], 0.)
|
||||
df = df.drop(columns=['open', 'high', 'low', 'close'])
|
||||
# we're not ready for this just yet
|
||||
if 'volume' in df.columns:
|
||||
df = df.drop(columns=['volume'])
|
||||
df = drop_columns_if_exist(df, ['open', 'high', 'low', 'close', 'volume'])
|
||||
|
||||
if narrow_format:
|
||||
df["ticker"] = np.repeat(prefix, df.shape[0])
|
||||
|
||||
@@ -23,6 +23,7 @@ dependencies:
|
||||
- tscv
|
||||
- tqdm
|
||||
- pip
|
||||
- pandas-ta
|
||||
- pip:
|
||||
- fracdiff
|
||||
- ray
|
||||
|
||||
+84
-588
File diff suppressed because one or more lines are too long
@@ -31,10 +31,3 @@ presets = __presets | dict(
|
||||
level_2 = __presets["mom"] + __presets["vol"] + __presets["roc"] + __presets["rsi"] + __presets["stod"] + __presets["stok"],
|
||||
)
|
||||
|
||||
def preprocess_feature_extractors_config(data_dict: dict) -> dict:
|
||||
data_dict = data_dict.copy()
|
||||
keys = ['own_features', 'other_features']
|
||||
for key in keys:
|
||||
preset_names = data_dict[key]
|
||||
data_dict[key] = flatten([presets[preset_name] for preset_name in preset_names])
|
||||
return data_dict
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
## Utility functions
|
||||
|
||||
def __get_close_low_high(df: pd.DataFrame) -> tuple[pd.Series, pd.Series, pd.Series]:
|
||||
close = df['close']
|
||||
low = df['low']
|
||||
high = df['high']
|
||||
return close, low, high
|
||||
|
||||
## Feature extractors
|
||||
from feature_extractors.utils import get_close_low_high
|
||||
|
||||
def feature_debug_future_lookahead(df: pd.DataFrame, period: int, is_log_return: bool) -> pd.Series:
|
||||
return df['returns'].shift(-period)
|
||||
@@ -37,7 +28,7 @@ def feature_mom(df: pd.DataFrame, period: int, is_log_return: bool) -> pd.Series
|
||||
return df['close'].pct_change(period)
|
||||
|
||||
def feature_STOK(df: pd.DataFrame, period: int, is_log_return: bool) -> pd.Series:
|
||||
close, low, high = __get_close_low_high(df)
|
||||
close, low, high = get_close_low_high(df)
|
||||
|
||||
STOK = ((close - low.rolling(period).min()) / (high.rolling(period).max() - low.rolling(period).min())) * 100
|
||||
return STOK
|
||||
|
||||
@@ -3,8 +3,7 @@ import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
def feature_fractional_differentiation(df: pd.DataFrame, period: int, is_log_return: bool) -> pd.Series:
|
||||
feature_selector = FracdiffStat(window = period)
|
||||
frac_diff = FracdiffStat(window = period)
|
||||
input_series = df["close"].to_numpy().reshape(-1, 1)
|
||||
feature_selector.fit(input_series)
|
||||
result = feature_selector.transform(input_series)
|
||||
return pd.Series(np.log(result.squeeze()), index = df.index)
|
||||
result = frac_diff.fit_transform(input_series)
|
||||
return pd.Series(result.squeeze(), index = df.index)
|
||||
@@ -0,0 +1,8 @@
|
||||
import pandas_ta as ta
|
||||
import pandas as pd
|
||||
from feature_extractors.utils import get_close_low_high
|
||||
|
||||
def feature_EBSW(df: pd.DataFrame, period: int, is_log_return: bool) -> pd.Series:
|
||||
close, low, high = get_close_low_high(df)
|
||||
|
||||
return ta.ebsw(close, period)
|
||||
@@ -0,0 +1,7 @@
|
||||
import pandas as pd
|
||||
|
||||
def get_close_low_high(df: pd.DataFrame) -> tuple[pd.Series, pd.Series, pd.Series]:
|
||||
close = df['close']
|
||||
low = df['low']
|
||||
high = df['high']
|
||||
return close, low, high
|
||||
@@ -45,9 +45,3 @@ model_names_regression = list(model_map["regression_models"].keys())
|
||||
default_feature_selector_regression = model_map['regression_models']['RF']
|
||||
default_feature_selector_classification = model_map['classification_models']['RF']
|
||||
|
||||
def preprocess_model_config(model_config:dict, method:str) -> dict:
|
||||
model_config['level_1_models'] = [(model_name, model_map[method + '_models'][model_name]) for model_name in model_config['level_1_models']]
|
||||
if model_config['level_2_model'] is not None:
|
||||
model_config['level_2_model'] = (model_config['level_2_model'], model_map[method + '_models'][model_config['level_2_model']])
|
||||
|
||||
return model_config
|
||||
@@ -0,0 +1,39 @@
|
||||
#%%
|
||||
import pandas as pd
|
||||
import pandas_ta as ta
|
||||
from config.config import get_default_level_2_daily_config
|
||||
from config.preprocess import preprocess_config
|
||||
from data_loader.load_data import load_data
|
||||
|
||||
# %%
|
||||
model_config, training_config, data_config = get_default_level_2_daily_config()
|
||||
model_config, training_config, data_config = preprocess_config(model_config, training_config, data_config)
|
||||
|
||||
data_config['target_asset'] = data_config['assets'][0]
|
||||
X, y, target_returns = load_data(**data_config)
|
||||
# %%
|
||||
X.ta.donchian()
|
||||
|
||||
|
||||
# %%
|
||||
X.ta.ema()
|
||||
# %%
|
||||
X.ta.adjusted = "ADA_USD_returns"
|
||||
|
||||
# %%
|
||||
X.ta.sma(length=10)
|
||||
|
||||
# %%
|
||||
X
|
||||
# %%
|
||||
X.ta.categories
|
||||
|
||||
# %%
|
||||
ind_list = X.ta.indicators(as_list=True)
|
||||
|
||||
# %%
|
||||
ind_list
|
||||
# %%
|
||||
X.ta.ao('ADA_USD_returns', length=10)
|
||||
# %%
|
||||
ta.ao()
|
||||
@@ -0,0 +1,62 @@
|
||||
#%%
|
||||
import pandas as pd
|
||||
from tqdm import tqdm
|
||||
from utils.glassnode import GlassnodeClient, Indicators, Mining
|
||||
|
||||
path = 'data/daily_glassnode/'
|
||||
client = GlassnodeClient(asset='BTC', since='2014-01-01', until='2021-12-17')
|
||||
print("Client initiated")
|
||||
indicator_client = Indicators(client)
|
||||
|
||||
indicator_names = ['rhodl_ratio',
|
||||
'cvdd',
|
||||
'difficulty_ribbon_compression',
|
||||
'nvt_ratio',
|
||||
'nvt_signal',
|
||||
'velocity',
|
||||
'supply_adjusted_cdd',
|
||||
'binary_cdd',
|
||||
'supply_adjusted_dormancy',
|
||||
'puell_multiple',
|
||||
'asopr',
|
||||
'reserve_risk',
|
||||
'sopr',
|
||||
'cdd',
|
||||
'asol',
|
||||
'msol',
|
||||
'dormancy',
|
||||
'liveliness',
|
||||
'relative_unrealized_profit',
|
||||
'relative_unrealized_loss',
|
||||
'nupl',
|
||||
# 'sth_nupl',
|
||||
# 'lth_nupl',
|
||||
'ssr',
|
||||
'bvin',
|
||||
]
|
||||
|
||||
def process_df(df: pd.DataFrame) -> pd.DataFrame:
|
||||
df.index.rename('time', inplace=True)
|
||||
if len(df.columns) != 1:
|
||||
df = df[['v']]
|
||||
df.rename(columns={df.columns[0]: 'close'}, inplace=True)
|
||||
df.sort_index(inplace=True)
|
||||
return df
|
||||
|
||||
for name in tqdm(indicator_names):
|
||||
method_to_call = getattr(indicator_client, name)
|
||||
df = method_to_call()
|
||||
df = process_df(df)
|
||||
df.to_csv(path + name + '.csv')
|
||||
|
||||
|
||||
# Mining data
|
||||
|
||||
mining_names = ['hash_rate']
|
||||
mining_client = Mining(client)
|
||||
|
||||
for name in tqdm(mining_names):
|
||||
method_to_call = getattr(mining_client, name)
|
||||
df = method_to_call()
|
||||
df = process_df(df)
|
||||
df.to_csv(path + name + '.csv')
|
||||
+11
-9
@@ -1,12 +1,11 @@
|
||||
from data_loader.collections import preprocess_data_collections_config
|
||||
from data_loader.load_data import load_data
|
||||
import pandas as pd
|
||||
from training.training import run_single_asset_trainig
|
||||
from reporting.wandb import launch_wandb, send_report_to_wandb, register_config_with_wandb
|
||||
from models.model_map import preprocess_model_config, default_feature_selector_regression, default_feature_selector_classification
|
||||
from feature_extractors.feature_extractor_presets import preprocess_feature_extractors_config
|
||||
from models.model_map import default_feature_selector_regression, default_feature_selector_classification
|
||||
from utils.helpers import get_first_valid_return_index, weighted_average
|
||||
from config import get_default_level_1_daily_config, get_default_level_2_daily_config, get_default_level_2_hourly_config, validate_config, get_model_name
|
||||
from config.config import get_default_level_1_daily_config, get_default_level_2_daily_config, get_default_level_2_hourly_config
|
||||
from config.preprocess import validate_config, get_model_name, preprocess_config
|
||||
from feature_selection.feature_selection import select_features
|
||||
from feature_selection.dim_reduction import reduce_dimensionality
|
||||
import ray
|
||||
@@ -14,21 +13,19 @@ ray.init()
|
||||
|
||||
def setup_pipeline(project_name:str, with_wandb: bool, sweep: bool):
|
||||
model_config, training_config, data_config = get_default_level_2_daily_config()
|
||||
|
||||
wandb = None
|
||||
if with_wandb:
|
||||
wandb = launch_wandb(project_name=project_name, default_config=dict(**model_config, **training_config, **data_config), sweep=sweep)
|
||||
register_config_with_wandb(wandb, model_config, training_config, data_config)
|
||||
model_config, training_config, data_config = preprocess_config(model_config, training_config, data_config)
|
||||
|
||||
model_config = preprocess_model_config(model_config, data_config['method'])
|
||||
data_config = preprocess_feature_extractors_config(data_config)
|
||||
data_config = preprocess_data_collections_config(data_config)
|
||||
pipeline(project_name, wandb, sweep, model_config, training_config, data_config)
|
||||
|
||||
|
||||
|
||||
def pipeline(project_name:str, wandb, sweep:bool, model_config:dict, training_config:dict, data_config:dict):
|
||||
results = pd.DataFrame()
|
||||
all_predictions = pd.DataFrame()
|
||||
validate_config(model_config, training_config, data_config)
|
||||
|
||||
for asset in data_config['assets']:
|
||||
@@ -109,11 +106,16 @@ def pipeline(project_name:str, wandb, sweep:bool, model_config:dict, training_co
|
||||
|
||||
level1_columns = results[[column for column in results.columns if 'lvl1' in column]]
|
||||
level2_columns = results[[column for column in results.columns if 'lvl2' in column]]
|
||||
|
||||
|
||||
# Only send the results of the final model to wandb
|
||||
results_to_send = level2_columns if level2_columns.shape[1] > 0 else level1_columns
|
||||
send_report_to_wandb(results_to_send, wandb, project_name, get_model_name(model_config))
|
||||
|
||||
level1_predictions = all_predictions[[column for column in all_predictions.columns if 'lvl1' in column]]
|
||||
level2_predictions = all_predictions[[column for column in all_predictions.columns if 'lvl2' in column]]
|
||||
predictions_to_save = level2_predictions if level2_predictions.shape[1] > 0 else level1_predictions
|
||||
predictions_to_save.to_csv('predictions.csv')
|
||||
|
||||
print("\n--------\n")
|
||||
print("Benchmark buy-and-hold sharpe: ", round(weighted_average(results, 'no_of_samples').loc['benchmark_sharpe'], 3))
|
||||
|
||||
|
||||
+11
-3
@@ -6,8 +6,13 @@ metric:
|
||||
goal: maximize
|
||||
name: sharpe
|
||||
parameters:
|
||||
path :
|
||||
value: 'data/'
|
||||
assets:
|
||||
value: ['daily_crypto']
|
||||
other_assets:
|
||||
value: ['daily_etf']
|
||||
exogenous_data:
|
||||
values: [['daily_glassnode'], []]
|
||||
distribution: categorical
|
||||
expanding_window_level1:
|
||||
value: True
|
||||
expanding_window_level2:
|
||||
@@ -19,7 +24,7 @@ parameters:
|
||||
feature_selection:
|
||||
value: True
|
||||
n_features_to_select:
|
||||
values: [10, 20, 30]
|
||||
values: [30, 40]
|
||||
distribution: categorical
|
||||
dimensionality_reduction:
|
||||
value: True
|
||||
@@ -51,3 +56,6 @@ parameters:
|
||||
other_features:
|
||||
values: [['level_2', 'lags_up_to_5'], ['level_2', 'fracdiff']]
|
||||
distribution: categorical
|
||||
exogenous_features:
|
||||
values: [[], ['fracdiff']]
|
||||
distribution: categorical
|
||||
|
||||
+6
-2
@@ -6,8 +6,12 @@ metric:
|
||||
goal: maximize
|
||||
name: sharpe
|
||||
parameters:
|
||||
path :
|
||||
value: 'data/'
|
||||
assets:
|
||||
value: ['daily_crypto']
|
||||
other_assets:
|
||||
value: ['daily_etf']
|
||||
exogenous_data:
|
||||
value: ['daily_glassnode']
|
||||
expanding_window_level1:
|
||||
values: [True, False]
|
||||
distribution: categorical
|
||||
|
||||
+6
-2
@@ -6,8 +6,12 @@ metric:
|
||||
goal: maximize
|
||||
name: sharpe
|
||||
parameters:
|
||||
path :
|
||||
value: 'data/'
|
||||
assets:
|
||||
value: ['daily_crypto']
|
||||
other_assets:
|
||||
value: ['daily_etf']
|
||||
exogenous_data:
|
||||
value: ['daily_glassnode']
|
||||
expanding_window_level1:
|
||||
values: [True, False]
|
||||
distribution: categorical
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
from .client import *
|
||||
from .utils import *
|
||||
|
||||
# Metrics API categories
|
||||
from .addresses import *
|
||||
from .blockchain import *
|
||||
from .derivatives import *
|
||||
from .distribution import *
|
||||
from .entities import *
|
||||
from .eth2 import *
|
||||
from .fees import *
|
||||
from .indicators import *
|
||||
from .market import *
|
||||
from .mining import *
|
||||
from .protocols import *
|
||||
from .supply import *
|
||||
from .transactions import *
|
||||
@@ -0,0 +1,285 @@
|
||||
from .utils import *
|
||||
from .client import GlassnodeClient
|
||||
|
||||
|
||||
class Blockchain:
|
||||
"""
|
||||
Blockchain class.
|
||||
|
||||
Methods
|
||||
-------
|
||||
__init__(glassnode_client):
|
||||
Constructs a Blockchain object.
|
||||
utxos_total():
|
||||
Returns the total number of UTXOs in the network.
|
||||
utxos_created():
|
||||
Returns the number of created unspent transaction outputs.
|
||||
utxos_spent():
|
||||
Returns the number of spent transaction outputs.
|
||||
utxo_value_created_total():
|
||||
Returns the total amount of coins in newly created UTXOs.
|
||||
utxo_value_spent_total():
|
||||
Returns the total amount of coins in spent transaction outputs.
|
||||
utxo_value_created_mean():
|
||||
Returns the mean amount of coins in newly created UTXOs.
|
||||
utxo_value_spent_mean():
|
||||
Returns the mean amount of coins in spent transaction outputs.
|
||||
utxo_value_created_median():
|
||||
Returns the median amount of coins in newly created UTXOs.
|
||||
utxo_value_spent_median():
|
||||
Returns the median amount of coins in spent transaction outputs.
|
||||
utxos_in_profit():
|
||||
Returns the number of unspent transaction outputs in profit.
|
||||
utxos_in_loss():
|
||||
Returns the number of unspent transaction outputs in loss.
|
||||
percent_utxos_in_profit():
|
||||
Returns the percentage of unspent transaction outputs in profit.
|
||||
block_heights():
|
||||
Returns the block height.
|
||||
blocks_mined():
|
||||
Returns the number of blocks mined.
|
||||
block_interval_mean():
|
||||
Returns the mean time (in seconds) between mined blocks.
|
||||
block_interval_median():
|
||||
Returns the median time (in seconds) between mined blocks.
|
||||
block_size_mean():
|
||||
Returns the mean size of all blocks created within the time period (in bytes).
|
||||
block_size_total():
|
||||
Returns the total size of all blocks created within the time period (in bytes).
|
||||
"""
|
||||
def __init__(self, glassnode_client: GlassnodeClient):
|
||||
self._gc = glassnode_client
|
||||
|
||||
def utxos_total(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total number of UTXOs in the network.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoCount>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def utxos_created(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number of created unspent transaction outputs.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoCreatedCount>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_created_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def utxos_spent(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number of spent transaction outputs.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoSpentCount>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_spent_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def utxo_value_created_total(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of coins in newly created UTXOs.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoCreatedValueSum>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_created_value_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def utxo_value_spent_total(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of coins in spent transaction outputs.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoSpentValueSum>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_spent_value_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def utxo_value_created_mean(self) -> pd.DataFrame:
|
||||
"""
|
||||
The mean amount of coins in newly created UTXOs.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoCreatedValueMean>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_created_value_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def utxo_value_spent_mean(self) -> pd.DataFrame:
|
||||
"""
|
||||
The mean amount of coins in spent transaction outputs.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoSpentValueMean>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_spent_value_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def utxo_value_created_median(self) -> pd.DataFrame:
|
||||
"""
|
||||
The median amount of coins in newly created UTXOs.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoCreatedValueMedian>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_created_value_median'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def utxo_value_spent_median(self) -> pd.DataFrame:
|
||||
"""
|
||||
The median amount of coins in spent transaction outputs.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoSpentValueMedian>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_spent_value_median'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def utxos_in_profit(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number of unspent transaction outputs whose price at creation time was lower than the current price.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoProfitCount>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_profit_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def utxos_in_loss(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number of unspent transaction outputs whose price at creation time was higher than the current price.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoLossCount>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_loss_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def percent_utxos_in_profit(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percentage of unspent transaction outputs whose price at creation time was lower than the current price.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.UtxoProfitRelative>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/utxo_profit_relative'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def block_heights(self) -> pd.DataFrame:
|
||||
"""
|
||||
The block height, i.e. the total number of blocks ever created and included in the main blockchain.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.BlockHeight>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/block_height'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def blocks_mined(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number of blocks created and included in the main blockchain in that time period.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.BlockCount>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/block_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def block_interval_mean(self) -> pd.DataFrame:
|
||||
"""
|
||||
The mean time (in seconds) between mined blocks.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.BlockIntervalMean>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/block_interval_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def block_interval_median(self) -> pd.DataFrame:
|
||||
"""
|
||||
The median time (in seconds) between mined blocks.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.BlockIntervalMedian>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/block_interval_median'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def block_size_mean(self) -> pd.DataFrame:
|
||||
"""
|
||||
The mean size of all blocks created within the time period (in bytes).
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.BlockSizeMean>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/block_size_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def block_size_total(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total size of all blocks created within the time period (in bytes).
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=blockchain.BlockSizeSum>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/blockchain/block_size_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
@@ -0,0 +1,79 @@
|
||||
import os
|
||||
import sys
|
||||
from .utils import *
|
||||
from .endpoints import Endpoints
|
||||
|
||||
|
||||
class GlassnodeClient:
|
||||
def __init__(
|
||||
self,
|
||||
api_key=None,
|
||||
asset='BTC',
|
||||
resolution='24h',
|
||||
currency='native',
|
||||
since=None,
|
||||
until=None
|
||||
):
|
||||
"""
|
||||
Glassnode API client.
|
||||
|
||||
:param asset: Asset to which the metric refers. (ex. BTC)
|
||||
:param resolution: Temporal resolution of the data received. Can be '10m', '1h', '24h', '1w' or '1month'.
|
||||
:param currency: NATIVE, USD
|
||||
:param since: Start date as a string (ex. 2015-11-27)
|
||||
:param until: Start date as a string (ex. 2018-05-03)
|
||||
"""
|
||||
if api_key:
|
||||
self._api_key = api_key
|
||||
elif 'GLASSNODE_API_KEY' in os.environ:
|
||||
self._api_key = os.environ.get('GLASSNODE_API_KEY')
|
||||
else:
|
||||
# API key is required for every endpoint!
|
||||
print(f'\033[91m ERROR: Glassnode API key required!\033[0m')
|
||||
sys.exit()
|
||||
|
||||
self.endpoints = Endpoints()
|
||||
self.endpoints.endpoints = self._api_key
|
||||
self._asset = asset
|
||||
self._resolution = resolution
|
||||
self._since = since
|
||||
self._until = until
|
||||
self._currency = currency
|
||||
|
||||
@property
|
||||
def asset(self):
|
||||
return self._asset
|
||||
|
||||
@property
|
||||
def resolution(self):
|
||||
return self._resolution
|
||||
|
||||
def get(self, endpoint, params=None):
|
||||
return fetch(endpoint, self.__prepare_request_params(params))
|
||||
|
||||
def __prepare_request_params(self, params):
|
||||
p = dict()
|
||||
p['api_key'] = self._api_key
|
||||
p['a'] = self._asset
|
||||
p['i'] = self._resolution
|
||||
|
||||
if self._since is not None:
|
||||
try:
|
||||
p['s'] = unix_timestamp(self._since)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
if self._until is not None:
|
||||
try:
|
||||
p['u'] = unix_timestamp(self._until)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# Set domain specific query parameters if available
|
||||
if params:
|
||||
if 'e' in p:
|
||||
p['e'] = params['e']
|
||||
if 'm' in p:
|
||||
p['miner'] = params['m']
|
||||
|
||||
return p
|
||||
@@ -0,0 +1,239 @@
|
||||
from .utils import *
|
||||
from .client import GlassnodeClient
|
||||
|
||||
|
||||
class Derivatives:
|
||||
"""
|
||||
Derivatives class.
|
||||
|
||||
Methods
|
||||
-------
|
||||
__init__(glassnode_client):
|
||||
Constructs a Derivatives object.
|
||||
futures_perpetual_funding_rate([exchange]):
|
||||
Returns the average funding rate (in %) set by exchanges for perpetual futures contracts.
|
||||
futures_perpetual_funding_rate_all():
|
||||
Returns the average funding rate (in %) set by exchanges for perpetual futures contracts.
|
||||
futures_volume([exchange]):
|
||||
Returns the total volume traded in futures contracts in the last 24 hours.
|
||||
futures_volume_latest_24h():
|
||||
Returns the total volume traded in futures contracts per exchange over the last 24 hours.
|
||||
futures_volume_stacked():
|
||||
Returns the total volume traded in futures contracts in the last 24 hours.
|
||||
futures_volume_perpetual([exchange]):
|
||||
Returns The total volume traded in perpetual futures contracts in the last 24 hours.
|
||||
futures_volume_perpetual_stacked():
|
||||
Returns the total volume traded in perpetual futures contracts in the last 24 hours.
|
||||
futures_open_interest([exchange]):
|
||||
Returns the total amount of funds allocated in open futures contracts.
|
||||
futures_open_interest_current():
|
||||
Returns the current amount of allocated funds in futures contracts per exchange.
|
||||
futures_open_interest_perpetual([exchange]):
|
||||
Returns the total amount of funds allocated in open perpetual futures contracts.
|
||||
futures_open_interest_perpetual_stacked():
|
||||
Returns the total amount of funds allocated in open perpetual futures contracts.
|
||||
futures_open_interest_stacked():
|
||||
Returns the total amount of funds allocated in open futures contracts.
|
||||
futures_long_liquidations([exchange]):
|
||||
Returns the sum liquidated volume from long positions in futures contracts.
|
||||
futures_long_liquidations_mean([exchange]):
|
||||
Returns the mean liquidated volume from long positions in futures contracts.
|
||||
futures_short_liquidations([exchange]):
|
||||
Returns the sum liquidated volume from short positions in futures contracts.
|
||||
futures_short_liquidations_mean([exchange]):
|
||||
Returns the mean liquidated volume from short positions in futures contracts.
|
||||
"""
|
||||
def __init__(self, glassnode_client: GlassnodeClient):
|
||||
self._gc = glassnode_client
|
||||
|
||||
def futures_perpetual_funding_rate(self, exchange: str = None) -> pd.DataFrame:
|
||||
"""
|
||||
The average funding rate (in %) set by exchanges for perpetual futures contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesFundingRatePerpetual>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_funding_rate_perpetual'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'e': exchange}))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def futures_perpetual_funding_rate_all(self) -> pd.DataFrame:
|
||||
"""
|
||||
The average funding rate (in %) set by exchanges for perpetual futures contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesFundingRatePerpetualAll>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_funding_rate_perpetual_all'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def futures_volume(self, exchange: str = None) -> pd.DataFrame:
|
||||
"""
|
||||
The total volume traded in futures contracts in the last 24 hours.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesVolumeDailySum>`_
|
||||
"""
|
||||
url = '/v1/metrics/derivatives/futures_volume_daily_sum'
|
||||
if not is_supported_by_endpoint(self._gc, url):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(url, {'e': exchange}))
|
||||
|
||||
# TODO: Unpack inner object from response
|
||||
def futures_volume_latest_24h(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total volume traded in futures contracts per exchange over the last 24 hours.
|
||||
Values are updated every 10 min.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesVolumeDailyLatest>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_volume_daily_latest'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def futures_volume_stacked(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total volume traded in futures contracts in the last 24 hours.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesVolumeDailySumAll>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_volume_daily_sum_all'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def futures_volume_perpetual(self, exchange: str = None) -> pd.DataFrame:
|
||||
"""
|
||||
The total volume traded in perpetual (non-expiring) futures contracts in the last 24 hours.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesVolumeDailyPerpetualSum>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_volume_daily_perpetual_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'e': exchange}))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def futures_volume_perpetual_stacked(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total volume traded in perpetual (non-expiring) futures contracts in the last 24 hours.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesVolumeDailyPerpetualSumAll>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_volume_daily_perpetual_sum_all'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def futures_open_interest(self, exchange: str = None) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of funds allocated in open futures contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesOpenInterestSum>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_open_interest_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'e': exchange}))
|
||||
|
||||
# TODO: Unpack inner object from response
|
||||
def futures_open_interest_current(self) -> pd.DataFrame:
|
||||
"""
|
||||
The current amount of allocated funds in futures contracts per exchange.Values are updated every 10 min.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesOpenInterestLatest>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_open_interest_latest'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def futures_open_interest_perpetual(self, exchange: str = None) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of funds allocated in open perpetual (non-expiring) futures contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesOpenInterestPerpetualSum>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_open_interest_perpetual_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'e': exchange}))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def futures_open_interest_perpetual_stacked(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of funds allocated in open perpetual (non-expiring) futures contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesOpenInterestPerpetualSumAll>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_open_interest_perpetual_sum_all'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def futures_open_interest_stacked(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of funds allocated in open futures contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesOpenInterestSumAll>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_open_interest_sum_all'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def futures_long_liquidations(self, exchange: str = None) -> pd.DataFrame:
|
||||
"""
|
||||
The sum liquidated volume from long positions in futures contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesLiquidatedVolumeLongSum>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_liquidated_volume_long_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'e': exchange}))
|
||||
|
||||
def futures_long_liquidations_mean(self, exchange: str = None) -> pd.DataFrame:
|
||||
"""
|
||||
The mean liquidated volume from long positions in futures contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesLiquidatedVolumeLongMean>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_liquidated_volume_long_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'e': exchange}))
|
||||
|
||||
def futures_short_liquidations(self, exchange: str = None) -> pd.DataFrame:
|
||||
"""
|
||||
The sum liquidated volume from short positions in futures contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesLiquidatedVolumeShortSum>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_liquidated_volume_short_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'e': exchange}))
|
||||
|
||||
def futures_short_liquidations_mean(self, exchange: str = None) -> pd.DataFrame:
|
||||
"""
|
||||
The mean liquidated volume from short positions in futures contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=derivatives.FuturesLiquidatedVolumeShortMean>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/derivatives/futures_liquidated_volume_short_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'e': exchange}))
|
||||
|
||||
def futures_estimated_leverage_ratio(self, exchange: str = None) -> pd.DataFrame:
|
||||
|
||||
endpoint = '/v1/metrics/derivatives/futures_estimated_leverage_ratio'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'e': exchange}))
|
||||
@@ -0,0 +1,174 @@
|
||||
from .utils import *
|
||||
|
||||
|
||||
class Distribution:
|
||||
"""
|
||||
Distribution class.
|
||||
|
||||
Methods
|
||||
-------
|
||||
__init__(glassnode_client):
|
||||
Constructs a Distribution object.
|
||||
exchange_balance_total(exchange):
|
||||
Returns the total amount of coins held on exchange addresses.
|
||||
exchange_balance_percent(exchange):
|
||||
Returns the percent supply held on exchange addresses.
|
||||
exchange_balance_stacked():
|
||||
Returns the total amount of coins held on exchange addresses.
|
||||
miner_balance():
|
||||
Returns the total supply held in miner addresses.
|
||||
miner_balance_stacked():
|
||||
Returns the total supply held in miner addresses.
|
||||
balance_miners_change():
|
||||
Returns 30d change of the supply held in miner addresses.
|
||||
supply_top_one_pct_addresses():
|
||||
Returns the percentage of supply held by the top 1% addresses.
|
||||
gini_coefficient():
|
||||
Returns gini coefficient data.
|
||||
herfindahl_index():
|
||||
Returns herfindahl index data.
|
||||
supply_in_smart_contracts():
|
||||
Returns percent of total supply that is held in smart contracts.
|
||||
"""
|
||||
def __init__(self, glassnode_client):
|
||||
self._gc = glassnode_client
|
||||
|
||||
def exchange_balance_total(self, exchange=None) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of coins held on exchange addresses.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=distribution.BalanceExchanges>`_
|
||||
|
||||
:return: A DataFrame with exchange balance data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/distribution/balance_exchanges'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'e': exchange}))
|
||||
|
||||
def exchange_balance_percent(self, exchange=None) -> pd.DataFrame:
|
||||
"""
|
||||
The percent supply held on exchange addresses.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=distribution.BalanceExchangesRelative>`_
|
||||
|
||||
:return: A DataFrame with exchange balance data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/distribution/balance_exchanges_relative'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'e': exchange}))
|
||||
|
||||
def exchange_balance_stacked(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of coins held on exchange addresses.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=distribution.BalanceExchangesAll>`_
|
||||
|
||||
:return: A DataFrame with stacked exchange balance data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/distribution/balance_exchanges_all'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def miner_balance(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total supply held in miner addresses.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=distribution.BalanceMinersSum>`_
|
||||
|
||||
:return: A DataFrame miner balance data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/distribution/balance_miners_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def miner_balance_stacked(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total supply held in miner addresses.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=distribution.BalanceMinersAll>`_
|
||||
|
||||
:return: A DataFrame with stacked miner balance data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/distribution/balance_miners_all'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def balance_miners_change(self) -> pd.DataFrame:
|
||||
"""
|
||||
The 30d change of the supply held in miner addresses.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=distribution.BalanceMinersChange>`_
|
||||
|
||||
:return: A DataFrame with 30d change of the supply held in miner addresses.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/distribution/balance_miners_change'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_top_one_pct_addresses(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percentage of supply held by the top 1% addresses.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=distribution.Balance1PctHolders>`_
|
||||
|
||||
:return: A DataFrame with top 1% supply data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/distribution/balance_1pct_holders'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def gini_coefficient(self) -> pd.DataFrame:
|
||||
"""
|
||||
The gini coefficient for the distribution of coins over addresses.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=distribution.Gini>`_
|
||||
|
||||
:return: A DataFrame Gini Coefficient data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/distribution/gini'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def herfindahl_index(self) -> pd.DataFrame:
|
||||
"""
|
||||
A metric for decentralization.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=distribution.Herfindahl>`_
|
||||
|
||||
:return: A DataFrame Herfindahl index data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/distribution/herfindahl'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_in_smart_contracts(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percent of total supply of the token that is held in smart contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=distribution.SupplyContracts>`_
|
||||
|
||||
:return: A DataFrame smart contracts supply data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/distribution/supply_contracts'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
@@ -0,0 +1,43 @@
|
||||
from .utils import fetch
|
||||
|
||||
|
||||
def create_endpoints_dict(endpoints):
|
||||
return {
|
||||
endpoint['path']: {
|
||||
'assets': {asset['symbol']: asset['tags'] for asset in endpoint['assets']},
|
||||
'currencies': endpoint['currencies'],
|
||||
'resolutions': endpoint['resolutions'],
|
||||
'formats': endpoint['formats']
|
||||
}
|
||||
for endpoint in endpoints
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
@pattern Singleton (GoF:127)
|
||||
"""
|
||||
|
||||
|
||||
class MetaEndpoints(type):
|
||||
_instances = {}
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
instance = super().__call__(*args, **kwargs)
|
||||
cls._instances[cls] = instance
|
||||
return cls._instances[cls]
|
||||
|
||||
|
||||
class Endpoints(metaclass=MetaEndpoints):
|
||||
_endpoints = None
|
||||
|
||||
@property
|
||||
def endpoints(self):
|
||||
return self._endpoints
|
||||
|
||||
@endpoints.setter
|
||||
def endpoints(self, api_key):
|
||||
self._endpoints = create_endpoints_dict(fetch('/v2/metrics/endpoints', {'api_key': api_key}))
|
||||
|
||||
def query(self, path):
|
||||
return self._endpoints[path]
|
||||
@@ -0,0 +1,251 @@
|
||||
from .utils import *
|
||||
from .client import GlassnodeClient
|
||||
|
||||
|
||||
class Entities:
|
||||
"""
|
||||
Entities class.
|
||||
|
||||
Methods
|
||||
-------
|
||||
__init__(glassnode_client):
|
||||
Constructs a Entities object.
|
||||
sending_entities():
|
||||
Returns the number of unique entities that were active as a sender.
|
||||
receiving_entities():
|
||||
Returns the number of unique entities that were active as a receiver.
|
||||
active_entities():
|
||||
Returns the number of unique entities that were active either as a sender or receiver.
|
||||
new_entities():
|
||||
Returns The number of unique entities that appeared for the first time in a transaction.
|
||||
entities_net_growth():
|
||||
Returns the net growth of unique entities in the network.
|
||||
number_of_whales():
|
||||
Returns the number of unique entities holding at least 1k coins.
|
||||
supply_balance_less_0001():
|
||||
Returns the total circulating supply held by entities with a balance lower than 0.001 coins.
|
||||
supply_balance_0001_001():
|
||||
Returns the total circulating supply held by entities with a balance between 0.001 and 0.01 coins.
|
||||
supply_balance_001_01():
|
||||
Returns the total circulating supply held by entities with a balance between 0.01 and 0.1 coins.
|
||||
supply_balance_01_1():
|
||||
Returns the total circulating supply held by entities with a balance between 0.1 and 1 coins.
|
||||
supply_balance_1_10():
|
||||
Returns the total circulating supply held by entities with a balance between 1 and 10 coins.
|
||||
supply_balance_10_100():
|
||||
Returns the total circulating supply held by entities with a balance between 10 and 100 coins.
|
||||
supply_balance_100_1k():
|
||||
Returns the total circulating supply held by entities with a balance between 100 and 1,000 coins.
|
||||
supply_balance_1k_10k():
|
||||
Returns the total circulating supply held by entities with a balance between 1,000 and 10,000 coins.
|
||||
supply_balance_10k_100k():
|
||||
Returns the total circulating supply held by entities with a balance between 10,000 and 100,000 coins.
|
||||
supply_balance_more_100k():
|
||||
Returns the total circulating supply held by entities with a balance of at least 100,000 coins.
|
||||
entities_supply_distribution():
|
||||
Returns relative distribution of the circulating supply held by entities with specific balance bands.
|
||||
percent_entities_in_profit():
|
||||
Returns the percentage of entities in the network that are currently in profit.
|
||||
"""
|
||||
def __init__(self, glassnode_client: GlassnodeClient):
|
||||
self._gc = glassnode_client
|
||||
|
||||
def sending_entities(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number of unique entities that were active as a sender.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SendingCount>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/sending_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def receiving_entities(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number of unique entities that were active as a receiver.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.ReceivingCount>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/receiving_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def active_entities(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number of unique entities that were active either as a sender or receiver.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.ActiveCount>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/active_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def new_entities(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number of unique entities that appeared for the first time
|
||||
in a transaction of the native coin in the network.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.NewCount>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/new_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def entities_net_growth(self) -> pd.DataFrame:
|
||||
"""
|
||||
The net growth of unique entities in the network.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.NetGrowthCount>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/net_growth_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def number_of_whales(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number of unique entities holding at least 1k coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.Min1KCount>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/min_1k_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_balance_less_0001(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total circulating supply held by entities with a balance lower than 0.001 coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SupplyBalanceLess0001>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/supply_balance_less_0001'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_balance_0001_001(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total circulating supply held by entities with a balance between 0.001 and 0.01 coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SupplyBalance0001001>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/supply_balance_0001_001'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_balance_001_01(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total circulating supply held by entities with a balance between 0.01 and 0.1 coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SupplyBalance00101>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/supply_balance_001_01'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_balance_01_1(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total circulating supply held by entities with a balance between 0.1 and 1 coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SupplyBalance011>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/supply_balance_01_1'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_balance_1_10(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total circulating supply held by entities with a balance between 1 and 10 coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SupplyBalance110>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/supply_balance_1_10'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_balance_10_100(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total circulating supply held by entities with a balance between 10 and 100 coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SupplyBalance10100>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/supply_balance_10_100'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_balance_100_1k(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total circulating supply held by entities with a balance between 100 and 1,000 coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SupplyBalance1001K>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/supply_balance_100_1k'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_balance_1k_10k(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total circulating supply held by entities with a balance between 1,000 and 10,000 coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SupplyBalance1K10K>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/supply_balance_1k_10k'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_balance_10k_100k(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total circulating supply held by entities with a balance between 10,000 and 100,000 coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SupplyBalance10K100K>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/supply_balance_10k_100k'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_balance_more_100k(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total circulating supply held by entities with a balance of at least 100,000 coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SupplyBalanceMore100K>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/supply_balance_more_100k'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def entities_supply_distribution(self) -> pd.DataFrame:
|
||||
"""
|
||||
Relative distribution of the circulating supply held by entities with specific balance bands.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.SupplyDistributionRelative>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/supply_distribution_relative'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def percent_entities_in_profit(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percentage of entities in the network that are currently in profit.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=entities.ProfitRelative>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/entities/profit_relative'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
@@ -0,0 +1,127 @@
|
||||
from .utils import *
|
||||
|
||||
|
||||
class ETH2:
|
||||
"""
|
||||
ETH 2.0 class.
|
||||
|
||||
Methods
|
||||
-------
|
||||
__init__(glassnode_client):
|
||||
Constructs an ETH2 object.
|
||||
new_deposits():
|
||||
Returns the number transactions depositing 32 ETH to the ETH2 deposit contract.
|
||||
new_value_staked():
|
||||
Returns the amount of ETH transferred to the ETH2 deposit contract.
|
||||
new_validators():
|
||||
Returns the number of new validators depositing 32 ETH to the ETH2 deposit contract.
|
||||
total_number_of_deposits():
|
||||
Returns the total number of transactions to the ETH2 deposit contract.
|
||||
total_value_staked():
|
||||
Returns the amount of ETH deposited to the ETH2 deposit contract.
|
||||
total_number_of_validators():
|
||||
Returns the total number of unique validators.
|
||||
phase_zero_staking_goal():
|
||||
Returns the percentage of the Phase 0 staking goal.
|
||||
"""
|
||||
def __init__(self, glassnode_client):
|
||||
self._gc = glassnode_client
|
||||
|
||||
def new_deposits(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number transactions depositing 32 ETH to the ETH2 deposit contract.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=eth2.StakingDepositsCount>`_
|
||||
|
||||
:return: A DataFrame with ETH2 deposit data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/eth2/staking_deposits_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def new_value_staked(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of ETH transferred to the ETH2 deposit contract.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=eth2.StakingVolumeSum>`_
|
||||
|
||||
:return: A DataFrame with staked value data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/eth2/staking_volume_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def new_validators(self) -> pd.DataFrame:
|
||||
"""
|
||||
The number of new validators (accounts) depositing 32 ETH to the ETH2 deposit contract.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=eth2.StakingValidatorsCount>`_
|
||||
|
||||
:return: A DataFrame with new validators data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/eth2/staking_validators_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def total_number_of_deposits(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total number of transactions to the ETH2 deposit contract.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=eth2.StakingTotalDepositsCount>`_
|
||||
|
||||
:return: A DataFrame with ETH2 deposit data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/eth2/staking_total_deposits_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def total_value_staked(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of ETH that has been deposited to the ETH2 deposit contract,
|
||||
the current ETH balance on the ETH2 deposit contract.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=eth2.StakingTotalVolumeSum>`_
|
||||
|
||||
:return: A DataFrame with ETH2 deposit data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/eth2/staking_total_volume_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def total_number_of_validators(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total number of unique validators (accounts) that have deposited 32 ETH to the ETH2 deposit contract.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=eth2.StakingTotalValidatorsCount>`_
|
||||
|
||||
:return: A DataFrame with ETH2 deposit data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/eth2/staking_total_validators_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def phase_zero_staking_goal(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percentage of the Phase 0 staking goal.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=eth2.StakingPhase0GoalPercent>`_
|
||||
|
||||
:return: A DataFrame with staking goal data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/eth2/staking_phase_0_goal_percent'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
@@ -0,0 +1,202 @@
|
||||
from .utils import *
|
||||
from .client import GlassnodeClient
|
||||
|
||||
|
||||
class Fees:
|
||||
"""
|
||||
Fees class.
|
||||
|
||||
Methods
|
||||
-------
|
||||
__init__(glassnode_client):
|
||||
Constructs a Mining object.
|
||||
fee_ratio_multiple():
|
||||
Returns the Fee Ratio Multiple (FRM).
|
||||
fees_total():
|
||||
Returns the total amount of fees paid to miners.
|
||||
fees_mean():
|
||||
Returns the mean fee per transaction.
|
||||
fees_median():
|
||||
Returns the median fee per transaction.
|
||||
gas_used_total():
|
||||
Returns the total amount of gas used in all transactions.
|
||||
gas_used_mean():
|
||||
Returns the mean amount of gas used per transaction.
|
||||
gas_used_median():
|
||||
Returns the median amount of gas used per transaction.
|
||||
gas_price_mean():
|
||||
Returns the mean gas price paid per transaction.
|
||||
gas_price_median():
|
||||
Returns the median gas price paid per transaction.
|
||||
transaction_gas_limit_mean():
|
||||
Returns the mean gas limit per transaction.
|
||||
transaction_gas_limit_median():
|
||||
Returns the median gas limit per transaction.
|
||||
exchange_fees_total():
|
||||
Returns the total amount of fees paid in transactions related to on-chain exchange activity.
|
||||
exchange_fees_mean():
|
||||
Returns the mean amount of fees paid in transactions related to on-chain exchange activity.
|
||||
exchange_fees_dominance():
|
||||
Returns the exchange fee dominance.
|
||||
"""
|
||||
def __init__(self, glassnode_client: GlassnodeClient):
|
||||
self._gc = glassnode_client
|
||||
|
||||
def fee_ratio_multiple(self) -> pd.DataFrame:
|
||||
"""
|
||||
The Fee Ratio Multiple (FRM) is a measure of a blockchain's security
|
||||
and gives an assessment how secure a chain is once block rewards disappear.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=fees.FeeRatioMultiple>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/fee_ratio_multiple'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def fees_total(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of fees paid to miners. Issued (minted) coins are not included.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=fees.VolumeSum>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/volume_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def fees_mean(self) -> pd.DataFrame:
|
||||
"""
|
||||
The mean fee per transaction. Issued (minted) coins are not included.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=fees.VolumeMean>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/volume_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def fees_median(self) -> pd.DataFrame:
|
||||
"""
|
||||
The median fee per transaction. Issued (minted) coins are not included.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=fees.VolumeMedian>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/volume_median'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def gas_used_total(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of gas used in all transactions.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=fees.GasUsedSum>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/gas_used_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def gas_used_mean(self) -> pd.DataFrame:
|
||||
"""
|
||||
The mean amount of gas used per transaction.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=fees.GasUsedMean>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/gas_used_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def gas_used_median(self) -> pd.DataFrame:
|
||||
"""
|
||||
The median amount of gas used per transaction.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=fees.GasUsedMedian>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/gas_used_median'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def gas_price_mean(self) -> pd.DataFrame:
|
||||
"""
|
||||
The mean gas price paid per transaction.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=fees.GasPriceMean>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/gas_price_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def gas_price_median(self) -> pd.DataFrame:
|
||||
"""
|
||||
The median gas price paid per transaction.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=fees.GasPriceMedian>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/gas_price_median'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def transaction_gas_limit_mean(self) -> pd.DataFrame:
|
||||
"""
|
||||
The mean gas limit per transaction.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=fees.GasLimitTxMean>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/gas_limit_tx_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def transaction_gas_limit_median(self) -> pd.DataFrame:
|
||||
"""
|
||||
The median gas limit per transaction.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=fees.GasLimitTxMedian>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/gas_limit_tx_median'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def exchange_fees_total(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of fees paid in transactions related to on-chain exchange activity.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=fees.ExchangesSum>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/exchanges_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def exchange_fees_mean(self) -> pd.DataFrame:
|
||||
"""
|
||||
The mean amount of fees paid in transactions related to on-chain exchange activity.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=fees.ExchangesMean>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/exchanges_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def exchange_fees_dominance(self) -> pd.DataFrame:
|
||||
"""
|
||||
The Exchange Fee Dominance metric is defined as the percent amount of total fees
|
||||
paid in transactions related to on-chain exchange activity.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=fees.ExchangesRelative>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/fees/exchanges_relative'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
@@ -0,0 +1,405 @@
|
||||
from .utils import *
|
||||
from .client import GlassnodeClient
|
||||
|
||||
|
||||
class Indicators:
|
||||
def __init__(self, glassnode_client: GlassnodeClient):
|
||||
self._gc = glassnode_client
|
||||
|
||||
def rhodl_ratio(self) -> pd.DataFrame:
|
||||
"""
|
||||
The Realized HODL Ratio is a market indicator that uses a ratio of the Realized Cap HODL Waves.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.RhodlRatio>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/rhodl_ratio'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def cvdd(self) -> pd.DataFrame:
|
||||
"""
|
||||
Cumulative Value-Days Destroyed (CVDD) is the ratio of the cumulative USD value of Coin Days Destroyed and
|
||||
the market age (in days). Historically, CVDD has been an accurate indicator for global Bitcoin market bottoms.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.Cvdd>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/cvdd'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def hash_ribbon(self) -> pd.DataFrame:
|
||||
"""
|
||||
The Hash Ribbon is a market indicator that assumes that Bitcoin tends to reach a bottom when miners capitulate,
|
||||
i.e. when Bitcoin becomes too expensive to mine relative to the cost of mining. The Hash Ribbon indicates that
|
||||
the worst of the miner capitulation is over when the 30d MA of the hash rate crosses above the 60d MA.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.HashRibbon>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/hash_ribbon'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def difficulty_ribbon(self) -> pd.DataFrame:
|
||||
"""
|
||||
The Difficulty Ribbon is an indicator that uses simple moving averages
|
||||
of the Bitcoin mining difficulty to create the ribbon.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.DifficultyRibbon>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/difficulty_ribbon'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def difficulty_ribbon_compression(self) -> pd.DataFrame:
|
||||
"""
|
||||
Difficulty Ribbon Compression is a market indicator that uses a normalized
|
||||
standard deviation to quantify compression of the Difficulty Ribbon.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.DifficultyRibbonCompression>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/difficulty_ribbon_compression'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def nvt_ratio(self) -> pd.DataFrame:
|
||||
"""
|
||||
The Network Value to Transactions (NVT) Ratio is computed by dividing
|
||||
the market cap by the transferred on-chain volume measured in USD.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.Nvt>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/nvt'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def nvt_signal(self) -> pd.DataFrame:
|
||||
"""
|
||||
The NVT Signal (NVTS) is a modified version of the original NVT Ratio.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.Nvts>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/nvts'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def velocity(self) -> pd.DataFrame:
|
||||
"""
|
||||
Velocity is a measure of how quickly units are circulating in the network and is calculated
|
||||
by dividing the on-chain transaction volume (in USD) by the market cap, i.e. the inverse of the NVT ratio.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.Velocity>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/velocity'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_adjusted_cdd(self) -> pd.DataFrame:
|
||||
"""
|
||||
Adjusted Coin Days Destroyed simply divides CDD by the circulating supply.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.CddSupplyAdjusted>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/cdd_supply_adjusted'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def binary_cdd(self) -> pd.DataFrame:
|
||||
"""
|
||||
Binary Coin Days Destroyed is computed by thresholding Adjusted CDD by its average over time.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.CddSupplyAdjustedBinary>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/cdd_supply_adjusted_binary'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_adjusted_dormancy(self) -> pd.DataFrame:
|
||||
"""
|
||||
Dormancy is the average number of days destroyed per coin transacted,
|
||||
and is defined as the ratio of coin days destroyed and total transfer volume.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.AverageDormancySupplyAdjusted>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/average_dormancy_supply_adjusted'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def sopd_ath_partitioned(self) -> pd.DataFrame:
|
||||
"""
|
||||
UTXO Realized Price Distribution (URPD) shows at which prices UTXOs were spent that day.
|
||||
ATH-partitioned means that the price buckets are defined by dividing the range between
|
||||
0 and the current ATH in 100 equally-spaced partitions.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.SpentOutputPriceDistributionAth>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/spent_output_price_distribution_ath'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def sopd_percent_partitioned(self) -> pd.DataFrame:
|
||||
"""
|
||||
UTXO Realized Price Distribution (URPD) shows at which prices UTXOs were spent that day.
|
||||
%-partitioned means that the price buckets are defined by taking the day's closing price
|
||||
and creating 50 equally-spaced bucket each above and below the current price in steps of +/- 2%.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.SpentOutputPriceDistributionPercent>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/spent_output_price_distribution_percent'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def puell_multiple(self) -> pd.DataFrame:
|
||||
"""
|
||||
The Puell Multiple is calculated by dividing the daily issuance value of bitcoins (in USD)
|
||||
by the 365-day moving average of daily issuance value.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.PuellMultiple>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/puell_multiple'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def asopr(self) -> pd.DataFrame:
|
||||
"""
|
||||
Adjusted SOPR is SOPR ignoring all outputs with a lifespan of less than 1 hour.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.SoprAdjusted>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/sopr_adjusted'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def reserve_risk(self) -> pd.DataFrame:
|
||||
"""
|
||||
When confidence is high and price is low, there is an attractive risk/reward to invest (Reserve Risk is low).
|
||||
When confidence is low and price is high then risk/reward is unattractive at that time (Reserve Risk is high).
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.ReserveRisk>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/reserve_risk'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def sth_sopr(self) -> pd.DataFrame:
|
||||
"""
|
||||
Short Term Holder SOPR (STH-SOPR) is SOPR that takes into account only spent outputs
|
||||
younger than 155 days and serves as an indicator to assess the behaviour of short term investors.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.SoprLess155>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/sopr_less_155'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def lth_sopr(self) -> pd.DataFrame:
|
||||
"""
|
||||
Long Term Holder SOPR (LTH-SOPR) is SOPR that takes into account only spent outputs with a lifespan
|
||||
of at least 155 days and serves as an indicator to assess the behaviour of long term investors.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.SoprMore155>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/sopr_more_155'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def hodler_net_position_change(self) -> pd.DataFrame:
|
||||
"""
|
||||
HODLer Net Position Change shows the monthly position change of long term investors.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.HodlerNetPositionChange>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/hodler_net_position_change'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def hodled_or_lost_coins(self) -> pd.DataFrame:
|
||||
"""
|
||||
Lost or HODLed Bitcoins indicates moves of large and old stashes.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.HodledLostCoins>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/hodled_lost_coins'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def sopr(self) -> pd.DataFrame:
|
||||
"""
|
||||
The Spent Output Profit Ratio (SOPR) is computed by dividing the realized value (in USD)
|
||||
divided by the value at creation (USD) of a spent output. Or simply: price sold / price paid.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.Sopr>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/sopr'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def cdd(self) -> pd.DataFrame:
|
||||
"""
|
||||
Coin Days Destroyed (CDD) for any given transaction is calculated by taking the number of coins
|
||||
in a transaction and multiplying it by the number of days it has been since those coins were last spent.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.Cdd>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/cdd'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def asol(self) -> pd.DataFrame:
|
||||
"""
|
||||
Average Spent Output Lifespan (ASOL) is the average age (in days) of spent transaction outputs.
|
||||
Outputs with a lifespan of less than 1h are discarded.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.Asol>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/asol'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def msol(self) -> pd.DataFrame:
|
||||
"""
|
||||
Median Spent Output Lifespan (MSOL) is the median age (in days) of spent transaction outputs.
|
||||
Outputs with a lifespan of less than 1h are discarded.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.Msol>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/msol'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def dormancy(self) -> pd.DataFrame:
|
||||
"""
|
||||
Dormancy is the average number of days destroyed per coin transacted,
|
||||
and is defined as the ratio of coin days destroyed and total transfer volume.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.AverageDormancy>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/average_dormancy'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def liveliness(self) -> pd.DataFrame:
|
||||
"""
|
||||
Liveliness is defined as the ratio of the sum of Coin Days Destroyed and the sum of all coin days ever created.
|
||||
Liveliness increases as long term holder liquidate positions and decreases while they accumulate to HODL.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.Liveliness>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/liveliness'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def relative_unrealized_profit(self) -> pd.DataFrame:
|
||||
"""
|
||||
Relative Unrealized Profit is defined as the total profit in USD of all coins in existence
|
||||
whose price at realisation time was lower than the current price normalised by the market cap.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.UnrealizedProfit>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/unrealized_profit'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def relative_unrealized_loss(self) -> pd.DataFrame:
|
||||
"""
|
||||
Relative Unrealized Loss is defined as the total loss in USD of all coins in existence
|
||||
whose price at realisation time was higher than the current price normalised by the market cap.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.UnrealizedLoss>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/unrealized_loss'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def nupl(self) -> pd.DataFrame:
|
||||
"""
|
||||
Net Unrealized Profit/Loss (NUPL) is the difference between Relative Unrealized Profit/Loss.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.NetUnrealizedProfitLoss>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/net_unrealized_profit_loss'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def sth_nupl(self) -> pd.DataFrame:
|
||||
"""
|
||||
Short Term Holder NUPL (STH-NUPL) is Net Unrealized Profit/Loss that takes into account only UTXOs
|
||||
younger than 155 days and serves as an indicator to assess the behaviour of short term investors.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.NuplLess155>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/nupl_less_155'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def lth_nupl(self) -> pd.DataFrame:
|
||||
"""
|
||||
Long Term Holder NUPL (LTH-NUPL) is Net Unrealized Profit/Loss that takes into account only UTXOs
|
||||
with a lifespan of at least 155 days and serves as an indicator to assess the behaviour of long term investors.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.NuplMore155>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/nupl_more_155'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def ssr(self) -> pd.DataFrame:
|
||||
"""
|
||||
The Stablecoin Supply Ratio (SSR) is the ratio between Bitcoin supply and the supply of stablecoins denoted
|
||||
in BTC, or: Bitcoin Marketcap / Stablecoin Marketcap. We use the following stablecoins for the supply: USDT,
|
||||
TUSD, USDC, PAX, GUSD, DAI, SAI, and BUSD. When the SSR is low, the current stablecoin supply has more "buying power"
|
||||
to purchase BTC. It serves as a proxy for the supply/demand mechanics between BTC and USD. For more information see
|
||||
this article (https://medium.com/@glassnode/stablecoins-buying-power-over-bitcoin-3475c0d8779d).
|
||||
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=indicators.Ssr>`_
|
||||
"""
|
||||
endpoint = '/v1/metrics/indicators/ssr'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
|
||||
def bvin(self):
|
||||
'''
|
||||
The Bitcoin Volatility Index (BVIN) is an implied volatility index that also represents the fair value of a bitcoin variance swap. The index is calculated by CryptoCompare using options data from Deribit and has been developed in collaboration with Carol Alexander and Arben Imeraj at the University of Sussex Business School. The index is suitable for use as a settlement price for bitcoin volatility futures. For more information on the methodology please see Alexander and Imeraj (2020).
|
||||
'''
|
||||
endpoint = '/v1/metrics/indicators/bvin'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
from .utils import *
|
||||
|
||||
|
||||
class Market:
|
||||
"""
|
||||
Market class.
|
||||
|
||||
Methods
|
||||
-------
|
||||
__init__(glassnode_client):
|
||||
Constructs a Market object.
|
||||
price():
|
||||
Returns the asset's price in USD.
|
||||
price_ohlc():
|
||||
Returns OHLC candlestick data.
|
||||
price_drawdown_from_ath():
|
||||
Returns the percent drawdown from previous all-time high.
|
||||
marketcap():
|
||||
Returns the market capitalization of the asset.
|
||||
mvrv_ratio():
|
||||
Returns MVRV ratio.
|
||||
realized_cap():
|
||||
Returns realized cap data.
|
||||
mvrv_z_score():
|
||||
Returns MVRV Z-Score.
|
||||
sth_mvrv():
|
||||
Returns Short Term Holder MVRV data.
|
||||
lth_mvrv():
|
||||
Returns Long Term Holder MVRV data.
|
||||
realized_price():
|
||||
Returns realized price data.
|
||||
"""
|
||||
def __init__(self, glassnode_client):
|
||||
self._gc = glassnode_client
|
||||
|
||||
def price(self) -> pd.DataFrame:
|
||||
"""
|
||||
The asset's price in USD.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=market.PriceUsd>`_
|
||||
|
||||
:return: A DataFrame containing the asset's price data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/market/price_usd'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
@dataframe_with_inner_object
|
||||
def price_ohlc(self) -> pd.DataFrame:
|
||||
"""
|
||||
OHLC candlestick chart of the asset's price in USD.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=market.PriceUsdOhlc>`_
|
||||
|
||||
:return: A DataFrame containing OHLC candlestick data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/market/price_usd_ohlc'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def price_drawdown_from_ath(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percent drawdown of the asset's price from the previous all-time high.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=market.PriceDrawdownRelative>`_
|
||||
|
||||
:return: A DataFrame containing the percent drawdown data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/market/price_drawdown_relative'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def marketcap(self) -> pd.DataFrame:
|
||||
"""
|
||||
The market capitalization (or network value) is defined as
|
||||
the product of the current supply by the current USD price.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=market.MarketcapUsd>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/market/marketcap_usd'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def mvrv_ratio(self) -> pd.DataFrame:
|
||||
"""
|
||||
MVRV is the ratio between market cap and realised cap.
|
||||
It gives an indication of when the traded price is below a “fair value”.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=market.Mvrv>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/market/mvrv'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def realized_cap(self) -> pd.DataFrame:
|
||||
"""
|
||||
Realized Cap values different part of the supplies at different prices (instead of using current daily close).
|
||||
Specifically, it is computed by valuing each UTXO by the price when it was last moved.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=market.MarketcapRealizedUsd>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/market/marketcap_realized_usd'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def mvrv_z_score(self) -> pd.DataFrame:
|
||||
"""
|
||||
The MVRV Z-Score is used to assess when Bitcoin is over/undervalued relative to its "fair value".
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=market.MvrvZScore>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/market/mvrv_z_score'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def sth_mvrv(self) -> pd.DataFrame:
|
||||
"""
|
||||
Short Term Holder MVRV (STH-MVRV) is MVRV that takes into account only UTXOs younger than 155 days and
|
||||
serves as an indicator to assess the behaviour of short term investors.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=market.MvrvLess155>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/market/mvrv_less_155'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def lth_mvrv(self) -> pd.DataFrame:
|
||||
"""
|
||||
Long Term Holder MVRV (LTH-MVRV) is MVRV that takes into account only UTXOs with a lifespan of at least 155 days
|
||||
and serves as an indicator to assess the behaviour of long term investors
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=market.MvrvMore155>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/market/mvrv_more_155'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def realized_price(self) -> pd.DataFrame:
|
||||
"""
|
||||
Realized Price is the Realized Cap divided by the current supply.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=market.PriceRealizedUsd>`_
|
||||
|
||||
:return: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/market/price_realized_usd'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
@@ -0,0 +1,174 @@
|
||||
from .utils import *
|
||||
|
||||
|
||||
class Mining:
|
||||
"""
|
||||
Mining class.
|
||||
|
||||
Methods
|
||||
-------
|
||||
__init__(glassnode_client):
|
||||
Constructs a Mining object.
|
||||
difficulty():
|
||||
Returns difficulty to mine a block.
|
||||
hash_rate():
|
||||
Returns hash rate.
|
||||
miner_revenue_total():
|
||||
Returns the total miner revenue.
|
||||
miner_revenue_fees():
|
||||
Returns the percentage of miner revenue derived from fees.
|
||||
miner_revenue_block_rewards():
|
||||
Returns the total amount of newly minted coins.
|
||||
miner_outflow_multiple():
|
||||
Returns the miner outflow multiple.
|
||||
thermocap():
|
||||
Returns Thermocap data.
|
||||
market_cap_to_thermocap_ratio():
|
||||
Returns the Marketcap to Thermocap Ratio.
|
||||
miner_unspent_supply():
|
||||
Returns unspent miner supply.
|
||||
miner_names():
|
||||
Returns miner names for a mining endpoint.
|
||||
"""
|
||||
def __init__(self, glassnode_client):
|
||||
self._gc = glassnode_client
|
||||
|
||||
def difficulty(self) -> pd.DataFrame:
|
||||
"""
|
||||
The current estimated number of hashes required to mine a block. Values are denoted in raw hashes.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=mining.DifficultyLatest>`_
|
||||
|
||||
:return: A DataFrame with the latest difficulty data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/mining/difficulty_latest'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def hash_rate(self) -> pd.DataFrame:
|
||||
"""
|
||||
The average estimated number of hashes per second produced by the miners in the network.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=mining.HashRateMean>`_
|
||||
|
||||
:return: A DataFrame with hash rate data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/mining/hash_rate_mean'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def miner_revenue_total(self, miner=None) -> pd.DataFrame:
|
||||
"""
|
||||
The total miner revenue, i.e. fees plus newly minted coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=mining.RevenueSum>`_
|
||||
|
||||
:return: A DataFrame with total revenue data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/mining/revenue_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'m': miner}))
|
||||
|
||||
def miner_revenue_fees(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percentage of miner revenue derived from fees, i.e. fees divided by fees plus minted coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=mining.RevenueFromFees>`_
|
||||
|
||||
:return: A DataFrame with revenue fees data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/mining/revenue_from_fees'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def miner_revenue_block_rewards(self, miner=None) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of newly minted coins, i.e. block rewards.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=mining.VolumeMinedSum>`_
|
||||
|
||||
:return: A DataFrame with revenue block rewards data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/mining/volume_mined_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'m': miner}))
|
||||
|
||||
def miner_outflow_multiple(self, miner=None) -> pd.DataFrame:
|
||||
"""
|
||||
The Miner Outflow Multiple indicates periods where the amount of bitcoins flowing out of
|
||||
miner addresses is high with respect to its historical average.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=mining.MinersOutflowMultiple>`_
|
||||
|
||||
:return: A DataFrame MOM data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/mining/miners_outflow_multiple'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint, {'m': miner}))
|
||||
|
||||
def thermocap(self) -> pd.DataFrame:
|
||||
"""
|
||||
"Thermocap" is the aggregated amount of coins paid to miners and serves as a proxy to mining resources spent.
|
||||
It serves a measure of the true capital flow into Bitcoin.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=mining.Thermocap>`_
|
||||
|
||||
:return: A DataFrame with thermocap data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/mining/thermocap'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def market_cap_to_thermocap_ratio(self) -> pd.DataFrame:
|
||||
"""
|
||||
The Marketcap to Thermocap Ratio can be used to assess if the asset's price is currently trading
|
||||
at a premium with respect to total security spend by miners.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=mining.MarketcapThermocapRatio>`_
|
||||
|
||||
:return: A DataFrame with M/T ratio data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/mining/marketcap_thermocap_ratio'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def miner_unspent_supply(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total mount of coins in coinbase transactions that have never been moved.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=mining.MinersUnspentSupply>`_
|
||||
|
||||
:return: A DataFrame with unspent miner supply data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/mining/miners_unspent_supply'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def miner_names(self, endpoint='revenue_sum') -> list:
|
||||
"""
|
||||
Returns a list of miner names for a mining endpoint.
|
||||
|
||||
:param endpoint: Available endpoints: revenue_sum, volume_mined_sum, miners_outflow_multiple
|
||||
:return: A List with miner names.
|
||||
:rtype: List
|
||||
"""
|
||||
miners = self._gc.get(f'/v1/metrics/mining/{endpoint}/miners')
|
||||
return miners[self._gc.asset]
|
||||
@@ -0,0 +1,65 @@
|
||||
from .utils import *
|
||||
|
||||
|
||||
class Protocols:
|
||||
"""
|
||||
Protocols class.
|
||||
|
||||
Methods
|
||||
-------
|
||||
__init__(glassnode_client):
|
||||
Constructs a Protocols object.
|
||||
uniswap_transactions():
|
||||
Returns the total number of transactions
|
||||
that contains an interaction within Uniswap contracts.
|
||||
uniswap_liquidity():
|
||||
Returns the current liquidity on Uniswap.
|
||||
uniswap_volume():
|
||||
Returns the total volume traded on Uniswap.
|
||||
"""
|
||||
def __init__(self, glassnode_client):
|
||||
self._gc = glassnode_client
|
||||
self._endpoints = self._gc.endpoints
|
||||
|
||||
def uniswap_transactions(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total number of transactions that contains an interaction within Uniswap contracts.
|
||||
Includes Mint, Burn, and Swap events on the Uniswap core contracts.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=protocols.UniswapTransactionCount>`_
|
||||
|
||||
:return: A DataFrame containing Uniswap transactions data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/protocols/uniswap_transaction_count'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def uniswap_liquidity(self) -> pd.DataFrame:
|
||||
"""
|
||||
The current liquidity on Uniswap.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=protocols.UniswapLiquidityLatest>`_
|
||||
|
||||
:return: A DataFrame containing Uniswap liquidity data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/protocols/uniswap_liquidity_latest'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def uniswap_volume(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total volume traded on Uniswap.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=ETH&m=protocols.UniswapVolumeSum>`_
|
||||
|
||||
:return: A DataFrame containing Uniswap volume data.
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/protocols/uniswap_volume_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
@@ -0,0 +1,560 @@
|
||||
from .utils import *
|
||||
from .client import GlassnodeClient
|
||||
|
||||
|
||||
class Supply:
|
||||
"""
|
||||
Supply class.
|
||||
|
||||
Methods
|
||||
-------
|
||||
__init__(glassnode_client):
|
||||
Constructs a Supply object.
|
||||
liquid_illiquid_supply():
|
||||
Returns the total supply held by illiquid, liquid, and highly liquid entities.
|
||||
liquid_supply_change():
|
||||
Returns the monthly (30d) net change of supply held by liquid and highly liquid entities.
|
||||
illiquid_supply_change():
|
||||
Returns the monthly (30d) net change of supply held by illiquid entities.
|
||||
circulating_supply():
|
||||
Returns the total amount of all coins ever created/issued.
|
||||
issuance():
|
||||
Returns the total amount of new coins added to the current supply.
|
||||
inflation_rate():
|
||||
Returns the yearly inflation rate.
|
||||
supply_last_active_less_24h():
|
||||
Returns the amount of circulating supply last moved in the last 24 hours.
|
||||
supply_last_active_1d_1w():
|
||||
Returns the amount of circulating supply last moved between 1 day and 1 week ago.
|
||||
supply_last_active_1w_1m():
|
||||
Returns the amount of circulating supply last moved between 1 week and 1 month ago.
|
||||
supply_last_active_1m_3m():
|
||||
Returns the amount of circulating supply last moved between 1 month and 3 months ago.
|
||||
supply_last_active_3m_6m():
|
||||
Returns the amount of circulating supply last moved between 3 months and 6 months ago.
|
||||
supply_last_active_6m_12m():
|
||||
Returns the amount of circulating supply last moved between 6 months and 12 months ago.
|
||||
supply_last_active_1y_2y():
|
||||
Returns the amount of circulating supply last moved between 1 year and 6 years ago.
|
||||
supply_last_active_2y_3y():
|
||||
Returns the amount of circulating supply last moved between 2 years and 3 years ago.
|
||||
supply_last_active_3y_5y():
|
||||
Returns the amount of circulating supply last moved between 3 years and 5 years ago.
|
||||
supply_last_active_5y_7y():
|
||||
Returns the amount of circulating supply last moved between 5 years and 7 years ago.
|
||||
supply_last_active_7y_10y():
|
||||
Returns the amount of circulating supply last moved between 7 years and 10 years ago.
|
||||
supply_last_active_more_10y():
|
||||
Returns the amount of circulating supply last moved more than 10 years ago.
|
||||
hodl_waves():
|
||||
Returns a bundle of all active supply age bands, aka HODL waves.
|
||||
supply_last_active_more_1y_ago():
|
||||
Returns the percent of circulating supply that has not moved in at least 1 year.
|
||||
supply_last_active_more_2y_ago():
|
||||
Returns the percent of circulating supply that has not moved in at least 2 years.
|
||||
supply_last_active_more_3y_ago():
|
||||
Returns the percent of circulating supply that has not moved in at least 3 years.
|
||||
supply_last_active_more_5y_ago():
|
||||
Returns the percent of circulating supply that has not moved in at least 5 years.
|
||||
realized_cap_hodl_waves():
|
||||
Returns HODL waves weighted by Realized Price.
|
||||
adjusted_supply():
|
||||
Returns the circulating supply adjusted by accounting for lost coins.
|
||||
supply_in_profit():
|
||||
Returns the circulating supply in profit.
|
||||
supply_in_loss():
|
||||
Returns the circulating supply in loss.
|
||||
supply_in_profit_relative():
|
||||
Returns the percentage of circulating supply in profit.
|
||||
short_term_holder_supply():
|
||||
Returns the total amount of circulating supply held by short-term holders.
|
||||
long_term_holder_supply():
|
||||
Returns the total amount of circulating supply held by long-term holders.
|
||||
short_term_holder_supply_in_loss():
|
||||
Returns the total amount of circulating supply that is currently at loss and held by short-term holders.
|
||||
long_term_holder_supply_in_loss():
|
||||
Returns the total amount of circulating supply that is currently at loss and held by long-term holders.
|
||||
short_term_holder_supply_in_profit():
|
||||
Returns the total amount of circulating supply that is currently in profit and held by short-term holders.
|
||||
long_term_holder_supply_in_profit():
|
||||
Returns the total amount of circulating supply that is currently in profit and held by long-term holders.
|
||||
relative_long_short_term_holder_supply():
|
||||
Returns the relative amount of circulating supply of held by long- and short-term holders in profit/loss.
|
||||
long_term_holder_position_change():
|
||||
Returns the monthly net position change of long-term holders.
|
||||
"""
|
||||
def __init__(self, glassnode_client: GlassnodeClient):
|
||||
self._gc = glassnode_client
|
||||
|
||||
def liquid_illiquid_supply(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total supply held by illiquid, liquid, and highly liquid entities.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.LiquidIlliquidSum>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/liquid_illiquid_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def liquid_supply_change(self) -> pd.DataFrame:
|
||||
"""
|
||||
The monthly (30d) net change of supply held by liquid and highly liquid entities.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.LiquidChange>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/liquid_change'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def illiquid_supply_change(self) -> pd.DataFrame:
|
||||
"""
|
||||
The monthly (30d) net change of supply held by illiquid entities.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.IlliquidChange>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/illiquid_change'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def circulating_supply(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of all coins ever created/issued, i.e. the circulating supply.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Current>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/current'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def issuance(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of new coins added to the current supply,
|
||||
i.e. minted coins or new coins released to the network.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Issued>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/issued'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def inflation_rate(self) -> pd.DataFrame:
|
||||
"""
|
||||
The yearly inflation rate, i.e. the percentage of new coins issued,
|
||||
divided by the current supply (annualized).
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.InflationRate>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/inflation_rate'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_less_24h(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved in the last 24 hours.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Active24H>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_24h'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_1d_1w(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved between 1 day and 1 week ago.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Active1D1W>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_1d_1w'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_1w_1m(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved between 1 week and 1 month ago.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Active1W1M>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_1w_1m'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_1m_3m(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved between 1 month and 3 months ago.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Active1M3M>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_1m_3m'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_3m_6m(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved between 3 months and 6 months ago.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Active3M6M>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_3m_6m'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_6m_12m(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved between 6 months and 12 months ago.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Active6M12M>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_6m_12m'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_1y_2y(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved between 1 year and 2 years ago.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Active1Y2Y>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_1y_2y'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_2y_3y(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved between 2 years and 3 years ago.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Active1Y2Y>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_2y_3y'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_3y_5y(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved between 3 years and 5 years ago.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Active3Y5Y>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_3y_5y'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_5y_7y(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved between 5 years and 7 years ago.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Active5Y7Y>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_5y_7y'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_7y_10y(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved between 7 years and 10 years ago.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.Active7Y10Y>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_7y_10y'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_more_10y(self) -> pd.DataFrame:
|
||||
"""
|
||||
The amount of circulating supply last moved more than 10 years ago.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.ActiveMore10Y>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_more_10y'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def hodl_waves(self) -> pd.DataFrame:
|
||||
"""
|
||||
Bundle of all active supply age bands, aka HODL waves.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.HodlWaves>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/hodl_waves'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_more_1y_ago(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percent of circulating supply that has not moved in at least 1 year.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.ActiveMore1YPercent>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_more_1y_percent'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_more_2y_ago(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percent of circulating supply that has not moved in at least 2 years.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.ActiveMore2YPercent>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_more_2y_percent'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_more_3y_ago(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percent of circulating supply that has not moved in at least 3 years.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.ActiveMore3YPercent>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_more_3y_percent'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_last_active_more_5y_ago(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percent of circulating supply that has not moved in at least 5 years.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.ActiveMore5YPercent>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/active_more_5y_percent'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def realized_cap_hodl_waves(self) -> pd.DataFrame:
|
||||
"""
|
||||
HODL Waves weighted by Realized Price.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.RcapHodlWaves>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/rcap_hodl_waves'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def adjusted_supply(self) -> pd.DataFrame:
|
||||
"""
|
||||
The circulating supply adjusted by accounting for lost coins.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.CurrentAdjusted>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/current_adjusted'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_in_profit(self) -> pd.DataFrame:
|
||||
"""
|
||||
The circulating supply in profit,
|
||||
i.e. the amount of coins whose price at the time they last moved was lower than the current price.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.ProfitSum>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/profit_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_in_loss(self) -> pd.DataFrame:
|
||||
"""
|
||||
The circulating supply in loss,
|
||||
i.e. the amount of coins whose price at the time they last moved was higher than the current price.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.LossSum>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/loss_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def supply_in_profit_relative(self) -> pd.DataFrame:
|
||||
"""
|
||||
The percentage of circulating supply in profit.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.ProfitRelative>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/profit_relative'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def short_term_holder_supply(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of circulating supply held by short-term holders.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.SthSum>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/sth_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def long_term_holder_supply(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of circulating supply held by long-term holders.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.LthSum>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/lth_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def short_term_holder_supply_in_loss(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of circulating supply that is currently at loss and held by short-term holders.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.SthLossSum>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/sth_loss_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def long_term_holder_supply_in_loss(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of circulating supply that is currently at loss and held by long-term holders.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.LthLossSum>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/lth_loss_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def short_term_holder_supply_in_profit(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of circulating supply that is currently in profit and held by short-term holders.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.SthProfitSum>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/sth_profit_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def long_term_holder_supply_in_profit(self) -> pd.DataFrame:
|
||||
"""
|
||||
The total amount of circulating supply that is currently in profit and held by long-term holders.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.LthProfitSum>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/lth_profit_sum'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def relative_long_short_term_holder_supply(self) -> pd.DataFrame:
|
||||
"""
|
||||
The relative amount of circulating supply of held by long- and short-term holders in profit/loss.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.LthSthProfitLossRelative>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/lth_sth_profit_loss_relative'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
|
||||
def long_term_holder_position_change(self) -> pd.DataFrame:
|
||||
"""
|
||||
The monthly net position change of long-term holders,
|
||||
i.e. the 30 day change in supply held by long-term holders.
|
||||
`View in Studio <https://studio.glassnode.com/metrics?a=BTC&m=supply.LthNetChange>`_
|
||||
|
||||
:rtype: DataFrame
|
||||
"""
|
||||
endpoint = '/v1/metrics/supply/lth_net_change'
|
||||
if not is_supported_by_endpoint(self._gc, endpoint):
|
||||
return pd.DataFrame()
|
||||
|
||||
return response_to_dataframe(self._gc.get(endpoint))
|
||||
@@ -0,0 +1,72 @@
|
||||
import requests
|
||||
import calendar
|
||||
import pandas as pd
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def unix_timestamp(date_str):
|
||||
"""
|
||||
Returns a unix timestamp to a given date string.
|
||||
|
||||
:param date_str: Date in string format (ex. '2021-01-01').
|
||||
:return: Int Unix-timestamp.
|
||||
"""
|
||||
dt_obj = datetime.strptime(date_str, "%Y-%m-%d")
|
||||
return calendar.timegm(dt_obj.utctimetuple())
|
||||
|
||||
|
||||
def is_supported_by_endpoint(glassnode_client, url):
|
||||
path = glassnode_client.endpoints.query(url)
|
||||
if glassnode_client.asset not in path['assets']:
|
||||
print(f'{url} metric is not available for {glassnode_client.asset}')
|
||||
return False
|
||||
if glassnode_client.resolution not in path['resolutions']:
|
||||
print(f'{url} metric is not available for {glassnode_client.resolution}')
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def response_to_dataframe(response):
|
||||
"""
|
||||
Returns DataFrame from a response objects (ex. {"t":1604361600,"v":0.002}).
|
||||
|
||||
:param response: Response from API.
|
||||
:return: DataFrame.
|
||||
"""
|
||||
try:
|
||||
df = pd.DataFrame(response)
|
||||
df.set_index('t', inplace=True)
|
||||
df.index = pd.to_datetime(df.index, unit='s')
|
||||
df.index.name = None
|
||||
df.sort_index(ascending=False, inplace=True)
|
||||
return df
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
def dataframe_with_inner_object(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
df = func(*args, **kwargs)
|
||||
return pd.concat([df.drop(['o'], axis=1), df['o'].apply(pd.Series)], axis=1)
|
||||
return wrapper
|
||||
|
||||
|
||||
def fetch(endpoint, params=None):
|
||||
"""
|
||||
Returns an object of time, value pairs for a metric from the Glassnode API.
|
||||
|
||||
:param params:
|
||||
:param endpoint: Endpoint url corresponding to some metric (ex. '/v1/metrics/market/price_usd')
|
||||
:return: DataFrame of {'t' : datetime, 'v' : 'metric-value'} pairs
|
||||
"""
|
||||
r = requests.get(f'https://api.glassnode.com{endpoint}', params=params, stream=True)
|
||||
try:
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(e.response.text)
|
||||
|
||||
try:
|
||||
return r.json()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
@@ -30,3 +30,9 @@ def weighted_average(df: pd.DataFrame, weights_source: str) -> pd.DataFrame:
|
||||
return mean_df
|
||||
|
||||
def deduplicate_indexes(df: pd.DataFrame) -> pd.DataFrame: return df[~df.index.duplicated(keep='last')]
|
||||
|
||||
def drop_columns_if_exist(df: pd.DataFrame, columns: list) -> pd.DataFrame:
|
||||
for column in columns:
|
||||
if column in df.columns:
|
||||
df = df.drop(column, axis=1)
|
||||
return df
|
||||
Reference in New Issue
Block a user