feat(Data): added daily_glassnode DataCollection (#99)

This commit is contained in:
Mark Aron Szulyovszky
2022-01-03 13:57:36 +01:00
committed by GitHub
parent 442915f847
commit 867269df2b
64 changed files with 74837 additions and 698 deletions
+1
View File
@@ -130,4 +130,5 @@ dmypy.json
lightning/lightning_logs/
results.csv
predictions.csv
wandb/
+31 -2
View File
@@ -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]
+6 -19
View File
@@ -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")
+53
View File
@@ -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
+382
View File
@@ -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
1 time close
2 2021-12-16 0.8795999999999999
3 2021-12-15 0.8729
4 2021-12-14 0.8724
5 2021-12-13 0.8636
6 2021-12-12 0.8543999999999999
7 2021-12-11 0.8449
8 2021-12-10 0.8482
9 2021-12-09 0.0
10 2021-12-08 0.0
11 2021-12-07 1.1388
12 2021-12-06 1.1293
13 2021-12-05 1.1533
14 2021-12-04 1.1755
15 2021-12-03 1.1336
16 2021-12-02 1.1024
17 2021-12-01 1.0802
18 2021-11-30 1.0655
19 2021-11-29 1.0434999999999999
20 2021-11-28 1.047
21 2021-11-27 1.0207
22 2021-11-26 1.0084
23 2021-11-25 1.0076
24 2021-11-24 1.0088
25 2021-11-23 0.9963
26 2021-11-22 0.9928
27 2021-11-21 1.0422
28 2021-11-20 1.0314
29 2021-11-19 1.1879
30 2021-11-18 1.187
31 2021-11-17 1.2176
32 2021-11-16 1.0144
33 2021-11-15 0.9781
34 2021-11-14 1.0138
35 2021-11-13 0.9940000000000001
36 2021-11-12 0.9986
37 2021-11-11 0.9871
38 2021-11-10 0.9823999999999999
39 2021-11-09 0.9833
40 2021-11-08 0.9948
41 2021-11-07 0.9512
42 2021-11-06 0.8983
43 2021-11-05 0.8861
44 2021-11-04 0.8774
45 2021-11-03 0.8637
46 2021-11-02 0.0
47 2021-11-01 0.9593
48 2021-10-31 0.9077
49 2021-10-30 0.8991
50 2021-10-29 0.8952
51 2021-10-28 0.8762000000000001
52 2021-10-27 0.8439
53 2021-10-26 0.879
54 2021-10-25 0.8533
55 2021-10-24 0.8595999999999999
56 2021-10-23 0.8653
57 2021-10-22 0.8526
58 2021-10-21 0.8739
59 2021-10-20 0.9177
60 2021-10-19 0.9172
61 2021-10-18 0.9220999999999999
62 2021-10-17 0.9041
63 2021-10-16 0.8881
64 2021-10-15 0.8955
65 2021-10-14 0.9701000000000001
66 2021-10-13 0.9198999999999999
67 2021-10-12 0.9270999999999999
68 2021-10-11 0.8776
69 2021-10-10 0.8748
70 2021-10-09 0.8765999999999999
71 2021-10-08 0.8915000000000001
72 2021-10-07 0.8675
73 2021-10-06 0.8964
74 2021-10-05 0.9904000000000001
75 2021-10-04 0.9732999999999999
76 2021-10-03 0.879
77 2021-10-02 0.8571
78 2021-10-01 0.8472
79 2021-09-30 0.8946999999999999
80 2021-09-29 0.8713
81 2021-09-28 0.8776
82 2021-09-27 0.8773000000000001
83 2021-09-26 0.8647
84 2021-09-25 0.8326
85 2021-09-24 0.8373999999999999
86 2021-09-23 0.8356999999999999
87 2021-09-22 0.85
88 2021-09-21 0.9041
89 2021-09-20 0.8842
90 2021-09-19 0.8845999999999999
91 2021-09-18 0.8371
92 2021-09-17 0.8814
93 2021-09-16 0.8885
94 2021-09-15 0.9269
95 2021-09-14 0.9279999999999999
96 2021-09-13 0.9225
97 2021-09-12 0.9187000000000001
98 2021-09-11 0.8898999999999999
99 2021-09-10 0.9041
100 2021-09-09 0.9037000000000001
101 2021-09-08 0.9134
102 2021-09-07 0.9484999999999999
103 2021-09-06 0.9122
104 2021-09-05 0.9264
105 2021-09-04 0.9113
106 2021-09-03 0.9046
107 2021-09-02 0.8981999999999999
108 2021-09-01 0.9420000000000001
109 2021-08-31 0.9173
110 2021-08-30 0.9163
111 2021-08-29 0.9037000000000001
112 2021-08-28 0.9018999999999999
113 2021-08-27 0.9018999999999999
114 2021-08-26 0.9079
115 2021-08-25 0.9124
116 2021-08-24 1.5045
117 2021-08-23 1.5569
118 2021-08-22 1.6386
119 2021-08-21 1.4488999999999999
120 2021-08-20 1.466
121 2021-08-19 1.3649
122 2021-08-18 1.26
123 2021-08-17 1.2416
124 2021-08-16 1.2707
125 2021-08-15 1.244
126 2021-08-14 1.1973
127 2021-08-13 1.4963
128 2021-08-12 1.4847
129 2021-08-11 1.1527
130 2021-08-10 1.1872
131 2021-08-09 1.1473
132 2021-08-08 1.0987
133 2021-08-07 1.0685
134 2021-08-06 1.0138
135 2021-08-05 0.9619
136 2021-08-04 0.9329999999999999
137 2021-08-03 0.9301
138 2021-08-02 0.9140999999999999
139 2021-08-01 0.9267
140 2021-07-31 0.9177
141 2021-07-30 0.9086
142 2021-07-29 0.8987999999999999
143 2021-07-28 0.9384999999999999
144 2021-07-27 0.95
145 2021-07-26 0.8615
146 2021-07-25 0.8438
147 2021-07-24 0.8440000000000001
148 2021-07-23 0.8375
149 2021-07-22 0.8714
150 2021-07-21 0.92
151 2021-07-20 0.8661
152 2021-07-19 0.9068
153 2021-07-18 0.8329000000000001
154 2021-07-17 0.8168000000000001
155 2021-07-16 0.8255
156 2021-07-15 0.8371
157 2021-07-14 0.8390000000000001
158 2021-07-13 0.8759
159 2021-07-12 0.8804000000000001
160 2021-07-11 0.8748
161 2021-07-10 0.8674
162 2021-07-09 0.8967
163 2021-07-08 0.9144
164 2021-07-07 0.943
165 2021-07-06 0.9534
166 2021-07-05 0.9512999999999999
167 2021-07-04 0.9781
168 2021-07-03 0.9768000000000001
169 2021-07-02 0.9854
170 2021-07-01 1.0090000000000001
171 2021-06-30 1.0228
172 2021-06-29 1.0228
173 2021-06-28 1.0141
174 2021-06-27 1.0595999999999999
175 2021-06-26 1.0454
176 2021-06-25 1.0627
177 2021-06-24 0.9639
178 2021-06-23 0.9954000000000001
179 2021-06-22 1.0141
180 2021-06-21 1.0025
181 2021-06-20 0.9837
182 2021-06-19 0.8994
183 2021-06-18 0.95
184 2021-06-17 1.0978
185 2021-06-16 1.0972
186 2021-06-15 1.0170000000000001
187 2021-06-14 1.0031
188 2021-06-13 1.0198
189 2021-06-12 0.9825
190 2021-06-11 0.9799
191 2021-06-10 0.9479000000000001
192 2021-06-09 0.9732
193 2021-06-08 1.0314
194 2021-06-07 1.0959999999999999
195 2021-06-06 1.0289
196 2021-06-05 1.0524
197 2021-06-04 1.0344
198 2021-06-03 1.0434
199 2021-06-02 1.0015
200 2021-06-01 1.0675
201 2021-05-31 1.0747
202 2021-05-30 1.1406999999999998
203 2021-05-29 1.2490999999999999
204 2021-05-28 1.1978
205 2021-05-27 1.1871
206 2021-05-26 1.1447
207 2021-05-25 1.2105
208 2021-05-24 1.2407
209 2021-05-23 1.3881999999999999
210 2021-05-22 1.4287
211 2021-05-21 1.4112
212 2021-05-20 1.1401999999999999
213 2021-05-19 1.2304000000000002
214 2021-05-18 1.2313
215 2021-05-17 1.153
216 2021-05-16 1.4169999999999998
217 2021-05-15 1.1312
218 2021-05-14 0.9767
219 2021-05-13 0.9294
220 2021-05-12 0.9333
221 2021-05-11 0.8891
222 2021-05-10 0.8926000000000001
223 2021-05-09 0.9045000000000001
224 2021-05-08 0.8761
225 2021-05-07 0.8675
226 2021-05-06 0.8242
227 2021-05-05 0.8428
228 2021-05-04 0.8452
229 2021-05-03 0.828
230 2021-05-02 0.7959
231 2021-05-01 0.7912
232 2021-04-30 0.784
233 2021-04-29 0.7778
234 2021-04-28 0.7753
235 2021-04-27 0.7866
236 2021-04-26 0.8046
237 2021-04-25 0.8358
238 2021-04-24 0.9231
239 2021-04-23 0.8405
240 2021-04-22 0.8654999999999999
241 2021-04-21 0.7313
242 2021-04-20 0.7206
243 2021-04-19 0.7331
244 2021-04-18 0.7186
245 2021-04-17 0.6662
246 2021-04-16 0.7728
247 2021-04-15 0.7504000000000001
248 2021-04-14 0.7820999999999999
249 2021-04-13 0.8443
250 2021-04-12 0.8729
251 2021-04-11 0.8606999999999999
252 2021-04-10 0.7369
253 2021-04-09 0.7369
254 2021-04-08 0.7369
255 2021-04-07 0.7228
256 2021-04-06 0.7833
257 2021-04-05 0.8011
258 2021-04-04 0.7822
259 2021-04-03 0.7947
260 2021-04-02 0.8273999999999999
261 2021-04-01 0.8203
262 2021-03-31 0.8454
263 2021-03-30 0.8462999999999999
264 2021-03-29 0.8434999999999999
265 2021-03-28 0.8072
266 2021-03-27 0.7955
267 2021-03-26 0.7966
268 2021-03-25 0.8306
269 2021-03-24 0.8779
270 2021-03-23 0.7995
271 2021-03-22 0.8499
272 2021-03-21 0.841
273 2021-03-20 0.8640000000000001
274 2021-03-19 0.9335
275 2021-03-18 1.1472
276 2021-03-17 1.2143000000000002
277 2021-03-16 1.0395999999999999
278 2021-03-15 1.0244
279 2021-03-14 1.0282
280 2021-03-13 1.0471
281 2021-03-12 1.0207
282 2021-03-11 1.02
283 2021-03-10 1.0114
284 2021-03-09 0.9897
285 2021-03-08 1.0117
286 2021-03-07 1.0278
287 2021-03-06 1.0441
288 2021-03-05 1.0454
289 2021-03-04 1.0964
290 2021-03-03 1.0458
291 2021-03-02 1.0773000000000001
292 2021-03-01 1.0701
293 2021-02-28 1.1184
294 2021-02-27 1.1171
295 2021-02-26 1.1195
296 2021-02-25 1.1301999999999999
297 2021-02-24 1.1287
298 2021-02-23 1.2639
299 2021-02-22 1.2473999999999998
300 2021-02-21 1.1508
301 2021-02-20 1.0979
302 2021-02-19 1.1478
303 2021-02-18 1.1699
304 2021-02-17 1.2989
305 2021-02-16 1.2006999999999999
306 2021-02-15 1.1735
307 2021-02-14 1.1683
308 2021-02-13 1.1798
309 2021-02-12 1.188
310 2021-02-11 1.185
311 2021-02-10 1.1858
312 2021-02-09 1.7429
313 2021-02-08 1.716
314 2021-02-07 1.4555
315 2021-02-06 1.4212
316 2021-02-05 1.4105
317 2021-02-04 1.3122
318 2021-02-03 1.3109
319 2021-02-02 1.2405
320 2021-02-01 1.218
321 2021-01-31 1.246
322 2021-01-30 1.2211
323 2021-01-29 1.2284
324 2021-01-28 1.3671
325 2021-01-27 1.2609000000000001
326 2021-01-26 1.1601000000000001
327 2021-01-25 1.1153
328 2021-01-24 1.1540000000000001
329 2021-01-23 1.1540000000000001
330 2021-01-22 1.1536
331 2021-01-21 1.331
332 2021-01-20 1.2878999999999998
333 2021-01-19 1.3485
334 2021-01-18 1.3819
335 2021-01-17 1.3386000000000002
336 2021-01-16 1.3733000000000002
337 2021-01-15 1.5174
338 2021-01-14 1.4821
339 2021-01-13 1.5258
340 2021-01-12 1.39
341 2021-01-11 1.4380000000000002
342 2021-01-10 1.3259999999999998
343 2021-01-09 1.4450999999999998
344 2021-01-08 1.3609
345 2021-01-07 1.3241999999999998
346 2021-01-06 1.2539
347 2021-01-05 1.1691
348 2021-01-04 1.0951
349 2021-01-03 1.0772
350 2021-01-02 1.0979999999999999
351 2021-01-01 0.9168999999999999
352 2020-12-31 0.9837
353 2020-12-30 1.0149
354 2020-12-29 0.9595
355 2020-12-28 0.9007
356 2020-12-27 0.9164
357 2020-12-26 0.9679000000000001
358 2020-12-25 0.8793000000000001
359 2020-12-24 0.8935
360 2020-12-23 1.0022
361 2020-12-22 0.8944
362 2020-12-21 0.8917
363 2020-12-20 0.8898999999999999
364 2020-12-19 0.8859
365 2020-12-18 0.8345999999999999
366 2020-12-17 0.8049
367 2020-12-16 0.84
368 2020-12-15 0.736
369 2020-12-14 0.7301000000000001
370 2020-12-13 0.7378
371 2020-12-12 0.705
372 2020-12-11 0.7308
373 2020-12-10 0.7559999999999999
374 2020-12-09 0.737
375 2020-12-08 0.7735
376 2020-12-07 0.8061
377 2020-12-06 0.8161
378 2020-12-05 0.8008
379 2020-12-04 0.7881
380 2020-12-03 0.8159000000000001
381 2020-12-02 0.8195
382 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
View File
@@ -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
View File
@@ -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])
+1
View File
@@ -23,6 +23,7 @@ dependencies:
- tscv
- tqdm
- pip
- pandas-ta
- pip:
- fracdiff
- ray
+84 -588
View File
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
+2 -11
View File
@@ -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)
+8
View File
@@ -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)
+7
View File
@@ -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
-6
View File
@@ -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
+39
View File
@@ -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()
+62
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+17
View File
@@ -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 *
View File
+285
View File
@@ -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))
+79
View File
@@ -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
+239
View File
@@ -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}))
+174
View File
@@ -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))
+43
View File
@@ -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]
+251
View File
@@ -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))
+127
View File
@@ -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))
+202
View File
@@ -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))
+405
View File
@@ -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))
+173
View File
@@ -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))
+174
View File
@@ -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]
+65
View File
@@ -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))
+560
View File
@@ -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))
View File
+72
View File
@@ -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)
+6
View File
@@ -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