Skip to content

Commit

Permalink
Merge pull request #329 from fasiondog/feature/one_side_sg
Browse files Browse the repository at this point in the history
add SG_OneSide
  • Loading branch information
fasiondog authored Feb 13, 2025
2 parents 1878046 + 1ef6c8a commit 8b53b1a
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 11 deletions.
8 changes: 6 additions & 2 deletions hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ void SignalBase::initParam() {
}

void SignalBase::baseCheckParam(const string& name) const {}
void SignalBase::paramChanged() {}
void SignalBase::paramChanged() {
m_calculated = false;
}

SignalPtr SignalBase::clone() {
SignalPtr p;
Expand All @@ -60,6 +62,7 @@ SignalPtr SignalBase::clone() {
p->m_name = m_name;
p->m_params = m_params;
p->m_kdata = m_kdata;
p->m_calculated = m_calculated;
p->m_hold_long = m_hold_long;
p->m_hold_short = m_hold_short;
p->m_buySig = m_buySig;
Expand All @@ -70,8 +73,9 @@ SignalPtr SignalBase::clone() {
}

void SignalBase::setTO(const KData& kdata) {
HKU_IF_RETURN(m_kdata == kdata, void());
HKU_IF_RETURN(m_calculated && m_kdata == kdata, void());
m_kdata = kdata;
m_calculated = false;
HKU_IF_RETURN(kdata.empty(), void());

bool cycle = getParam<bool>("cycle");
Expand Down
4 changes: 4 additions & 0 deletions hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ class HKU_API SignalBase : public enable_shared_from_this<SignalBase> {
protected:
string m_name;
KData m_kdata;
bool m_calculated{false}; // 仅针对 setTO 时的计算

/* 多头持仓 */
bool m_hold_long;
/* 空头持仓 */
Expand Down Expand Up @@ -169,6 +171,7 @@ class HKU_API SignalBase : public enable_shared_from_this<SignalBase> {
ar& BOOST_SERIALIZATION_NVP(m_ignore_cycle);
// m_kdata都是系统运行时临时设置,不需要序列化
// ar & BOOST_SERIALIZATION_NVP(m_kdata);
// ar & BOOST_SERIALIZATION_NVP(m_calculated);
}

template <class Archive>
Expand All @@ -182,6 +185,7 @@ class HKU_API SignalBase : public enable_shared_from_this<SignalBase> {
ar& BOOST_SERIALIZATION_NVP(m_ignore_cycle);
// m_kdata都是系统运行时临时设置,不需要序列化
// ar & BOOST_SERIALIZATION_NVP(m_kdata);
// ar & BOOST_SERIALIZATION_NVP(m_calculated);
}

BOOST_SERIALIZATION_SPLIT_MEMBER()
Expand Down
1 change: 1 addition & 0 deletions hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@
#include "crt/SG_Band.h"
#include "crt/SG_Logic.h"
#include "crt/SG_Manual.h"
#include "crt/SG_OneSide.h"

#endif /* SIGNAL_BUILD_IN_H_ */
28 changes: 28 additions & 0 deletions hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_OneSide.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2025 hikyuu.org
*
* Created on: 2025-02-13
* Author: fasiondog
*/

#pragma once
#ifndef TRADE_SYS_SIGNAL_CRT_SG_ONESIDE_H_
#define TRADE_SYS_SIGNAL_CRT_SG_ONESIDE_H_

#include "../../../indicator/Indicator.h"
#include "../SignalBase.h"

namespace hku {

/**
* 根据输入指标构建单边信号(单纯的只包含买入或卖出信号),如果指标值大于0,则加入信号
* @param ind 指示指标
* @param is_buy 加入的是买入信号还是卖出信号
* @return 信号指示器
* @ingroup Signal
*/
SignalPtr HKU_API SG_OneSide(const Indicator& ind, bool is_buy);

} /* namespace hku */

#endif /* TRADE_SYS_SIGNAL_CRT_SG_ONESIDE_H_ */
68 changes: 68 additions & 0 deletions hikyuu_cpp/hikyuu/trade_sys/signal/imp/OneSideSignal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2025 hikyuu.org
*
* Created on: 2025-02-13
* Author: fasiondog
*/

#include "hikyuu/indicator/crt/ALIGN.h"
#include "hikyuu/indicator/crt/KDATA.h"
#include "OneSideSignal.h"

#if HKU_SUPPORT_SERIALIZATION
BOOST_CLASS_EXPORT(hku::OneSideSignal)
#endif

namespace hku {

OneSideSignal::OneSideSignal() : SignalBase("SG_OneSide") {
setParam<bool>("alternate", false);
setParam<bool>("is_buy", true); // 买入信号,否则为添加卖出信号
}

OneSideSignal::OneSideSignal(const Indicator& ind, bool is_buy)
: SignalBase("SG_OneSide"), m_ind(ind.clone()) {
setParam<bool>("alternate", false);
setParam<bool>("is_buy", is_buy);
}

OneSideSignal::~OneSideSignal() {}

void OneSideSignal::_checkParam(const string& name) const {
if (name == "alternate") {
HKU_CHECK(!getParam<bool>(name), "alternate only be false!");
}
}

SignalPtr OneSideSignal::_clone() {
auto p = make_shared<OneSideSignal>();
p->m_ind = m_ind.clone();
return p;
}

void OneSideSignal::_calculate(const KData& kdata) {
Indicator ind = m_ind(kdata);
HKU_IF_RETURN(ind.empty() || ind.size() != kdata.size(), void());

bool is_buy = getParam<bool>("is_buy");
const auto* src = ind.data();
auto const* ks = kdata.data();
size_t discard = ind.discard();
size_t total = ind.size();

for (size_t i = discard; i < total; ++i) {
if (src[i] > 0.0) {
if (is_buy) {
_addBuySignal(ks[i].datetime);
} else {
_addSellSignal(ks[i].datetime);
}
}
}
}

SignalPtr HKU_API SG_OneSide(const Indicator& ind, bool is_buy) {
return make_shared<OneSideSignal>(ind, is_buy);
}

} /* namespace hku */
46 changes: 46 additions & 0 deletions hikyuu_cpp/hikyuu/trade_sys/signal/imp/OneSideSignal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2025 hikyuu.org
*
* Created on: 2025-02-13
* Author: fasiondog
*/

#pragma once
#ifndef TRADE_SYS_SIGNAL_IMP_ONESIDESIGNAL_H_
#define TRADE_SYS_SIGNAL_IMP_ONESIDESIGNAL_H_

#include "../../../indicator/Indicator.h"
#include "../SignalBase.h"

namespace hku {

// 根据输入指标构建单边信号(单纯的只包含买入或卖出信号),如果指标值大于0,则加入信号
class OneSideSignal : public SignalBase {
public:
OneSideSignal();
OneSideSignal(const Indicator& ind, bool is_buy);
virtual ~OneSideSignal();

virtual SignalPtr _clone() override;
virtual void _calculate(const KData& kdata) override;
virtual void _checkParam(const string& name) const override;

private:
Indicator m_ind;

//============================================
// 序列化支持
//============================================
#if HKU_SUPPORT_SERIALIZATION
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int version) {
ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SignalBase);
ar& BOOST_SERIALIZATION_NVP(m_ind);
}
#endif
};

} /* namespace hku */

#endif /* TRADE_SYS_SIGNAL_IMP_ONESIDESIGNAL_H_ */
95 changes: 95 additions & 0 deletions hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_SG_OneSide.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2025 hikyuu.org
*
* Created on: 2025-02-13
* Author: fasiondog
*/

#include "doctest/doctest.h"
#include "hikyuu/StockManager.h"
#include "hikyuu/indicator/crt/KDATA.h"
#include "hikyuu/indicator/crt/REF.h"
#include "hikyuu/trade_sys/signal/crt/SG_Logic.h"
#include "hikyuu/trade_sys/signal/crt/SG_Bool.h"
#include "hikyuu/trade_sys/signal/crt/SG_OneSide.h"

using namespace hku;

/**
* @defgroup test_Signal test_Signal
* @ingroup test_hikyuu_trade_sys_suite
* @{
*/

/** @par 检测点 */
TEST_CASE("test_SG_OneSide") {
auto k = getKData("sh000001", KQuery(-30));
Indicator ind;
SignalPtr sg;

/** @arg 输入ind为空 */
sg = SG_OneSide(ind, true);
sg->setTO(k);
CHECK_UNARY(sg->getBuySignal().empty());
CHECK_UNARY(sg->getSellSignal().empty());

sg = SG_OneSide(ind, false);
sg->setTO(k);
CHECK_UNARY(sg->getBuySignal().empty());
CHECK_UNARY(sg->getSellSignal().empty());

/** @arg 单边买入信号 */
ind = CLOSE() > REF(CLOSE(), 1);
auto sg_buy = SG_OneSide(ind, true);
sg_buy->setTO(k);
auto expect = ind(k);
for (size_t i = expect.discard(); i < expect.size(); i++) {
if (expect[i] > 0.0) {
CHECK_UNARY(sg_buy->shouldBuy(k[i].datetime));
CHECK_UNARY(!sg_buy->shouldSell(k[i].datetime));
} else {
CHECK_UNARY(!sg_buy->shouldBuy(k[i].datetime));
CHECK_UNARY(!sg_buy->shouldSell(k[i].datetime));
}
}

/** @arg 单边卖出信号 */
ind = CLOSE() < REF(CLOSE(), 1);
auto sg_sell = SG_OneSide(ind, false);
sg_sell->setTO(k);
expect = ind(k);
for (size_t i = expect.discard(); i < expect.size(); i++) {
if (expect[i] > 0.0) {
CHECK_UNARY(sg_sell->shouldSell(k[i].datetime));
CHECK_UNARY(!sg_sell->shouldBuy(k[i].datetime));
} else {
CHECK_UNARY(!sg_sell->shouldSell(k[i].datetime));
CHECK_UNARY(!sg_sell->shouldBuy(k[i].datetime));
}
}

/** @arg 尝试改变 alternate 参数 */
sg_sell->setParam<bool>("alternate", false);
CHECK_THROWS(sg_sell->setParam<bool>("alternate", true));

/** @arg 单边买入 + 单边卖出 */
sg = sg_buy + sg_sell;
sg->setTO(k);
auto sg_expect = SG_Bool(CLOSE() > REF(CLOSE(), 1), CLOSE() < REF(CLOSE(), 1));
sg_expect->setTO(k);
for (size_t i = 0; i < k.size(); i++) {
CHECK_EQ(sg->shouldBuy(k[i].datetime), sg_expect->shouldBuy(k[i].datetime));
CHECK_EQ(sg->shouldSell(k[i].datetime), sg_expect->shouldSell(k[i].datetime));
}

sg->setParam<bool>("alternate", false);
sg_expect = SG_Bool(CLOSE() > REF(CLOSE(), 1), CLOSE() < REF(CLOSE(), 1), false);
sg->setTO(k);
sg_expect->setTO(k);
for (size_t i = 0; i < k.size(); i++) {
CHECK_EQ(sg->shouldBuy(k[i].datetime), sg_expect->shouldBuy(k[i].datetime));
CHECK_EQ(sg->shouldSell(k[i].datetime), sg_expect->shouldSell(k[i].datetime));
}
}

/** @} */
9 changes: 9 additions & 0 deletions hikyuu_pywrap/trade_sys/_Signal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,13 @@ void export_Signal(py::module& m) {
m.def("SG_Cycle", SG_Cycle, R"(SG_Cycle()
一个特殊的SG,配合PF使用,以 PF 调仓周期为买入信号)");

m.def("SG_OneSide", SG_OneSide, py::arg("ind"), py::arg("is_buy"),
R"(SG_OneSide(ind, is_buy)
根据输入指标构建单边信号(单纯的只包含买入或卖出信号),如果指标值大于0,则加入信号
:param Indicator ind: 输入指标
:param bool is_buy: 构建的是买入信号,否则为卖出信号
:return: 信号指示器)");
}
11 changes: 9 additions & 2 deletions hikyuu_pywrap/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,15 @@ target("core")
end

-- get python include directory.
local pydir = try { function () return os.iorun("python3-config --includes"):trim() end }
target:add("cxflags", pydir)
local pydir = nil;
if os.getenv("CONDA_PREFIX") ~= nil then
local py3config = os.getenv("CONDA_PREFIX") .. "/bin/python3-config"
pydir = os.iorun(py3config .. " --includes"):trim()
else
pydir = os.iorun("python3-config --includes"):trim()
end
assert(pydir, "python3-config not found!")
target:add("cxflags", pydir)
end)

after_build(function(target)
Expand Down
14 changes: 7 additions & 7 deletions xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -149,20 +149,20 @@ add_requires("boost " .. boost_version, {
},
})

add_requires("fmt " .. fmt_version, {configs = {header_only = true}})
add_requires("spdlog " .. spdlog_version, {configs = {header_only = true, fmt_external = true}})
add_requireconfs("spdlog.fmt", {override = true, version=fmt_version, configs = {header_only = true}})
add_requires("fmt " .. fmt_version, {system = false, configs = {header_only = true}})
add_requires("spdlog " .. spdlog_version, {system = false, configs = {header_only = true, fmt_external = true}})
add_requireconfs("spdlog.fmt", {override = true, version=fmt_version, system = false, configs = {header_only = true}})
add_requires("sqlite3 " .. sqlite_version, {configs = {shared = true, safe_mode="2", cxflags = "-fPIC"}})
add_requires("flatbuffers v" .. flatbuffers_version, {system = false, configs= {runtimes = get_config("runtimes")}})
add_requires("nng " .. nng_version, {configs = {NNG_ENABLE_TLS = has_config("http_client_ssl"), cxflags = "-fPIC"}})
add_requires("nlohmann_json")
add_requires("nng " .. nng_version, {system = false, configs = {NNG_ENABLE_TLS = has_config("http_client_ssl"), cxflags = "-fPIC"}})
add_requires("nlohmann_json", {system = false})

if has_config("http_client_zip") then
add_requires("gzip-hpp")
add_requires("gzip-hpp", {system = false})
end

if has_config("ta_lib") then
add_requires("ta-lib")
add_requires("ta-lib", {system = false})
end

add_defines("SPDLOG_DISABLE_DEFAULT_LOGGER") -- 禁用 spdlog 默认ogger
Expand Down

0 comments on commit 8b53b1a

Please sign in to comment.