diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index af68a302..5aad9e1e 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -207,7 +207,7 @@ :param Indicator ind1: 指标1 :param Indicator ind2: 指标2 - :param int n: 按指定 n 的长度计算两个 ind 直接数据相关系数 + :param int n: 按指定 n 的长度计算两个 ind 直接数据相关系数。如果为0,使用输入的ind长度。 :rtype: Indicator diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp index fe0093b3..323c5e84 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp @@ -305,14 +305,4 @@ Indicator HKU_API IF(const Indicator& x, Indicator::value_t a, Indicator::value_ return IF(x, CVAL(x, a), CVAL(x, b)); } -Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n) { - HKU_ERROR_IF_RETURN(!ind1.getImp() || !ind2.getImp(), Indicator(), - "ind1 or ind2 is Null Indicator!"); - HKU_ERROR_IF_RETURN(n < 2, Indicator(), "Invalid param n: {} (need >= 2)", n); - IndicatorImpPtr p = make_shared("CORR"); - p->setParam("n", n); - p->add(IndicatorImp::CORR, ind1.getImp(), ind2.getImp()); - return p->calculate(); -} - } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.h b/hikyuu_cpp/hikyuu/indicator/Indicator.h index 984ea1f7..677474e8 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.h +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.h @@ -379,14 +379,6 @@ Indicator HKU_API IF(const Indicator& x, Indicator::value_t a, const Indicator& Indicator HKU_API IF(const Indicator& x, const Indicator& a, Indicator::value_t b); Indicator HKU_API IF(const Indicator& x, Indicator::value_t a, Indicator::value_t b); -/** - * 计算样本相关系数与协方差。返回的结果集中,第一个为相关系数,第二个为协方差 - * @param ind1 指标1 - * @param ind2 指标2 - * @ingroup Indicator - */ -Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n); - } /* namespace hku */ #if FMT_VERSION >= 90000 diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index 51d3c032..cf1b37d2 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -93,10 +93,6 @@ string HKU_API getOPTypeName(IndicatorImp::OPType op) { name = "IF"; break; - case IndicatorImp::CORR: - name = "CORR"; - break; - default: name = "UNKNOWN"; break; @@ -534,10 +530,6 @@ string IndicatorImp::formula() const { << m_right->formula() << ")"; break; - case CORR: - buf << m_name << "(" << m_left->formula() << ", " << m_right->formula() << ")"; - break; - default: HKU_ERROR("Wrong optype! {}", int(m_optype)); break; @@ -797,10 +789,6 @@ Indicator IndicatorImp::calculate() { execute_if(); break; - case CORR: - execute_corr(); - break; - default: HKU_ERROR("Unkown Indicator::OPType! {}", int(m_optype)); break; @@ -1463,88 +1451,6 @@ void IndicatorImp::execute_if() { } } -void IndicatorImp::execute_corr() { - m_right->calculate(); - m_left->calculate(); - - const IndicatorImp *maxp, *minp; - if (m_right->size() > m_left->size()) { - maxp = m_right.get(); - minp = m_left.get(); - } else { - maxp = m_left.get(); - minp = m_right.get(); - } - - size_t total = maxp->size(); - size_t discard = maxp->size() - minp->size() + minp->discard(); - if (discard < maxp->discard()) { - discard = maxp->discard(); - } - - // 结果 0 存放相关系数结果 - // 结果 1 存放协方差(COV)结果 - _readyBuffer(total, 2); - - int n = getParam("n"); - if (n < 2 || discard + 2 > total) { - setDiscard(total); - return; - } - - size_t startPos = discard; - size_t first_end = startPos + n >= total ? total : startPos + n; - - value_t kx = maxp->get(discard); - value_t ky = minp->get(discard); - value_t ex = 0.0, ey = 0.0, exy = 0.0, varx = 0.0, vary = 0.0, cov = 0.0; - value_t ex2 = 0.0, ey2 = 0.0; - value_t ix, iy; - - auto *dst0 = this->data(0); - auto *dst1 = this->data(1); - auto const *maxdata = maxp->data(0); - auto const *mindata = minp->data(0); - for (size_t i = startPos + 1; i < first_end; i++) { - ix = maxdata[i] - kx; - iy = mindata[i] - ky; - ex += ix; - ey += iy; - value_t powx2 = ix * ix; - value_t powy2 = iy * iy; - value_t powxy = ix * iy; - exy += powxy; - ex2 += powx2; - ey2 += powy2; - size_t nobs = i - startPos; - varx = ex2 - powx2 / nobs; - vary = ey2 - powy2 / nobs; - cov = exy - powxy / nobs; - dst0[i] = cov / std::sqrt(varx * vary); - dst1[i] = cov / (nobs - 1); - } - - for (size_t i = first_end; i < total; i++) { - ix = maxdata[i] - kx; - iy = mindata[i] - ky; - ex += maxdata[i] - maxdata[i - n]; - ey += mindata[i] - mindata[i - n]; - value_t preix = maxdata[i - n] - kx; - value_t preiy = mindata[i - n] - ky; - ex2 += ix * ix - preix * preix; - ey2 += iy * iy - preiy * preiy; - exy += ix * iy - preix * preiy; - varx = (ex2 - ex * ex / n); - vary = (ey2 - ey * ey / n); - cov = (exy - ex * ey / n); - dst0[i] = cov / std::sqrt(varx * vary); - dst1[i] = cov / (n - 1); - } - - // 修正 discard - setDiscard(discard + 2); -} - void IndicatorImp::_dyn_calculate(const Indicator &ind) { // SPEND_TIME(IndicatorImp__dyn_calculate); const auto &ind_param = getIndParamImp("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h index 91e93952..ea43e041 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h @@ -48,7 +48,6 @@ public: OR, ///< 或 WEAVE, ///< 特殊的,需要两个指标作为参数的指标 OP_IF, /// if操作 - CORR, ///< 相关系数,需要两个指标作为参数 INVALID }; @@ -195,8 +194,6 @@ private: void execute_or(); void execute_weave(); void execute_if(); - void execute_corr(); - void execute_spearman(); std::vector getAllSubNodes(); void repeatALikeNodes(); diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index 6105286c..f116d14d 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -27,6 +27,7 @@ #include "crt/BARSSINCE.h" #include "crt/BETWEEN.h" #include "crt/CEILING.h" +#include "crt/CORR.h" #include "crt/COS.h" #include "crt/COST.h" #include "crt/COUNT.h" diff --git a/hikyuu_cpp/hikyuu/indicator/crt/CORR.h b/hikyuu_cpp/hikyuu/indicator/crt/CORR.h new file mode 100644 index 00000000..ebafe4de --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/CORR.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +/** + * 计算样本相关系数与协方差。返回的结果集中,第一个为相关系数,第二个为协方差 + * @param ind1 指标1 + * @param ind2 指标2 + * @ingroup Indicator + */ +Indicator HKU_API CORR(int n = 10); +Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n = 10); + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h index e559918a..b12f6c15 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h @@ -5,6 +5,7 @@ * Author: fasiondog */ +#pragma once #include "../Indicator.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp new file mode 100644 index 00000000..65c9a03a --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#include "hikyuu/indicator/crt/ALIGN.h" +#include "ICorr.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::ICorr) +#endif + +namespace hku { + +ICorr::ICorr() : IndicatorImp("CORR") { + setParam("n", 10); +} + +ICorr::ICorr(int n) : IndicatorImp("CORR") { + setParam("n", n); +} + +ICorr::ICorr(const Indicator& ref_ind, int n) : IndicatorImp("CORR"), m_ref_ind(ref_ind) { + setParam("n", n); +} + +ICorr::~ICorr() {} + +bool ICorr::check() { + int n = getParam("n"); + return n == 0 || n >= 2; +} + +IndicatorImpPtr ICorr::_clone() { + ICorr* p = new ICorr(); + p->m_ref_ind = m_ref_ind.clone(); + return IndicatorImpPtr(p); +} + +void ICorr::_calculate(const Indicator& ind) { + auto k = getContext(); + m_ref_ind.setContext(k); + Indicator ref = m_ref_ind; + if (m_ref_ind.size() != ind.size()) { + ref = ALIGN(m_ref_ind, ind); + } + + size_t total = ind.size(); + _readyBuffer(total, 2); + HKU_IF_RETURN(total == 0, void()); + + int n = getParam("n"); + if (n == 0) { + n = total; + } + + m_discard = std::max(ind.discard(), ref.discard()); + size_t startPos = m_discard; + size_t first_end = startPos + n >= total ? total : startPos + n; + + auto const* datax = ind.data(); + auto const* datay = ref.data(); + value_t kx = datax[m_discard]; + value_t ky = datay[m_discard]; + value_t ex = 0.0, ey = 0.0, exy = 0.0, varx = 0.0, vary = 0.0, cov = 0.0; + value_t ex2 = 0.0, ey2 = 0.0; + value_t ix, iy; + + auto* dst0 = this->data(0); + auto* dst1 = this->data(1); + for (size_t i = startPos + 1; i < first_end; i++) { + ix = datax[i] - kx; + iy = datay[i] - ky; + ex += ix; + ey += iy; + value_t powx2 = ix * ix; + value_t powy2 = iy * iy; + value_t powxy = ix * iy; + exy += powxy; + ex2 += powx2; + ey2 += powy2; + size_t nobs = i - startPos; + varx = ex2 - powx2 / nobs; + vary = ey2 - powy2 / nobs; + cov = exy - powxy / nobs; + dst0[i] = cov / std::sqrt(varx * vary); + dst1[i] = cov / (nobs - 1); + } + + for (size_t i = first_end; i < total; i++) { + ix = datax[i] - kx; + iy = datay[i] - ky; + ex += datax[i] - datax[i - n]; + ey += datay[i] - datay[i - n]; + value_t preix = datax[i - n] - kx; + value_t preiy = datay[i - n] - ky; + ex2 += ix * ix - preix * preix; + ey2 += iy * iy - preiy * preiy; + exy += ix * iy - preix * preiy; + varx = (ex2 - ex * ex / n); + vary = (ey2 - ey * ey / n); + cov = (exy - ex * ey / n); + dst0[i] = cov / std::sqrt(varx * vary); + dst1[i] = cov / (n - 1); + } + + // 修正 discard + m_discard = (m_discard + 2 < total) ? m_discard + 2 : total; +} + +Indicator HKU_API CORR(int n) { + return make_shared(n); +} + +Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n) { + auto p = make_shared(ind2, n); + Indicator result(p); + return result(ind1); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h new file mode 100644 index 00000000..796f8b40 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +class ICorr : public IndicatorImp { +public: + ICorr(); + ICorr(int n); + ICorr(const Indicator& ref_ind, int n); + virtual ~ICorr(); + + virtual bool check() override; + virtual void _calculate(const Indicator& data) override; + virtual IndicatorImpPtr _clone() override; + +private: + Indicator m_ref_ind; + +//============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(IndicatorImp); + ar& BOOST_SERIALIZATION_NVP(m_ref_ind); + } +#endif +}; + +} \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp index 7de300ec..d81dcc50 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp @@ -76,7 +76,6 @@ static void spearmanLevel(const IndicatorImp::value_t *data, IndicatorImp::value void ISpearman::_calculate(const Indicator &ind) { auto k = getContext(); - m_ref_ind.setContext(k); Indicator ref = m_ref_ind; if (m_ref_ind.size() != ind.size()) { diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp index 312bd73a..26a7b592 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp @@ -10,6 +10,7 @@ #include "../test_config.h" #include #include +#include #include #include @@ -41,13 +42,13 @@ TEST_CASE("test_CORR") { Indicator y = PRICELIST(b); // 非法参数 n - result = CORR(x, y, 0); + result = CORR(x, y, -1); CHECK_UNARY(result.empty()); result = CORR(x, y, 1); CHECK_UNARY(result.empty()); // 正常情况 - result = CORR(x, y, a.size()); + result = CORR(x, y, 0); CHECK_EQ(result.name(), "CORR"); CHECK_EQ(result.discard(), 2); CHECK_EQ(result.size(), a.size()); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp index 03d899a0..f2a26fb4 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp @@ -165,21 +165,14 @@ TEST_CASE("test_SPEARMAN") { price_t null_value = Null(); x = PRICELIST({3., 8., null_value, 4., 7., 2., null_value, null_value}); y = PRICELIST({null_value, 5., 10., 8., null_value, 10., 6., null_value}); - // expect = {null_value, , 1., 0.875, 1.}; - // nan, 8, nan, 4, nan, 2, nan - // nan, 5, nan, 8, nan, 10, nan, result = SPEARMAN(x, y, 4); - HKU_INFO("{}", result); - for (size_t i = result.discard(); i < result.size(); i++) { - HKU_INFO("{}: {}", i, result[i]); - } - - x = PRICELIST({8., 4., 2.}); - y = PRICELIST({5., 8., 10.}); - result = SPEARMAN(x, y, x.size()); - HKU_INFO("{}", result); - HKU_INFO("{}", std::pow(null_value, 2)); - HKU_INFO("{}", 1.0 * 6.0 * null_value / (std::pow(x.size(), 3) - x.size())); + CHECK_EQ(result.name(), "SPEARMAN"); + CHECK_EQ(result.discard(), 3); + CHECK_EQ(result.size(), x.size()); + CHECK_UNARY(std::isnan(result[0])); + CHECK_EQ(result[5], doctest::Approx(-1.)); + CHECK_EQ(result[6], doctest::Approx(-1.)); + CHECK_UNARY(std::isnan(result[7])); } //----------------------------------------------------------------------------- diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 2aa66225..395bc815 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -466,6 +466,9 @@ Indicator (*ZHBOND10_2)(const DatetimeList&, double) = ZHBOND10; Indicator (*ZHBOND10_3)(const KData& k, double) = ZHBOND10; Indicator (*ZHBOND10_4)(const Indicator&, double) = ZHBOND10; +Indicator (*CORR_1)(int) = CORR; +Indicator (*CORR_2)(const Indicator&, const Indicator&, int) = CORR; + Indicator (*SPEARMAN_1)(int) = SPEARMAN; Indicator (*SPEARMAN_2)(const Indicator&, const Indicator&, int) = SPEARMAN; @@ -850,13 +853,15 @@ void export_Indicator_build_in(py::module& m) { :param Indicator ind2: 指标2 :rtype: Indicator)"); - m.def("CORR", CORR, R"(CORR(ind1, ind2, n) + m.def("CORR", CORR_1, py::arg("n") = 10); + m.def("CORR", CORR_2, py::arg("ind1"), py::arg("ind2"), py::arg("n") = 10, + R"(CORR(ind1, ind2, n) 计算 ind1 和 ind2 的相关系数。返回中存在两个结果,第一个为相关系数,第二个为协方差。 :param Indicator ind1: 指标1 :param Indicator ind2: 指标2 - :param int n: 按指定 n 的长度计算两个 ind 直接数据相关系数 + :param int n: 按指定 n 的长度计算两个 ind 直接数据相关系数。如果为0,使用输入的ind长度。 :rtype: Indicator)"); m.def("IF", IF_1);