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

add CYCLE Indicator #326

Merged
merged 5 commits into from
Feb 10, 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
4 changes: 4 additions & 0 deletions hikyuu_cpp/hikyuu/Stock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,10 @@ const vector<HistoryFinanceInfo>& Stock::getHistoryFinance() const {
return m_data->m_history_finance;
}

DatetimeList Stock::getTradingCalendar(const KQuery& query) const {
return StockManager::instance().getTradingCalendar(query, market());
}

Stock HKU_API getStock(const string& querystr) {
const StockManager& sm = StockManager::instance();
return sm.getStock(querystr);
Expand Down
7 changes: 7 additions & 0 deletions hikyuu_cpp/hikyuu/Stock.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,13 @@ class HKU_API Stock {
*/
const vector<HistoryFinanceInfo>& getHistoryFinance() const;

/**
* 获取自身市场的交易日日历(不是本身的交易日期)
* @param query
* @return DatetimeList
*/
DatetimeList getTradingCalendar(const KQuery& query) const;

/** 设置权息信息, 仅供初始化时调用 */
void setWeightList(const StockWeightList&);

Expand Down
1 change: 1 addition & 0 deletions hikyuu_cpp/hikyuu/indicator/build_in.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "crt/BETWEEN.h"
#include "crt/BLOCKSETNUM.h"
#include "crt/CEILING.h"
#include "crt/CYCLE.h"
#include "crt/CONTEXT.h"
#include "crt/CORR.h"
#include "crt/COS.h"
Expand Down
24 changes: 24 additions & 0 deletions hikyuu_cpp/hikyuu/indicator/crt/CYCLE.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2025 hikyuu.org
*
* Created on: 2025-02-10
* Author: fasiondog
*/

#pragma once

#include "hikyuu/indicator/Indicator.h"

namespace hku {

/**
* PF调仓周期指标,主要用于PF调仓日验证,及作为SG
* @param k 关联的K线数据
* @ingroup Indicator
*/
Indicator CYCLE(const KData& k, int adjust_cycle = 1, const string& adjust_mode = "query",
bool delay_to_trading_day = true);
Indicator CYCLE(int adjust_cycle = 1, const string& adjust_mode = "query",
bool delay_to_trading_day = true);

} // namespace hku
287 changes: 287 additions & 0 deletions hikyuu_cpp/hikyuu/indicator/imp/ICycle.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
/*
* Copyright (c) 2025 hikyuu.org
*
* Created on: 2025-02-10
* Author: fasiondog
*/

#include "hikyuu/indicator/crt/ALIGN.h"
#include "hikyuu/indicator/crt/PRICELIST.h"
#include "ICycle.h"

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

namespace hku {

ICycle::ICycle() : IndicatorImp("CYCLE", 1) {
_initParams();
}

ICycle::ICycle(const KData& k) : IndicatorImp("CYCLE", 1) {
_initParams();
setParam<KData>("kdata", k);
ICycle::_calculate(Indicator());
}

ICycle::~ICycle() {}

void ICycle::_initParams() {
setParam<int>("adjust_cycle", 1); // 调仓周期
setParam<string>("adjust_mode", "query"); // 调仓模式
setParam<bool>("delay_to_trading_day", true); // 延迟至交易日
}

void ICycle::_checkParam(const string& name) const {
if ("adjust_mode" == name || "adjust_cycle" == name) {
if (!haveParam("adjust_mode") || !haveParam("adjust_cycle")) {
// 同时判断两个参数时,可能一个参数还未设定
return;
}
string adjust_mode = getParam<string>("adjust_mode");
to_lower(adjust_mode);
int adjust_cycle = getParam<int>("adjust_cycle");
if ("query" == adjust_mode) {
HKU_ASSERT(adjust_cycle >= 1);
} else if ("day" == adjust_mode) {
HKU_ASSERT(adjust_cycle >= 1);
} else if ("week" == adjust_mode) {
HKU_ASSERT(adjust_cycle >= 1 && adjust_cycle <= 5);
} else if ("month" == adjust_mode) {
HKU_ASSERT(adjust_cycle >= 1 && adjust_cycle <= 31);
} else if ("quarter" == adjust_mode) {
HKU_ASSERT(adjust_cycle >= 1 && adjust_cycle <= 92);
} else if ("year" == adjust_mode) {
HKU_ASSERT(adjust_cycle >= 1 && adjust_cycle <= 366);
} else {
HKU_THROW("Invalid adjust_mode: {}!", adjust_mode);
}
}
}

static void calculate_no_delay(const DatetimeList& datelist, int adjust_cycle, const string& mode,
PriceList& buf) {
if ("week" == mode) {
Datetime cur_cycle_end = datelist.front().nextWeek();
for (size_t i = 0, total = datelist.size(); i < total; i++) {
const auto& date = datelist[i];
bool adjust = (date.dayOfWeek() == adjust_cycle);
if (adjust) {
cur_cycle_end = date.nextWeek();
}
if (cur_cycle_end >= datelist.back()) {
cur_cycle_end = datelist.back() + Seconds(1);
}
buf[i] = adjust;
}
} else if ("month" == mode) {
Datetime cur_cycle_end = datelist.front().nextMonth();
for (size_t i = 0, total = datelist.size(); i < total; i++) {
const auto& date = datelist[i];
bool adjust = (date.day() == adjust_cycle);
if (adjust) {
cur_cycle_end = date.nextMonth();
}
if (cur_cycle_end >= datelist.back()) {
cur_cycle_end = datelist.back() + Seconds(1);
}
buf[i] = adjust;
}
} else if ("quarter" == mode) {
Datetime cur_cycle_end = datelist.front().nextQuarter();
for (size_t i = 0, total = datelist.size(); i < total; i++) {
const auto& date = datelist[i];
bool adjust = (date.day() == adjust_cycle);
if (adjust) {
cur_cycle_end = date.nextQuarter();
}
if (cur_cycle_end >= datelist.back()) {
cur_cycle_end = datelist.back() + Seconds(1);
}
buf[i] = adjust;
}
} else if ("year" == mode) {
Datetime cur_cycle_end = datelist.front().nextYear();
for (size_t i = 0, total = datelist.size(); i < total; i++) {
const auto& date = datelist[i];
bool adjust = (date.dayOfYear() == adjust_cycle);
if (adjust) {
cur_cycle_end = date.nextYear();
}
if (cur_cycle_end >= datelist.back()) {
cur_cycle_end = datelist.back() + Seconds(1);
}
buf[i] = adjust;
}
}
}

static void calculate_delay(const DatetimeList& datelist, int adjust_cycle, const string& mode,
PriceList& buf) {
std::set<Datetime> adjust_date_set;
if ("week" == mode) {
Datetime cur_cycle_end = datelist.front().nextWeek();
for (size_t i = 0, total = datelist.size(); i < total; i++) {
const auto& date = datelist[i];
Datetime adjust_date = date.startOfWeek() + Days(adjust_cycle - 1);
bool adjust = false;
if (date == adjust_date) {
adjust = true;
adjust_date_set.emplace(adjust_date);
} else if (adjust_date_set.find(adjust_date) == adjust_date_set.end() &&
date > adjust_date) {
adjust = true;
adjust_date_set.emplace(adjust_date);
}

if (adjust) {
cur_cycle_end = date.nextWeek();
}
if (cur_cycle_end >= datelist.back()) {
cur_cycle_end = datelist.back() + Seconds(1);
}

buf[i] = adjust;
}

} else if ("month" == mode) {
Datetime cur_cycle_end = datelist.front().nextMonth();
for (size_t i = 0, total = datelist.size(); i < total; i++) {
const auto& date = datelist[i];
Datetime adjust_date = date.startOfMonth() + Days(adjust_cycle - 1);
bool adjust = false;
if (date == adjust_date) {
adjust = true;
adjust_date_set.emplace(adjust_date);
} else if (adjust_date_set.find(adjust_date) == adjust_date_set.end() &&
date > adjust_date) {
adjust = true;
adjust_date_set.emplace(adjust_date);
}

if (adjust) {
cur_cycle_end = date.nextMonth();
}
if (cur_cycle_end >= datelist.back()) {
cur_cycle_end = datelist.back() + Seconds(1);
}

buf[i] = adjust;
}

} else if ("quarter" == mode) {
Datetime cur_cycle_end = datelist.front().nextQuarter();
for (size_t i = 0, total = datelist.size(); i < total; i++) {
const auto& date = datelist[i];
Datetime adjust_date = date.startOfQuarter() + Days(adjust_cycle - 1);
bool adjust = false;
if (date == adjust_date) {
adjust = true;
adjust_date_set.emplace(adjust_date);
} else if (adjust_date_set.find(adjust_date) == adjust_date_set.end() &&
date > adjust_date) {
adjust = true;
adjust_date_set.emplace(adjust_date);
}

if (adjust) {
cur_cycle_end = date.nextQuarter();
}
if (cur_cycle_end >= datelist.back()) {
cur_cycle_end = datelist.back() + Seconds(1);
}

buf[i] = adjust;
}

} else if ("year" == mode) {
Datetime cur_cycle_end = datelist.front().nextYear();
for (size_t i = 0, total = datelist.size(); i < total; i++) {
const auto& date = datelist[i];
Datetime adjust_date = date.startOfYear() + Days(adjust_cycle - 1);
bool adjust = false;
if (date == adjust_date) {
adjust = true;
adjust_date_set.emplace(adjust_date);
} else if (adjust_date_set.find(adjust_date) == adjust_date_set.end() &&
date > adjust_date) {
adjust = true;
adjust_date_set.emplace(adjust_date);
}

if (adjust) {
cur_cycle_end = date.nextYear();
}
if (cur_cycle_end >= datelist.back()) {
cur_cycle_end = datelist.back() + Seconds(1);
}

buf[i] = adjust;
}
}
}

void ICycle::_calculate(const Indicator& data) {
HKU_WARN_IF(!isLeaf() && !data.empty(),
"The input is ignored because {} depends on the context!", m_name);

KData k = getContext();
size_t total = k.size();
HKU_IF_RETURN(total == 0, void());

_readyBuffer(total, 1);

DatetimeList datelist = k.getStock().getTradingCalendar(k.getQuery());
HKU_IF_RETURN(datelist.empty(), void());

int adjust_cycle = getParam<int>("adjust_cycle");
string adjust_mode = getParam<string>("adjust_mode");
bool delay_to_trading_day = getParam<bool>("delay_to_trading_day");

PriceList buf(datelist.size());
if ("query" == adjust_mode || "day" == adjust_mode) {
size_t cur_adjust_ix = 0;
Datetime cur_cycle_end;
for (size_t i = 0, total = datelist.size(); i < total; i++) {
bool adjust = false;
if (i == cur_adjust_ix) {
adjust = true;
cur_adjust_ix += adjust_cycle;
cur_cycle_end =
cur_adjust_ix < total ? datelist[cur_adjust_ix] : datelist.back() + Seconds(1);
}
buf[i] = adjust;
}

} else if (delay_to_trading_day) {
calculate_delay(datelist, adjust_cycle, adjust_mode, buf);
} else {
calculate_no_delay(datelist, adjust_cycle, adjust_mode, buf);
}

Indicator tmpind = ALIGN(PRICELIST(buf, datelist), k);
const auto* src = tmpind.data();
auto* dst = this->data();
HKU_ASSERT(tmpind.size() == total);
memcpy(dst, src, sizeof(value_t) * total);
}

Indicator HKU_API CYCLE(int adjust_cycle, const string& adjust_mode, bool delay_to_trading_day) {
auto p = make_shared<ICycle>();
p->setParam<int>("adjust_cycle", adjust_cycle);
p->setParam<string>("adjust_mode", adjust_mode);
p->setParam<bool>("delay_to_trading_day", delay_to_trading_day);
return Indicator(p);
}

Indicator HKU_API CYCLE(const KData& k, int adjust_cycle, const string& adjust_mode,
bool delay_to_trading_day) {
auto p = make_shared<ICycle>(k);
p->setParam<int>("adjust_cycle", adjust_cycle);
p->setParam<string>("adjust_mode", adjust_mode);
p->setParam<bool>("delay_to_trading_day", delay_to_trading_day);
return p->calculate();
}

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

#pragma once

#include "../Indicator.h"

namespace hku {

/* 获取换手率,等于 VOL(k) / CAPITAL(k) */
class ICycle : public IndicatorImp {
INDICATOR_IMP(ICycle)
INDICATOR_NEED_CONTEXT
INDICATOR_IMP_NO_PRIVATE_MEMBER_SERIALIZATION

public:
ICycle();
explicit ICycle(const KData&);
virtual ~ICycle();
virtual void _checkParam(const string& name) const override;

private:
void _initParams();
};

} /* namespace hku */
9 changes: 9 additions & 0 deletions hikyuu_pywrap/_Stock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,15 @@ void export_Stock(py::module& m) {

获取所有历史财务信息历史记录)")

.def("get_trading_calendar", &Stock::getTradingCalendar, py::arg("query"),
R"(get_trading_calendar(self, query)

获取自身市场的交易日日历((不是本身的交易日期)

:param KQuery query: Query查询条件
:return: 日期列表
:rtype: DatetimeList)")

.def("load_kdata_to_buffer", &Stock::loadKDataToBuffer, R"(load_kdata_to_buffer(self,
ktype)

Expand Down
Loading