diff --git a/environment.yml b/environment.yml
index deaaf2428e75d27e30fe3e9fda2f490ad4f6ed81..b1f5e68243c28f695864e2d64abfde09012e617d 100644
--- a/environment.yml
+++ b/environment.yml
@@ -8,7 +8,7 @@ dependencies:
   - make
   # Host dependencies
   - xtl
-  - xtensor
+  - xtensor=0.25
   # Test dependencies
   - gtest
   - gmock
diff --git a/include/evalhyd/detail/probabilist/evaluator.hpp b/include/evalhyd/detail/probabilist/evaluator.hpp
index 3f94fd6d85dcdd94fa5da2224bad5dc914a8397f..c01c54be9479593c98ced5f6a23c472f6c510fb4 100644
--- a/include/evalhyd/detail/probabilist/evaluator.hpp
+++ b/include/evalhyd/detail/probabilist/evaluator.hpp
@@ -36,6 +36,7 @@ namespace evalhyd
             // members for optional input data
             const XD2& _q_thr;
             const xt::xtensor<double, 1>& _c_lvl;
+            const xt::xtensor<double, 1>& _q_lvl;
             xtl::xoptional<const std::string, bool> _events;
             xt::xtensor<bool, 4> t_msk;
             const std::vector<xt::xkeep_slice<int>>& b_exp;
@@ -157,6 +158,11 @@ namespace evalhyd
                 }
             }
 
+            auto get_q_lvl()
+            {
+                return _q_lvl;
+            }
+
             bool is_high_flow_event()
             {
                 if (_events.has_value())
@@ -336,7 +342,7 @@ namespace evalhyd
                 if (!itv_bnds.has_value())
                 {
                     itv_bnds = elements::calc_itv_bnds(
-                            q_prd, get_c_lvl(),
+                            q_prd, get_c_lvl(), get_q_lvl(),
                             n_sit, n_ldt, n_itv, n_tim
                     );
                 }
@@ -393,7 +399,7 @@ namespace evalhyd
                 if (!qs.has_value())
                 {
                     qs = intermediate::calc_qs(
-                            q_obs, get_q_qnt(), n_mbr
+                            q_obs, get_q_qnt(), n_mbr, get_q_lvl()
                     );
                 }
                 return qs.value();
@@ -482,12 +488,13 @@ namespace evalhyd
                       const XD4& prd,
                       const XD2& thr,
                       const xt::xtensor<double, 1>& lvl,
+                      const xt::xtensor<double, 1>& qlvl,
                       xtl::xoptional<const std::string&, bool> events,
                       const XB4& msk,
                       const std::vector<xt::xkeep_slice<int>>& exp,
                       const long int seed) :
                     q_obs{obs}, q_prd{prd},
-                    _q_thr{thr}, _c_lvl{lvl}, _events{events},
+                    _q_thr{thr}, _c_lvl{lvl}, _q_lvl{qlvl}, _events{events},
                     t_msk(msk), b_exp(exp),
                     random_seed{seed}
             {
diff --git a/include/evalhyd/detail/probabilist/intervals.hpp b/include/evalhyd/detail/probabilist/intervals.hpp
index adbd487b592ab4903210072e9ae01836ba41e0ac..f52fd5a3f5a574ee0ad10b56ba94af6c0045d37c 100644
--- a/include/evalhyd/detail/probabilist/intervals.hpp
+++ b/include/evalhyd/detail/probabilist/intervals.hpp
@@ -44,6 +44,7 @@ namespace evalhyd
             inline xt::xtensor<double, 5> calc_itv_bnds(
                     const XD4& q_prd,
                     const xt::xtensor<double, 1>& c_lvl,
+                    const xt::xtensor<double, 1>& q_lvl,
                     std::size_t n_sit,
                     std::size_t n_ldt,
                     std::size_t n_itv,
@@ -60,15 +61,41 @@ namespace evalhyd
                 xt::col(quantiles, 0) = 0.5 - c_lvl / 200.;
                 xt::col(quantiles, 1) = 0.5 + c_lvl / 200.;
 
-                // compute predictive interval bounds from quantiles
-                for (std::size_t i = 0; i < n_itv; i++)
+                // get or compute predictive interval bounds from quantiles
+                if (q_lvl.size() > 0)
+                {   
+                    for (std::size_t i = 0; i < n_itv; i++)
+                    {
+                    auto a = xt::broadcast(xt::view(quantiles, i), std::vector<std::size_t>({q_lvl.size(), 2}));
+                    auto b = xt::broadcast(q_lvl / 100., std::vector<std::size_t>({2, q_lvl.size()}));
+                    auto res = xt::where(xt::equal(a, xt::transpose(b)));
+
+                    if (res.size() != 2) 
+                    {
+                        throw std::runtime_error(
+                                "interval-based metric requested, "
+                                "but *c_lvl* not matching *q_lvl"
+                        );
+                    } else 
+                    {
+                        xt::view(itv_bnds, xt::all(), xt::all(), i, 0, xt::all()) =
+                                xt::view(q_prd, xt::all(), xt::all(), std::min(res[0][0], res[0][1]), xt::all());
+                        xt::view(itv_bnds, xt::all(), xt::all(), i, 1, xt::all()) = 
+                                xt::view(q_prd, xt::all(), xt::all(), std::max(res[0][0], res[0][1]), xt::all());
+                    }
+                    }
+                }
+                else
                 {
+                    for (std::size_t i = 0; i < n_itv; i++)
+                    {
                     auto q =  xt::quantile(q_prd, xt::view(quantiles, i), 2);
 
                     xt::view(itv_bnds, xt::all(), xt::all(), i, 0, xt::all()) =
                             xt::view(q, 0);
                     xt::view(itv_bnds, xt::all(), xt::all(), i, 1, xt::all()) =
                             xt::view(q, 1);
+                    }
                 }
 
                 return itv_bnds;
diff --git a/include/evalhyd/detail/probabilist/quantiles.hpp b/include/evalhyd/detail/probabilist/quantiles.hpp
index 88bd5284b5c3983a2b562ce8e88e37bd92d8b95a..bb13751ea119c9a4538269c29d9b9abfb1e7d62f 100644
--- a/include/evalhyd/detail/probabilist/quantiles.hpp
+++ b/include/evalhyd/detail/probabilist/quantiles.hpp
@@ -60,13 +60,22 @@ namespace evalhyd
             inline xt::xtensor<double, 4> calc_qs(
                     const XD2 &q_obs,
                     const xt::xtensor<double, 4>& q_qnt,
-                    std::size_t n_mbr
+                    std::size_t n_mbr,
+                    const xt::xtensor<double, 1>& q_lvl
             )
             {
-                // compute the quantile order $alpha$
-                xt::xtensor<double, 1> alpha =
+                xt::xtensor<double, 1> alpha;
+                // get or compute the quantile order $alpha$
+                if (q_lvl.size() > 0)
+                {
+                    alpha = xt::sort(q_lvl);
+                }
+                else
+                {
+                    alpha =
                         xt::arange<double>(1., double(n_mbr + 1))
                         / double(n_mbr + 1);
+                }
 
                 // calculate the difference
                 xt::xtensor<double, 4> diff =
diff --git a/include/evalhyd/evalp.hpp b/include/evalhyd/evalp.hpp
index f7f4340f210a2027d49d30d6fcf0a8505c0e4110..0039677616398742211b14cb5ce05e63feea3e08 100644
--- a/include/evalhyd/evalp.hpp
+++ b/include/evalhyd/evalp.hpp
@@ -181,6 +181,7 @@ namespace evalhyd
             xtl::xoptional<const std::string, bool> events =
                     xtl::missing<const std::string>(),
             const std::vector<double>& c_lvl = {},
+            const std::vector<double>& q_lvl = {},
             const xt::xexpression<XB4>& t_msk = XB4({}),
             const xt::xexpression<XS2>& m_cdt = XS2({}),
             xtl::xoptional<const std::unordered_map<std::string, int>, bool> bootstrap =
@@ -233,6 +234,7 @@ namespace evalhyd
 
         // adapt vector to tensor
         const xt::xtensor<double, 1> c_lvl_ = xt::adapt(c_lvl);
+        const xt::xtensor<double, 1> q_lvl_ = xt::adapt(q_lvl);
 
         // check that the metrics/diagnostics to be computed are valid
         utils::check_metrics(
@@ -418,7 +420,7 @@ namespace evalhyd
 
         // instantiate determinist evaluator
         probabilist::Evaluator<XD2, XD4, XB4> evaluator(
-                q_obs_, q_prd_, q_thr_, c_lvl_, events,
+                q_obs_, q_prd_, q_thr_, c_lvl_, q_lvl_, events,
                 t_msk_.size() > 0 ? t_msk_: (m_cdt_.size() > 0 ? c_msk : t_msk_),
                 b_exp,
                 random_seed
diff --git a/tests/expected/evalp/CR_QLVL.csv b/tests/expected/evalp/CR_QLVL.csv
new file mode 100644
index 0000000000000000000000000000000000000000..dfefd7e9ae92a09448178e081d471ade3fe9cc3f
--- /dev/null
+++ b/tests/expected/evalp/CR_QLVL.csv
@@ -0,0 +1 @@
+0.00643087,0.0514469
diff --git a/tests/test_probabilist.cpp b/tests/test_probabilist.cpp
index 87a0303a7387cc0fa14e515ccf4c10ac4ece21da..a4bd6b0c5973e5380009e835202bfad36d27bd7a 100644
--- a/tests/test_probabilist.cpp
+++ b/tests/test_probabilist.cpp
@@ -39,6 +39,17 @@ std::vector<std::string> all_metrics_p = {
         "ES"
 };
 
+std::vector<std::string> all_metrics_p_ = {
+        "BS", "BSS", "BS_CRD", "BS_LBD", "REL_DIAG", "CRPS_FROM_BS",
+        "CRPS_FROM_ECDF",
+        "QS", "CRPS_FROM_QS",
+        "CONT_TBL", "POD", "POFD", "FAR", "CSI", "ROCSS",
+        "RANK_HIST", "DS", "AS",
+        "CR", "AW", "AWN", "WS",
+        "ES",
+        "CR_QLVL"
+};
+
 std::tuple<xt::xtensor<double, 1>, xt::xtensor<double, 2>> load_data_p()
 {
     // read in data
@@ -60,7 +71,7 @@ std::unordered_map<std::string, xt::xarray<double>> load_expected_p()
     std::ifstream ifs;
     std::unordered_map<std::string, xt::xarray<double>> expected;
 
-    for (const auto& metric : all_metrics_p)
+    for (const auto& metric : all_metrics_p_)
     {
         ifs.open(EVALHYD_DATA_DIR "/expected/evalp/" + metric + ".csv");
         expected[metric] = xt::view(
@@ -246,6 +257,7 @@ TEST(ProbabilistTests, TestRanks)
                     xt::xtensor<double, 2>({}),
                     "high",  // events
                     {},  // c_lvl
+                    {},  // q_lvl
                     xt::xtensor<bool, 4>({}),  // t_msk
                     xt::xtensor<std::array<char, 32>, 2>({}),  // m_cdt
                     xtl::missing<const std::unordered_map<std::string, int>>(),  // bootstrap
@@ -296,6 +308,42 @@ TEST(ProbabilistTests, TestIntervals)
     }
 }
 
+TEST(ProbabilistTests, TestIntervalsQLVL)
+{
+    // read in data
+    xt::xtensor<double, 1> observed;
+    xt::xtensor<double, 2> predicted;
+    std::tie(observed, predicted) = load_data_p();
+
+    // read in expected results
+    auto expected = load_expected_p();
+
+    // compute scores
+    std::vector<std::string> metrics = {"CR"};
+    std::vector<std::string> metrics_ = {"CR_QLVL"};
+
+    std::vector<xt::xarray<double>> results =
+            evalhyd::evalp(
+                    // shape: (sites [1], time [t])
+                    xt::eval(xt::view(observed, xt::newaxis(), xt::all())),
+                    // shape: (sites [1], lead times [1], members [m], time [t])
+                    xt::eval(xt::view(predicted, xt::newaxis(), xt::newaxis(), xt::keep(0, 15, 30, 50), xt::all())),
+                    metrics,
+                    xt::xtensor<double, 2>({}),
+                    "",  // events
+                    {50., 80.},  // c_lvl
+                    {10., 25., 75., 90.}  // q_lvl
+            );
+
+    // check results
+    for (std::size_t m = 0; m < metrics.size(); m++)
+    {
+        EXPECT_TRUE(xt::all(xt::isclose(
+                results[m], expected[metrics_[m]], 1e-05, 1e-08, true
+        ))) << "Failure for (" << metrics[m] << ")";
+    }
+}
+
 TEST(ProbabilistTests, TestMultiVariate)
 {
     // read in data
@@ -361,6 +409,7 @@ TEST(ProbabilistTests, TestMasks)
                     thresholds,
                     "high",
                     confidence_levels,
+                    {},  // q_lvl
                     // shape: (sites [1], lead times [1], subsets [1], time [t])
                     masks
             );
@@ -431,6 +480,7 @@ TEST(ProbabilistTests, TestMaskingConditions)
                     thresholds,
                     "high",
                     confidence_levels,
+                    {},  // q_lvl
                     masks,
                     q_conditions
             );
@@ -488,6 +538,7 @@ TEST(ProbabilistTests, TestMaskingConditions)
                     thresholds,
                     "high",
                     confidence_levels,
+                    {},  // q_lvl
                     masks,
                     q_conditions_
             );
@@ -542,6 +593,7 @@ TEST(ProbabilistTests, TestMaskingConditions)
                     thresholds,
                     "high",
                     confidence_levels,
+                    {},  // q_lvl
                     masks,
                     t_conditions
             );
@@ -705,6 +757,7 @@ TEST(ProbabilistTests, TestBootstrap)
                     thresholds,
                     "high",  // events
                     confidence_levels,
+                    {},  // q_lvl
                     xt::xtensor<bool, 4>({}),  // t_msk
                     xt::xtensor<std::array<char, 32>, 2>({}),  // m_cdt
                     bootstrap,
@@ -790,6 +843,7 @@ TEST(ProbabilistTests, TestBootstrapSummary)
                     thresholds,
                     "high",  // events
                     confidence_levels,
+                    {},  // q_lvl
                     xt::xtensor<bool, 4>({}),  // t_msk
                     xt::xtensor<std::array<char, 32>, 2>({}),  // m_cdt
                     bootstrap_0,
@@ -808,6 +862,7 @@ TEST(ProbabilistTests, TestBootstrapSummary)
                     thresholds,
                     "high",  // events
                     confidence_levels,
+                    {},  // q_lvl
                     xt::xtensor<bool, 4>({}),  // t_msk
                     xt::xtensor<std::array<char, 32>, 2>({}),  // m_cdt
                     bootstrap_1,
@@ -859,6 +914,7 @@ TEST(ProbabilistTests, TestBootstrapSummary)
                     thresholds,
                     "high",  // events
                     confidence_levels,
+                    {},  // q_lvl
                     xt::xtensor<bool, 4>({}),  // t_msk
                     xt::xtensor<std::array<char, 32>, 2>({}),  // m_cdt
                     bootstrap_2,
@@ -935,6 +991,7 @@ TEST(ProbabilistTests, TestCompleteness)
                     xt::xtensor<double, 2>({}),  // thresholds
                     xtl::missing<const std::string>(),  // events
                     {},
+                    {},  // q_lvl
                     msk,  // t_msk
                     xt::xtensor<std::array<char, 32>, 2>({}),  // m_cdt
                     xtl::missing<const std::unordered_map<std::string, int>>(),  // bootstrap