mirror of
https://gitee.com/fasiondog/hikyuu.git
synced 2024-11-30 02:48:57 +08:00
调整 SPEARMAN 实现
This commit is contained in:
parent
3ff9cc4a13
commit
e46c2044df
@ -315,14 +315,14 @@ 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();
|
||||
}
|
||||
// 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 */
|
||||
|
@ -393,7 +393,7 @@ Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n);
|
||||
* @param ind2 指标2
|
||||
* @ingroup Indicator
|
||||
*/
|
||||
Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n);
|
||||
// Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n);
|
||||
|
||||
} /* namespace hku */
|
||||
|
||||
|
@ -1629,7 +1629,11 @@ void IndicatorImp::execute_spearman() {
|
||||
auto *ptrb = levelb.get();
|
||||
|
||||
// 不处理 n 不足的情况,防止只需要计算全部序列时,过于耗时
|
||||
value_t back = std::pow(value_t(n), 3) - 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);
|
||||
@ -1637,13 +1641,26 @@ void IndicatorImp::execute_spearman() {
|
||||
auto const *a = maxdata + discard + 1 - n;
|
||||
auto const *b = mindata + discard + 1 - diff - n;
|
||||
for (size_t i = discard; i < total; ++i) {
|
||||
spearmanLevel(a, ptra, n);
|
||||
spearmanLevel(b, ptrb, n);
|
||||
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 (size_t j = 0; j < n; j++) {
|
||||
for (int j = 0; j < act_count; j++) {
|
||||
sum += std::pow(ptra[j] - ptrb[j], 2);
|
||||
}
|
||||
dst[i] = 1 - 6.0 * sum / back;
|
||||
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++;
|
||||
}
|
||||
|
@ -84,6 +84,7 @@
|
||||
#include "crt/SGN.h"
|
||||
#include "crt/SLOPE.h"
|
||||
#include "crt/SMA.h"
|
||||
#include "crt/SPEARMAN.h"
|
||||
#include "crt/SQRT.h"
|
||||
#include "crt/STDEV.h"
|
||||
#include "crt/STDP.h"
|
||||
|
20
hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h
Normal file
20
hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2024 hikyuu.org
|
||||
*
|
||||
* Created on: 2024-03-10
|
||||
* Author: fasiondog
|
||||
*/
|
||||
|
||||
#include "../Indicator.h"
|
||||
|
||||
namespace hku {
|
||||
|
||||
/**
|
||||
* Spearman 相关系数
|
||||
* @param ind1 指标1
|
||||
* @param ind2 指标2
|
||||
* @ingroup Indicator
|
||||
*/
|
||||
Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n = 0);
|
||||
|
||||
} // namespace hku
|
144
hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp
Normal file
144
hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (c) 2024 hikyuu.org
|
||||
*
|
||||
* Created on: 2024-03-10
|
||||
* Author: fasiondog
|
||||
*/
|
||||
|
||||
#include "hikyuu/indicator/crt/ALIGN.h"
|
||||
#include "ISpearman.h"
|
||||
|
||||
#if HKU_SUPPORT_SERIALIZATION
|
||||
BOOST_CLASS_EXPORT(hku::ISpearman)
|
||||
#endif
|
||||
|
||||
namespace hku {
|
||||
|
||||
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() {}
|
||||
|
||||
bool ISpearman::check() {
|
||||
int n = getParam<int>("n");
|
||||
return n == 0 || n >= 2;
|
||||
}
|
||||
|
||||
IndicatorImpPtr ISpearman::_clone() {
|
||||
ISpearman *p = new ISpearman();
|
||||
p->m_ref_ind = m_ref_ind.clone();
|
||||
return IndicatorImpPtr(p);
|
||||
}
|
||||
|
||||
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 ISpearman::_calculate(const Indicator &ind) {
|
||||
auto k = getContext();
|
||||
|
||||
m_ref_ind.setContext(k);
|
||||
Indicator ref = m_ref_ind;
|
||||
if (m_ref_ind.size() != ind.size()) {
|
||||
ref = ALIGN(m_ref_ind, ind);
|
||||
}
|
||||
|
||||
size_t total = ind.size();
|
||||
_readyBuffer(total, 1);
|
||||
HKU_IF_RETURN(total == 0, void());
|
||||
|
||||
int n = getParam<int>("n");
|
||||
if (n == 0) {
|
||||
n = total;
|
||||
}
|
||||
|
||||
m_discard = std::max(ind.discard(), ref.discard());
|
||||
m_discard += n - 1;
|
||||
if (m_discard > total) {
|
||||
m_discard = total;
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
auto *dst = this->data();
|
||||
auto const *a = ind.data() + m_discard + 1 - n;
|
||||
auto const *b = ref.data() + m_discard + 1 - n;
|
||||
for (size_t i = m_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++;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace hku
|
41
hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h
Normal file
41
hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2024 hikyuu.org
|
||||
*
|
||||
* Created on: 2024-03-10
|
||||
* Author: fasiondog
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Indicator.h"
|
||||
|
||||
namespace hku {
|
||||
|
||||
class ISpearman : public IndicatorImp {
|
||||
public:
|
||||
ISpearman();
|
||||
ISpearman(const Indicator& ref_ind);
|
||||
virtual ~ISpearman();
|
||||
|
||||
virtual bool check() override;
|
||||
virtual void _calculate(const Indicator& data) override;
|
||||
virtual IndicatorImpPtr _clone() override;
|
||||
|
||||
private:
|
||||
Indicator m_ref_ind;
|
||||
|
||||
//============================================
|
||||
// 序列化支持
|
||||
//============================================
|
||||
#if HKU_SUPPORT_SERIALIZATION
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int version) {
|
||||
ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(IndicatorImp);
|
||||
ar& BOOST_SERIALIZATION_NVP(m_ref_ind);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace hku
|
@ -10,6 +10,7 @@
|
||||
#include "../test_config.h"
|
||||
#include <fstream>
|
||||
#include <hikyuu/StockManager.h>
|
||||
#include <hikyuu/indicator/crt/SPEARMAN.h>
|
||||
#include <hikyuu/indicator/crt/KDATA.h>
|
||||
#include <hikyuu/indicator/crt/PRICELIST.h>
|
||||
|
||||
@ -125,7 +126,7 @@ TEST_CASE("test_spearmanLevel") {
|
||||
TEST_CASE("test_SPEARMAN") {
|
||||
Indicator result;
|
||||
|
||||
// 空指标
|
||||
/** @arg 空指标 */
|
||||
result = SPEARMAN(Indicator(), Indicator(), 10);
|
||||
CHECK_UNARY(result.empty());
|
||||
|
||||
@ -135,13 +136,13 @@ TEST_CASE("test_SPEARMAN") {
|
||||
Indicator x = PRICELIST(a);
|
||||
Indicator y = PRICELIST(b);
|
||||
|
||||
// 非法参数 n
|
||||
result = SPEARMAN(x, y, 0);
|
||||
/** @arg 非法参数 n */
|
||||
result = SPEARMAN(x, y, -1);
|
||||
CHECK_UNARY(result.empty());
|
||||
result = SPEARMAN(x, y, 1);
|
||||
CHECK_UNARY(result.empty());
|
||||
|
||||
// 正常情况
|
||||
/** @arg 正常情况 n */
|
||||
PriceList expect{Null<price_t>(), 1., 1., 0.95, 0.875};
|
||||
result = SPEARMAN(x, y, a.size());
|
||||
CHECK_EQ(result.name(), "SPEARMAN");
|
||||
@ -151,14 +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]);
|
||||
// }
|
||||
|
||||
// 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()));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -189,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 */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user