diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index ecc9f3b8..af68a302 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -869,7 +869,7 @@ :param Indicator ind1: 输入参数1 :param Indicator ind2: 输入参数2 - :param int n: 指定窗口 + :param int n: 滚动窗口(大于2 或 等于0),等于0时,代表 n 实际使用 ind 的长度 .. py:function:: SQRT([data]) diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp index 5e7da4b4..fe0093b3 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp @@ -315,14 +315,4 @@ Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n) { return p->calculate(); } -// Indicator HKU_API SPEARMAN(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("SPEARMAN"); -// p->setParam("n", n); -// p->add(IndicatorImp::SPEARMAN, 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 c43163ed..984ea1f7 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.h +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.h @@ -387,14 +387,6 @@ Indicator HKU_API IF(const Indicator& x, Indicator::value_t a, Indicator::value_ */ Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n); -/** - * Spearman 相关系数 - * @param ind1 指标1 - * @param ind2 指标2 - * @ingroup Indicator - */ -// Indicator HKU_API SPEARMAN(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 d2662a1a..51d3c032 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -97,10 +97,6 @@ string HKU_API getOPTypeName(IndicatorImp::OPType op) { name = "CORR"; break; - case IndicatorImp::SPEARMAN: - name = "SPEARMAN"; - break; - default: name = "UNKNOWN"; break; @@ -542,10 +538,6 @@ string IndicatorImp::formula() const { buf << m_name << "(" << m_left->formula() << ", " << m_right->formula() << ")"; break; - case SPEARMAN: - buf << m_name << "(" << m_left->formula() << ", " << m_right->formula() << ")"; - break; - default: HKU_ERROR("Wrong optype! {}", int(m_optype)); break; @@ -809,10 +801,6 @@ Indicator IndicatorImp::calculate() { execute_corr(); break; - case SPEARMAN: - execute_spearman(); - break; - default: HKU_ERROR("Unkown Indicator::OPType! {}", int(m_optype)); break; @@ -1557,116 +1545,6 @@ void IndicatorImp::execute_corr() { setDiscard(discard + 2); } -static void spearmanLevel(const IndicatorImp::value_t *data, IndicatorImp::value_t *level, - size_t total) { - std::vector> data_index(total); - for (size_t i = 0; i < total; i++) { - data_index[i].first = data[i]; - data_index[i].second = i; - } - - std::sort( - data_index.begin(), data_index.end(), - std::bind( - std::less(), - std::bind(&std::pair::first, std::placeholders::_1), - std::bind(&std::pair::first, std::placeholders::_2))); - - size_t i = 0; - while (i < total) { - size_t count = 1; - IndicatorImp::value_t score = i + 1.0; - for (size_t j = i + 1; j < total; j++) { - if (data_index[i].first != data_index[j].first) { - break; - } - count++; - score += j + 1; - } - score = score / count; - for (size_t j = 0; j < count; j++) { - level[data_index[i + j].second] = score; - } - i += count; - } -} - -void IndicatorImp::execute_spearman() { - 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(); - } - - size_t result_number = std::min(minp->getResultNumber(), maxp->getResultNumber()); - size_t diff = maxp->size() - minp->size(); - _readyBuffer(total, result_number); - - int n = getParam("n"); - if (n < 2 || discard + 2 > total) { - setDiscard(total); - return; - } - - discard += n - 1; - setDiscard(discard); - - auto levela = std::make_unique(n); - auto levelb = std::make_unique(n); - auto *ptra = levela.get(); - auto *ptrb = levelb.get(); - - // 不处理 n 不足的情况,防止只需要计算全部序列时,过于耗时 - double back = std::pow(n, 3) - n; - vector tmpa; - vector tmpb; - tmpa.reserve(n); - tmpa.reserve(n); - for (size_t r = 0; r < result_number; ++r) { - auto *dst = this->data(r); - auto const *maxdata = maxp->data(r); - auto const *mindata = minp->data(r); - auto const *a = maxdata + discard + 1 - n; - auto const *b = mindata + discard + 1 - diff - n; - for (size_t i = discard; i < total; ++i) { - tmpa.clear(); - tmpb.clear(); - for (int j = 0; j < n; j++) { - if (!std::isnan(a[j]) && !std::isnan(b[j])) { - tmpa.push_back(a[j]); - tmpb.push_back(b[j]); - } - } - int act_count = tmpa.size(); - if (act_count < 2) { - continue; - } - spearmanLevel(tmpa.data(), ptra, act_count); - spearmanLevel(tmpb.data(), ptrb, act_count); - value_t sum = 0.0; - for (int j = 0; j < act_count; j++) { - sum += std::pow(ptra[j] - ptrb[j], 2); - } - dst[i] = act_count == n ? 1.0 - 6.0 * sum / back - : 1.0 - 6.0 * sum / (std::pow(act_count, 3) - act_count); - a++; - b++; - } - } -} - 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 e2100e26..91e93952 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h @@ -31,25 +31,24 @@ class HKU_API IndicatorImp : public enable_shared_from_this { public: enum OPType { - LEAF, ///< 叶子节点 - OP, /// OP(OP1,OP2) OP1->calcalue(OP2->calculate(ind)) - ADD, ///< 加 - SUB, ///< 减 - MUL, ///< 乘 - DIV, ///< 除 - MOD, ///< 取模 - EQ, ///< 等于 - GT, ///< 大于 - LT, ///< 小于 - NE, ///< 不等于 - GE, ///< 大于等于 - LE, ///< 小于等于 - AND, ///< 与 - OR, ///< 或 - WEAVE, ///< 特殊的,需要两个指标作为参数的指标 - OP_IF, /// if操作 - CORR, ///< 相关系数,需要两个指标作为参数 - SPEARMAN, ///< spearman 相关系数 + LEAF, ///< 叶子节点 + OP, /// OP(OP1,OP2) OP1->calcalue(OP2->calculate(ind)) + ADD, ///< 加 + SUB, ///< 减 + MUL, ///< 乘 + DIV, ///< 除 + MOD, ///< 取模 + EQ, ///< 等于 + GT, ///< 大于 + LT, ///< 小于 + NE, ///< 不等于 + GE, ///< 大于等于 + LE, ///< 小于等于 + AND, ///< 与 + OR, ///< 或 + WEAVE, ///< 特殊的,需要两个指标作为参数的指标 + OP_IF, /// if操作 + CORR, ///< 相关系数,需要两个指标作为参数 INVALID }; diff --git a/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h index af285f9c..e559918a 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h @@ -13,8 +13,10 @@ namespace hku { * Spearman 相关系数 * @param ind1 指标1 * @param ind2 指标2 + * @param n 滚动窗口(大于2 或 等于0),等于0时,代表 n 实际使用 ind 的长度 * @ingroup Indicator */ +Indicator HKU_API SPEARMAN(int n = 0); Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n = 0); } // namespace hku \ 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 e34bdce7..7de300ec 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp @@ -18,8 +18,13 @@ ISpearman::ISpearman() : IndicatorImp("SPEARMAN") { setParam("n", 0); } -ISpearman::ISpearman(const Indicator &ref_ind) : IndicatorImp("SPEARMAN"), m_ref_ind(ref_ind) { - setParam("n", 0); +ISpearman::ISpearman(int n) : IndicatorImp("SPEARMAN") { + setParam("n", n); +} + +ISpearman::ISpearman(const Indicator &ref_ind, int n) +: IndicatorImp("SPEARMAN"), m_ref_ind(ref_ind) { + setParam("n", n); } ISpearman::~ISpearman() {} @@ -135,10 +140,14 @@ void ISpearman::_calculate(const Indicator &ind) { } } +Indicator HKU_API SPEARMAN(int n) { + return make_shared(n); +} + Indicator HKU_API SPEARMAN(const Indicator &ind1, const Indicator &ind2, int n) { - ISpearman *p = new ISpearman(ind2); - p->setParam("n", n); - return IndicatorImpPtr(p); + 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/ISpearman.h b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h index dc407e0c..ac313e1d 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h @@ -14,7 +14,8 @@ namespace hku { class ISpearman : public IndicatorImp { public: ISpearman(); - ISpearman(const Indicator& ref_ind); + ISpearman(int n); + ISpearman(const Indicator& ref_ind, int n); virtual ~ISpearman(); virtual bool check() override; diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp index cf3c0511..03d899a0 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp @@ -152,34 +152,34 @@ TEST_CASE("test_SPEARMAN") { CHECK_EQ(result[i], doctest::Approx(expect[i])); } - // expect = {Null(), Null(), 1., 0.875, 1.}; - // result = SPEARMAN(x, y, 3); - // CHECK_EQ(result.name(), "SPEARMAN"); - // CHECK_EQ(result.discard(), 2); - // CHECK_EQ(result.size(), a.size()); - // for (size_t i = result.discard(); i < result.size(); i++) { - // CHECK_EQ(result[i], doctest::Approx(expect[i])); - // } + expect = {Null(), Null(), 1., 0.875, 1.}; + result = SPEARMAN(x, y, 3); + CHECK_EQ(result.name(), "SPEARMAN"); + CHECK_EQ(result.discard(), 2); + CHECK_EQ(result.size(), a.size()); + for (size_t i = result.discard(); i < result.size(); i++) { + CHECK_EQ(result[i], doctest::Approx(expect[i])); + } - // /** @arg 包含 nan 值 */ - // price_t null_value = Null(); - // auto x = PRICELIST({3., 8., null_value, 4., 7., 2., null_value, null_value}); - // auto 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]); - // } + /** @arg 包含 nan 值 */ + 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())); + 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())); } //----------------------------------------------------------------------------- @@ -210,50 +210,50 @@ TEST_CASE("test_SPEARMAN_benchmark") { /** @par 检测点 */ TEST_CASE("test_SPEARMAN_export") { - // StockManager &sm = StockManager::instance(); - // string filename(sm.tmpdir()); - // filename += "/SPEARMAN.xml"; + StockManager &sm = StockManager::instance(); + string filename(sm.tmpdir()); + filename += "/SPEARMAN.xml"; - // Stock stock = sm.getStock("sh000001"); - // KData kdata = stock.getKData(KQuery(-20)); - // Indicator x1 = SPEARMAN(CLOSE(kdata), OPEN(kdata), 10); - // { - // std::ofstream ofs(filename); - // boost::archive::xml_oarchive oa(ofs); - // oa << BOOST_SERIALIZATION_NVP(x1); - // } + Stock stock = sm.getStock("sh000001"); + KData kdata = stock.getKData(KQuery(-20)); + Indicator x1 = SPEARMAN(CLOSE(kdata), OPEN(kdata), 10); + { + std::ofstream ofs(filename); + boost::archive::xml_oarchive oa(ofs); + oa << BOOST_SERIALIZATION_NVP(x1); + } - // Indicator x2; - // { - // std::ifstream ifs(filename); - // boost::archive::xml_iarchive ia(ifs); - // ia >> BOOST_SERIALIZATION_NVP(x2); - // } + Indicator x2; + { + std::ifstream ifs(filename); + boost::archive::xml_iarchive ia(ifs); + ia >> BOOST_SERIALIZATION_NVP(x2); + } - // CHECK_EQ(x2.name(), "SPEARMAN"); - // CHECK_EQ(x1.size(), x2.size()); - // CHECK_EQ(x1.discard(), x2.discard()); - // CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); - // for (size_t i = x1.discard(); i < x1.size(); ++i) { - // if (std::isnan(x1[i])) { - // CHECK_UNARY(std::isnan(x2[i])); - // } else { - // CHECK_EQ(x1[i], doctest::Approx(x2[i])); - // } - // } + CHECK_EQ(x2.name(), "SPEARMAN"); + CHECK_EQ(x1.size(), x2.size()); + CHECK_EQ(x1.discard(), x2.discard()); + CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); + for (size_t i = x1.discard(); i < x1.size(); ++i) { + if (std::isnan(x1[i])) { + CHECK_UNARY(std::isnan(x2[i])); + } else { + CHECK_EQ(x1[i], doctest::Approx(x2[i])); + } + } - // Indicator x11 = x1.getResult(1); - // Indicator x21 = x2.getResult(1); - // CHECK_EQ(x11.size(), x21.size()); - // CHECK_EQ(x11.discard(), x21.discard()); - // CHECK_EQ(x11.getResultNumber(), x21.getResultNumber()); - // for (size_t i = x11.discard(); i < x21.size(); ++i) { - // if (std::isnan(x11[i])) { - // CHECK_UNARY(std::isnan(x21[i])); - // } else { - // CHECK_EQ(x11[i], doctest::Approx(x21[i])); - // } - // } + Indicator x11 = x1.getResult(1); + Indicator x21 = x2.getResult(1); + CHECK_EQ(x11.size(), x21.size()); + CHECK_EQ(x11.discard(), x21.discard()); + CHECK_EQ(x11.getResultNumber(), x21.getResultNumber()); + for (size_t i = x11.discard(); i < x21.size(); ++i) { + if (std::isnan(x11[i])) { + CHECK_UNARY(std::isnan(x21[i])); + } else { + CHECK_EQ(x11[i], doctest::Approx(x21[i])); + } + } } #endif /* #if HKU_SUPPORT_SERIALIZATION */ diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 3be04865..2aa66225 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 (*SPEARMAN_1)(int) = SPEARMAN; +Indicator (*SPEARMAN_2)(const Indicator&, const Indicator&, int) = SPEARMAN; + void export_Indicator_build_in(py::module& m) { m.def("KDATA", KDATA1); m.def("KDATA", KDATA3, R"(KDATA([data]) @@ -1645,12 +1648,13 @@ void export_Indicator_build_in(py::module& m) { :param DatetimeList|KDate|Indicator data: 输入的日期参考,优先使用上下文中的日期 :param float default_val: 如果输入的日期早于已有国债数据的最早记录,则使用此默认值)"); - m.def("SPEARMAN", SPEARMAN, py::arg("ind1"), py::arg("ind2"), py::arg("n"), - R"(SPEARMAN(ind1, ind2, n) + m.def("SPEARMAN", SPEARMAN_1, py::arg("n") = 0); + m.def("SPEARMAN", SPEARMAN_2, py::arg("ind1"), py::arg("ind2"), py::arg("n") = 0, + R"(SPEARMAN(ind1, ind2[, n]) Spearman 相关系数 :param Indicator ind1: 输入参数1 :param Indicator ind2: 输入参数2 - :param int n: 指定窗口)"); + :param int n: 滚动窗口(大于2 或 等于0),等于0时,代表 n 实际使用 ind 的长度)"); }