From f094b58c5880660139552e491d6dc63a75e53e93 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 8 Feb 2025 03:28:12 +0800 Subject: [PATCH 1/6] =?UTF-8?q?SG=E5=A2=9E=E5=BC=BA=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E9=80=BB=E8=BE=91=E5=8F=8A=E5=9B=9B=E5=88=99=E8=BF=90?= =?UTF-8?q?=E7=AE=97(continue)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/DataType.h | 5 + .../hikyuu/trade_sys/signal/SignalBase.cpp | 143 ++++++++++++++++-- .../hikyuu/trade_sys/signal/SignalBase.h | 44 +++++- .../trade_sys/signal/imp/logic/AddSignal.cpp | 69 +++++++++ .../trade_sys/signal/imp/logic/AddSignal.h | 41 +++++ .../hikyuu/trade_sys/signal/test_Signal.cpp | 68 +++++++++ hikyuu_pywrap/trade_sys/_Signal.cpp | 9 +- 7 files changed, 356 insertions(+), 23 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.h diff --git a/hikyuu_cpp/hikyuu/DataType.h b/hikyuu_cpp/hikyuu/DataType.h index 1a7486470..b54ba71a5 100644 --- a/hikyuu_cpp/hikyuu/DataType.h +++ b/hikyuu_cpp/hikyuu/DataType.h @@ -148,6 +148,11 @@ using std::isfinite; using std::isinf; using std::isnan; +inline bool iszero(price_t num) { + const price_t epsilon = std::numeric_limits::epsilon(); + return std::abs(num) < epsilon; +} + using fmt::format; inline std::ostream &operator<<(std::ostream &os, const PriceList &p) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index 51dea9666..cfb07ebf1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -108,48 +108,159 @@ void SignalBase::startCycle(const Datetime& start, const Datetime& close) { } DatetimeList SignalBase::getBuySignal() const { - DatetimeList result(m_buySig.size()); - std::copy(m_buySig.begin(), m_buySig.end(), result.begin()); + DatetimeList result; + result.reserve(m_buySig.size()); + for (auto iter = m_buySig.begin(); iter != m_buySig.end(); ++iter) { + result.emplace_back(iter->first); + } return result; } DatetimeList SignalBase::getSellSignal() const { - DatetimeList result(m_sellSig.size()); - std::copy(m_sellSig.begin(), m_sellSig.end(), result.begin()); + DatetimeList result; + result.reserve(m_sellSig.size()); + for (auto iter = m_sellSig.begin(); iter != m_sellSig.end(); ++iter) { + result.emplace_back(iter->first); + } return result; } -void SignalBase::_addBuySignal(const Datetime& datetime) { - if (!getParam("alternate")) { - m_buySig.insert(datetime); - } else { +double SignalBase::getBuyValue(const Datetime& datetime) const { + auto iter = m_buySig.find(datetime); + return iter != m_buySig.end() ? iter->second : 0.0; +} +double SignalBase::getSellValue(const Datetime& datetime) const { + auto iter = m_sellSig.find(datetime); + return iter != m_sellSig.end() ? iter->second : 0.0; +} + +void SignalBase::_addSignal(const Datetime& datetime, double value) { + HKU_IF_RETURN(iszero(value), void()); + + double new_value = value + getBuyValue(datetime) + getSellValue(datetime); + HKU_IF_RETURN(iszero(new_value), void()); + + if (new_value > 0.0) { + auto iter = m_buySig.find(datetime); + if (!getParam("alternate")) { + if (iter != m_buySig.end()) { + iter->second += new_value; + } else { + m_buySig.insert({datetime, new_value}); + } + return; + } + if (!m_hold_long) { - m_buySig.insert(datetime); + if (iter != m_buySig.end()) { + iter->second += new_value; + } else { + m_buySig.insert({datetime, new_value}); + } if (getParam("support_borrow_stock") && m_hold_short) { m_hold_short = false; } else { m_hold_long = true; } } - } -} -void SignalBase::_addSellSignal(const Datetime& datetime) { - if (!getParam("alternate")) { - m_sellSig.insert(datetime); } else { + auto iter = m_sellSig.find(datetime); + if (!getParam("alternate")) { + if (iter != m_sellSig.end()) { + iter->second += new_value; + } else { + m_sellSig.insert({datetime, new_value}); + } + return; + } + if (!m_hold_short) { if (m_hold_long) { - m_sellSig.insert(datetime); + if (iter != m_sellSig.end()) { + iter->second += new_value; + } else { + m_sellSig.insert({datetime, new_value}); + } m_hold_long = false; } else if (getParam("support_borrow_stock")) { - m_sellSig.insert(datetime); + if (iter != m_sellSig.end()) { + iter->second += new_value; + } else { + m_sellSig.insert({datetime, new_value}); + } m_hold_short = true; } } } } +// void SignalBase::_addBuySignal(const Datetime& datetime, double value) { +// HKU_CHECK(value > 0.0, "value must > 0!"); +// HKU_WARN_IF_RETURN(m_sellSig.find(datetime) != m_sellSig.end(), void(), +// "Ignore buy sigal! Conflict with sell signal at {}!", datetime); + +// double nvalue = getBuyValue(datetime) + getSellValue(datetime); +// HKU_IF_RETURN(iszero(nvalue), void()); + +// auto iter = m_buySig.find(datetime); +// if (!getParam("alternate")) { +// if (iter != m_buySig.end()) { +// iter->second += value; +// } else { +// m_buySig.insert({datetime, value}); +// } +// return; +// } + +// if (!m_hold_long) { +// if (iter != m_buySig.end()) { +// iter->second += value; +// } else { +// m_buySig.insert({datetime, value}); +// } +// if (getParam("support_borrow_stock") && m_hold_short) { +// m_hold_short = false; +// } else { +// m_hold_long = true; +// } +// } +// } + +// void SignalBase::_addSellSignal(const Datetime& datetime, double value) { +// HKU_CHECK(value < 0.0, "value must < 0!"); +// HKU_WARN_IF_RETURN(m_buySig.find(datetime) != m_buySig.end(), void(), +// "Ignore sell sigal! Conflict with buy signal at {}!", datetime); + +// auto iter = m_sellSig.find(datetime); +// if (!getParam("alternate")) { +// if (iter != m_sellSig.end()) { +// iter->second += value; +// } else { +// m_sellSig.insert({datetime, value}); +// } +// return; +// } + +// if (!m_hold_short) { +// if (m_hold_long) { +// if (iter != m_sellSig.end()) { +// iter->second += value; +// } else { +// m_sellSig.insert({datetime, value}); +// } +// m_hold_long = false; +// } else if (getParam("support_borrow_stock")) { +// if (iter != m_sellSig.end()) { +// iter->second += value; +// } else { +// m_sellSig.insert({datetime, value}); +// } +// m_hold_short = true; +// } +// } +// } + bool SignalBase::nextTimeShouldBuy() const { size_t total = m_kdata.size(); HKU_IF_RETURN(total == 0, false); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h index 6cab84286..e79c70056 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h @@ -44,6 +44,22 @@ class HKU_API SignalBase : public enable_shared_from_this { */ bool shouldSell(const Datetime& datetime) const; + /** + * 获取指定时刻的买入信号数值,返回值小于等于0时,表示无买入信号 + * @param datetime + * @return double + */ + double getBuyValue(const Datetime& datetime) const; + + /** + * 获取指定时刻的买出信号数值,返回值大于等于0时,表示无卖出信号 + * @param datetime + * @return double + */ + double getSellValue(const Datetime& datetime) const; + + double getValue(const Datetime& datetime) const; + /** * 下一时刻是否可以买入,相当于最后时刻是否指示买入 */ @@ -60,17 +76,21 @@ class HKU_API SignalBase : public enable_shared_from_this { /** 获取所有卖出指示日期列表 */ DatetimeList getSellSignal() const; + void _addSignal(const Datetime& datetime, double value); + /** * 加入买入信号,在_calculate中调用 * @param datetime 发生买入信号的日期 + * @param value 信号值,默认为1.0, 必须大于0,否则抛出异常 */ - void _addBuySignal(const Datetime& datetime); + void _addBuySignal(const Datetime& datetime, double value = 1.0); /** * 加入卖出信号,在_calculate中调用 * @param datetime + * @param value 信号值,默认为-1.0,必须小于0,否则抛出异常 */ - void _addSellSignal(const Datetime& datetime); + void _addSellSignal(const Datetime& datetime, double value = -1.0); /** * 指定交易对象,指K线数据 @@ -121,9 +141,9 @@ class HKU_API SignalBase : public enable_shared_from_this { /* 空头持仓 */ bool m_hold_short; - // 用 set 保存,以便获取是能保持顺序 - std::set m_buySig; - std::set m_sellSig; + // 用 map 保存,以便获取是能保持顺序 + std::map m_buySig; + std::map m_sellSig; Datetime m_cycle_start; Datetime m_cycle_end; @@ -236,6 +256,20 @@ inline const Datetime& SignalBase::getCycleEnd() const { return m_cycle_end; } +inline double SignalBase::getValue(const Datetime& datetime) const { + return getBuyValue(datetime) + getSellValue(datetime); +} + +inline void SignalBase::_addBuySignal(const Datetime& datetime, double value) { + HKU_CHECK(value > 0.0, "buy value muse be > 0", value); + _addSignal(datetime, value); +} + +inline void SignalBase::_addSellSignal(const Datetime& datetime, double value) { + HKU_CHECK(value < 0.0, "sell value muse be > 0", value); + _addSignal(datetime, value); +} + } /* namespace hku */ #if FMT_VERSION >= 90000 diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp new file mode 100644 index 000000000..ea34b0b8e --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#include "AddSignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::AddSignal) +#endif + +namespace hku { + +AddSignal::AddSignal() : SignalBase("SG_Add") {} + +AddSignal::AddSignal(const SignalPtr& sg1, const SignalPtr& sg2) : SignalBase("SG_Add") { + if (sg1) { + m_sg1 = sg1->clone(); + } + if (sg2) { + m_sg2 = sg2->clone(); + } +} + +AddSignal::~AddSignal() {} + +SignalPtr AddSignal::_clone() { + return make_shared(m_sg1, m_sg2); +} + +void AddSignal::_calculate(const KData& kdata) { + HKU_IF_RETURN(!m_sg1 && !m_sg2, void()); + + auto const* ks = kdata.data(); + size_t total = kdata.size(); + + if (m_sg1 && !m_sg2) { + m_sg1->_calculate(kdata); + for (size_t i = 0; i < total; ++i) { + double value = m_sg1->getValue(ks[i].datetime); + _addSignal(ks[i].datetime, value); + } + return; + } + + if (!m_sg1 && m_sg2) { + m_sg2->_calculate(kdata); + for (size_t i = 0; i < total; i++) { + double value = m_sg2->getValue(ks[i].datetime); + _addSignal(ks[i].datetime, value); + return; + } + } + + m_sg1->_calculate(kdata); + m_sg2->_calculate(kdata); + for (size_t i = 0; i < total; ++i) { + double value = m_sg1->getValue(ks[i].datetime) + m_sg2->getValue(ks[i].datetime); + _addSignal(ks[i].datetime, value); + } +} + +SignalPtr HKU_API SG_Add(const SignalPtr& sg1, const SignalPtr& sg2) { + return make_shared(sg1, sg2); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.h new file mode 100644 index 000000000..b585aa1dc --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/trade_sys/signal/SignalBase.h" + +namespace hku { + +class AddSignal : public SignalBase { +public: + AddSignal(); + AddSignal(const SignalPtr& sg1, const SignalPtr& sg2); + virtual ~AddSignal(); + + virtual SignalPtr _clone() override; + virtual void _calculate(const KData& kdata) override; + +private: + SignalPtr m_sg1; + SignalPtr m_sg2; + +//============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SignalBase); + ar& BOOST_SERIALIZATION_NVP(m_sg1); + ar& BOOST_SERIALIZATION_NVP(m_sg2); + } +#endif +}; + +} /* namespace hku */ diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp index 87967fea2..7feff37d6 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp @@ -62,7 +62,9 @@ TEST_CASE("test_Signal") { CHECK_EQ(p->shouldBuy(Datetime(200101010000)), false); p->_addBuySignal(Datetime(200101010000)); CHECK_EQ(p->shouldBuy(Datetime(200101010000)), true); + CHECK_EQ(p->getBuyValue(Datetime(200101010000)), 1.0); CHECK_EQ(p->shouldSell(Datetime(200101030000)), false); + CHECK_EQ(p->getSellValue(Datetime(200101030000)), 0.0); p->_addSellSignal(Datetime(200101030000)); CHECK_EQ(p->shouldSell(Datetime(200101030000)), true); @@ -74,6 +76,72 @@ TEST_CASE("test_Signal") { CHECK_EQ(p_src->getX(), 10); CHECK_EQ(p_clone->shouldBuy(Datetime(200101010000)), true); CHECK_EQ(p_clone->shouldSell(Datetime(200101030000)), true); + + /** @arg 插入重复买入日期 */ + p->reset(); + p->setParam("alternate", true); + REQUIRE(!p->shouldBuy(Datetime(200201010000))); + p->_addBuySignal(Datetime(200201010000)); + CHECK_UNARY(p->shouldBuy(Datetime(200201010000))); + p->_addBuySignal(Datetime(200201010000)); + CHECK_UNARY(p->shouldBuy(Datetime(200201010000))); + + p->reset(); + p->setParam("alternate", false); + REQUIRE(!p->shouldBuy(Datetime(200201010000))); + p->_addBuySignal(Datetime(200201010000)); + CHECK_UNARY(p->shouldBuy(Datetime(200201010000))); + p->_addBuySignal(Datetime(200201010000)); + CHECK_UNARY(p->shouldBuy(Datetime(200201010000))); + + /** @arg 插入重复卖出日期 */ + p->reset(); + p->setParam("alternate", true); + p->_addBuySignal(Datetime(200201010000)); + REQUIRE(!p->shouldSell(Datetime(200201020000))); + p->_addSellSignal(Datetime(200201020000)); + CHECK_UNARY(p->shouldSell(Datetime(200201020000))); + p->_addSellSignal(Datetime(200201020000)); + CHECK_UNARY(p->shouldSell(Datetime(200201020000))); + + p->reset(); + p->setParam("alternate", false); + REQUIRE(!p->shouldSell(Datetime(200201020000))); + p->_addSellSignal(Datetime(200201020000)); + CHECK_UNARY(p->shouldSell(Datetime(200201020000))); + p->_addSellSignal(Datetime(200201020000)); + CHECK_UNARY(p->shouldSell(Datetime(200201020000))); + + /** @arg 插入买入日期已经存在卖出指示 */ + p->reset(); + p->setParam("alternate", false); + p->_addSellSignal(Datetime(200202010000)); + REQUIRE(p->shouldSell(Datetime(200202010000))); + p->_addBuySignal(Datetime(200202010000)); + CHECK_UNARY(!p->shouldBuy(Datetime(200202010000))); + + p->reset(); + p->setParam("alternate", true); + p->_addBuySignal(Datetime(200201010000)); + p->_addSellSignal(Datetime(200202010000)); + REQUIRE(p->shouldSell(Datetime(200202010000))); + p->_addBuySignal(Datetime(200202010000)); + CHECK_UNARY(!p->shouldBuy(Datetime(200202010000))); + + /** @arg 插入卖出日期已经存在买入指示 */ + p->reset(); + p->setParam("alternate", false); + p->_addBuySignal(Datetime(200202010000)); + p->_addSellSignal(Datetime(200202010000)); + CHECK_UNARY(p->shouldBuy(Datetime(200202010000))); + CHECK_UNARY(!p->shouldSell(Datetime(200202010000))); + + p->reset(); + p->setParam("alternate", true); + p->_addBuySignal(Datetime(200202010000)); + p->_addSellSignal(Datetime(200202010000)); + CHECK_UNARY(p->shouldBuy(Datetime(200202010000))); + CHECK_UNARY(!p->shouldSell(Datetime(200202010000))); } SUBCASE("Short sell") { diff --git a/hikyuu_pywrap/trade_sys/_Signal.cpp b/hikyuu_pywrap/trade_sys/_Signal.cpp index e028f7780..52487b2a5 100644 --- a/hikyuu_pywrap/trade_sys/_Signal.cpp +++ b/hikyuu_pywrap/trade_sys/_Signal.cpp @@ -108,13 +108,18 @@ void export_Signal(py::module& m) { :rtype: DatetimeList)") - .def("_add_buy_signal", &SignalBase::_addBuySignal, R"(_add_buy_signal(self, datetime) + .def("_add_signal", &SignalBase::_addSignal, py::arg("datetime"), py::arg("value"), R"()") + + .def("_add_buy_signal", &SignalBase::_addBuySignal, py::arg("datetime"), + py::arg("value") = 1.0, + R"(_add_buy_signal(self, datetime) 加入买入信号,在_calculate中调用 :param Datetime datetime: 指示买入的日期)") - .def("_add_sell_signal", &SignalBase::_addSellSignal, R"(_add_sell_signal(self, datetime) + .def("_add_sell_signal", &SignalBase::_addSellSignal, py::arg("datetime"), + py::arg("value") = -1.0, R"(_add_sell_signal(self, datetime) 加入卖出信号,在_calculate中调用 From 60007c752f3f2aa1d4ae887e9a8ed20f07a2b309 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 8 Feb 2025 15:53:03 +0800 Subject: [PATCH 2/6] =?UTF-8?q?SG=E5=A2=9E=E5=BC=BA=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E9=80=BB=E8=BE=91=E5=8F=8A=E5=9B=9B=E5=88=99=E8=BF=90?= =?UTF-8?q?=E7=AE=97(continue)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h | 1 + .../hikyuu/trade_sys/signal/crt/SG_Logic.h | 37 ++++++++++ .../trade_sys/signal/imp/logic/AddSignal.cpp | 19 +----- .../trade_sys/signal/imp/logic/AddSignal.h | 30 ++------- .../signal/imp/logic/AddValueSignal.cpp | 44 ++++++++++++ .../signal/imp/logic/AddValueSignal.h | 19 ++++++ .../trade_sys/signal/imp/logic/DivSignal.cpp | 60 +++++++++++++++++ .../trade_sys/signal/imp/logic/DivSignal.h | 19 ++++++ .../signal/imp/logic/DivValueSignal.cpp | 48 +++++++++++++ .../signal/imp/logic/DivValueSignal.h | 22 ++++++ .../trade_sys/signal/imp/logic/MulSignal.cpp | 36 ++++++++++ .../trade_sys/signal/imp/logic/MulSignal.h | 19 ++++++ .../signal/imp/logic/MulValueSignal.cpp | 36 ++++++++++ .../signal/imp/logic/MulValueSignal.h | 19 ++++++ .../signal/imp/logic/OperatorSignal.cpp | 44 ++++++++++++ .../signal/imp/logic/OperatorSignal.h | 65 ++++++++++++++++++ .../signal/imp/logic/OperatorValueSignal.cpp | 40 +++++++++++ .../signal/imp/logic/OperatorValueSignal.h | 67 +++++++++++++++++++ .../trade_sys/signal/imp/logic/SubSignal.cpp | 52 ++++++++++++++ .../trade_sys/signal/imp/logic/SubSignal.h | 19 ++++++ .../signal/imp/logic/SubValueSignal.cpp | 62 +++++++++++++++++ .../signal/imp/logic/SubValueSignal.h | 22 ++++++ 22 files changed, 736 insertions(+), 44 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Logic.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivValueSignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivValueSignal.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulValueSignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulValueSignal.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorSignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorSignal.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h b/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h index 204bda4b5..49d12a59d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h @@ -17,6 +17,7 @@ #include "crt/SG_Single.h" #include "crt/SG_Bool.h" #include "crt/SG_Band.h" +#include "crt/SG_Logic.h" #include "crt/SG_Manual.h" #endif /* SIGNAL_BUILD_IN_H_ */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Logic.h b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Logic.h new file mode 100644 index 000000000..0188fc472 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Logic.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#pragma once + +#include "../SignalBase.h" + +namespace hku { + +HKU_API SignalPtr operator+(const SignalPtr& sg1, const SignalPtr& sg2); +HKU_API SignalPtr operator-(const SignalPtr& sg1, const SignalPtr& sg2); +HKU_API SignalPtr operator*(const SignalPtr& sg1, const SignalPtr& sg2); +HKU_API SignalPtr operator/(const SignalPtr& sg1, const SignalPtr& sg2); + +HKU_API SignalPtr operator+(const SignalPtr& sg, double value); +HKU_API SignalPtr operator-(const SignalPtr& sg, double value); +HKU_API SignalPtr operator*(const SignalPtr& sg, double value); +HKU_API SignalPtr operator/(const SignalPtr& sg, double value); + +HKU_API SignalPtr operator+(double value, const SignalPtr& sg); +HKU_API SignalPtr operator-(double value, const SignalPtr& sg); +HKU_API SignalPtr operator*(double value, const SignalPtr& sg); +HKU_API SignalPtr operator/(double value, const SignalPtr& sg); + +inline SignalPtr operator&(const SignalPtr& sg1, const SignalPtr& sg2) { + return sg1 * sg2; +} + +inline SignalPtr operator|(const SignalPtr& sg1, const SignalPtr& sg2) { + return sg1 + sg2; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp index ea34b0b8e..e81b1044b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp @@ -13,23 +13,6 @@ BOOST_CLASS_EXPORT(hku::AddSignal) namespace hku { -AddSignal::AddSignal() : SignalBase("SG_Add") {} - -AddSignal::AddSignal(const SignalPtr& sg1, const SignalPtr& sg2) : SignalBase("SG_Add") { - if (sg1) { - m_sg1 = sg1->clone(); - } - if (sg2) { - m_sg2 = sg2->clone(); - } -} - -AddSignal::~AddSignal() {} - -SignalPtr AddSignal::_clone() { - return make_shared(m_sg1, m_sg2); -} - void AddSignal::_calculate(const KData& kdata) { HKU_IF_RETURN(!m_sg1 && !m_sg2, void()); @@ -62,7 +45,7 @@ void AddSignal::_calculate(const KData& kdata) { } } -SignalPtr HKU_API SG_Add(const SignalPtr& sg1, const SignalPtr& sg2) { +HKU_API SignalPtr operator+(const SignalPtr& sg1, const SignalPtr& sg2) { return make_shared(sg1, sg2); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.h index b585aa1dc..610dc60d1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.h @@ -7,35 +7,13 @@ #pragma once -#include "hikyuu/trade_sys/signal/SignalBase.h" +#include "OperatorSignal.h" namespace hku { -class AddSignal : public SignalBase { -public: - AddSignal(); - AddSignal(const SignalPtr& sg1, const SignalPtr& sg2); - virtual ~AddSignal(); - - virtual SignalPtr _clone() override; - virtual void _calculate(const KData& kdata) override; - -private: - SignalPtr m_sg1; - SignalPtr m_sg2; - -//============================================ -// 序列化支持 -//============================================ -#if HKU_SUPPORT_SERIALIZATION - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) { - ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SignalBase); - ar& BOOST_SERIALIZATION_NVP(m_sg1); - ar& BOOST_SERIALIZATION_NVP(m_sg2); - } -#endif +class AddSignal : public OperatorSignal { + OPERATOR_SIGNAL_IMP(AddSignal, "SG_Add") + OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.cpp new file mode 100644 index 000000000..678d99589 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#include "AddValueSignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::AddValueSignal) +#endif + +namespace hku { + +void AddValueSignal::_calculate(const KData& kdata) { + HKU_IF_RETURN(!m_sg || std::isnan(m_value), void()); + + auto const* ks = kdata.data(); + size_t total = kdata.size(); + + m_sg->_calculate(kdata); + if (m_value == 0.0) { + for (size_t i = 0; i < total; ++i) { + _addSignal(ks[i].datetime, m_sg->getValue(ks[i].datetime)); + } + } else { + for (size_t i = 0; i < total; ++i) { + double buy_value = m_sg->getBuyValue(ks[i].datetime) + m_value; + double sell_value = m_sg->getSellValue(ks[i].datetime) - m_value; + _addSignal(ks[i].datetime, buy_value + sell_value); + } + } +} + +HKU_API SignalPtr operator+(const SignalPtr& sg, double value) { + return make_shared(sg, value); +} + +HKU_API SignalPtr operator+(double value, const SignalPtr& sg) { + return make_shared(sg, value); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.h new file mode 100644 index 000000000..f2904c5c1 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorValueSignal.h" + +namespace hku { + +class AddValueSignal : public OperatorValueSignal { + OPERATOR_SIGNAL_IMP(AddValueSignal, "SG_AddValue") + OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION +}; + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.cpp new file mode 100644 index 000000000..663a45637 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#include "DivSignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::DivSignal) +#endif + +namespace hku { + +void DivSignal::_calculate(const KData& kdata) { + HKU_IF_RETURN(!m_sg1 && !m_sg2, void()); + + auto const* ks = kdata.data(); + size_t total = kdata.size(); + + if (m_sg1 && !m_sg2) { + m_sg1->_calculate(kdata); + for (size_t i = 0; i < total; ++i) { + double value = m_sg1->getValue(ks[i].datetime); + _addSignal(ks[i].datetime, value); + } + return; + } + + if (!m_sg1 && m_sg2) { + m_sg2->_calculate(kdata); + for (size_t i = 0; i < total; i++) { + double value = m_sg2->getValue(ks[i].datetime); + if (value != 0.0) { + _addSignal(ks[i].datetime, 1.0 / value); + } + return; + } + } + + m_sg1->_calculate(kdata); + m_sg2->_calculate(kdata); + for (size_t i = 0; i < total; ++i) { + double buy_value1 = m_sg1->getBuyValue(ks[i].datetime); + double buy_value2 = m_sg2->getBuyValue(ks[i].datetime); + double buy_value = (buy_value2 != 0.0) ? buy_value1 / buy_value2 : 0.0; + double sell_value1 = m_sg1->getSellValue(ks[i].datetime); + double sell_value2 = m_sg2->getSellValue(ks[i].datetime); + double sell_value = (sell_value2 != 0.0) ? sell_value1 / sell_value2 : 0.0; + double value = buy_value - sell_value; + _addSignal(ks[i].datetime, value); + } +} + +HKU_API SignalPtr operator/(const SignalPtr& sg1, const SignalPtr& sg2) { + return make_shared(sg1, sg2); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.h new file mode 100644 index 000000000..6cdc985cb --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorSignal.h" + +namespace hku { + +class DivSignal : public OperatorSignal { + OPERATOR_SIGNAL_IMP(DivSignal, "SG_Div") + OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION +}; + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivValueSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivValueSignal.cpp new file mode 100644 index 000000000..9fe51415f --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivValueSignal.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#include "DivValueSignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::DivValueSignal) +#endif + +namespace hku { + +DivValueSignal::DivValueSignal(double value, const SignalPtr& sg) +: OperatorValueSignal("SG_DivVlaue", sg, value, 1) {} + +void DivValueSignal::_calculate(const KData& kdata) { + HKU_IF_RETURN(!m_sg || m_value == 0.0 || std::isnan(m_value), void()); + + auto const* ks = kdata.data(); + size_t total = kdata.size(); + + m_sg->_calculate(kdata); + if (m_mode == 0) { + for (size_t i = 0; i < total; ++i) { + _addSignal(ks[i].datetime, m_sg->getValue(ks[i].datetime) / m_value); + } + } else { + for (size_t i = 0; i < total; ++i) { + double sg_value = m_sg->getValue(ks[i].datetime); + if (sg_value != 0.0) { + _addSignal(ks[i].datetime, m_value / sg_value); + } + } + } +} + +HKU_API SignalPtr operator/(const SignalPtr& sg, double value) { + return make_shared(sg, value); +} + +HKU_API SignalPtr operator/(double value, const SignalPtr& sg) { + return make_shared(value, sg); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivValueSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivValueSignal.h new file mode 100644 index 000000000..c9bdeca12 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivValueSignal.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorValueSignal.h" + +namespace hku { + +class DivValueSignal : public OperatorValueSignal { + OPERATOR_SIGNAL_IMP(DivValueSignal, "SG_DivValue") + OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + DivValueSignal(double value, const SignalPtr& sg); +}; + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.cpp new file mode 100644 index 000000000..e74b53519 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#include "MulSignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::MulSignal) +#endif + +namespace hku { + +void MulSignal::_calculate(const KData& kdata) { + HKU_IF_RETURN(!m_sg1 || !m_sg2, void()); + + auto const* ks = kdata.data(); + size_t total = kdata.size(); + + m_sg1->_calculate(kdata); + m_sg2->_calculate(kdata); + for (size_t i = 0; i < total; ++i) { + double buy_value = m_sg1->getBuyValue(ks[i].datetime) * m_sg2->getBuyValue(ks[i].datetime); + double sell_value = + m_sg1->getSellValue(ks[i].datetime) * m_sg2->getSellValue(ks[i].datetime); + _addSignal(ks[i].datetime, buy_value + sell_value); + } +} + +HKU_API SignalPtr operator*(const SignalPtr& sg1, const SignalPtr& sg2) { + return make_shared(sg1, sg2); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.h new file mode 100644 index 000000000..3447d27db --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorSignal.h" + +namespace hku { + +class MulSignal : public OperatorSignal { + OPERATOR_SIGNAL_IMP(MulSignal, "SG_Mul") + OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION +}; + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulValueSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulValueSignal.cpp new file mode 100644 index 000000000..cd23963c2 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulValueSignal.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#include "MulValueSignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::MulValueSignal) +#endif + +namespace hku { + +void MulValueSignal::_calculate(const KData& kdata) { + HKU_IF_RETURN(!m_sg || m_value == 0.0 || std::isnan(m_value), void()); + + auto const* ks = kdata.data(); + size_t total = kdata.size(); + + m_sg->_calculate(kdata); + for (size_t i = 0; i < total; ++i) { + _addSignal(ks[i].datetime, m_sg->getValue(ks[i].datetime) * m_value); + } +} + +HKU_API SignalPtr operator*(const SignalPtr& sg, double value) { + return make_shared(sg, value); +} + +HKU_API SignalPtr operator*(double value, const SignalPtr& sg) { + return make_shared(sg, value); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulValueSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulValueSignal.h new file mode 100644 index 000000000..88a6bffe5 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulValueSignal.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorValueSignal.h" + +namespace hku { + +class MulValueSignal : public OperatorValueSignal { + OPERATOR_SIGNAL_IMP(MulValueSignal, "SG_MulValue") + OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION +}; + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorSignal.cpp new file mode 100644 index 000000000..fbeef86c6 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorSignal.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#include "OperatorSignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorSignal) +#endif + +namespace hku { + +OperatorSignal::OperatorSignal() : SignalBase("SG_Operator") {} +OperatorSignal::OperatorSignal(const string& name) : SignalBase(name) {} + +OperatorSignal::OperatorSignal(const string& name, const SignalPtr& sg1, const SignalPtr& sg2) +: SignalBase(name) { + if (sg1) { + m_sg1 = sg1->clone(); + } + if (sg2) { + m_sg2 = sg2->clone(); + } +} + +OperatorSignal::~OperatorSignal() {} + +void OperatorSignal::_reset() { + if (m_sg1) { + m_sg1->reset(); + } + if (m_sg2) { + m_sg2->reset(); + } +} + +SignalPtr OperatorSignal::_clone() { + return make_shared(m_name, m_sg1, m_sg2); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorSignal.h new file mode 100644 index 000000000..a476934f2 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorSignal.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/trade_sys/signal/SignalBase.h" + +namespace hku { + +class OperatorSignal : public SignalBase { +public: + OperatorSignal(); + explicit OperatorSignal(const string& name); + OperatorSignal(const string& name, const SignalPtr& sg1, const SignalPtr& sg2); + virtual ~OperatorSignal(); + + virtual void _reset() override; + virtual SignalPtr _clone() override; + virtual void _calculate(const KData& kdata) override {} + +protected: + SignalPtr m_sg1; + SignalPtr m_sg2; + +//============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SignalBase); + ar& BOOST_SERIALIZATION_NVP(m_sg1); + ar& BOOST_SERIALIZATION_NVP(m_sg2); + } +#endif +}; + +#define OPERATOR_SIGNAL_IMP(classname, name) \ +public: \ + classname() : OperatorSignal(name) {} \ + classname(const SignalPtr& sg1, const SignalPtr& sg2) : OperatorSignal(name, sg1, sg2) {} \ + virtual ~classname() {} \ + virtual SignalPtr _clone() override { \ + return make_shared(m_sg1, m_sg2); \ + } \ + virtual void _calculate(const KData&) override; + +#if HKU_SUPPORT_SERIALIZATION +#define OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION \ +private: \ + friend class boost::serialization::access; \ + template \ + void serialize(Archive& ar, const unsigned int version) { \ + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(OperatorSignal); \ + } +#else +#define OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION +#endif + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.cpp new file mode 100644 index 000000000..07bc10e08 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#include "OperatorValueSignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorValueSignal) +#endif + +namespace hku { + +OperatorValueSignal::OperatorValueSignal() : SignalBase("SG_OperatorValue") {} +OperatorValueSignal::OperatorValueSignal(const string& name) : SignalBase(name) {} + +OperatorValueSignal::OperatorValueSignal(const string& name, const SignalPtr& sg, double value, + int mode) +: SignalBase(name), m_value(value), m_mode(mode) { + if (sg) { + m_sg = sg->clone(); + } +} + +OperatorValueSignal::~OperatorValueSignal() {} + +void OperatorValueSignal::_reset() { + m_value = 0.0; + if (m_sg) { + m_sg->reset(); + } +} + +SignalPtr OperatorValueSignal::_clone() { + return make_shared(m_name, m_sg, m_value); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.h new file mode 100644 index 000000000..11bd66953 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/trade_sys/signal/SignalBase.h" + +namespace hku { + +class OperatorValueSignal : public SignalBase { +public: + OperatorValueSignal(); + explicit OperatorValueSignal(const string& name); + OperatorValueSignal(const string& name, const SignalPtr& sg, double value, int mode = 0); + virtual ~OperatorValueSignal(); + + virtual void _reset() override; + virtual SignalPtr _clone() override; + virtual void _calculate(const KData& kdata) override {} + +protected: + double m_value{0.0}; + SignalPtr m_sg; + int m_mode{0}; // 仅对-、/有效,0:(sg, value), 1: (value, sg) + +//============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SignalBase); + ar& BOOST_SERIALIZATION_NVP(m_sg); + ar& BOOST_SERIALIZATION_NVP(m_value); + ar& BOOST_SERIALIZATION_NVP(m_mode); + } +#endif +}; + +#define OPERATOR_SIGNAL_IMP(classname, name) \ +public: \ + classname() : OperatorValueSignal(name) {} \ + classname(const SignalPtr& sg, double value) : OperatorValueSignal(name, sg, value) {} \ + virtual ~classname() {} \ + virtual SignalPtr _clone() override { \ + return make_shared(m_sg, m_value); \ + } \ + virtual void _calculate(const KData&) override; + +#if HKU_SUPPORT_SERIALIZATION +#define OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION \ +private: \ + friend class boost::serialization::access; \ + template \ + void serialize(Archive& ar, const unsigned int version) { \ + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(OperatorValueSignal); \ + } +#else +#define OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION +#endif + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.cpp new file mode 100644 index 000000000..c662bf072 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#include "SubSignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::SubSignal) +#endif + +namespace hku { + +void SubSignal::_calculate(const KData& kdata) { + HKU_IF_RETURN(!m_sg1 && !m_sg2, void()); + + auto const* ks = kdata.data(); + size_t total = kdata.size(); + + if (m_sg1 && !m_sg2) { + m_sg1->_calculate(kdata); + for (size_t i = 0; i < total; ++i) { + double value = m_sg1->getValue(ks[i].datetime); + _addSignal(ks[i].datetime, value); + } + return; + } + + if (!m_sg1 && m_sg2) { + m_sg2->_calculate(kdata); + for (size_t i = 0; i < total; i++) { + double value = 0.0 - m_sg2->getValue(ks[i].datetime); + _addSignal(ks[i].datetime, value); + return; + } + } + + m_sg1->_calculate(kdata); + m_sg2->_calculate(kdata); + for (size_t i = 0; i < total; ++i) { + double value = m_sg1->getValue(ks[i].datetime) - m_sg2->getValue(ks[i].datetime); + _addSignal(ks[i].datetime, value); + } +} + +HKU_API SignalPtr operator-(const SignalPtr& sg1, const SignalPtr& sg2) { + return make_shared(sg1, sg2); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.h new file mode 100644 index 000000000..8c4071d1c --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorSignal.h" + +namespace hku { + +class SubSignal : public OperatorSignal { + OPERATOR_SIGNAL_IMP(SubSignal, "SG_Sub") + OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION +}; + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.cpp new file mode 100644 index 000000000..4914c7e35 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#include "SubValueSignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::SubValueSignal) +#endif + +namespace hku { + +SubValueSignal::SubValueSignal(double value, const SignalPtr& sg) +: OperatorValueSignal("SG_SubVlaue", sg, value, 1) {} + +void SubValueSignal::_calculate(const KData& kdata) { + HKU_IF_RETURN(!m_sg || std::isnan(m_value), void()); + + auto const* ks = kdata.data(); + size_t total = kdata.size(); + + m_sg->_calculate(kdata); + if (m_value == 0.0) { + if (m_mode == 0) { + for (size_t i = 0; i < total; ++i) { + _addSignal(ks[i].datetime, m_sg->getValue(ks[i].datetime)); + } + } else { + for (size_t i = 0; i < total; ++i) { + _addSignal(ks[i].datetime, -(m_sg->getValue(ks[i].datetime))); + } + } + + } else { + if (m_mode == 0) { + for (size_t i = 0; i < total; ++i) { + double buy_value = m_sg->getBuyValue(ks[i].datetime) - m_value; + double sell_value = m_sg->getSellValue(ks[i].datetime) + m_value; + _addSignal(ks[i].datetime, buy_value + sell_value); + } + } else { + for (size_t i = 0; i < total; ++i) { + double buy_value = m_value - m_sg->getBuyValue(ks[i].datetime); + double sell_value = m_sg->getSellValue(ks[i].datetime) + m_value; + _addSignal(ks[i].datetime, buy_value + sell_value); + } + } + } +} + +HKU_API SignalPtr operator-(const SignalPtr& sg, double value) { + return make_shared(sg, value); +} + +HKU_API SignalPtr operator-(double value, const SignalPtr& sg) { + return make_shared(value, sg); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.h new file mode 100644 index 000000000..0f4370d5e --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorValueSignal.h" + +namespace hku { + +class SubValueSignal : public OperatorValueSignal { + OPERATOR_SIGNAL_IMP(SubValueSignal, "SG_AddValue") + OPERATOR_SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + SubValueSignal(double value, const SignalPtr& sg); +}; + +} /* namespace hku */ From 373b555e6ba77a042e2bcabea03ad7ebdecb6227 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 9 Feb 2025 02:42:52 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E6=94=B9=E8=BF=9BIndicator,=20=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=88=9B=E5=BB=BA=E7=9A=84=E7=A9=BAIndicator=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA=E6=94=B9=E4=B8=BA=E5=A4=8D=E5=88=B6=E5=AD=90indicator?= =?UTF-8?q?=EF=BC=8C=E8=AE=BE=E7=BD=AE=E4=B8=8A=E4=B8=8B=E6=96=87=E6=97=B6?= =?UTF-8?q?=E4=B8=BA=E9=95=BF=E5=BA=A6=E4=B8=8E=E4=B8=8A=E4=B8=8B=E6=96=87?= =?UTF-8?q?=E7=9B=B8=E5=90=8C=E5=85=A8=E9=83=A8discard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/Indicator.h | 2 +- hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp | 25 ++++++++++++++++++++ hikyuu_cpp/hikyuu/indicator/IndicatorImp.h | 4 +--- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.h b/hikyuu_cpp/hikyuu/indicator/Indicator.h index eccd35648..290e36072 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.h +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.h @@ -44,7 +44,7 @@ class HKU_API Indicator { typedef IndicatorImp::value_t value_t; public: - Indicator() {} + Indicator() : m_imp(make_shared()) {} Indicator(const IndicatorImpPtr& imp); Indicator(const Indicator& ind); Indicator(Indicator&& ind) noexcept; diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index 7256071aa..50cafb17a 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -694,6 +694,31 @@ bool IndicatorImp::needCalculate() { return false; } +void IndicatorImp::_calculate(const Indicator &ind) { + if (isLeaf()) { + auto k = getContext(); + size_t total = k.size(); + HKU_IF_RETURN(total == 0, void()); + _readyBuffer(total, 1); + m_discard = total; + return; + } + + size_t total = ind.size(); + m_result_num = ind.getResultNumber(); + HKU_IF_RETURN(total == 0, void()); + + _readyBuffer(total, m_result_num); + m_discard = ind.discard(); + for (size_t r = 0; r < m_result_num; ++r) { + const auto *src = ind.data(r); + auto *dst = this->data(r); + for (size_t i = m_discard; i < total; ++i) { + dst[i] = src[i]; + } + } +} + Indicator IndicatorImp::calculate() { IndicatorImpPtr result; if (!needCalculate()) { diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h index 89106fa25..2c9c9d534 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h @@ -152,9 +152,7 @@ class HKU_API IndicatorImp : public enable_shared_from_this { // =================== // 子类接口 // =================== - virtual void _calculate(const Indicator&) { - HKU_WARN("{} will be empty always!", m_name); - } + virtual void _calculate(const Indicator&); virtual void _dyn_run_one_step(const Indicator& ind, size_t curPos, size_t step) {} From 188bba290ed3ecd8db59896c03bb03af20b95e3b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 9 Feb 2025 05:35:49 +0800 Subject: [PATCH 4/6] update --- hikyuu_cpp/unit_test/hikyuu/indicator_talib/test_TA_MAVP.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator_talib/test_TA_MAVP.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator_talib/test_TA_MAVP.cpp index c8cb196a6..caad7d28c 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator_talib/test_TA_MAVP.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator_talib/test_TA_MAVP.cpp @@ -83,8 +83,8 @@ TEST_CASE("test_TA_MAVP_params") { /** @arg 输入两个空指标, 指定上下文 */ result = TA_MAVP(Indicator(), Indicator())(k1); - CHECK_EQ(result.size(), 0); - CHECK_EQ(result.discard(), 0); + CHECK_EQ(result.size(), k1.size()); + CHECK_EQ(result.discard(), k1.size()); // CHECK_EQ(result.name(), "TA_MAVP(Indicator)"); } From 9206789c30274d83918e213265e8eb6b2e1e16c2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 9 Feb 2025 05:51:57 +0800 Subject: [PATCH 5/6] =?UTF-8?q?SG=E5=A2=9E=E5=BC=BA=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E9=80=BB=E8=BE=91=E5=8F=8A=E5=9B=9B=E5=88=99=E8=BF=90?= =?UTF-8?q?=E7=AE=97(continue)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/signal/SignalBase.cpp | 2 +- .../hikyuu/trade_sys/signal/SignalBase.h | 2 + .../hikyuu/trade_sys/signal/crt/SG_Bool.h | 3 +- .../trade_sys/signal/imp/BoolSignal.cpp | 13 ++-- .../hikyuu/trade_sys/signal/test_SG_Logic.cpp | 67 +++++++++++++++++++ hikyuu_pywrap/trade_sys/_Signal.cpp | 19 +++++- 6 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_SG_Logic.cpp diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index cfb07ebf1..a8702c16d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -135,7 +135,7 @@ double SignalBase::getSellValue(const Datetime& datetime) const { } void SignalBase::_addSignal(const Datetime& datetime, double value) { - HKU_IF_RETURN(iszero(value), void()); + HKU_IF_RETURN(iszero(value) || std::isnan(value), void()); double new_value = value + getBuyValue(datetime) + getSellValue(datetime); HKU_IF_RETURN(iszero(new_value), void()); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h index e79c70056..d4019e4d4 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h @@ -261,11 +261,13 @@ inline double SignalBase::getValue(const Datetime& datetime) const { } inline void SignalBase::_addBuySignal(const Datetime& datetime, double value) { + HKU_IF_RETURN(std::isnan(value), void()); HKU_CHECK(value > 0.0, "buy value muse be > 0", value); _addSignal(datetime, value); } inline void SignalBase::_addSellSignal(const Datetime& datetime, double value) { + HKU_IF_RETURN(std::isnan(value), void()); HKU_CHECK(value < 0.0, "sell value muse be > 0", value); _addSignal(datetime, value); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Bool.h b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Bool.h index af4a24218..77805bf13 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Bool.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Bool.h @@ -18,10 +18,11 @@ namespace hku { * 布尔信号指示器 * @param buy 买入指示(结果Indicator中相应位置>0则代表买入) * @param sell 卖出指示(结果Indicator中相应位置>0则代表卖出) + * @param alternate 买入与卖出信号是否交替出现,默认为true * @return 信号指示器 * @ingroup Signal */ -SignalPtr HKU_API SG_Bool(const Indicator& buy, const Indicator& sell); +SignalPtr HKU_API SG_Bool(const Indicator& buy, const Indicator& sell, bool alternate = true); } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp index 7e220e9b8..5aca63547 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp @@ -5,7 +5,8 @@ * Author: fasiondog */ -#include "../../../indicator/crt/KDATA.h" +#include "hikyuu/indicator/crt/ALIGN.h" +#include "hikyuu/indicator/crt/KDATA.h" #include "BoolSignal.h" #if HKU_SUPPORT_SERIALIZATION @@ -29,8 +30,8 @@ SignalPtr BoolSignal::_clone() { } void BoolSignal::_calculate(const KData& kdata) { - Indicator buy = m_bool_buy(kdata); - Indicator sell = m_bool_sell(kdata); + Indicator buy = ALIGN(m_bool_buy(kdata), kdata); + Indicator sell = ALIGN(m_bool_sell(kdata), kdata); HKU_ERROR_IF_RETURN(buy.size() != sell.size(), void(), "buy.size() != sell.size()"); size_t discard = buy.discard() > sell.discard() ? buy.discard() : sell.discard(); @@ -46,8 +47,10 @@ void BoolSignal::_calculate(const KData& kdata) { } } -SignalPtr HKU_API SG_Bool(const Indicator& buy, const Indicator& sell) { - return make_shared(buy, sell); +SignalPtr HKU_API SG_Bool(const Indicator& buy, const Indicator& sell, bool alternate) { + auto p = make_shared(buy, sell); + p->setParam("alternate", alternate); + return p; } } /* namespace hku */ diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_SG_Logic.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_SG_Logic.cpp new file mode 100644 index 000000000..01af384ac --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_SG_Logic.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025 hikyuu.org + * + * Created on: 2025-02-08 + * Author: fasiondog + */ + +#include "../../test_config.h" +#include "hikyuu/trade_sys/signal/crt/SG_Logic.h" +#include "hikyuu/trade_sys/signal/crt/SG_Manual.h" + +using namespace hku; + +/** + * @defgroup test_Signal test_Signal_logic + * @ingroup test_hikyuu_trade_sys_suite + * @{ + */ + +static void reset_expect(std::map& expect) { + for (auto& iter : expect) { + iter.second = 0.0; + } +} + +/** @par 检测点 */ +TEST_CASE("test_SG_Add") { + auto k = getKData("sz000001", KQueryByDate(Datetime(20111108), Datetime(20111201))); + std::map expect; + for (size_t i = 0; i < k.size(); i++) { + HKU_INFO("{}", k[i]); + expect[k[i].datetime] = 0.0; + } + + /** @arg sg1, sg2 不存在重叠 */ + auto sg1 = SG_Manual(); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111114)); + + auto sg2 = SG_Manual(); + sg2->_addBuySignal(Datetime(20111115)); + sg2->_addSellSignal(Datetime(20111116)); + + auto ret = sg1 + sg2; + ret->setParam("alternate", true); + ret->setTO(k); + + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111114)] = -1.0; + expect[Datetime(20111115)] = 1.0; + expect[Datetime(20111116)] = -1.0; + for (auto iter = expect.begin(); iter != expect.end(); ++iter) { + CHECK_EQ(ret->getValue(iter->first), iter->second); + } + + /** @arg sg1, sg2 存在重叠 */ + sg1->reset(); + sg2->reset(); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111114)); + sg2->_addBuySignal(Datetime(20111115)); + sg2->_addSellSignal(Datetime(20111116)); + + reset_expect(expect); +} + +/** @} */ \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_Signal.cpp b/hikyuu_pywrap/trade_sys/_Signal.cpp index 52487b2a5..9ab0a1326 100644 --- a/hikyuu_pywrap/trade_sys/_Signal.cpp +++ b/hikyuu_pywrap/trade_sys/_Signal.cpp @@ -133,15 +133,32 @@ void export_Signal(py::module& m) { .def("_reset", &SignalBase::_reset, "【重载接口】子类复位接口,复位内部私有变量") + .def("__add__", [](const SignalPtr& self, const SignalPtr& other) { return self + other; }) + .def("__add__", [](const SignalPtr& self, double other) { return self + other; }) + .def("__radd__", [](const SignalPtr& self, double other) { return other + self; }) + .def("__sub__", [](const SignalPtr& self, const SignalPtr& other) { return self - other; }) + .def("__sub__", [](const SignalPtr& self, double other) { return self - other; }) + .def("__rsub__", [](const SignalPtr& self, double other) { return other - self; }) + .def("__mul__", [](const SignalPtr& self, const SignalPtr& other) { return self * other; }) + .def("__mul__", [](const SignalPtr& self, double other) { return self * other; }) + .def("__rmul__", [](const SignalPtr& self, double other) { return other * self; }) + .def("__truediv__", + [](const SignalPtr& self, const SignalPtr& other) { return self / other; }) + .def("__truediv__", [](const SignalPtr& self, double other) { return self / other; }) + .def("__rtruediv__", [](const SignalPtr& self, double other) { return other / self; }) + .def("__and__", [](const SignalPtr& self, const SignalPtr& other) { return self & other; }) + .def("__or__", [](const SignalPtr& self, const SignalPtr& other) { return self | other; }) + DEF_PICKLE(SGPtr); - m.def("SG_Bool", SG_Bool, py::arg("buy"), py::arg("sell"), + m.def("SG_Bool", SG_Bool, py::arg("buy"), py::arg("sell"), py::arg("alternate") = true, R"(SG_Bool(buy, sell) 布尔信号指示器,使用运算结果为类似bool数组的Indicator分别作为买入、卖出指示。 :param Indicator buy: 买入指示(结果Indicator中相应位置>0则代表买入) :param Indicator sell: 卖出指示(结果Indicator中相应位置>0则代表卖出) + :param bool alternate: 是否交替买入卖出,默认为True :return: 信号指示器)"); m.def("SG_Single", SG_Single, py::arg("ind"), py::arg("filter_n") = 10, From 6fb9e791e051c9ce6185465af2ddfa589a66e80a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 9 Feb 2025 17:38:45 +0800 Subject: [PATCH 6/6] =?UTF-8?q?SG=E5=A2=9E=E5=BC=BA=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E9=80=BB=E8=BE=91=E5=8F=8A=E5=9B=9B=E5=88=99=E8=BF=90?= =?UTF-8?q?=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/signal/SignalBase.cpp | 66 -- .../trade_sys/signal/imp/logic/AddSignal.cpp | 8 +- .../signal/imp/logic/AddValueSignal.cpp | 11 +- .../trade_sys/signal/imp/logic/DivSignal.cpp | 22 +- .../trade_sys/signal/imp/logic/MulSignal.cpp | 2 +- .../signal/imp/logic/OperatorValueSignal.cpp | 4 +- .../trade_sys/signal/imp/logic/SubSignal.cpp | 5 +- .../signal/imp/logic/SubValueSignal.cpp | 22 +- .../hikyuu/trade_sys/signal/test_SG_Logic.cpp | 731 +++++++++++++++++- 9 files changed, 755 insertions(+), 116 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index a8702c16d..89cbc4eca 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -195,72 +195,6 @@ void SignalBase::_addSignal(const Datetime& datetime, double value) { } } -// void SignalBase::_addBuySignal(const Datetime& datetime, double value) { -// HKU_CHECK(value > 0.0, "value must > 0!"); -// HKU_WARN_IF_RETURN(m_sellSig.find(datetime) != m_sellSig.end(), void(), -// "Ignore buy sigal! Conflict with sell signal at {}!", datetime); - -// double nvalue = getBuyValue(datetime) + getSellValue(datetime); -// HKU_IF_RETURN(iszero(nvalue), void()); - -// auto iter = m_buySig.find(datetime); -// if (!getParam("alternate")) { -// if (iter != m_buySig.end()) { -// iter->second += value; -// } else { -// m_buySig.insert({datetime, value}); -// } -// return; -// } - -// if (!m_hold_long) { -// if (iter != m_buySig.end()) { -// iter->second += value; -// } else { -// m_buySig.insert({datetime, value}); -// } -// if (getParam("support_borrow_stock") && m_hold_short) { -// m_hold_short = false; -// } else { -// m_hold_long = true; -// } -// } -// } - -// void SignalBase::_addSellSignal(const Datetime& datetime, double value) { -// HKU_CHECK(value < 0.0, "value must < 0!"); -// HKU_WARN_IF_RETURN(m_buySig.find(datetime) != m_buySig.end(), void(), -// "Ignore sell sigal! Conflict with buy signal at {}!", datetime); - -// auto iter = m_sellSig.find(datetime); -// if (!getParam("alternate")) { -// if (iter != m_sellSig.end()) { -// iter->second += value; -// } else { -// m_sellSig.insert({datetime, value}); -// } -// return; -// } - -// if (!m_hold_short) { -// if (m_hold_long) { -// if (iter != m_sellSig.end()) { -// iter->second += value; -// } else { -// m_sellSig.insert({datetime, value}); -// } -// m_hold_long = false; -// } else if (getParam("support_borrow_stock")) { -// if (iter != m_sellSig.end()) { -// iter->second += value; -// } else { -// m_sellSig.insert({datetime, value}); -// } -// m_hold_short = true; -// } -// } -// } - bool SignalBase::nextTimeShouldBuy() const { size_t total = m_kdata.size(); HKU_IF_RETURN(total == 0, false); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp index e81b1044b..98938f0d3 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp @@ -22,8 +22,7 @@ void AddSignal::_calculate(const KData& kdata) { if (m_sg1 && !m_sg2) { m_sg1->_calculate(kdata); for (size_t i = 0; i < total; ++i) { - double value = m_sg1->getValue(ks[i].datetime); - _addSignal(ks[i].datetime, value); + _addSignal(ks[i].datetime, m_sg1->getValue(ks[i].datetime)); } return; } @@ -31,10 +30,9 @@ void AddSignal::_calculate(const KData& kdata) { if (!m_sg1 && m_sg2) { m_sg2->_calculate(kdata); for (size_t i = 0; i < total; i++) { - double value = m_sg2->getValue(ks[i].datetime); - _addSignal(ks[i].datetime, value); - return; + _addSignal(ks[i].datetime, m_sg2->getValue(ks[i].datetime)); } + return; } m_sg1->_calculate(kdata); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.cpp index 678d99589..04c65d52d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.cpp @@ -25,9 +25,16 @@ void AddValueSignal::_calculate(const KData& kdata) { _addSignal(ks[i].datetime, m_sg->getValue(ks[i].datetime)); } } else { + HKU_INFO("m_value: {}", m_value); for (size_t i = 0; i < total; ++i) { - double buy_value = m_sg->getBuyValue(ks[i].datetime) + m_value; - double sell_value = m_sg->getSellValue(ks[i].datetime) - m_value; + double buy_value = m_sg->getBuyValue(ks[i].datetime); + if (buy_value > 0.0) { + buy_value += m_value; + } + double sell_value = m_sg->getSellValue(ks[i].datetime); + if (sell_value < 0.0) { + sell_value -= m_value; + } _addSignal(ks[i].datetime, buy_value + sell_value); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.cpp index 663a45637..a5c8d0956 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.cpp @@ -14,31 +14,11 @@ BOOST_CLASS_EXPORT(hku::DivSignal) namespace hku { void DivSignal::_calculate(const KData& kdata) { - HKU_IF_RETURN(!m_sg1 && !m_sg2, void()); + HKU_IF_RETURN(!m_sg1 || !m_sg2, void()); auto const* ks = kdata.data(); size_t total = kdata.size(); - if (m_sg1 && !m_sg2) { - m_sg1->_calculate(kdata); - for (size_t i = 0; i < total; ++i) { - double value = m_sg1->getValue(ks[i].datetime); - _addSignal(ks[i].datetime, value); - } - return; - } - - if (!m_sg1 && m_sg2) { - m_sg2->_calculate(kdata); - for (size_t i = 0; i < total; i++) { - double value = m_sg2->getValue(ks[i].datetime); - if (value != 0.0) { - _addSignal(ks[i].datetime, 1.0 / value); - } - return; - } - } - m_sg1->_calculate(kdata); m_sg2->_calculate(kdata); for (size_t i = 0; i < total; ++i) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.cpp index e74b53519..675544d3d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/MulSignal.cpp @@ -24,7 +24,7 @@ void MulSignal::_calculate(const KData& kdata) { for (size_t i = 0; i < total; ++i) { double buy_value = m_sg1->getBuyValue(ks[i].datetime) * m_sg2->getBuyValue(ks[i].datetime); double sell_value = - m_sg1->getSellValue(ks[i].datetime) * m_sg2->getSellValue(ks[i].datetime); + 0.0 - m_sg1->getSellValue(ks[i].datetime) * m_sg2->getSellValue(ks[i].datetime); _addSignal(ks[i].datetime, buy_value + sell_value); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.cpp index 07bc10e08..04708e2e2 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.cpp @@ -22,12 +22,14 @@ OperatorValueSignal::OperatorValueSignal(const string& name, const SignalPtr& sg if (sg) { m_sg = sg->clone(); } + if (std::isnan(m_value)) { + m_value = 0.0; + } } OperatorValueSignal::~OperatorValueSignal() {} void OperatorValueSignal::_reset() { - m_value = 0.0; if (m_sg) { m_sg->reset(); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.cpp index c662bf072..b4b1a1cdf 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.cpp @@ -22,8 +22,7 @@ void SubSignal::_calculate(const KData& kdata) { if (m_sg1 && !m_sg2) { m_sg1->_calculate(kdata); for (size_t i = 0; i < total; ++i) { - double value = m_sg1->getValue(ks[i].datetime); - _addSignal(ks[i].datetime, value); + _addSignal(ks[i].datetime, m_sg1->getValue(ks[i].datetime)); } return; } @@ -33,8 +32,8 @@ void SubSignal::_calculate(const KData& kdata) { for (size_t i = 0; i < total; i++) { double value = 0.0 - m_sg2->getValue(ks[i].datetime); _addSignal(ks[i].datetime, value); - return; } + return; } m_sg1->_calculate(kdata); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.cpp index 4914c7e35..cbf5ce5e8 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.cpp @@ -30,21 +30,33 @@ void SubValueSignal::_calculate(const KData& kdata) { } } else { for (size_t i = 0; i < total; ++i) { - _addSignal(ks[i].datetime, -(m_sg->getValue(ks[i].datetime))); + _addSignal(ks[i].datetime, 0.0 - (m_sg->getValue(ks[i].datetime))); } } } else { if (m_mode == 0) { for (size_t i = 0; i < total; ++i) { - double buy_value = m_sg->getBuyValue(ks[i].datetime) - m_value; - double sell_value = m_sg->getSellValue(ks[i].datetime) + m_value; + double buy_value = m_sg->getBuyValue(ks[i].datetime); + if (buy_value > 0.0) { + buy_value -= m_value; + } + double sell_value = m_sg->getSellValue(ks[i].datetime); + if (sell_value < 0.0) { + sell_value -= m_value; + } _addSignal(ks[i].datetime, buy_value + sell_value); } } else { for (size_t i = 0; i < total; ++i) { - double buy_value = m_value - m_sg->getBuyValue(ks[i].datetime); - double sell_value = m_sg->getSellValue(ks[i].datetime) + m_value; + double buy_value = m_sg->getBuyValue(ks[i].datetime); + if (buy_value > 0.0) { + buy_value = m_value - buy_value; + } + double sell_value = m_sg->getSellValue(ks[i].datetime); + if (sell_value < 0.0) { + sell_value -= sell_value; + } _addSignal(ks[i].datetime, buy_value + sell_value); } } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_SG_Logic.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_SG_Logic.cpp index 01af384ac..556fdf8d6 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_SG_Logic.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_SG_Logic.cpp @@ -23,45 +23,752 @@ static void reset_expect(std::map& expect) { } } +static void check_result(SignalPtr sg, const std::map& expect) { + for (auto iter = expect.begin(); iter != expect.end(); ++iter) { + // HKU_INFO("{}: {}, {}", iter->first, sg->getValue(iter->first), iter->second); + CHECK_EQ(sg->getValue(iter->first), iter->second); + } +} + /** @par 检测点 */ TEST_CASE("test_SG_Add") { - auto k = getKData("sz000001", KQueryByDate(Datetime(20111108), Datetime(20111201))); + auto k = getKData("sz000001", KQueryByDate(Datetime(20111108), Datetime(20111125))); std::map expect; for (size_t i = 0; i < k.size(); i++) { - HKU_INFO("{}", k[i]); expect[k[i].datetime] = 0.0; } - /** @arg sg1, sg2 不存在重叠 */ - auto sg1 = SG_Manual(); + /** @arg 输入的 sg 都为空 */ + SGPtr sg1, sg2; + auto ret = sg1 + sg2; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg 其中一个 sg 为空 */ + sg1 = SG_Manual(); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111115)); + + ret = sg1 + sg2; + ret->setTO(k); + + reset_expect(expect); + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111115)] = -1.0; + check_result(ret, expect); + + ret = sg2 + sg1; + ret->setTO(k); + check_result(ret, expect); + + /** @arg 交替模式,sg1, sg2 不存在重叠 */ + sg1 = SG_Manual(); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111115)); + + sg2 = SG_Manual(); + sg2->_addBuySignal(Datetime(20111114)); + sg2->_addSellSignal(Datetime(20111116)); + + ret = sg1 + sg2; + ret->setParam("alternate", true); + ret->setTO(k); + + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111114)] = 0.0; + expect[Datetime(20111115)] = -1.0; + expect[Datetime(20111116)] = 0.0; + check_result(ret, expect); + + /** @arg 非交替模式,sg1, sg2 不存在重叠 */ + ret = sg1 + sg2; + ret->setParam("alternate", false); + ret->setTO(k); + + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111114)] = 1.0; + expect[Datetime(20111115)] = -1.0; + expect[Datetime(20111116)] = -1.0; + check_result(ret, expect); + + /** @arg 交替模式,sg1, sg2 存在重叠 */ + sg1->reset(); + sg2->reset(); + sg1->setParam("alternate", false); + sg2->setParam("alternate", false); sg1->_addBuySignal(Datetime(20111110)); sg1->_addSellSignal(Datetime(20111114)); + sg1->_addSellSignal(Datetime(20111115)); + REQUIRE(sg1->getValue(Datetime(20111115)) == -1.0); + sg2->_addBuySignal(Datetime(20111110)); + sg2->_addBuySignal(Datetime(20111114)); + sg2->_addSellSignal(Datetime(20111116)); + + reset_expect(expect); + expect[Datetime(20111110)] = 2.0; + expect[Datetime(20111114)] = 0.0; + expect[Datetime(20111115)] = -1.0; + expect[Datetime(20111116)] = 0.0; + + ret = sg1 + sg2; + ret->setParam("alternate", true); + ret->setTO(k); + check_result(ret, expect); - auto sg2 = SG_Manual(); - sg2->_addBuySignal(Datetime(20111115)); + /** @arg 非交替模式,sg1, sg2 存在重叠 */ + sg1->reset(); + sg2->reset(); + sg1->setParam("alternate", false); + sg2->setParam("alternate", false); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111114)); + sg1->_addSellSignal(Datetime(20111115)); + REQUIRE(sg1->getValue(Datetime(20111115)) == -1.0); + sg2->_addBuySignal(Datetime(20111110)); + sg2->_addBuySignal(Datetime(20111114)); sg2->_addSellSignal(Datetime(20111116)); + REQUIRE(sg2->getValue(Datetime(20111116)) == -1.0); - auto ret = sg1 + sg2; + reset_expect(expect); + expect[Datetime(20111110)] = 2.0; + expect[Datetime(20111114)] = 0.0; + expect[Datetime(20111115)] = -1.0; + expect[Datetime(20111116)] = -1.0; + + ret = sg1 + sg2; + ret->setParam("alternate", false); + ret->setTO(k); + check_result(ret, expect); +} + +/** @par 检测点 */ +TEST_CASE("test_SG_Sub") { + auto k = getKData("sz000001", KQueryByDate(Datetime(20111108), Datetime(20111125))); + std::map expect; + for (size_t i = 0; i < k.size(); i++) { + expect[k[i].datetime] = 0.0; + } + + /** @arg 输入的 sg 都为空 */ + SGPtr sg1, sg2; + auto ret = sg1 - sg2; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg 其中一个 sg 为空 */ + sg1 = SG_Manual(); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111115)); + + REQUIRE(!sg2); + ret = sg1 - sg2; + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111115)] = -1.0; + check_result(ret, expect); + + ret = sg2 - sg1; + ret->setParam("alternate", false); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = -1.0; + expect[Datetime(20111115)] = 1.0; + check_result(ret, expect); + + /** @arg 交替模式,sg1, sg2 不存在重叠 */ + sg1 = SG_Manual(); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111115)); + + sg2 = SG_Manual(); + sg2->_addBuySignal(Datetime(20111114)); + sg2->_addSellSignal(Datetime(20111116)); + + ret = sg1 - sg2; ret->setParam("alternate", true); ret->setTO(k); expect[Datetime(20111110)] = 1.0; expect[Datetime(20111114)] = -1.0; - expect[Datetime(20111115)] = 1.0; + expect[Datetime(20111115)] = 0.0; + expect[Datetime(20111116)] = 1.0; + check_result(ret, expect); + + /** @arg 非交替模式,sg1, sg2 不存在重叠 */ + ret = sg1 - sg2; + ret->setParam("alternate", false); + ret->setTO(k); + + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111114)] = -1.0; + expect[Datetime(20111115)] = -1.0; + expect[Datetime(20111116)] = 1.0; + check_result(ret, expect); + + /** @arg 交替模式,sg1, sg2 存在重叠 */ + sg1->reset(); + sg2->reset(); + sg1->setParam("alternate", false); + sg2->setParam("alternate", false); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111114)); + sg1->_addSellSignal(Datetime(20111115)); + REQUIRE(sg1->getValue(Datetime(20111115)) == -1.0); + sg2->_addBuySignal(Datetime(20111110)); + sg2->_addBuySignal(Datetime(20111114)); + sg2->_addSellSignal(Datetime(20111116)); + + ret = sg1 - sg2; + ret->setParam("alternate", true); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 0.0; + expect[Datetime(20111114)] = 0.0; + expect[Datetime(20111115)] = 0.0; + expect[Datetime(20111116)] = 1.0; + check_result(ret, expect); + + /** @arg 非交替模式,sg1, sg2 存在重叠 */ + sg1->reset(); + sg2->reset(); + sg1->setParam("alternate", false); + sg2->setParam("alternate", false); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111114)); + sg1->_addSellSignal(Datetime(20111115)); + REQUIRE(sg1->getValue(Datetime(20111115)) == -1.0); + sg2->_addBuySignal(Datetime(20111110)); + sg2->_addBuySignal(Datetime(20111114)); + sg2->_addSellSignal(Datetime(20111116)); + REQUIRE(sg2->getValue(Datetime(20111116)) == -1.0); + + ret = sg1 - sg2; + ret->setParam("alternate", false); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 0.0; + expect[Datetime(20111114)] = -2.0; + expect[Datetime(20111115)] = -1.0; + expect[Datetime(20111116)] = 1.0; + check_result(ret, expect); +} + +/** @par 检测点 */ +TEST_CASE("test_SG_Mul") { + auto k = getKData("sz000001", KQueryByDate(Datetime(20111108), Datetime(20111125))); + std::map expect; + for (size_t i = 0; i < k.size(); i++) { + expect[k[i].datetime] = 0.0; + } + + /** @arg 输入的 sg 都为空 */ + SGPtr sg1, sg2; + auto ret = sg1 * sg2; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg 其中一个 sg 为空 */ + sg1 = SG_Manual(); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111115)); + + REQUIRE(!sg2); + ret = sg1 * sg2; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = sg2 * sg1; + ret->setParam("alternate", false); + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg 交替模式,sg1, sg2 不存在重叠 */ + sg1 = SG_Manual(); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111115)); + + sg2 = SG_Manual(); + sg2->_addBuySignal(Datetime(20111114)); + sg2->_addSellSignal(Datetime(20111116)); + + ret = sg1 * sg2; + ret->setParam("alternate", true); + ret->setTO(k); + + reset_expect(expect); + check_result(ret, expect); + + /** @arg 非交替模式,sg1, sg2 不存在重叠 */ + ret = sg1 * sg2; + ret->setParam("alternate", false); + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg 交替模式,sg1, sg2 存在重叠 */ + sg1->reset(); + sg2->reset(); + sg1->setParam("alternate", false); + sg2->setParam("alternate", false); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111114)); + sg1->_addSellSignal(Datetime(20111115)); + REQUIRE(sg1->getValue(Datetime(20111115)) == -1.0); + sg2->_addBuySignal(Datetime(20111110)); + sg2->_addBuySignal(Datetime(20111114)); + sg2->_addSellSignal(Datetime(20111116)); + + ret = sg1 * sg2; + ret->setParam("alternate", true); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111114)] = 0.0; + expect[Datetime(20111115)] = 0.0; + expect[Datetime(20111116)] = 0.0; + check_result(ret, expect); + + /** @arg 非交替模式,sg1, sg2 存在重叠 */ + sg1->reset(); + sg2->reset(); + sg1->setParam("alternate", false); + sg2->setParam("alternate", false); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111114)); + sg1->_addSellSignal(Datetime(20111115)); + sg1->_addSellSignal(Datetime(20111116)); + sg2->_addBuySignal(Datetime(20111110)); + sg2->_addBuySignal(Datetime(20111114)); + sg2->_addSellSignal(Datetime(20111116)); + + ret = sg1 * sg2; + ret->setParam("alternate", false); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111114)] = 0.0; + expect[Datetime(20111115)] = 0.0; expect[Datetime(20111116)] = -1.0; - for (auto iter = expect.begin(); iter != expect.end(); ++iter) { - CHECK_EQ(ret->getValue(iter->first), iter->second); + check_result(ret, expect); +} + +/** @par 检测点 */ +TEST_CASE("test_SG_Div") { + auto k = getKData("sz000001", KQueryByDate(Datetime(20111108), Datetime(20111125))); + std::map expect; + for (size_t i = 0; i < k.size(); i++) { + expect[k[i].datetime] = 0.0; } - /** @arg sg1, sg2 存在重叠 */ + /** @arg 输入的 sg 都为空 */ + SGPtr sg1, sg2; + auto ret = sg1 / sg2; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg 其中一个 sg 为空 */ + sg1 = SG_Manual(); + sg1->_addBuySignal(Datetime(20111110), 2.0); + sg1->_addSellSignal(Datetime(20111115), -3.0); + + REQUIRE(!sg2); + ret = sg1 / sg2; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = sg2 / sg1; + ret->setParam("alternate", false); + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg 交替模式,sg1, sg2 不存在重叠 */ + sg1 = SG_Manual(); + sg1->_addBuySignal(Datetime(20111110)); + sg1->_addSellSignal(Datetime(20111115)); + + sg2 = SG_Manual(); + sg2->_addBuySignal(Datetime(20111114)); + sg2->_addSellSignal(Datetime(20111116)); + + ret = sg1 / sg2; + ret->setParam("alternate", true); + ret->setTO(k); + + reset_expect(expect); + check_result(ret, expect); + + /** @arg 非交替模式,sg1, sg2 不存在重叠 */ + ret = sg1 / sg2; + ret->setParam("alternate", false); + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg 交替模式,sg1, sg2 存在重叠 */ sg1->reset(); sg2->reset(); + sg1->setParam("alternate", false); + sg2->setParam("alternate", false); sg1->_addBuySignal(Datetime(20111110)); sg1->_addSellSignal(Datetime(20111114)); - sg2->_addBuySignal(Datetime(20111115)); + sg1->_addSellSignal(Datetime(20111115)); + REQUIRE(sg1->getValue(Datetime(20111115)) == -1.0); + sg2->_addBuySignal(Datetime(20111110)); + sg2->_addBuySignal(Datetime(20111114)); sg2->_addSellSignal(Datetime(20111116)); + ret = sg1 / sg2; + ret->setParam("alternate", true); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111114)] = 0.0; + expect[Datetime(20111115)] = 0.0; + expect[Datetime(20111116)] = 0.0; + check_result(ret, expect); + + /** @arg 非交替模式,sg1, sg2 存在重叠 */ + sg1->reset(); + sg2->reset(); + sg1->setParam("alternate", false); + sg2->setParam("alternate", false); + sg1->_addBuySignal(Datetime(20111110), 6.0); + sg1->_addSellSignal(Datetime(20111114)); + sg1->_addSellSignal(Datetime(20111115)); + sg1->_addSellSignal(Datetime(20111116), -6.0); + sg2->_addBuySignal(Datetime(20111110), 2.0); + sg2->_addBuySignal(Datetime(20111114)); + sg2->_addSellSignal(Datetime(20111116), -2.0); + + ret = sg1 / sg2; + ret->setParam("alternate", false); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 3.0; + expect[Datetime(20111114)] = 0.0; + expect[Datetime(20111115)] = 0.0; + expect[Datetime(20111116)] = -3.0; + check_result(ret, expect); +} + +/** @par 检测点 */ +TEST_CASE("test_SG_AddValue") { + auto k = getKData("sz000001", KQueryByDate(Datetime(20111108), Datetime(20111125))); + std::map expect; + for (size_t i = 0; i < k.size(); i++) { + expect[k[i].datetime] = 0.0; + } + + /** @arg 输入的 sg 为空 */ + SGPtr sg; + auto ret = sg + 1.0; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = 1.0 + sg; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg sg + number */ + sg = SG_Manual(); + sg->_addBuySignal(Datetime(20111110)); + sg->_addSellSignal(Datetime(20111115)); + + ret = sg + 0.0; + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111115)] = -1.0; + check_result(ret, expect); + + ret = sg + Null(); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111115)] = -1.0; + check_result(ret, expect); + + ret = sg + 1.0; + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 2.0; + expect[Datetime(20111115)] = -2.0; + check_result(ret, expect); + + /** @arg number + sg */ + ret = 0.0 + sg; + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111115)] = -1.0; + check_result(ret, expect); + + ret = Null() + sg; + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 1.0; + expect[Datetime(20111115)] = -1.0; + check_result(ret, expect); + + ret = 1.0 + sg; + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 2.0; + expect[Datetime(20111115)] = -2.0; + check_result(ret, expect); +} + +/** @par 检测点 */ +TEST_CASE("test_SG_MulValue") { + auto k = getKData("sz000001", KQueryByDate(Datetime(20111108), Datetime(20111125))); + std::map expect; + for (size_t i = 0; i < k.size(); i++) { + expect[k[i].datetime] = 0.0; + } + + /** @arg 输入的 sg 为空 */ + SGPtr sg; + auto ret = sg * 1.0; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = 1.0 * sg; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = Null() * sg; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = sg * Null(); + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg sg * number */ + sg = SG_Manual(); + sg->_addBuySignal(Datetime(20111110)); + sg->_addSellSignal(Datetime(20111115)); + + ret = sg * 0.0; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = sg * Null(); + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = sg * 2.0; + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 2.0; + expect[Datetime(20111115)] = -2.0; + check_result(ret, expect); + + /** @arg number * sg */ + ret = 0.0 * sg; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = Null() * sg; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = 2.0 * sg; + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 2.0; + expect[Datetime(20111115)] = -2.0; + check_result(ret, expect); +} + +/** @par 检测点 */ +TEST_CASE("test_SG_SubValue") { + auto k = getKData("sz000001", KQueryByDate(Datetime(20111108), Datetime(20111125))); + std::map expect; + for (size_t i = 0; i < k.size(); i++) { + expect[k[i].datetime] = 0.0; + } + + /** @arg 输入的 sg 为空 */ + SGPtr sg; + auto ret = sg - 1.0; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = 1.0 - sg; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = Null() - sg; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = sg - Null(); + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg sg - number */ + sg = SG_Manual(); + sg->_addBuySignal(Datetime(20111110), 2.0); + sg->_addSellSignal(Datetime(20111115), -3.0); + + ret = sg - 0.0; + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 2.0; + expect[Datetime(20111115)] = -3.0; + check_result(ret, expect); + + ret = sg - Null(); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 2.0; + expect[Datetime(20111115)] = -3.0; + check_result(ret, expect); + + ret = sg - 2.0; + ret->setParam("alternate", true); + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = sg - 2.0; + ret->setParam("alternate", false); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111115)] = -5.0; + check_result(ret, expect); + + /** @arg number - sg */ + ret = 0.0 - sg; + ret->setParam("alternate", true); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111115)] = 3.0; + check_result(ret, expect); + + ret = 0.0 - sg; + ret->setParam("alternate", false); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = -2.0; + expect[Datetime(20111115)] = 3.0; + check_result(ret, expect); + + ret = Null() - sg; + ret->setParam("alternate", true); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111115)] = 3.0; + check_result(ret, expect); + + ret = Null() - sg; + ret->setParam("alternate", false); + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = -2.0; + expect[Datetime(20111115)] = 3.0; + check_result(ret, expect); + + ret = 2.0 - sg; + ret->setParam("alternate", true); + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); +} + +/** @par 检测点 */ +TEST_CASE("test_SG_DivValue") { + auto k = getKData("sz000001", KQueryByDate(Datetime(20111108), Datetime(20111125))); + std::map expect; + for (size_t i = 0; i < k.size(); i++) { + expect[k[i].datetime] = 0.0; + } + + /** @arg 输入的 sg 为空 */ + SGPtr sg; + auto ret = sg / 1.0; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = 1.0 / sg; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = Null() / sg; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = sg / Null(); + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + /** @arg sg / number */ + sg = SG_Manual(); + sg->_addBuySignal(Datetime(20111110)); + sg->_addSellSignal(Datetime(20111115)); + + ret = sg / 0.0; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = sg / Null(); + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = sg / 2.0; + ret->setTO(k); + reset_expect(expect); + expect[Datetime(20111110)] = 0.5; + expect[Datetime(20111115)] = -0.5; + check_result(ret, expect); + + /** @arg number / sg */ + ret = 0.0 / sg; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = Null() / sg; + ret->setTO(k); + reset_expect(expect); + check_result(ret, expect); + + ret = 2.0 / sg; + ret->setTO(k); reset_expect(expect); + expect[Datetime(20111110)] = 2.0; + expect[Datetime(20111115)] = -2.0; + check_result(ret, expect); } /** @} */ \ No newline at end of file