dquant 1.2.1__tar.gz → 1.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dquant
3
- Version: 1.2.1
3
+ Version: 1.2.2
4
4
  Summary: DQuant is an open-source Python library for automated volatility forecasting of financial time series. It handles all stages of model construction, from raw prices to the final forecast.
5
5
  Author: Denis Makarov
6
6
  Project-URL: Homepage, https://dquant.space
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dquant"
7
- version = "1.2.1"
7
+ version = "1.2.2"
8
8
  authors = [
9
9
  { name="Denis Makarov" },
10
10
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dquant
3
- Version: 1.2.1
3
+ Version: 1.2.2
4
4
  Summary: DQuant is an open-source Python library for automated volatility forecasting of financial time series. It handles all stages of model construction, from raw prices to the final forecast.
5
5
  Author: Denis Makarov
6
6
  Project-URL: Homepage, https://dquant.space
@@ -0,0 +1,8 @@
1
+ import numpy as np
2
+
3
+
4
+ def qlike_score(y_true, y_pred):
5
+ sigma2_true = y_true ** 2
6
+ sigma2_pred = np.maximum(y_pred, 1e-10)
7
+ return np.mean(np.log(sigma2_pred) + sigma2_true / sigma2_pred)
8
+
@@ -12,7 +12,7 @@ import numpy as np
12
12
  import xgboost
13
13
  from sklearn.ensemble import GradientBoostingRegressor
14
14
  from sklearn.model_selection import train_test_split
15
- from sklearn.metrics import mean_squared_error, r2_score
15
+ from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
16
16
  from .metrics import qlike_score
17
17
  from sklearn.preprocessing import StandardScaler
18
18
  from typing import Tuple
@@ -72,9 +72,9 @@ class FichEn:
72
72
  raw_windows_X = []
73
73
  raw_windows_y = []
74
74
 
75
- for i in range(window_in + 1, len(data) - window_out + 1):
75
+ for i in range(window_in + 1, len(data) - (window_out + 2)):
76
76
  x_window = data.iloc[i - window_in: i]
77
- y_window = data.iloc[i - 1: i + window_out]
77
+ y_window = data.iloc[i: i + window_out+1]
78
78
 
79
79
  raw_windows_X.append(x_window)
80
80
  raw_windows_y.append(y_window)
@@ -563,12 +563,15 @@ class FichEn:
563
563
 
564
564
  self.train_errors = []
565
565
  self.val_errors = []
566
+ self.train_mae = []
567
+ self.val_mae = []
566
568
  self.train_qlike = []
567
569
  self.val_qlike = []
568
570
  self.train_r2 = []
569
571
  self.val_r2 = []
570
572
 
571
573
  self.best_val_error = float('inf')
574
+ self.best_val_mae = float('inf')
572
575
  self.best_val_qlike = float('inf')
573
576
  self.best_r2 = -float('inf')
574
577
  self.patience_counter = 0
@@ -581,6 +584,8 @@ class FichEn:
581
584
  self.dquantprint(f'{i} trees')
582
585
  t_error = 0
583
586
  v_error = 0
587
+ t_mae = 0
588
+ v_mae = 0
584
589
  t_qlike = 0
585
590
  v_qlike = 0
586
591
  t_r2 = 0
@@ -618,6 +623,8 @@ class FichEn:
618
623
  if i != 1:
619
624
  t_error += mean_squared_error(y_h_clean, self.models[h_idx].predict(X_h))
620
625
  v_error += mean_squared_error(y_h_v_clean, self.models[h_idx].predict(X_h_v))
626
+ t_mae += mean_absolute_error(y_h_clean, self.models[h_idx].predict(X_h))
627
+ v_mae += mean_absolute_error(y_h_v_clean, self.models[h_idx].predict(X_h_v))
621
628
  t_qlike += qlike_score(y_h_clean, self.models[h_idx].predict(X_h))
622
629
  v_qlike += qlike_score(y_h_v_clean, self.models[h_idx].predict(X_h_v))
623
630
  t_r2 += r2_score(y_h_clean, self.models[h_idx].predict(X_h))
@@ -625,6 +632,8 @@ class FichEn:
625
632
  else:
626
633
  t_error += mean_squared_error(y_h_clean, model.predict(X_h))
627
634
  v_error += mean_squared_error(y_h_v_clean, model.predict(X_h_v))
635
+ t_mae += mean_absolute_error(y_h_clean, model.predict(X_h))
636
+ v_mae += mean_absolute_error(y_h_v_clean, model.predict(X_h_v))
628
637
  t_qlike += qlike_score(y_h_clean, model.predict(X_h))
629
638
  v_qlike += qlike_score(y_h_v_clean, model.predict(X_h_v))
630
639
  t_r2 += r2_score(y_h_clean, model.predict(X_h))
@@ -633,6 +642,8 @@ class FichEn:
633
642
 
634
643
  var_test_error = float(t_error)/horizon
635
644
  var_val_error = float(v_error)/horizon
645
+ var_test_mae = float(t_mae) / horizon
646
+ var_val_mae = float(v_mae) / horizon
636
647
  var_test_qlike = float(t_qlike) / horizon
637
648
  var_val_qlike = float(v_qlike) / horizon
638
649
  var_test_r2 = float(t_r2)/horizon
@@ -640,10 +651,20 @@ class FichEn:
640
651
 
641
652
  if self.early_stopping:
642
653
  if len(self.val_errors) > 0:
643
- current_min = min(self.val_errors)
644
- best_so_far = min(self.best_val_error, current_min)
645
-
646
- no_improvement_count = len(self.val_errors) - self.val_errors.index(best_so_far) - 1
654
+ if self.loss == "MAE":
655
+ current_min = min(self.val_mae)
656
+ best_so_far = min(self.best_val_mae, current_min)
657
+ no_improvement_count = len(self.val_mae) - self.val_mae.index(best_so_far) - 1
658
+ elif self.loss == "MSE":
659
+ current_min = min(self.val_errors)
660
+ best_so_far = min(self.best_val_error, current_min)
661
+ no_improvement_count = len(self.val_errors) - self.val_errors.index(best_so_far) - 1
662
+ elif self.loss == "QLIKE":
663
+ current_min = min(self.val_qlike)
664
+ best_so_far = min(self.best_val_qlike, current_min)
665
+ no_improvement_count = len(self.val_qlike) - self.val_qlike.index(best_so_far) - 1
666
+ else:
667
+ raise "Unavailable loss function"
647
668
 
648
669
  if no_improvement_count >= self.patience:
649
670
  self.dquantprint(f'Early stopping at {i} trees (no improvement for {self.patience} steps)')
@@ -655,6 +676,8 @@ class FichEn:
655
676
 
656
677
  self.train_errors.append(var_test_error)
657
678
  self.val_errors.append(var_val_error)
679
+ self.train_mae.append(var_test_mae)
680
+ self.val_mae.append(var_val_mae)
658
681
  self.train_qlike.append(var_test_qlike)
659
682
  self.val_qlike.append(var_val_qlike)
660
683
  self.train_r2.append(var_test_r2)
@@ -663,6 +686,8 @@ class FichEn:
663
686
  self.dquantprint('Validation QLIKE: ', var_val_qlike)
664
687
  self.dquantprint('Train MSE: ', var_test_error)
665
688
  self.dquantprint('Validation MSE: ', var_val_error)
689
+ self.dquantprint('Train MAE: ', var_test_mae)
690
+ self.dquantprint('Validation MAE: ', var_val_mae)
666
691
  self.dquantprint('Train r2: ', var_test_r2)
667
692
  self.dquantprint('Validation r2: ', var_val_r2)
668
693
  self.dquantprint(f"{time.time() - start} seconds spent")
@@ -1439,7 +1464,8 @@ class FichEn:
1439
1464
 
1440
1465
 
1441
1466
  class VolClustGB(FichEn):
1442
- def __init__(self, sett, early_stopping=True, output=True):
1467
+ def __init__(self, sett, early_stopping=True, output=True, loss="MAE"):
1468
+ self.loss = loss
1443
1469
  self.output = output
1444
1470
  self.models = []
1445
1471
  self.scaler = StandardScaler()
@@ -1462,7 +1488,8 @@ class VolClustGB(FichEn):
1462
1488
  }
1463
1489
  self.meta = {
1464
1490
  "model_type": "gb",
1465
- "model_settings": self.default_sett
1491
+ "model_settings": self.default_sett,
1492
+ "model_loss": loss
1466
1493
  }
1467
1494
  if sett == {}:
1468
1495
  self.base_model = GradientBoostingRegressor(**self.default_sett)
@@ -1599,7 +1626,8 @@ class VolClustGB(FichEn):
1599
1626
 
1600
1627
 
1601
1628
  class VolClustXGB(FichEn):
1602
- def __init__(self, sett, early_stopping=True, output=True, qlike=True):
1629
+ def __init__(self, sett, early_stopping=True, output=True, loss="QLIKE"):
1630
+ self.loss = loss
1603
1631
  self.output = output
1604
1632
  self.models = []
1605
1633
  self.scaler = StandardScaler()
@@ -1623,15 +1651,18 @@ class VolClustXGB(FichEn):
1623
1651
  'device': 'cpu'
1624
1652
  }
1625
1653
 
1626
- if qlike == False:
1654
+ if loss == "MSE":
1627
1655
  self.default_sett['objective'] = 'reg:squarederror'
1656
+ elif loss == "MAE":
1657
+ self.default_sett['objective'] = 'reg:absoluteerror'
1628
1658
 
1629
1659
  self.meta = {
1630
1660
  "model_type": "xgb",
1631
- "model_settings": self.default_sett
1661
+ "model_settings": self.default_sett,
1662
+ "model_loss": loss
1632
1663
  }
1633
1664
  if sett == {}:
1634
- if qlike:
1665
+ if loss == "QLIKE":
1635
1666
  self.base_model = xgboost.XGBRegressor(**self.default_sett, objective=self.qlike_obj)
1636
1667
  else:
1637
1668
  self.base_model = xgboost.XGBRegressor(**self.default_sett)
@@ -1640,7 +1671,7 @@ class VolClustXGB(FichEn):
1640
1671
  if sett['objective']: del sett['objective']
1641
1672
  except KeyError:
1642
1673
  pass
1643
- if qlike:
1674
+ if loss == "QLIKE":
1644
1675
  self.base_model = xgboost.XGBRegressor(**sett, objective=self.qlike_obj)
1645
1676
  else:
1646
1677
  self.base_model = xgboost.XGBRegressor(**sett)
@@ -1772,7 +1803,8 @@ class VolClustXGB(FichEn):
1772
1803
 
1773
1804
 
1774
1805
  class VolClustLightGBM(FichEn):
1775
- def __init__(self, sett, early_stopping=True, output=True, qlike=True):
1806
+ def __init__(self, sett, early_stopping=True, output=True, loss="QLIKE"):
1807
+ self.loss = loss
1776
1808
  self.output = output
1777
1809
  self.models = []
1778
1810
  self.scaler = StandardScaler()
@@ -1798,15 +1830,18 @@ class VolClustLightGBM(FichEn):
1798
1830
  'boosting_type': 'gbdt'
1799
1831
  }
1800
1832
 
1801
- if qlike == False:
1802
- self.default_sett['objective'] = 'regression'
1833
+ if loss == "MSE":
1834
+ self.default_sett['objective'] = 'mse'
1835
+ elif loss == "MAE":
1836
+ self.default_sett['objective'] = 'mae'
1803
1837
 
1804
1838
  self.meta = {
1805
1839
  "model_type": "lgbm",
1806
- "model_settings": self.default_sett
1840
+ "model_settings": self.default_sett,
1841
+ "models_loss": loss
1807
1842
  }
1808
1843
  if sett == {}:
1809
- if qlike:
1844
+ if loss == "QLIKE":
1810
1845
  self.base_model = lgb.LGBMRegressor(**self.default_sett, objective=self.qlike_obj)
1811
1846
  else:
1812
1847
  self.base_model = lgb.LGBMRegressor(**self.default_sett)
@@ -1815,7 +1850,7 @@ class VolClustLightGBM(FichEn):
1815
1850
  if sett['objective']: del sett['objective']
1816
1851
  except KeyError:
1817
1852
  pass
1818
- if qlike:
1853
+ if loss == "QLIKE":
1819
1854
  self.base_model = lgb.LGBMRegressor(**sett, objective=self.qlike_obj)
1820
1855
  else:
1821
1856
  self.base_model = lgb.LGBMRegressor(**sett)
@@ -1,10 +0,0 @@
1
- import numpy as np
2
-
3
-
4
- def qlike_score(y_true, y_pred):
5
- y_true = np.asarray(y_true, dtype=np.float64)
6
- y_pred = np.asarray(y_pred, dtype=np.float64)
7
- eps = 1e-10
8
- y_pred = np.clip(y_pred, eps, None)
9
- loss = np.log(y_pred) + y_true / y_pred
10
- return np.mean(loss)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes