Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SG增强,支持逻辑及四则运算; 改进默认 Indicator 创建 #324

Merged
merged 6 commits into from
Feb 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions hikyuu_cpp/hikyuu/DataType.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<price_t>::epsilon();
return std::abs(num) < epsilon;
}

using fmt::format;

inline std::ostream &operator<<(std::ostream &os, const PriceList &p) {
Expand Down
2 changes: 1 addition & 1 deletion hikyuu_cpp/hikyuu/indicator/Indicator.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class HKU_API Indicator {
typedef IndicatorImp::value_t value_t;

public:
Indicator() {}
Indicator() : m_imp(make_shared<IndicatorImp>()) {}
Indicator(const IndicatorImpPtr& imp);
Indicator(const Indicator& ind);
Indicator(Indicator&& ind) noexcept;
Expand Down
25 changes: 25 additions & 0 deletions hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
4 changes: 1 addition & 3 deletions hikyuu_cpp/hikyuu/indicator/IndicatorImp.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,7 @@ class HKU_API IndicatorImp : public enable_shared_from_this<IndicatorImp> {
// ===================
// 子类接口
// ===================
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) {}

Expand Down
77 changes: 61 additions & 16 deletions hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool>("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<bool>("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<bool>("support_borrow_stock") && m_hold_short) {
m_hold_short = false;
} else {
m_hold_long = true;
}
}
}
}

void SignalBase::_addSellSignal(const Datetime& datetime) {
if (!getParam<bool>("alternate")) {
m_sellSig.insert(datetime);
} else {
auto iter = m_sellSig.find(datetime);
if (!getParam<bool>("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<bool>("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;
}
}
Expand Down
46 changes: 41 additions & 5 deletions hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ class HKU_API SignalBase : public enable_shared_from_this<SignalBase> {
*/
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;

/**
* 下一时刻是否可以买入,相当于最后时刻是否指示买入
*/
Expand All @@ -60,17 +76,21 @@ class HKU_API SignalBase : public enable_shared_from_this<SignalBase> {
/** 获取所有卖出指示日期列表 */
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线数据
Expand Down Expand Up @@ -121,9 +141,9 @@ class HKU_API SignalBase : public enable_shared_from_this<SignalBase> {
/* 空头持仓 */
bool m_hold_short;

// 用 set 保存,以便获取是能保持顺序
std::set<Datetime> m_buySig;
std::set<Datetime> m_sellSig;
// 用 map 保存,以便获取是能保持顺序
std::map<Datetime, double> m_buySig;
std::map<Datetime, double> m_sellSig;

Datetime m_cycle_start;
Datetime m_cycle_end;
Expand Down Expand Up @@ -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
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 @@ -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_ */
3 changes: 2 additions & 1 deletion hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Bool.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down
37 changes: 37 additions & 0 deletions hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Logic.h
Original file line number Diff line number Diff line change
@@ -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
13 changes: 8 additions & 5 deletions hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
Expand All @@ -46,8 +47,10 @@ void BoolSignal::_calculate(const KData& kdata) {
}
}

SignalPtr HKU_API SG_Bool(const Indicator& buy, const Indicator& sell) {
return make_shared<BoolSignal>(buy, sell);
SignalPtr HKU_API SG_Bool(const Indicator& buy, const Indicator& sell, bool alternate) {
auto p = make_shared<BoolSignal>(buy, sell);
p->setParam<bool>("alternate", alternate);
return p;
}

} /* namespace hku */
50 changes: 50 additions & 0 deletions hikyuu_cpp/hikyuu/trade_sys/signal/imp/logic/AddSignal.cpp
Original file line number Diff line number Diff line change
@@ -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<AddSignal>(sg1, sg2);
}

} /* namespace hku */
Loading