调整 SPEARMAN 实现

This commit is contained in:
fasiondog 2024-03-10 02:03:08 +08:00
parent e46c2044df
commit 3ba6a0767f
10 changed files with 110 additions and 235 deletions

View File

@ -869,7 +869,7 @@
:param Indicator ind1: 输入参数1
:param Indicator ind2: 输入参数2
:param int n: 指定窗口
:param int n: 滚动窗口(大于2 或 等于0)等于0时代表 n 实际使用 ind 的长度
.. py:function:: SQRT([data])

View File

@ -315,14 +315,4 @@ Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n) {
return p->calculate();
}
// Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n) {
// HKU_ERROR_IF_RETURN(!ind1.getImp() || !ind2.getImp(), Indicator(),
// "ind1 or ind2 is Null Indicator!");
// HKU_ERROR_IF_RETURN(n < 2, Indicator(), "Invalid param n: {} (need >= 2)", n);
// IndicatorImpPtr p = make_shared<IndicatorImp>("SPEARMAN");
// p->setParam<int>("n", n);
// p->add(IndicatorImp::SPEARMAN, ind1.getImp(), ind2.getImp());
// return p->calculate();
// }
} /* namespace hku */

View File

@ -387,14 +387,6 @@ Indicator HKU_API IF(const Indicator& x, Indicator::value_t a, Indicator::value_
*/
Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n);
/**
* Spearman
* @param ind1 1
* @param ind2 2
* @ingroup Indicator
*/
// Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n);
} /* namespace hku */
#if FMT_VERSION >= 90000

View File

@ -97,10 +97,6 @@ string HKU_API getOPTypeName(IndicatorImp::OPType op) {
name = "CORR";
break;
case IndicatorImp::SPEARMAN:
name = "SPEARMAN";
break;
default:
name = "UNKNOWN";
break;
@ -542,10 +538,6 @@ string IndicatorImp::formula() const {
buf << m_name << "(" << m_left->formula() << ", " << m_right->formula() << ")";
break;
case SPEARMAN:
buf << m_name << "(" << m_left->formula() << ", " << m_right->formula() << ")";
break;
default:
HKU_ERROR("Wrong optype! {}", int(m_optype));
break;
@ -809,10 +801,6 @@ Indicator IndicatorImp::calculate() {
execute_corr();
break;
case SPEARMAN:
execute_spearman();
break;
default:
HKU_ERROR("Unkown Indicator::OPType! {}", int(m_optype));
break;
@ -1557,116 +1545,6 @@ void IndicatorImp::execute_corr() {
setDiscard(discard + 2);
}
static void spearmanLevel(const IndicatorImp::value_t *data, IndicatorImp::value_t *level,
size_t total) {
std::vector<std::pair<IndicatorImp::value_t, size_t>> data_index(total);
for (size_t i = 0; i < total; i++) {
data_index[i].first = data[i];
data_index[i].second = i;
}
std::sort(
data_index.begin(), data_index.end(),
std::bind(
std::less<IndicatorImp::value_t>(),
std::bind(&std::pair<IndicatorImp::value_t, size_t>::first, std::placeholders::_1),
std::bind(&std::pair<IndicatorImp::value_t, size_t>::first, std::placeholders::_2)));
size_t i = 0;
while (i < total) {
size_t count = 1;
IndicatorImp::value_t score = i + 1.0;
for (size_t j = i + 1; j < total; j++) {
if (data_index[i].first != data_index[j].first) {
break;
}
count++;
score += j + 1;
}
score = score / count;
for (size_t j = 0; j < count; j++) {
level[data_index[i + j].second] = score;
}
i += count;
}
}
void IndicatorImp::execute_spearman() {
m_right->calculate();
m_left->calculate();
const IndicatorImp *maxp, *minp;
if (m_right->size() > m_left->size()) {
maxp = m_right.get();
minp = m_left.get();
} else {
maxp = m_left.get();
minp = m_right.get();
}
size_t total = maxp->size();
size_t discard = maxp->size() - minp->size() + minp->discard();
if (discard < maxp->discard()) {
discard = maxp->discard();
}
size_t result_number = std::min(minp->getResultNumber(), maxp->getResultNumber());
size_t diff = maxp->size() - minp->size();
_readyBuffer(total, result_number);
int n = getParam<int>("n");
if (n < 2 || discard + 2 > total) {
setDiscard(total);
return;
}
discard += n - 1;
setDiscard(discard);
auto levela = std::make_unique<value_t[]>(n);
auto levelb = std::make_unique<value_t[]>(n);
auto *ptra = levela.get();
auto *ptrb = levelb.get();
// 不处理 n 不足的情况,防止只需要计算全部序列时,过于耗时
double back = std::pow(n, 3) - n;
vector<IndicatorImp::value_t> tmpa;
vector<IndicatorImp::value_t> tmpb;
tmpa.reserve(n);
tmpa.reserve(n);
for (size_t r = 0; r < result_number; ++r) {
auto *dst = this->data(r);
auto const *maxdata = maxp->data(r);
auto const *mindata = minp->data(r);
auto const *a = maxdata + discard + 1 - n;
auto const *b = mindata + discard + 1 - diff - n;
for (size_t i = discard; i < total; ++i) {
tmpa.clear();
tmpb.clear();
for (int j = 0; j < n; j++) {
if (!std::isnan(a[j]) && !std::isnan(b[j])) {
tmpa.push_back(a[j]);
tmpb.push_back(b[j]);
}
}
int act_count = tmpa.size();
if (act_count < 2) {
continue;
}
spearmanLevel(tmpa.data(), ptra, act_count);
spearmanLevel(tmpb.data(), ptrb, act_count);
value_t sum = 0.0;
for (int j = 0; j < act_count; j++) {
sum += std::pow(ptra[j] - ptrb[j], 2);
}
dst[i] = act_count == n ? 1.0 - 6.0 * sum / back
: 1.0 - 6.0 * sum / (std::pow(act_count, 3) - act_count);
a++;
b++;
}
}
}
void IndicatorImp::_dyn_calculate(const Indicator &ind) {
// SPEND_TIME(IndicatorImp__dyn_calculate);
const auto &ind_param = getIndParamImp("n");

View File

@ -31,25 +31,24 @@ class HKU_API IndicatorImp : public enable_shared_from_this<IndicatorImp> {
public:
enum OPType {
LEAF, ///< 叶子节点
OP, /// OP(OP1,OP2) OP1->calcalue(OP2->calculate(ind))
ADD, ///< 加
SUB, ///< 减
MUL, ///< 乘
DIV, ///< 除
MOD, ///< 取模
EQ, ///< 等于
GT, ///< 大于
LT, ///< 小于
NE, ///< 不等于
GE, ///< 大于等于
LE, ///< 小于等于
AND, ///< 与
OR, ///< 或
WEAVE, ///< 特殊的,需要两个指标作为参数的指标
OP_IF, /// if操作
CORR, ///< 相关系数,需要两个指标作为参数
SPEARMAN, ///< spearman 相关系数
LEAF, ///< 叶子节点
OP, /// OP(OP1,OP2) OP1->calcalue(OP2->calculate(ind))
ADD, ///< 加
SUB, ///< 减
MUL, ///< 乘
DIV, ///< 除
MOD, ///< 取模
EQ, ///< 等于
GT, ///< 大于
LT, ///< 小于
NE, ///< 不等于
GE, ///< 大于等于
LE, ///< 小于等于
AND, ///< 与
OR, ///< 或
WEAVE, ///< 特殊的,需要两个指标作为参数的指标
OP_IF, /// if操作
CORR, ///< 相关系数,需要两个指标作为参数
INVALID
};

View File

@ -13,8 +13,10 @@ namespace hku {
* Spearman
* @param ind1 1
* @param ind2 2
* @param n (2 0)0 n 使 ind
* @ingroup Indicator
*/
Indicator HKU_API SPEARMAN(int n = 0);
Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n = 0);
} // namespace hku

View File

@ -18,8 +18,13 @@ ISpearman::ISpearman() : IndicatorImp("SPEARMAN") {
setParam<int>("n", 0);
}
ISpearman::ISpearman(const Indicator &ref_ind) : IndicatorImp("SPEARMAN"), m_ref_ind(ref_ind) {
setParam<int>("n", 0);
ISpearman::ISpearman(int n) : IndicatorImp("SPEARMAN") {
setParam<int>("n", n);
}
ISpearman::ISpearman(const Indicator &ref_ind, int n)
: IndicatorImp("SPEARMAN"), m_ref_ind(ref_ind) {
setParam<int>("n", n);
}
ISpearman::~ISpearman() {}
@ -135,10 +140,14 @@ void ISpearman::_calculate(const Indicator &ind) {
}
}
Indicator HKU_API SPEARMAN(int n) {
return make_shared<ISpearman>(n);
}
Indicator HKU_API SPEARMAN(const Indicator &ind1, const Indicator &ind2, int n) {
ISpearman *p = new ISpearman(ind2);
p->setParam<int>("n", n);
return IndicatorImpPtr(p);
auto p = make_shared<ISpearman>(ind2, n);
Indicator result(p);
return result(ind1);
}
} // namespace hku

View File

@ -14,7 +14,8 @@ namespace hku {
class ISpearman : public IndicatorImp {
public:
ISpearman();
ISpearman(const Indicator& ref_ind);
ISpearman(int n);
ISpearman(const Indicator& ref_ind, int n);
virtual ~ISpearman();
virtual bool check() override;

View File

@ -152,34 +152,34 @@ TEST_CASE("test_SPEARMAN") {
CHECK_EQ(result[i], doctest::Approx(expect[i]));
}
// expect = {Null<price_t>(), Null<price_t>(), 1., 0.875, 1.};
// result = SPEARMAN(x, y, 3);
// CHECK_EQ(result.name(), "SPEARMAN");
// CHECK_EQ(result.discard(), 2);
// CHECK_EQ(result.size(), a.size());
// for (size_t i = result.discard(); i < result.size(); i++) {
// CHECK_EQ(result[i], doctest::Approx(expect[i]));
// }
expect = {Null<price_t>(), Null<price_t>(), 1., 0.875, 1.};
result = SPEARMAN(x, y, 3);
CHECK_EQ(result.name(), "SPEARMAN");
CHECK_EQ(result.discard(), 2);
CHECK_EQ(result.size(), a.size());
for (size_t i = result.discard(); i < result.size(); i++) {
CHECK_EQ(result[i], doctest::Approx(expect[i]));
}
// /** @arg 包含 nan 值 */
// price_t null_value = Null<price_t>();
// auto x = PRICELIST({3., 8., null_value, 4., 7., 2., null_value, null_value});
// auto y = PRICELIST({null_value, 5., 10., 8., null_value, 10., 6., null_value});
// // expect = {null_value, , 1., 0.875, 1.};
// // nan, 8, nan, 4, nan, 2, nan
// // nan, 5, nan, 8, nan, 10, nan,
// result = SPEARMAN(x, y, 4);
// HKU_INFO("{}", result);
// for (size_t i = result.discard(); i < result.size(); i++) {
// HKU_INFO("{}: {}", i, result[i]);
// }
/** @arg 包含 nan 值 */
price_t null_value = Null<price_t>();
x = PRICELIST({3., 8., null_value, 4., 7., 2., null_value, null_value});
y = PRICELIST({null_value, 5., 10., 8., null_value, 10., 6., null_value});
// expect = {null_value, , 1., 0.875, 1.};
// nan, 8, nan, 4, nan, 2, nan
// nan, 5, nan, 8, nan, 10, nan,
result = SPEARMAN(x, y, 4);
HKU_INFO("{}", result);
for (size_t i = result.discard(); i < result.size(); i++) {
HKU_INFO("{}: {}", i, result[i]);
}
// x = PRICELIST({8., 4., 2.});
// y = PRICELIST({5., 8., 10.});
// result = SPEARMAN(x, y, x.size());
// HKU_INFO("{}", result);
// HKU_INFO("{}", std::pow(null_value, 2));
// HKU_INFO("{}", 1.0 * 6.0 * null_value / (std::pow(x.size(), 3) - x.size()));
x = PRICELIST({8., 4., 2.});
y = PRICELIST({5., 8., 10.});
result = SPEARMAN(x, y, x.size());
HKU_INFO("{}", result);
HKU_INFO("{}", std::pow(null_value, 2));
HKU_INFO("{}", 1.0 * 6.0 * null_value / (std::pow(x.size(), 3) - x.size()));
}
//-----------------------------------------------------------------------------
@ -210,50 +210,50 @@ TEST_CASE("test_SPEARMAN_benchmark") {
/** @par 检测点 */
TEST_CASE("test_SPEARMAN_export") {
// StockManager &sm = StockManager::instance();
// string filename(sm.tmpdir());
// filename += "/SPEARMAN.xml";
StockManager &sm = StockManager::instance();
string filename(sm.tmpdir());
filename += "/SPEARMAN.xml";
// Stock stock = sm.getStock("sh000001");
// KData kdata = stock.getKData(KQuery(-20));
// Indicator x1 = SPEARMAN(CLOSE(kdata), OPEN(kdata), 10);
// {
// std::ofstream ofs(filename);
// boost::archive::xml_oarchive oa(ofs);
// oa << BOOST_SERIALIZATION_NVP(x1);
// }
Stock stock = sm.getStock("sh000001");
KData kdata = stock.getKData(KQuery(-20));
Indicator x1 = SPEARMAN(CLOSE(kdata), OPEN(kdata), 10);
{
std::ofstream ofs(filename);
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(x1);
}
// Indicator x2;
// {
// std::ifstream ifs(filename);
// boost::archive::xml_iarchive ia(ifs);
// ia >> BOOST_SERIALIZATION_NVP(x2);
// }
Indicator x2;
{
std::ifstream ifs(filename);
boost::archive::xml_iarchive ia(ifs);
ia >> BOOST_SERIALIZATION_NVP(x2);
}
// CHECK_EQ(x2.name(), "SPEARMAN");
// CHECK_EQ(x1.size(), x2.size());
// CHECK_EQ(x1.discard(), x2.discard());
// CHECK_EQ(x1.getResultNumber(), x2.getResultNumber());
// for (size_t i = x1.discard(); i < x1.size(); ++i) {
// if (std::isnan(x1[i])) {
// CHECK_UNARY(std::isnan(x2[i]));
// } else {
// CHECK_EQ(x1[i], doctest::Approx(x2[i]));
// }
// }
CHECK_EQ(x2.name(), "SPEARMAN");
CHECK_EQ(x1.size(), x2.size());
CHECK_EQ(x1.discard(), x2.discard());
CHECK_EQ(x1.getResultNumber(), x2.getResultNumber());
for (size_t i = x1.discard(); i < x1.size(); ++i) {
if (std::isnan(x1[i])) {
CHECK_UNARY(std::isnan(x2[i]));
} else {
CHECK_EQ(x1[i], doctest::Approx(x2[i]));
}
}
// Indicator x11 = x1.getResult(1);
// Indicator x21 = x2.getResult(1);
// CHECK_EQ(x11.size(), x21.size());
// CHECK_EQ(x11.discard(), x21.discard());
// CHECK_EQ(x11.getResultNumber(), x21.getResultNumber());
// for (size_t i = x11.discard(); i < x21.size(); ++i) {
// if (std::isnan(x11[i])) {
// CHECK_UNARY(std::isnan(x21[i]));
// } else {
// CHECK_EQ(x11[i], doctest::Approx(x21[i]));
// }
// }
Indicator x11 = x1.getResult(1);
Indicator x21 = x2.getResult(1);
CHECK_EQ(x11.size(), x21.size());
CHECK_EQ(x11.discard(), x21.discard());
CHECK_EQ(x11.getResultNumber(), x21.getResultNumber());
for (size_t i = x11.discard(); i < x21.size(); ++i) {
if (std::isnan(x11[i])) {
CHECK_UNARY(std::isnan(x21[i]));
} else {
CHECK_EQ(x11[i], doctest::Approx(x21[i]));
}
}
}
#endif /* #if HKU_SUPPORT_SERIALIZATION */

View File

@ -466,6 +466,9 @@ Indicator (*ZHBOND10_2)(const DatetimeList&, double) = ZHBOND10;
Indicator (*ZHBOND10_3)(const KData& k, double) = ZHBOND10;
Indicator (*ZHBOND10_4)(const Indicator&, double) = ZHBOND10;
Indicator (*SPEARMAN_1)(int) = SPEARMAN;
Indicator (*SPEARMAN_2)(const Indicator&, const Indicator&, int) = SPEARMAN;
void export_Indicator_build_in(py::module& m) {
m.def("KDATA", KDATA1);
m.def("KDATA", KDATA3, R"(KDATA([data])
@ -1645,12 +1648,13 @@ void export_Indicator_build_in(py::module& m) {
:param DatetimeList|KDate|Indicator data: 使
:param float default_val: 使)");
m.def("SPEARMAN", SPEARMAN, py::arg("ind1"), py::arg("ind2"), py::arg("n"),
R"(SPEARMAN(ind1, ind2, n)
m.def("SPEARMAN", SPEARMAN_1, py::arg("n") = 0);
m.def("SPEARMAN", SPEARMAN_2, py::arg("ind1"), py::arg("ind2"), py::arg("n") = 0,
R"(SPEARMAN(ind1, ind2[, n])
Spearman
:param Indicator ind1: 1
:param Indicator ind2: 2
:param int n: )");
:param int n: (2 0)0 n 使 ind )");
}