From 9290a8457134c8c53d198be19d9a66c2e5d193aa Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 5 Mar 2022 13:56:01 +0800 Subject: [PATCH] =?UTF-8?q?SAFTYLOSS=20=E6=94=AF=E6=8C=81=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/crt/SAFTYLOSS.h | 30 +++++- .../hikyuu/indicator/imp/ISaftyLoss.cpp | 93 ++++++++++++++++++- hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.h | 5 + .../hikyuu/indicator/test_SAFTYLOSS.cpp | 27 ++++++ hikyuu_pywrap/indicator/_build_in.cpp | 28 ++++-- 5 files changed, 171 insertions(+), 12 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/crt/SAFTYLOSS.h b/hikyuu_cpp/hikyuu/indicator/crt/SAFTYLOSS.h index 511b74fe..386d0714 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/SAFTYLOSS.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/SAFTYLOSS.h @@ -24,13 +24,14 @@ namespace hku { * 上移,在上述结果的基础上再取起N日(一般为3天)内的最高值 * * @note:返回结果中前(回溯周期宽度+去最高值的宽度)个点是无效的 - * @param data 输入数据,单一输入 * @param n1 计算平均噪音的回溯时间窗口,默认为10天 * @param n2 对初步止损线去n2日内的最高值,默认为3 * @param p 噪音系数,默认为2 * @ingroup Indicator */ -Indicator HKU_API SAFTYLOSS(const Indicator& data, int n1 = 10, int n2 = 3, double p = 2.0); +Indicator HKU_API SAFTYLOSS(int n1 = 10, int n2 = 3, double p = 2.0); +Indicator HKU_API SAFTYLOSS(const IndParam& n1, const IndParam& n2, double p = 2.0); +Indicator HKU_API SAFTYLOSS(const IndParam& n1, const IndParam& n2, const IndParam& p); /** * 亚历山大 艾尔德安全地带止损 @@ -43,12 +44,35 @@ Indicator HKU_API SAFTYLOSS(const Indicator& data, int n1 = 10, int n2 = 3, doub * 上移,在上述结果的基础上再取起N日(一般为3天)内的最高值 * * @note:返回结果中前(回溯周期宽度+去最高值的宽度)个点是无效的 + * @param data 输入数据,单一输入 * @param n1 计算平均噪音的回溯时间窗口,默认为10天 * @param n2 对初步止损线去n2日内的最高值,默认为3 * @param p 噪音系数,默认为2 * @ingroup Indicator */ -Indicator HKU_API SAFTYLOSS(int n1 = 10, int n2 = 3, double p = 2.0); +inline Indicator SAFTYLOSS(const Indicator& data, int n1 = 10, int n2 = 3, double p = 2.0) { + return SAFTYLOSS(n1, n2, p)(data); +} + +inline Indicator SAFTYLOSS(const Indicator& data, const IndParam& n1, const IndParam& n2, + double p = 2.0) { + return SAFTYLOSS(n1, n2, p)(data); +} + +inline Indicator SAFTYLOSS(const Indicator& data, const IndParam& n1, const IndParam& n2, + const IndParam& p) { + return SAFTYLOSS(n1, n2, p)(data); +} + +inline Indicator SAFTYLOSS(const Indicator& data, const Indicator& n1, const Indicator& n2, + double p = 2.0) { + return SAFTYLOSS(IndParam(n1), IndParam(n2), p)(data); +} + +inline Indicator SAFTYLOSS(const Indicator& data, const Indicator& n1, const Indicator& n2, + const Indicator& p) { + return SAFTYLOSS(IndParam(n1), IndParam(n2), IndParam(p))(data); +} } // namespace hku diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp index 03e8e37d..dee96661 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp @@ -5,6 +5,9 @@ * Author: fasiondog */ +#include "../crt/SLICE.h" +#include "../crt/CVAL.h" +#include "../crt/SAFTYLOSS.h" #include "ISaftyLoss.h" #if HKU_SUPPORT_SERIALIZATION @@ -71,6 +74,80 @@ void ISaftyLoss::_calculate(const Indicator& data) { } } +void ISaftyLoss::_dyn_one_circle(const Indicator& ind, size_t curPos, int n1, int n2, double p) { + HKU_IF_RETURN(n1 < 2 || n2 < 2, void()); + Indicator slice = SLICE(ind, 0, curPos + 1); + Indicator st = SAFTYLOSS(slice, n1, n2, p); + if (st.size() > 0) { + _set(st[st.size() - 1], curPos); + } +} + +void ISaftyLoss::_dyn_calculate(const Indicator& ind) { + auto iter = m_ind_params.find("n1"); + Indicator n1 = + iter != m_ind_params.end() ? Indicator(iter->second) : CVAL(ind, getParam("n1")); + iter = m_ind_params.find("n2"); + Indicator n2 = + iter != m_ind_params.end() ? Indicator(iter->second) : CVAL(ind, getParam("n2")); + iter = m_ind_params.find("p"); + Indicator p = + iter != m_ind_params.end() ? Indicator(iter->second) : CVAL(ind, getParam("p")); + + HKU_CHECK(n1.size() == ind.size(), "ind_param(n1).size()={}, ind.size()={}!", n1.size(), + ind.size()); + HKU_CHECK(n2.size() == ind.size(), "ind_param(n2).size()={}, ind.size()={}!", n2.size(), + ind.size()); + HKU_CHECK(p.size() == ind.size(), "ind_param(p).size()={}, ind.size()={}!", p.size(), + ind.size()); + + m_discard = std::max(ind.discard(), n1.discard()); + m_discard = std::max(m_discard, n2.discard()); + m_discard = std::max(m_discard, p.discard()); + size_t total = ind.size(); + HKU_IF_RETURN(0 == total || m_discard >= total, void()); + + static const size_t minCircleLength = 400; + size_t workerNum = ms_tg->worker_num(); + if (total < minCircleLength || workerNum == 1) { + for (size_t i = ind.discard(); i < total; i++) { + _dyn_one_circle(ind, i, n1[i], n2[i], p[i]); + } + _update_discard(); + return; + } + + size_t circleLength = minCircleLength; + if (minCircleLength * workerNum >= total) { + circleLength = minCircleLength; + } else { + size_t tailCount = total % workerNum; + circleLength = tailCount == 0 ? total / workerNum : total / workerNum + 1; + } + + std::vector> tasks; + for (size_t group = 0; group < workerNum; group++) { + size_t first = circleLength * group; + if (first >= total) { + break; + } + tasks.push_back(ms_tg->submit([=, &ind, &n1, &n2, &p]() { + size_t endPos = first + circleLength; + if (endPos > total) { + endPos = total; + } + for (size_t i = circleLength * group; i < endPos; i++) { + _dyn_one_circle(ind, i, n1[i], n2[i], p[i]); + } + })); + } + + for (auto& task : tasks) { + task.get(); + } + _update_discard(); +} + Indicator HKU_API SAFTYLOSS(int n1, int n2, double p) { IndicatorImpPtr result = make_shared(); result->setParam("n1", n1); @@ -79,8 +156,20 @@ Indicator HKU_API SAFTYLOSS(int n1, int n2, double p) { return Indicator(result); } -Indicator HKU_API SAFTYLOSS(const Indicator& data, int n1, int n2, double p) { - return SAFTYLOSS(n1, n2, p)(data); +Indicator HKU_API SAFTYLOSS(const IndParam& n1, const IndParam& n2, double p) { + IndicatorImpPtr result = make_shared(); + result->setIndParam("n1", n1); + result->setIndParam("n2", n2); + result->setParam("p", p); + return Indicator(result); +} + +Indicator HKU_API SAFTYLOSS(const IndParam& n1, const IndParam& n2, const IndParam& p) { + IndicatorImpPtr result = make_shared(); + result->setIndParam("n1", n1); + result->setIndParam("n2", n2); + result->setIndParam("p", p); + return Indicator(result); } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.h b/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.h index cb52ed5e..34fc860c 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.h @@ -32,6 +32,11 @@ class ISaftyLoss : public hku::IndicatorImp { public: ISaftyLoss(); virtual ~ISaftyLoss(); + + virtual void _dyn_calculate(const Indicator&) override; + +private: + void _dyn_one_circle(const Indicator& ind, size_t curPos, int n1, int n2, double p); }; } /* namespace hku */ diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SAFTYLOSS.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SAFTYLOSS.cpp index 98dbfed3..c3aba8e7 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SAFTYLOSS.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SAFTYLOSS.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include using namespace hku; @@ -128,6 +129,32 @@ TEST_CASE("test_SAFTYLOSS") { } } +/** @par 检测点 */ +TEST_CASE("test_SAFTYLOSS_dyn") { + Stock stock = StockManager::instance().getStock("sh000001"); + KData kdata = stock.getKData(KQuery(-50)); + // KData kdata = stock.getKData(KQuery(0, Null(), KQuery::MIN)); + Indicator c = CLOSE(kdata); + Indicator expect = SAFTYLOSS(c, 10, 3, 2.0); + Indicator result = SAFTYLOSS(c, CVAL(c, 10), CVAL(c, 3), CVAL(c, 2.0)); + CHECK_EQ(expect.size(), result.size()); + for (size_t i = 0; i < result.discard(); i++) { + CHECK_UNARY(std::isnan(result[i])); + } + for (size_t i = result.discard(); i < result.size(); i++) { + CHECK_EQ(expect.get(i, 0), doctest::Approx(result.get(i, 0))); + } + + result = SAFTYLOSS(c, IndParam(CVAL(c, 10)), IndParam(CVAL(c, 3)), IndParam(CVAL(c, 2.0))); + CHECK_EQ(expect.size(), result.size()); + for (size_t i = 0; i < result.discard(); i++) { + CHECK_UNARY(std::isnan(result[i])); + } + for (size_t i = result.discard(); i < result.size(); i++) { + CHECK_EQ(expect.get(i, 0), doctest::Approx(result.get(i, 0))); + } +} + //----------------------------------------------------------------------------- // test export //----------------------------------------------------------------------------- diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index b401a9a2..37c51966 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -83,9 +83,17 @@ Indicator (*REF_4)(const Indicator&, const Indicator&) = REF; Indicator (*REF_5)(const Indicator&, int) = REF; Indicator (*SAFTYLOSS_1)(int n1, int n2, double p) = SAFTYLOSS; -Indicator (*SAFTYLOSS_2)(const Indicator&, int n1, int n2, double p) = SAFTYLOSS; -// BOOST_PYTHON_FUNCTION_OVERLOADS(SAFTYLOSS_1_overload, SAFTYLOSS, 0, 3); -// BOOST_PYTHON_FUNCTION_OVERLOADS(SAFTYLOSS_2_overload, SAFTYLOSS, 1, 4); +Indicator (*SAFTYLOSS_2)(const IndParam& n1, const IndParam& n2, double p) = SAFTYLOSS; +Indicator (*SAFTYLOSS_3)(const IndParam& n1, const IndParam& n2, const IndParam& p) = SAFTYLOSS; +Indicator (*SAFTYLOSS_4)(const Indicator&, int n1, int n2, double p) = SAFTYLOSS; +Indicator (*SAFTYLOSS_5)(const Indicator&, const IndParam& n1, const IndParam& n2, + double p) = SAFTYLOSS; +Indicator (*SAFTYLOSS_6)(const Indicator&, const IndParam& n1, const IndParam& n2, + const IndParam& p) = SAFTYLOSS; +Indicator (*SAFTYLOSS_7)(const Indicator&, const Indicator& n1, const Indicator& n2, + double p) = SAFTYLOSS; +Indicator (*SAFTYLOSS_8)(const Indicator&, const Indicator& n1, const Indicator& n2, + const Indicator& p) = SAFTYLOSS; Indicator (*STDEV_1)(int) = STDEV; Indicator (*STDEV_2)(const IndParam&) = STDEV; @@ -576,7 +584,13 @@ void export_Indicator_build_in() { :rtype: Indicator)"); def("SAFTYLOSS", SAFTYLOSS_1, (arg("n1") = 10, arg("n2") = 3, arg("p") = 2.0)); - def("SAFTYLOSS", SAFTYLOSS_2, (arg("data"), arg("n1") = 10, arg("n2") = 3, arg("p") = 2.0), + def("SAFTYLOSS", SAFTYLOSS_2, (arg("n1"), arg("n2"), arg("p") = 2.0)); + def("SAFTYLOSS", SAFTYLOSS_3, (arg("n1"), arg("n2"), arg("p"))); + def("SAFTYLOSS", SAFTYLOSS_4, (arg("data"), arg("n1") = 10, arg("n2") = 3, arg("p") = 2.0)); + def("SAFTYLOSS", SAFTYLOSS_5, (arg("data"), arg("n1"), arg("n2"), arg("p") = 2.0)); + def("SAFTYLOSS", SAFTYLOSS_6, (arg("data"), arg("n1"), arg("n2"), arg("p"))); + def("SAFTYLOSS", SAFTYLOSS_7, (arg("data"), arg("n1"), arg("n2"), arg("p") = 2.0)); + def("SAFTYLOSS", SAFTYLOSS_8, (arg("data"), arg("n1"), arg("n2"), arg("p")), R"(SAFTYLOSS([data, n1=10, n2=3, p=2.0]) 亚历山大 艾尔德安全地带止损线,参见 [BOOK2]_ @@ -584,9 +598,9 @@ void export_Indicator_build_in() { 计算说明:在回溯周期内(一般为10到20天),将所有向下穿越的长度相加除以向下穿越的次数,得到噪音均值(即回溯期内所有最低价低于前一日最低价的长度除以次数),并用今日最低价减去(前日噪音均值乘以一个倍数)得到该止损线。为了抵消波动并且保证止损线的上移,在上述结果的基础上再取起N日(一般为3天)内的最高值 :param Indicator data: 输入数据 - :param int n1: 计算平均噪音的回溯时间窗口 - :param int n2: 对初步止损线去n2日内的最高值 - :param float p: 噪音系数 + :param int|Indicator|IndParam n1: 计算平均噪音的回溯时间窗口 + :param int|Indicator|IndParam n2: 对初步止损线去n2日内的最高值 + :param float|Indicator|IndParam p: 噪音系数 :rtype: Indicator)"); def("DIFF", DIFF_1);