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/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) {} diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index 51dea9666..89cbc4eca 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -108,42 +108,87 @@ 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) || std::isnan(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; } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h index 6cab84286..d4019e4d4 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,22 @@ 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_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); +} + } /* namespace hku */ #if FMT_VERSION >= 90000 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_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/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/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/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..98938f0d3 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp @@ -0,0 +1,50 @@ +/* + * 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 { + +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) { + _addSignal(ks[i].datetime, m_sg1->getValue(ks[i].datetime)); + } + return; + } + + if (!m_sg1 && m_sg2) { + m_sg2->_calculate(kdata); + for (size_t i = 0; i < total; i++) { + _addSignal(ks[i].datetime, m_sg2->getValue(ks[i].datetime)); + } + 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/AddSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.h new file mode 100644 index 000000000..610dc60d1 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.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 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..04c65d52d --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddValueSignal.cpp @@ -0,0 +1,51 @@ +/* + * 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 { + HKU_INFO("m_value: {}", m_value); + for (size_t i = 0; i < total; ++i) { + 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); + } + } +} + +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..a5c8d0956 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/DivSignal.cpp @@ -0,0 +1,40 @@ +/* + * 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(); + + 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..675544d3d --- /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 = + 0.0 - 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..04708e2e2 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/OperatorValueSignal.cpp @@ -0,0 +1,42 @@ +/* + * 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(); + } + if (std::isnan(m_value)) { + m_value = 0.0; + } +} + +OperatorValueSignal::~OperatorValueSignal() {} + +void OperatorValueSignal::_reset() { + 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..b4b1a1cdf --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubSignal.cpp @@ -0,0 +1,51 @@ +/* + * 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) { + _addSignal(ks[i].datetime, m_sg1->getValue(ks[i].datetime)); + } + 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..cbf5ce5e8 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/SubValueSignal.cpp @@ -0,0 +1,74 @@ +/* + * 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, 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); + 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_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); + } + } + } +} + +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 */ 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)"); } 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..556fdf8d6 --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_SG_Logic.cpp @@ -0,0 +1,774 @@ +/* + * 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; + } +} + +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(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)); + + 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); + + /** @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); + + 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)] = 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; + 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 输入的 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)); + 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 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..9ab0a1326 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中调用 @@ -128,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,