Skip to content

Commit 4e7d15e

Browse files
committed
add CYCLE Indicator
1 parent 10b4a10 commit 4e7d15e

File tree

7 files changed

+253
-8
lines changed

7 files changed

+253
-8
lines changed

hikyuu_cpp/hikyuu/Stock.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,7 @@ const vector<HistoryFinanceInfo>& Stock::getHistoryFinance() const {
974974
return m_data->m_history_finance;
975975
}
976976

977-
DatetimeList Stock::getTradingCalendar(const KQuery& query) {
977+
DatetimeList Stock::getTradingCalendar(const KQuery& query) const {
978978
return StockManager::instance().getTradingCalendar(query, market());
979979
}
980980

hikyuu_cpp/hikyuu/Stock.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,9 @@ class HKU_API Stock {
207207
/**
208208
* 获取自身市场的交易日日历(不是本身的交易日期)
209209
* @param query
210-
* @param market
211210
* @return DatetimeList
212211
*/
213-
DatetimeList getTradingCalendar(const KQuery& query);
212+
DatetimeList getTradingCalendar(const KQuery& query) const;
214213

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

hikyuu_cpp/hikyuu/indicator/build_in.h

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "crt/BETWEEN.h"
2929
#include "crt/BLOCKSETNUM.h"
3030
#include "crt/CEILING.h"
31+
#include "crt/CYCLE.h"
3132
#include "crt/CONTEXT.h"
3233
#include "crt/CORR.h"
3334
#include "crt/COS.h"
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2025 hikyuu.org
3+
*
4+
* Created on: 2025-02-10
5+
* Author: fasiondog
6+
*/
7+
8+
#pragma once
9+
10+
#include "hikyuu/indicator/Indicator.h"
11+
12+
namespace hku {
13+
14+
/**
15+
* PF调仓周期指标,主要用于PF调仓日验证,及作为SG
16+
* @param k 关联的K线数据
17+
* @ingroup Indicator
18+
*/
19+
Indicator CYCLE(const KData& k, int adjust_cycle = 1, const string& adjust_mode = "query",
20+
bool delay_to_trading_day = true);
21+
Indicator CYCLE(int adjust_cycle = 1, const string& adjust_mode = "query",
22+
bool delay_to_trading_day = true);
23+
24+
} // namespace hku

hikyuu_cpp/hikyuu/indicator/imp/ICycle.cpp

+211-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* Author: fasiondog
66
*/
77

8+
#include "hikyuu/indicator/crt/ALIGN.h"
9+
#include "hikyuu/indicator/crt/PRICELIST.h"
810
#include "ICycle.h"
911

1012
#if HKU_SUPPORT_SERIALIZATION
@@ -58,6 +60,168 @@ void ICycle::_checkParam(const string& name) const {
5860
}
5961
}
6062

63+
static void calculate_no_delay(const DatetimeList& datelist, int adjust_cycle, const string& mode,
64+
PriceList& buf) {
65+
if ("week" == mode) {
66+
Datetime cur_cycle_end = datelist.front().nextWeek();
67+
for (size_t i = 0, total = datelist.size(); i < total; i++) {
68+
const auto& date = datelist[i];
69+
bool adjust = (date.dayOfWeek() == adjust_cycle);
70+
if (adjust) {
71+
cur_cycle_end = date.nextWeek();
72+
}
73+
if (cur_cycle_end >= datelist.back()) {
74+
cur_cycle_end = datelist.back() + Seconds(1);
75+
}
76+
buf[i] = adjust;
77+
}
78+
} else if ("month" == mode) {
79+
Datetime cur_cycle_end = datelist.front().nextMonth();
80+
for (size_t i = 0, total = datelist.size(); i < total; i++) {
81+
const auto& date = datelist[i];
82+
bool adjust = (date.day() == adjust_cycle);
83+
if (adjust) {
84+
cur_cycle_end = date.nextMonth();
85+
}
86+
if (cur_cycle_end >= datelist.back()) {
87+
cur_cycle_end = datelist.back() + Seconds(1);
88+
}
89+
buf[i] = adjust;
90+
}
91+
} else if ("quarter" == mode) {
92+
Datetime cur_cycle_end = datelist.front().nextQuarter();
93+
for (size_t i = 0, total = datelist.size(); i < total; i++) {
94+
const auto& date = datelist[i];
95+
bool adjust = (date.day() == adjust_cycle);
96+
if (adjust) {
97+
cur_cycle_end = date.nextQuarter();
98+
}
99+
if (cur_cycle_end >= datelist.back()) {
100+
cur_cycle_end = datelist.back() + Seconds(1);
101+
}
102+
buf[i] = adjust;
103+
}
104+
} else if ("year" == mode) {
105+
Datetime cur_cycle_end = datelist.front().nextYear();
106+
for (size_t i = 0, total = datelist.size(); i < total; i++) {
107+
const auto& date = datelist[i];
108+
bool adjust = (date.dayOfYear() == adjust_cycle);
109+
if (adjust) {
110+
cur_cycle_end = date.nextYear();
111+
}
112+
if (cur_cycle_end >= datelist.back()) {
113+
cur_cycle_end = datelist.back() + Seconds(1);
114+
}
115+
buf[i] = adjust;
116+
}
117+
}
118+
}
119+
120+
static void calculate_delay(const DatetimeList& datelist, int adjust_cycle, const string& mode,
121+
PriceList& buf) {
122+
std::set<Datetime> adjust_date_set;
123+
if ("week" == mode) {
124+
Datetime cur_cycle_end = datelist.front().nextWeek();
125+
for (size_t i = 0, total = datelist.size(); i < total; i++) {
126+
const auto& date = datelist[i];
127+
Datetime adjust_date = date.startOfWeek() + Days(adjust_cycle - 1);
128+
bool adjust = false;
129+
if (date == adjust_date) {
130+
adjust = true;
131+
adjust_date_set.emplace(adjust_date);
132+
} else if (adjust_date_set.find(adjust_date) == adjust_date_set.end() &&
133+
date > adjust_date) {
134+
adjust = true;
135+
adjust_date_set.emplace(adjust_date);
136+
}
137+
138+
if (adjust) {
139+
cur_cycle_end = date.nextWeek();
140+
}
141+
if (cur_cycle_end >= datelist.back()) {
142+
cur_cycle_end = datelist.back() + Seconds(1);
143+
}
144+
145+
buf[i] = adjust;
146+
}
147+
148+
} else if ("month" == mode) {
149+
Datetime cur_cycle_end = datelist.front().nextMonth();
150+
for (size_t i = 0, total = datelist.size(); i < total; i++) {
151+
const auto& date = datelist[i];
152+
Datetime adjust_date = date.startOfMonth() + Days(adjust_cycle - 1);
153+
bool adjust = false;
154+
if (date == adjust_date) {
155+
adjust = true;
156+
adjust_date_set.emplace(adjust_date);
157+
} else if (adjust_date_set.find(adjust_date) == adjust_date_set.end() &&
158+
date > adjust_date) {
159+
adjust = true;
160+
adjust_date_set.emplace(adjust_date);
161+
}
162+
163+
if (adjust) {
164+
cur_cycle_end = date.nextMonth();
165+
}
166+
if (cur_cycle_end >= datelist.back()) {
167+
cur_cycle_end = datelist.back() + Seconds(1);
168+
}
169+
170+
buf[i] = adjust;
171+
}
172+
173+
} else if ("quarter" == mode) {
174+
Datetime cur_cycle_end = datelist.front().nextQuarter();
175+
for (size_t i = 0, total = datelist.size(); i < total; i++) {
176+
const auto& date = datelist[i];
177+
Datetime adjust_date = date.startOfQuarter() + Days(adjust_cycle - 1);
178+
bool adjust = false;
179+
if (date == adjust_date) {
180+
adjust = true;
181+
adjust_date_set.emplace(adjust_date);
182+
} else if (adjust_date_set.find(adjust_date) == adjust_date_set.end() &&
183+
date > adjust_date) {
184+
adjust = true;
185+
adjust_date_set.emplace(adjust_date);
186+
}
187+
188+
if (adjust) {
189+
cur_cycle_end = date.nextQuarter();
190+
}
191+
if (cur_cycle_end >= datelist.back()) {
192+
cur_cycle_end = datelist.back() + Seconds(1);
193+
}
194+
195+
buf[i] = adjust;
196+
}
197+
198+
} else if ("year" == mode) {
199+
Datetime cur_cycle_end = datelist.front().nextYear();
200+
for (size_t i = 0, total = datelist.size(); i < total; i++) {
201+
const auto& date = datelist[i];
202+
Datetime adjust_date = date.startOfYear() + Days(adjust_cycle - 1);
203+
bool adjust = false;
204+
if (date == adjust_date) {
205+
adjust = true;
206+
adjust_date_set.emplace(adjust_date);
207+
} else if (adjust_date_set.find(adjust_date) == adjust_date_set.end() &&
208+
date > adjust_date) {
209+
adjust = true;
210+
adjust_date_set.emplace(adjust_date);
211+
}
212+
213+
if (adjust) {
214+
cur_cycle_end = date.nextYear();
215+
}
216+
if (cur_cycle_end >= datelist.back()) {
217+
cur_cycle_end = datelist.back() + Seconds(1);
218+
}
219+
220+
buf[i] = adjust;
221+
}
222+
}
223+
}
224+
61225
void ICycle::_calculate(const Indicator& data) {
62226
HKU_WARN_IF(!isLeaf() && !data.empty(),
63227
"The input is ignored because {} depends on the context!", m_name);
@@ -67,14 +231,57 @@ void ICycle::_calculate(const Indicator& data) {
67231
HKU_IF_RETURN(total == 0, void());
68232

69233
_readyBuffer(total, 1);
234+
235+
DatetimeList datelist = k.getStock().getTradingCalendar(k.getQuery());
236+
HKU_IF_RETURN(datelist.empty(), void());
237+
238+
int adjust_cycle = getParam<int>("adjust_cycle");
239+
string adjust_mode = getParam<string>("adjust_mode");
240+
bool delay_to_trading_day = getParam<bool>("delay_to_trading_day");
241+
242+
PriceList buf(datelist.size());
243+
if ("query" == adjust_mode || "day" == adjust_mode) {
244+
size_t cur_adjust_ix = 0;
245+
Datetime cur_cycle_end;
246+
for (size_t i = 0, total = datelist.size(); i < total; i++) {
247+
bool adjust = false;
248+
if (i == cur_adjust_ix) {
249+
adjust = true;
250+
cur_adjust_ix += adjust_cycle;
251+
cur_cycle_end =
252+
cur_adjust_ix < total ? datelist[cur_adjust_ix] : datelist.back() + Seconds(1);
253+
}
254+
buf[i] = adjust;
255+
}
256+
257+
} else if (delay_to_trading_day) {
258+
calculate_delay(datelist, adjust_cycle, adjust_mode, buf);
259+
} else {
260+
calculate_no_delay(datelist, adjust_cycle, adjust_mode, buf);
261+
}
262+
263+
Indicator tmpind = ALIGN(PRICELIST(buf, datelist), k);
264+
const auto* src = tmpind.data();
265+
auto* dst = this->data();
266+
HKU_ASSERT(tmpind.size() == total);
267+
memcpy(dst, src, sizeof(value_t) * total);
70268
}
71269

72-
Indicator HKU_API CYCLE() {
73-
return make_shared<ICycle>()->calculate();
270+
Indicator HKU_API CYCLE(int adjust_cycle, const string& adjust_mode, bool delay_to_trading_day) {
271+
auto p = make_shared<ICycle>();
272+
p->setParam<int>("adjust_cycle", adjust_cycle);
273+
p->setParam<string>("adjust_mode", adjust_mode);
274+
p->setParam<bool>("delay_to_trading_day", delay_to_trading_day);
275+
return Indicator(p);
74276
}
75277

76-
Indicator HKU_API CYCLE(const KData& k) {
77-
return Indicator(make_shared<ICycle>(k));
278+
Indicator HKU_API CYCLE(const KData& k, int adjust_cycle, const string& adjust_mode,
279+
bool delay_to_trading_day) {
280+
auto p = make_shared<ICycle>(k);
281+
p->setParam<int>("adjust_cycle", adjust_cycle);
282+
p->setParam<string>("adjust_mode", adjust_mode);
283+
p->setParam<bool>("delay_to_trading_day", delay_to_trading_day);
284+
return p->calculate();
78285
}
79286

80287
} /* namespace hku */

hikyuu_pywrap/_Stock.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,6 @@ void export_Stock(py::module& m) {
211211
获取自身市场的交易日日历((不是本身的交易日期)
212212
213213
:param KQuery query: Query查询条件
214-
:param str market: 市场简称
215214
:return: 日期列表
216215
:rtype: DatetimeList)")
217216

hikyuu_pywrap/indicator/_build_in.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -2145,4 +2145,19 @@ void export_Indicator_build_in(py::module& m) {
21452145
21462146
:param Indicator ind: 指标
21472147
:rtype: Indicator)");
2148+
2149+
m.def("CYCLE", py::overload_cast<int, const string&, bool>(CYCLE), py::arg("adjust_cycle") = 1,
2150+
py::arg("adjust_mode") = "query", py::arg("delay_to_trading_day") = true);
2151+
m.def("CYCLE", py::overload_cast<const KData&, int, const string&, bool>(CYCLE),
2152+
py::arg("kdata"), py::arg("adjust_cycle") = 1, py::arg("adjust_mode") = "query",
2153+
py::arg("delay_to_trading_day") = true,
2154+
R"(CYCLE(kdata, [adjust_cycle=1], [adjust_mode='query'], [delay_to_trading_day=True])
2155+
2156+
PF调仓周期指标,主要用于PF调仓日验证,及作为SG
2157+
2158+
:param KData kdata: K线数据
2159+
:param int adjust_cycle: 调整周期
2160+
:param string adjust_mode: 调整方式
2161+
:param bool delay_to_trading_day: 调整周期是否延至交易日
2162+
:rtype: Indicator)");
21482163
}

0 commit comments

Comments
 (0)