Merge pull request #194 from fasiondog/feature/factor

add MultiFactor
This commit is contained in:
fasiondog 2024-03-16 12:57:06 +08:00 committed by GitHub
commit c60c2f4975
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 1823 additions and 100 deletions

View File

@ -3,7 +3,7 @@
from hikyuu.util.slice import list_getitem
from hikyuu.core import (
System, SystemPart, ConditionBase, EnvironmentBase, MoneyManagerBase,
ProfitGoalBase, SelectorBase, SignalBase, SlippageBase, StoplossBase
ProfitGoalBase, SelectorBase, SignalBase, SlippageBase, StoplossBase, AllocateFundsBase
)
@ -15,6 +15,14 @@ def part_iter(self):
ConditionBase.__iter__ = part_iter
def part_init(self, name, params):
super(self.__class__, self).__init__(name)
self._name = name
self._params = params
for k, v in params.items():
self.set_param(k, v)
# ------------------------------------------------------------------
# System
# ------------------------------------------------------------------
@ -33,14 +41,6 @@ System.INVALID = System.Part.INVALID
# ------------------------------------------------------------------
# condition
# ------------------------------------------------------------------
def cn_init(self, name, params):
super(self.__class__, self).__init__(name)
self._name = name
self._params = params
for k, v in params.items():
self.set_param(k, v)
def crtCN(func, params={}, name='crtCN'):
"""
@ -50,7 +50,7 @@ def crtCN(func, params={}, name='crtCN'):
:param str name:
:return:
"""
meta_x = type(name, (ConditionBase, ), {'__init__': cn_init})
meta_x = type(name, (ConditionBase, ), {'__init__': part_init})
meta_x._clone = lambda self: meta_x(self._name, self._params)
meta_x._calculate = func
return meta_x(name, params)
@ -59,14 +59,6 @@ def crtCN(func, params={}, name='crtCN'):
# ------------------------------------------------------------------
# environment
# ------------------------------------------------------------------
def ev_init(self, name, params):
super(self.__class__, self).__init__(name)
self._name = name
self._params = params
for k, v in params.items():
self.set_param(k, v)
def crtEV(func, params={}, name='crtEV'):
"""
@ -76,7 +68,7 @@ def crtEV(func, params={}, name='crtEV'):
:param str name:
:return:
"""
meta_x = type(name, (EnvironmentBase, ), {'__init__': ev_init})
meta_x = type(name, (EnvironmentBase, ), {'__init__': part_init})
meta_x._clone = lambda self: meta_x(self._name, self._params)
meta_x._calculate = func
return meta_x(name, params)
@ -85,14 +77,6 @@ def crtEV(func, params={}, name='crtEV'):
# ------------------------------------------------------------------
# moneymanager
# ------------------------------------------------------------------
def mm_init(self, name, params):
super(self.__class__, self).__init__(name)
self._name = name
self._params = params
for k, v in params.items():
self.set_param(k, v)
def crtMM(func, params={}, name='crtMM'):
"""
@ -102,7 +86,7 @@ def crtMM(func, params={}, name='crtMM'):
:param str name:
:return:
"""
meta_x = type(name, (MoneyManagerBase, ), {'__init__': mm_init})
meta_x = type(name, (MoneyManagerBase, ), {'__init__': part_init})
meta_x._clone = lambda self: meta_x(self._name, self._params)
meta_x._calculate = func
return meta_x(name, params)
@ -111,14 +95,6 @@ def crtMM(func, params={}, name='crtMM'):
# ------------------------------------------------------------------
# profitgoal
# ------------------------------------------------------------------
def pg_init(self, name, params):
super(self.__class__, self).__init__(name)
self._name = name
self._params = params
for k, v in params.items():
self.set_param(k, v)
def crtPG(func, params={}, name='crtPG'):
"""
@ -128,7 +104,7 @@ def crtPG(func, params={}, name='crtPG'):
:param str name:
:return:
"""
meta_x = type(name, (ProfitGoalBase, ), {'__init__': pg_init})
meta_x = type(name, (ProfitGoalBase, ), {'__init__': part_init})
meta_x._clone = lambda self: meta_x(self._name, self._params)
meta_x._calculate = func
return meta_x(name, params)
@ -137,14 +113,6 @@ def crtPG(func, params={}, name='crtPG'):
# ------------------------------------------------------------------
# signal
# ------------------------------------------------------------------
def sig_init(self, name, params):
super(self.__class__, self).__init__(name)
self._name = name
self._params = params
for k, v in params.items():
self.set_param(k, v)
def crtSG(func, params={}, name='crtSG'):
"""
@ -154,7 +122,7 @@ def crtSG(func, params={}, name='crtSG'):
:param str name:
:return:
"""
meta_x = type(name, (SignalBase, ), {'__init__': sig_init})
meta_x = type(name, (SignalBase, ), {'__init__': part_init})
meta_x._clone = lambda self: meta_x(self._name, self._params)
meta_x._calculate = func
return meta_x(name, params)
@ -175,11 +143,43 @@ def se_add_stock_list(self, stk_list, proto_sys):
SelectorBase.add_stock_list = se_add_stock_list
def crtSE(func, params={}, name='crtSE'):
"""
:param func:
:param {} params:
:param str name:
:return:
"""
meta_x = type(name, (SelectorBase, ), {'__init__': part_init})
meta_x._clone = lambda self: meta_x(self._name, self._params)
meta_x._calculate = func
return meta_x(name, params)
# ------------------------------------------------------------------
# allocatefunds
# ------------------------------------------------------------------
def crtAF(func, params={}, name='crtAF'):
"""
:param func:
:param {} params:
:param str name:
:return:
"""
meta_x = type(name, (AllocateFundsBase, ), {'__init__': part_init})
meta_x._clone = lambda self: meta_x(self._name, self._params)
meta_x._calculate = func
return meta_x(name, params)
# ------------------------------------------------------------------
# slippage
# ------------------------------------------------------------------
def sl_init(self, name, params):
super(self.__class__, self).__init__(name)
self._name = name
@ -197,7 +197,7 @@ def crtSL(func, params={}, name='crtSL'):
:param str name:
:return:
"""
meta_x = type(name, (SlippageBase, ), {'__init__': sl_init})
meta_x = type(name, (SlippageBase, ), {'__init__': part_init})
meta_x._clone = lambda self: meta_x(self._name, self._params)
meta_x._calculate = func
return meta_x(name, params)

View File

@ -133,6 +133,10 @@
* @details
* @ingroup TradeSystem
*
* @defgroup MultiFactor MultiFactor
* @details
* @ingroup TradeSystem
*
* @defgroup SystemInstance SystemInstance
* @details
* @ingroup Hikyuu

View File

@ -52,6 +52,21 @@ bool Indicator::alike(const Indicator& other) const {
return m_imp->alike(*other.m_imp);
}
bool Indicator::equal(const Indicator& other) const {
HKU_IF_RETURN(this == &other || m_imp == other.m_imp, true);
HKU_IF_RETURN(size() != other.size() || discard() != other.discard(), false);
auto const* d1 = this->data();
auto const* d2 = other.data();
for (size_t i = 0, total = size(); i < total; i++) {
HKU_IF_RETURN(
(std::isnan(d1[i]) && !std::isnan(d2[i])) || (!std::isnan(d1[i]) && std::isnan(d2[i])),
false);
HKU_IF_RETURN(
(!std::isnan(d1[i]) && !std::isnan(d2[i])) && (std::abs(d1[i] - d2[i]) >= 0.0001), false);
}
return true;
}
Indicator& Indicator::operator=(const Indicator& indicator) {
HKU_IF_RETURN(this == &indicator, *this);
m_imp = indicator.m_imp;

View File

@ -184,6 +184,17 @@ public:
return m_imp ? m_imp->data(result_num) : nullptr;
}
/**
* ind的值是否相等
* @note operator==Indicatorind进行值比较
*/
bool equal(const Indicator& other) const;
/** 判断是否是同一个实例 */
bool isSame(const Indicator& other) const {
return !m_imp && m_imp == other.m_imp;
}
protected:
IndicatorImpPtr m_imp;
@ -197,6 +208,9 @@ private:
#endif /* HKU_SUPPORT_SERIALIZATION */
};
/** @ingroup Indicator */
typedef vector<Indicator> IndicatorList;
inline string Indicator::name() const {
return m_imp ? m_imp->name() : "IndicatorImp";
}

View File

@ -21,7 +21,7 @@ namespace hku {
* @return Indicator
* @ingroup Indicator
*/
inline Indicator ICIR(const Indicator& ic, int n = 10) {
inline Indicator ICIR(const Indicator& ic, int n = 120) {
Indicator x = MA(ic, n) / STDEV(ic, n);
x.name("IR");
x.setParam<int>("n", n);

View File

@ -16,7 +16,7 @@
namespace hku {
/**
* (price - prePrice) / prevPrice
* (price - prePrice) / prevPrice N
* @ingroup Indicator
*/
Indicator HKU_API ROCP(int n = 10);

View File

@ -16,7 +16,7 @@
namespace hku {
/**
* (price / prevPrice)
* (price / prevPrice), N
* @ingroup Indicator
*/
Indicator HKU_API ROCR(int n = 10);

View File

@ -15,7 +15,7 @@ BOOST_CLASS_EXPORT(hku::IEma)
namespace hku {
IEma::IEma() : IndicatorImp("IEma", 1) {
IEma::IEma() : IndicatorImp("EMA", 1) {
setParam<int>("n", 22);
}

View File

@ -7,6 +7,7 @@
#include "hikyuu/Block.h"
#include "hikyuu/indicator/crt/ALIGN.h"
#include "hikyuu/indicator/crt/REF.h"
#include "hikyuu/indicator/crt/ROCP.h"
#include "hikyuu/indicator/crt/PRICELIST.h"
#include "hikyuu/indicator/crt/SPEARMAN.h"
@ -18,13 +19,14 @@ BOOST_CLASS_EXPORT(hku::IIc)
namespace hku {
IIc::IIc() : IndicatorImp("IC") {
setParam<int>("n", 1);
IIc::IIc() : IndicatorImp("IC", 1) {
setParam<int>("n", 1); // 调仓周期
// 对齐时是否以 nan 值进行填充,否则以小于当前日期的最后值作为填充
setParam<bool>("fill_null", true);
}
IIc::IIc(const StockList& stks, const KQuery& query, int n, const Stock& ref_stk)
: IndicatorImp("IC"), m_query(query), m_ref_stk(ref_stk), m_stks(stks) {
: IndicatorImp("IC", 1), m_query(query), m_ref_stk(ref_stk), m_stks(stks) {
setParam<int>("n", n);
setParam<bool>("fill_null", true);
}
@ -44,58 +46,65 @@ IndicatorImpPtr IIc::_clone() {
}
void IIc::_calculate(const Indicator& inputInd) {
HKU_ERROR_IF_RETURN(m_ref_stk.isNull(), void(), "ref_stk is null!");
// 先申请内存,保持和参考日期等长
auto ref_dates = m_ref_stk.getDatetimeList(m_query);
size_t total = ref_dates.size();
_readyBuffer(total, 1);
size_t days_total = ref_dates.size();
_readyBuffer(days_total, 1);
if (total < 2 || m_stks.size() < 2) {
HKU_WARN(
"The number(>=2) of stock or data length(>=2) is insufficient! current data len: {}, "
"current stock number: {}",
total, m_stks.size());
m_discard = total;
// 检测异常输入数据
m_discard = days_total;
HKU_ERROR_IF_RETURN(m_ref_stk.isNull(), void(), "ref_stk is null!");
HKU_ERROR_IF_RETURN(days_total < 2, void(),
"The data length(>=2) is insufficient! current data len: {}", days_total);
size_t stk_count = m_stks.size();
HKU_ERROR_IF_RETURN(stk_count < 2, void(),
"The number(>=2) of stock is insufficient! current stock number: {}",
stk_count);
for (size_t i = 0; i < stk_count; i++) {
HKU_ERROR_IF_RETURN(m_stks[i].isNull(), void(), "The [{}] stock is null!", i);
}
int n = getParam<int>("n");
// 由于 ic 是当前收盘价和 n 日后收益的相关系数,需要避免未来函数,最终的 ic 需要右移 n 日
// spearman 本身需要数据长度大于等于2所以 n + 1 >= days_totals 时,直接抛弃
HKU_IF_RETURN(n + 1 >= days_total, void());
bool fill_null = getParam<bool>("fill_null");
vector<Indicator> all_inds;
all_inds.reserve(m_stks.size());
vector<Indicator> all_returns;
all_returns.reserve(m_stks.size());
// 计算每支证券对齐后的因子值与 n 日收益率
vector<Indicator> all_inds(stk_count); // 保存每支证券对齐后的因子值
vector<Indicator> all_returns(stk_count); // 保存每支证券对齐后的 n 日收益率
Indicator ind = inputInd;
for (const auto& stk : m_stks) {
if (stk.isNull()) {
HKU_WARN("Exist null stock, it was ignored!");
} else {
auto k = stk.getKData(m_query);
all_inds.push_back(ALIGN(ind(k), ref_dates, fill_null));
all_returns.push_back(ALIGN(ROCP(k.close(), n), ref_dates, fill_null));
size_t discard = n;
for (size_t i = 0; i < stk_count; i++) {
auto k = m_stks[i].getKData(m_query);
all_inds[i] = ALIGN(ind(k), ref_dates, fill_null);
if (all_inds[i].discard() > discard) {
discard = all_inds[i].discard();
}
// 计算 n 日收益率,同时需要右移 n 位,即第 i 日的因子值和第 i + n 的收益率对应
all_returns[i] = ALIGN(REF(ROCP(k.close(), n), n), ref_dates, fill_null);
if (all_returns[i].discard() > discard) {
discard = all_returns[i].discard();
}
}
size_t ind_count = all_inds.size();
if (all_inds.size() < 2) {
HKU_WARN("The number of exist stock is insufficient!");
m_discard = total;
return;
}
m_discard = discard;
HKU_IF_RETURN(m_discard >= days_total, void());
PriceList tmp(ind_count, Null<price_t>());
PriceList tmp_return(ind_count, Null<price_t>());
PriceList tmp(stk_count, Null<price_t>());
PriceList tmp_return(stk_count, Null<price_t>());
auto* dst = this->data();
for (size_t i = 0; i < total; i++) {
for (size_t j = 0; j < ind_count; j++) {
if (i >= all_inds[j].size() || i >= all_returns[j].size()) {
continue;
}
for (size_t i = m_discard; i < days_total; i++) {
// 计算日截面 spearman 相关系数即 ic 值
for (size_t j = 0; j < stk_count; j++) {
tmp[j] = all_inds[j][i];
tmp_return[j] = all_returns[j][i];
}
auto a = PRICELIST(tmp);
auto b = PRICELIST(tmp_return);
auto ic = hku::SPEARMAN(a, b, ind_count);
auto ic = hku::SPEARMAN(a, b, stk_count);
dst[i] = ic[ic.size() - 1];
}
}

View File

@ -67,6 +67,12 @@ void IStdev::_calculate(const Indicator& data) {
ex2 += d_pow;
dst[i] = std::sqrt((ex2 - std::pow(ex, 2) / n) / (n - 1));
}
// 排除第一位的0值
if (m_discard < total) {
dst[0] = Null<value_t>();
m_discard += 1;
}
}
void IStdev::_dyn_run_one_step(const Indicator& ind, size_t curPos, size_t step) {

View File

@ -0,0 +1,389 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-12
* Author: fasiondog
*/
#include <cmath>
#include "hikyuu/indicator/crt/ALIGN.h"
#include "hikyuu/indicator/crt/ROCP.h"
#include "hikyuu/indicator/crt/REF.h"
#include "hikyuu/indicator/crt/PRICELIST.h"
#include "hikyuu/indicator/crt/IC.h"
#include "hikyuu/indicator/crt/ICIR.h"
#include "hikyuu/indicator/crt/SPEARMAN.h"
#include "MultiFactorBase.h"
namespace hku {
HKU_API std::ostream& operator<<(std::ostream& out, const MultiFactorBase& mf) {
out << "MultiFactor{"
<< "\n name: " << mf.name() << "\n params: " << mf.getParameter()
<< "\n query: " << mf.getQuery() << "\n ref stock: " << mf.m_ref_stk;
out << "\n src inds count: " << mf.m_inds.size() << " [";
if (mf.m_inds.size() <= 5) {
for (const auto& ind : mf.m_inds) {
out << ind.name() << ", ";
}
} else {
for (size_t i = 0; i < 5; i++) {
out << mf.m_inds[i].name() << ", ";
}
out << "......";
}
out << "]";
out << "\n stocks count: " << mf.m_stks.size() << " [";
size_t print_stk_len = std::min<size_t>(5, mf.m_stks.size());
for (size_t i = 0; i < print_stk_len; i++) {
out << mf.m_stks[i].market_code() << ", ";
}
if (mf.m_stks.size() > 5) {
out << "......";
}
out << "]";
out << "\n}";
return out;
}
HKU_API std::ostream& operator<<(std::ostream& out, const MultiFactorPtr& mf) {
if (mf) {
out << *mf;
} else {
out << "MultiFactor(NULL)";
}
return out;
}
HKU_API std::ostream& operator<<(std::ostream& out,
const std::pair<Stock, MultiFactorBase::value_t>& td) {
out << std::fixed;
out.precision(4);
out << "(" << td.first.market_code() << ", " << td.second << ")" << std::endl;
out.unsetf(std::ostream::floatfield);
out.precision();
return out;
}
HKU_API std::ostream& operator<<(std::ostream& out,
const vector<std::pair<Stock, MultiFactorBase::value_t>>& td) {
out << std::fixed;
out.precision(4);
size_t total = td.size();
if (total < 10) {
for (size_t i = 0; i < total; i++) {
out << i << ": " << td[i];
}
} else {
for (size_t i = 0; i < 5; i++) {
out << i << ": " << td[i];
}
out << "......" << std::endl;
for (size_t i = total - 5; i < total; i++) {
out << i << ": " << td[i];
}
}
out.unsetf(std::ostream::floatfield);
out.precision();
return out;
}
HKU_API std::ostream& operator<<(
std::ostream& out, const vector<vector<std::pair<Stock, MultiFactorBase::value_t>>>& td) {
out << std::fixed;
out.precision(4);
size_t total = td.size();
if (total <= 2) {
for (size_t i = 0; i < total; i++) {
out << "========= " << i << " =========" << std::endl;
out << td[i];
}
} else {
out << "========= 0 =========" << std::endl;
out << td[0];
out << "......" << std::endl;
out << "......" << std::endl;
out << "========= " << total - 1 << " =========" << std::endl;
out << td[total - 1];
}
out.unsetf(std::ostream::floatfield);
out.precision();
return out;
}
MultiFactorBase::MultiFactorBase() {
setParam<bool>("fill_null", true);
setParam<int>("ic_n", 1);
}
MultiFactorBase::MultiFactorBase(const string& name) {
setParam<bool>("fill_null", true);
setParam<int>("ic_n", 1);
}
MultiFactorBase::MultiFactorBase(const IndicatorList& inds, const StockList& stks,
const KQuery& query, const Stock& ref_stk, const string& name,
int ic_n)
: m_name(name), m_inds(inds), m_stks(stks), m_ref_stk(ref_stk), m_query(query) {
setParam<bool>("fill_null", true);
setParam<int>("ic_n", ic_n);
HKU_CHECK(!m_ref_stk.isNull(), "The reference stock must be set!");
HKU_CHECK(!m_inds.empty(), "Input source factor list is empty!");
// 后续计算需要保持对齐,夹杂 Null stock 处理麻烦,直接抛出异常屏蔽
for (const auto& stk : m_stks) {
HKU_CHECK(!stk.isNull(), "Exist null stock in stks!");
}
// 获取用于对齐的参考日期
m_ref_dates = m_ref_stk.getDatetimeList(m_query);
HKU_CHECK(m_ref_dates.size() >= 2, "The dates len is insufficient! current len: {}",
m_ref_dates.size());
HKU_CHECK(m_stks.size() >= 2, "The number of stock is insufficient! current stock number: {}",
m_stks.size());
}
MultiFactorPtr MultiFactorBase::clone() {
std::lock_guard<std::mutex> lock(m_mutex);
MultiFactorPtr p;
try {
p = _clone();
} catch (...) {
HKU_ERROR("Subclass _clone failed!");
p = MultiFactorPtr();
}
if (!p || p.get() == this) {
HKU_ERROR("Failed clone! Will use self-ptr!");
return shared_from_this();
}
p->m_params = m_params;
p->m_stks = m_stks;
p->m_ref_stk = m_ref_stk;
p->m_query = m_query;
p->m_stk_map = m_stk_map;
p->m_all_factors = m_all_factors;
p->m_date_index = m_date_index;
p->m_stk_factor_by_date = m_stk_factor_by_date;
p->m_ref_dates = m_ref_dates;
p->m_ic = m_ic.clone();
p->m_calculated = m_calculated;
p->m_inds.reserve(m_inds.size());
for (const auto& ind : m_inds) {
p->m_inds.emplace_back(ind.clone());
}
p->m_all_factors.reserve(m_all_factors.size());
for (const auto& ind : m_all_factors) {
p->m_all_factors.emplace_back(ind.clone());
}
return p;
}
const Indicator& MultiFactorBase::getFactor(const Stock& stk) {
calculate();
const auto iter = m_stk_map.find(stk);
HKU_CHECK(iter != m_stk_map.cend(), "Could not find this stock: {}", stk);
return m_all_factors[iter->second];
}
const IndicatorList& MultiFactorBase::getAllFactors() {
calculate();
return m_all_factors;
}
const vector<std::pair<Stock, MultiFactorBase::value_t>>& MultiFactorBase::getCross(
const Datetime& d) {
calculate();
const auto iter = m_date_index.find(d);
HKU_CHECK(iter != m_date_index.cend(), "Could not find this date: {}", d);
return m_stk_factor_by_date[iter->second];
}
const vector<vector<std::pair<Stock, MultiFactorBase::value_t>>>& MultiFactorBase::getAllCross() {
calculate();
return m_stk_factor_by_date;
}
Indicator MultiFactorBase::getIC(int ndays) {
calculate();
std::lock_guard<std::mutex> lock(m_mutex);
// 如果 ndays 和 ic_n 参数相同,优先取缓存的 ic 结果
// 新的因子计算 IC本质上不需要缓存如等权重但通过 IC 或 ICIR 计算权重的新因子
// 虽然可以用不同的 N 日收益率来计算新因子IC但最好还是使用相同值
// 通过IC/ICIR计算权重的情况较多所以这里直接缓存一份减少重复计算
// 实际使用时,最好保证 getIC(ndays) 中的 ndays 和 ic_n 一致
int ic_n = getParam<int>("ic_n");
if (ndays == 0) {
ndays = ic_n;
}
HKU_IF_RETURN(ic_n == ndays && !m_ic.empty(), m_ic.clone());
size_t days_total = m_ref_dates.size();
Indicator result = PRICELIST(PriceList(days_total, Null<price_t>()));
result.name("IC");
if (ndays + 1 >= days_total || ndays < 0) {
result.setDiscard(days_total);
if (ic_n == ndays) {
m_ic = result;
}
return result;
}
auto all_returns = _getAllReturns(ndays);
size_t discard = ndays;
size_t ind_count = m_all_factors.size();
for (size_t i = 0; i < ind_count; i++) {
if (all_returns[i].discard() > discard) {
discard = all_returns[i].discard();
}
if (m_all_factors[i].discard() > discard) {
discard = m_all_factors[i].discard();
}
}
if (discard >= days_total) {
result.setDiscard(days_total);
if (ic_n == ndays) {
m_ic = result;
}
return result;
}
result.setDiscard(discard);
PriceList tmp(ind_count, Null<price_t>());
PriceList tmp_return(ind_count, Null<price_t>());
auto* dst = result.data();
for (size_t i = discard; i < days_total; i++) {
for (size_t j = 0; j < ind_count; j++) {
tmp[j] = m_all_factors[j][i];
tmp_return[j] = all_returns[j][i];
}
auto a = PRICELIST(tmp);
auto b = PRICELIST(tmp_return);
auto ic = hku::SPEARMAN(a, b, ind_count);
dst[i] = ic[ic.size() - 1];
}
// 如果 ndays 和 ic_n 参数相同,缓存计算结果
if (ic_n == ndays) {
m_ic = result;
}
return result;
}
Indicator MultiFactorBase::getICIR(int ic_n, int ir_n) {
return ICIR(getIC(ic_n), ir_n);
}
IndicatorList MultiFactorBase::_getAllReturns(int ndays) const {
bool fill_null = getParam<bool>("fill_null");
vector<Indicator> all_returns;
all_returns.reserve(m_stks.size());
for (const auto& stk : m_stks) {
auto k = stk.getKData(m_query);
all_returns.push_back(ALIGN(REF(ROCP(k.close(), ndays), ndays), m_ref_dates, fill_null));
}
return all_returns;
}
vector<IndicatorList> MultiFactorBase::_alignAllInds() {
bool fill_null = getParam<bool>("fill_null");
size_t stk_count = m_stks.size();
size_t ind_count = m_inds.size();
vector<IndicatorList> all_stk_inds;
all_stk_inds.resize(stk_count);
for (size_t i = 0; i < stk_count; i++) {
const auto& stk = m_stks[i];
auto kdata = stk.getKData(m_query);
auto& cur_stk_inds = all_stk_inds[i];
cur_stk_inds.resize(ind_count);
for (size_t j = 0; j < ind_count; j++) {
cur_stk_inds[j] = ALIGN(m_inds[j](kdata), m_ref_dates, fill_null);
cur_stk_inds[j].name(m_inds[j].name());
}
}
return all_stk_inds;
}
void MultiFactorBase::_buildIndex() {
size_t stk_count = m_stks.size();
HKU_ASSERT(stk_count == m_all_factors.size());
for (size_t i = 0; i < stk_count; i++) {
m_stk_map[m_stks[i]] = i;
}
// 建立每日截面的索引,并每日降序排序
size_t days_total = m_ref_dates.size();
m_stk_factor_by_date.resize(days_total);
vector<std::pair<Stock, value_t>> one_day;
for (size_t i = 0; i < days_total; i++) {
one_day.resize(stk_count);
for (size_t j = 0; j < stk_count; j++) {
one_day[j] = std::make_pair(m_stks[j], m_all_factors[j][i]);
}
std::sort(one_day.begin(), one_day.end(),
[](const std::pair<Stock, value_t>& a, const std::pair<Stock, value_t>& b) {
if (std::isnan(a.second) && std::isnan(b.second)) {
return false;
} else if (!std::isnan(a.second) && std::isnan(b.second)) {
return true;
} else if (std::isnan(a.second) && !std::isnan(b.second)) {
return false;
}
return a.second > b.second;
});
m_stk_factor_by_date[i] = std::move(one_day);
m_date_index[m_ref_dates[i]] = i;
}
}
void MultiFactorBase::calculate() {
// SPEND_TIME(MultiFactorBase_calculate);
std::lock_guard<std::mutex> lock(m_mutex);
HKU_IF_RETURN(m_calculated, void());
// 获取所有证券所有对齐后的原始因子
vector<vector<Indicator>> all_stk_inds = _alignAllInds();
try {
if (m_inds.size() == 1) {
// 直接使用原始因子
size_t stk_count = m_stks.size();
m_all_factors.resize(stk_count);
for (size_t i = 0; i < stk_count; i++) {
m_all_factors[i] = std::move(all_stk_inds[i][0]);
}
} else {
// 计算每支证券调整后的合成因子
m_all_factors = _calculate(all_stk_inds);
}
// 计算完成后创建截面索引
_buildIndex();
} catch (const std::exception& e) {
HKU_ERROR(e.what());
} catch (...) {
HKU_ERROR_UNKNOWN;
}
// 更新计算状态
m_calculated = true;
}
} // namespace hku

View File

@ -0,0 +1,232 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-12
* Author: fasiondog
*/
#pragma once
#include "hikyuu/KData.h"
#include "hikyuu/indicator/Indicator.h"
#include "hikyuu/utilities/Parameter.h"
namespace hku {
/**
*
* @ingroup MultiFactor
*/
class HKU_API MultiFactorBase : public enable_shared_from_this<MultiFactorBase> {
PARAMETER_SUPPORT
public:
typedef Indicator::value_t value_t;
friend HKU_API std::ostream& operator<<(std::ostream&, const MultiFactorBase&);
public:
MultiFactorBase();
MultiFactorBase(const string& name);
MultiFactorBase(const IndicatorList& inds, const StockList& stks, const KQuery& query,
const Stock& ref_stk, const string& name, int ic_n);
/** 获取名称 */
const string& name() const {
return m_name;
}
/** 设置名称 */
void name(const string& name) {
m_name = name;
}
const DatetimeList& getDatetimeList() const {
return m_ref_dates;
}
const KQuery& getQuery() const {
return m_query;
}
/** 获取指定证券合成因子 */
const Indicator& getFactor(const Stock&);
/**
*
*/
const IndicatorList& getAllFactors();
/** 获取指定日期截面的所有因子值,已经降序排列 */
const vector<std::pair<Stock, value_t>>& getCross(const Datetime&);
/** 获取所有截面数据,已按降序排列 */
const vector<vector<std::pair<Stock, value_t>>>& getAllCross();
/**
* IC
* @note ndays 使 IC/ICIR ic_n
* 使 ic_n
* ndays 0, 使 ic_n IC
* @param ndays ndays IC值
*/
Indicator getIC(int ndays = 0);
/**
* ICIR
* @param ir_n IR n
* @param ic_n IC n
*/
Indicator getICIR(int ir_n, int ic_n = 0);
typedef std::shared_ptr<MultiFactorBase> MultiFactorPtr;
MultiFactorPtr clone();
virtual MultiFactorPtr _clone() = 0;
virtual IndicatorList _calculate(const vector<IndicatorList>&) = 0;
private:
/** 执行计算 */
void calculate();
protected:
vector<IndicatorList> _alignAllInds();
void _buildIndex(); // 计算完成后创建截面索引
IndicatorList _getAllReturns(int ndays) const;
protected:
string m_name;
IndicatorList m_inds; // 输入的原始因子列表
StockList m_stks; // 证券组合
Stock m_ref_stk; // 指定的参考证券
KQuery m_query; // 计算的日期范围条件
// 以下变量为计算后生成
DatetimeList m_ref_dates; // 依据参考证券和query计算的参考日期合成因子和该日期对齐
unordered_map<Stock, size_t> m_stk_map; // 证券->合成后因子位置索引
IndicatorList m_all_factors; // 保存所有证券合成后的新因子
unordered_map<Datetime, size_t> m_date_index;
vector<vector<std::pair<Stock, value_t>>> m_stk_factor_by_date;
Indicator m_ic;
private:
std::mutex m_mutex;
bool m_calculated{false};
//============================================
// 序列化支持
//============================================
#if HKU_SUPPORT_SERIALIZATION
private:
friend class boost::serialization::access;
template <class Archive>
void save(Archive& ar, const unsigned int version) const {
ar& BOOST_SERIALIZATION_NVP(m_name);
ar& BOOST_SERIALIZATION_NVP(m_params);
ar& BOOST_SERIALIZATION_NVP(m_inds);
ar& BOOST_SERIALIZATION_NVP(m_stks);
ar& BOOST_SERIALIZATION_NVP(m_ref_stk);
ar& BOOST_SERIALIZATION_NVP(m_query);
ar& BOOST_SERIALIZATION_NVP(m_ref_dates);
// 以下不需要保存,加载后重新计算
// ar& BOOST_SERIALIZATION_NVP(m_stk_map);
// ar& BOOST_SERIALIZATION_NVP(m_all_factors);
// ar& BOOST_SERIALIZATION_NVP(m_date_index);
// ar& BOOST_SERIALIZATION_NVP(m_ic);
// ar& BOOST_SERIALIZATION_NVP(m_calculated);
// ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date);
}
template <class Archive>
void load(Archive& ar, const unsigned int version) {
ar& BOOST_SERIALIZATION_NVP(m_name);
ar& BOOST_SERIALIZATION_NVP(m_params);
ar& BOOST_SERIALIZATION_NVP(m_inds);
ar& BOOST_SERIALIZATION_NVP(m_stks);
ar& BOOST_SERIALIZATION_NVP(m_ref_stk);
ar& BOOST_SERIALIZATION_NVP(m_query);
ar& BOOST_SERIALIZATION_NVP(m_ref_dates);
// ar& BOOST_SERIALIZATION_NVP(m_stk_map);
// ar& BOOST_SERIALIZATION_NVP(m_all_factors);
// ar& BOOST_SERIALIZATION_NVP(m_date_index);
// ar& BOOST_SERIALIZATION_NVP(m_ic);
// ar& BOOST_SERIALIZATION_NVP(m_calculated);
// ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date);
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
#endif /* HKU_SUPPORT_SERIALIZATION */
};
#if HKU_SUPPORT_SERIALIZATION
BOOST_SERIALIZATION_ASSUME_ABSTRACT(MultiFactorBase)
#endif
#if HKU_SUPPORT_SERIALIZATION
/**
* 使
* @code
* class Drived: public MultiFactorBase {
* MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION
*
* public:
* Drived();
* ...
* };
* @endcode
* @ingroup MultiFactor
*/
#define MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION \
private: \
friend class boost::serialization::access; \
template <class Archive> \
void serialize(Archive& ar, const unsigned int version) { \
ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(MultiFactorBase); \
}
#else
#define MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION
#endif
typedef std::shared_ptr<MultiFactorBase> FactorPtr;
typedef std::shared_ptr<MultiFactorBase> MultiFactorPtr;
typedef std::shared_ptr<MultiFactorBase> MFPtr;
#define MULTIFACTOR_IMP(classname) \
public: \
virtual MultiFactorPtr _clone() override { \
return std::make_shared<classname>(); \
} \
virtual IndicatorList _calculate(const vector<IndicatorList>&) override;
HKU_API std::ostream& operator<<(std::ostream&, const MultiFactorBase&);
HKU_API std::ostream& operator<<(std::ostream&, const MultiFactorPtr&);
HKU_API std::ostream& operator<<(std::ostream& out,
const std::pair<Stock, MultiFactorBase::value_t>& td);
HKU_API std::ostream& operator<<(std::ostream& out,
const vector<std::pair<Stock, MultiFactorBase::value_t>>& td);
HKU_API std::ostream& operator<<(
std::ostream& out, const vector<vector<std::pair<Stock, MultiFactorBase::value_t>>>& td);
} // namespace hku
#if FMT_VERSION >= 90000
template <>
struct fmt::formatter<hku::MultiFactorBase> : ostream_formatter {};
template <>
struct fmt::formatter<hku::MultiFactorPtr> : ostream_formatter {};
template <>
struct fmt::formatter<std::pair<hku::Stock, hku::MultiFactorBase::value_t>> : ostream_formatter {};
template <>
struct fmt::formatter<std::vector<std::pair<hku::Stock, hku::MultiFactorBase::value_t>>>
: ostream_formatter {};
template <>
struct fmt::formatter<
std::vector<std::vector<std::pair<hku::Stock, hku::MultiFactorBase::value_t>>>>
: ostream_formatter {};
#endif

View File

@ -0,0 +1,12 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-13
* Author: fasiondog
*/
#pragma once
#include "crt/MF_EqualWeight.h"
#include "crt/MF_ICWeight.h"
#include "crt/MF_ICIRWeight.h"

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-13
* Author: fasiondog
*/
#pragma once
#include "../MultiFactorBase.h"
namespace hku {
/**
* @brief
* @param inds
* @param stks
* @param query
* @param ref_stk
* @param ic_n IC N
* @return MultiFactorPtr
*/
MultiFactorPtr HKU_API MF_EqualWeight(const IndicatorList& inds, const StockList& stks,
const KQuery& query, const Stock& ref_stk, int ic_n = 5);
} // namespace hku

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-13
* Author: fasiondog
*/
#pragma once
#include "../MultiFactorBase.h"
namespace hku {
/**
* @brief ICIR权重合成因子
* @param inds
* @param stks
* @param query
* @param ref_stk
* @param ic_n IC N
* @param ic_rolling_n IC
* @return MultiFactorPtr
*/
MultiFactorPtr HKU_API MF_ICIRWeight(const IndicatorList& inds, const StockList& stks,
const KQuery& query, const Stock& ref_stk, int ic_n = 5,
int ic_rolling_n = 120);
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-13
* Author: fasiondog
*/
#pragma once
#include "../MultiFactorBase.h"
namespace hku {
/**
* @brief IC权重合成因子
* @param inds
* @param stks
* @param query
* @param ref_stk
* @param ic_n IC N
* @param ic_rolling_n IC
* @return MultiFactorPtr
*/
MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks,
const KQuery& query, const Stock& ref_stk, int ic_n = 5,
int ic_rolling_n = 120);
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-13
* Author: fasiondog
*/
#include "hikyuu/indicator/crt/PRICELIST.h"
#include "hikyuu/indicator/crt/IC.h"
#include "hikyuu/indicator/crt/ICIR.h"
#include "hikyuu/indicator/crt/SPEARMAN.h"
#include "EqualWeightMultiFactor.h"
#if HKU_SUPPORT_SERIALIZATION
BOOST_CLASS_EXPORT(hku::EqualWeightMultiFactor)
#endif
namespace hku {
EqualWeightMultiFactor::EqualWeightMultiFactor() : MultiFactorBase("MF_EqualWeight") {}
EqualWeightMultiFactor::EqualWeightMultiFactor(const vector<Indicator>& inds, const StockList& stks,
const KQuery& query, const Stock& ref_stk, int ic_n)
: MultiFactorBase(inds, stks, query, ref_stk, "MF_EqualWeight", ic_n) {}
vector<Indicator> EqualWeightMultiFactor::_calculate(
const vector<vector<Indicator>>& all_stk_inds) {
size_t days_total = m_ref_dates.size();
size_t stk_count = m_stks.size();
size_t ind_count = m_inds.size();
value_t null_value = Null<value_t>();
vector<price_t> sumByDate(days_total);
vector<size_t> countByDate(days_total);
vector<Indicator> all_factors(stk_count);
for (size_t si = 0; si < stk_count; si++) {
memset(sumByDate.data(), 0, sizeof(price_t) * days_total);
memset(countByDate.data(), 0, sizeof(size_t) * days_total);
const auto& curStkInds = all_stk_inds[si];
for (size_t di = 0; di < days_total; di++) {
for (size_t ii = 0; ii < ind_count; ii++) {
const auto& value = curStkInds[ii][di];
if (!std::isnan(value)) {
sumByDate[di] += value;
countByDate[di] += 1;
}
}
}
// 均值权重
for (size_t di = 0; di < days_total; di++) {
sumByDate[di] = (countByDate[di] == 0) ? null_value : sumByDate[di] / countByDate[di];
}
all_factors[si] = PRICELIST(sumByDate);
all_factors[si].name("IC");
// 更新 discard
for (size_t di = 0; di < days_total; di++) {
if (!std::isnan(all_factors[si][di])) {
all_factors[si].setDiscard(di);
break;
}
if (di == days_total - 1 && std::isnan(all_factors[si][di])) {
all_factors[si].setDiscard(di);
}
}
}
return all_factors;
}
MultiFactorPtr HKU_API MF_EqualWeight(const IndicatorList& inds, const StockList& stks,
const KQuery& query, const Stock& ref_stk, int ic_n) {
return make_shared<EqualWeightMultiFactor>(inds, stks, query, ref_stk, ic_n);
}
} // namespace hku

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-13
* Author: fasiondog
*/
#pragma once
#include "../MultiFactorBase.h"
namespace hku {
class EqualWeightMultiFactor : public MultiFactorBase {
MULTIFACTOR_IMP(EqualWeightMultiFactor)
MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION
public:
EqualWeightMultiFactor();
EqualWeightMultiFactor(const vector<Indicator>& inds, const StockList& stks,
const KQuery& query, const Stock& ref_stk, int ic_n);
};
} // namespace hku

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-13
* Author: fasiondog
*/
#include "hikyuu/indicator/crt/PRICELIST.h"
#include "hikyuu/indicator/crt/IC.h"
#include "hikyuu/indicator/crt/ICIR.h"
#include "hikyuu/indicator/crt/SPEARMAN.h"
#include "ICIRMultiFactor.h"
#if HKU_SUPPORT_SERIALIZATION
BOOST_CLASS_EXPORT(hku::ICIRMultiFactor)
#endif
namespace hku {
ICIRMultiFactor::ICIRMultiFactor() : MultiFactorBase("MF_ICIRWeight") {
setParam<int>("ic_rolling_n", 120);
}
ICIRMultiFactor::ICIRMultiFactor(const vector<Indicator>& inds, const StockList& stks,
const KQuery& query, const Stock& ref_stk, int ic_n,
int ic_rolling_n)
: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICIRWeight", ic_n) {
setParam<int>("ic_rolling_n", ic_rolling_n);
}
IndicatorList ICIRMultiFactor::_calculate(const vector<IndicatorList>& all_stk_inds) {
size_t days_total = m_ref_dates.size();
size_t stk_count = m_stks.size();
size_t ind_count = m_inds.size();
int ic_n = getParam<int>("ic_n");
int ir_n = getParam<int>("ic_rolling_n");
size_t discard = 0;
vector<Indicator> icir(ind_count);
for (size_t ii = 0; ii < ind_count; ii++) {
icir[ii] = ICIR(IC(m_inds[ii], m_stks, m_query, ic_n, m_ref_stk), ir_n);
if (icir[ii].discard() > discard) {
discard = icir[ii].discard();
}
}
// 计算 IC 权重
vector<value_t> sumByDate(days_total, 0.0);
vector<size_t> countByDate(days_total, 0);
for (size_t di = discard; di < days_total; di++) {
for (size_t ii = 0; ii < ind_count; ii++) {
const auto& value = icir[ii][di];
if (!std::isnan(value)) {
sumByDate[di] += value;
countByDate[di] += 1;
}
}
}
for (size_t di = discard; di < days_total; di++) {
sumByDate[di] = (countByDate[di] == 0) ? Null<value_t>() : sumByDate[di] / countByDate[di];
}
vector<Indicator> all_factors(stk_count);
PriceList new_values(days_total);
for (size_t si = 0; si < stk_count; si++) {
memset(new_values.data(), 0, sizeof(price_t) * days_total);
for (size_t di = discard; di < days_total; di++) {
for (size_t ii = 0; ii < ind_count; ii++) {
const auto& value = all_stk_inds[si][ii][di];
new_values[di] += value * sumByDate[di];
}
}
for (size_t di = discard; di < days_total; di++) {
new_values[di] =
(countByDate[di] == 0) ? Null<value_t>() : new_values[di] / countByDate[di];
}
all_factors[si] = PRICELIST(new_values);
all_factors[si].name("IC");
// 更新 discard
for (size_t di = discard; di < days_total; di++) {
if (!std::isnan(all_factors[si][di])) {
all_factors[si].setDiscard(di);
break;
}
if (di == days_total - 1 && std::isnan(all_factors[si][di])) {
all_factors[si].setDiscard(di);
}
}
}
return all_factors;
}
MultiFactorPtr HKU_API MF_ICIRWeight(const IndicatorList& inds, const StockList& stks,
const KQuery& query, const Stock& ref_stk, int ic_n,
int ic_rolling_n) {
return make_shared<ICIRMultiFactor>(inds, stks, query, ref_stk, ic_n, ic_rolling_n);
}
} // namespace hku

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-13
* Author: fasiondog
*/
#pragma once
#include "../MultiFactorBase.h"
namespace hku {
class ICIRMultiFactor : public MultiFactorBase {
MULTIFACTOR_IMP(ICIRMultiFactor)
MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION
public:
ICIRMultiFactor();
ICIRMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query,
const Stock& ref_stk, int ic_n, int ic_rolling_n);
};
} // namespace hku

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-13
* Author: fasiondog
*/
#include "hikyuu/indicator/crt/PRICELIST.h"
#include "hikyuu/indicator/crt/IC.h"
#include "hikyuu/indicator/crt/MA.h"
#include "hikyuu/indicator/crt/SPEARMAN.h"
#include "ICMultiFactor.h"
#if HKU_SUPPORT_SERIALIZATION
BOOST_CLASS_EXPORT(hku::ICMultiFactor)
#endif
namespace hku {
ICMultiFactor::ICMultiFactor() : MultiFactorBase("MF_ICWeight") {
setParam<int>("ic_rolling_n", 120); // 计算滚动ic的滚动周期, 通常取 120 或 250
}
ICMultiFactor::ICMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query,
const Stock& ref_stk, int ic_n, int ic_rolling_n)
: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICWeight", ic_n) {
setParam<int>("ic_rolling_n", ic_rolling_n);
}
IndicatorList ICMultiFactor::_calculate(const vector<IndicatorList>& all_stk_inds) {
size_t days_total = m_ref_dates.size();
size_t stk_count = m_stks.size();
size_t ind_count = m_inds.size();
int ic_n = getParam<int>("ic_n");
int ic_rolling_n = getParam<int>("ic_rolling_n");
// 计算每个原始因子的IC值
size_t discard = 0;
IndicatorList ic(ind_count);
for (size_t ii = 0; ii < ind_count; ii++) {
ic[ii] = MA(IC(m_inds[ii], m_stks, m_query, ic_n, m_ref_stk), ic_rolling_n);
if (ic[ii].discard() > discard) {
discard = ic[ii].discard();
}
}
// 计算每个原始因子的滚动 IC 权重
vector<value_t> sumByDate(days_total, 0.0); // 累积合、权重均值
vector<size_t> countByDate(days_total, 0);
for (size_t di = discard; di < days_total; di++) {
for (size_t ii = 0; ii < ind_count; ii++) {
const auto& value = ic[ii][di];
if (!std::isnan(value)) {
sumByDate[di] += value;
countByDate[di] += 1;
}
}
}
for (size_t di = discard; di < days_total; di++) {
sumByDate[di] = (countByDate[di] == 0) ? Null<value_t>() : sumByDate[di] / countByDate[di];
}
// 对每支证券计算根据滚动 IC 权重计算合成因子
IndicatorList all_factors(stk_count);
PriceList new_values(days_total, 0);
for (size_t si = 0; si < stk_count; si++) {
memset(new_values.data(), 0, sizeof(price_t) * days_total);
for (size_t di = discard; di < days_total; di++) {
for (size_t ii = 0; ii < ind_count; ii++) {
const auto& value = all_stk_inds[si][ii][di];
new_values[di] += value * sumByDate[di];
}
}
for (size_t di = discard; di < days_total; di++) {
new_values[di] =
(countByDate[di] == 0) ? Null<value_t>() : new_values[di] / countByDate[di];
}
all_factors[si] = PRICELIST(new_values);
all_factors[si].name("IC");
// 更新 discard
for (size_t di = discard; di < days_total; di++) {
if (!std::isnan(all_factors[si][di])) {
all_factors[si].setDiscard(di);
break;
}
if (di == days_total - 1 && std::isnan(all_factors[si][di])) {
all_factors[si].setDiscard(di);
}
}
}
return all_factors;
}
MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks,
const KQuery& query, const Stock& ref_stk, int ic_n,
int ic_rolling_n) {
return std::make_shared<ICMultiFactor>(inds, stks, query, ref_stk, ic_n, ic_rolling_n);
}
} // namespace hku

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-13
* Author: fasiondog
*/
#pragma once
#include "../MultiFactorBase.h"
namespace hku {
class ICMultiFactor : public MultiFactorBase {
MULTIFACTOR_IMP(ICMultiFactor)
MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION
public:
ICMultiFactor();
ICMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query,
const Stock& ref_stk, int ic_n, int ic_rolling_n);
};
} // namespace hku

View File

@ -0,0 +1,12 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-12
* Author: fasiondog
*/
#pragma once
#include "../SignalBase.h"
namespace hku {}

View File

@ -75,19 +75,20 @@ TEST_CASE("test_IC") {
CHECK_EQ(result.name(), "IC");
CHECK_UNARY(!result.empty());
CHECK_EQ(result.size(), 2);
CHECK_EQ(result.discard(), 0);
CHECK_EQ(result.discard(), result.size());
CHECK_UNARY(std::isnan(result[0]));
CHECK_EQ(result[1], doctest::Approx(-1.0));
CHECK_UNARY(std::isnan(result[1]));
/** @arg 正常执行 */
result = IC(MA(CLOSE()), stks, query, 1, ref_stk);
CHECK_EQ(result.name(), "IC");
CHECK_UNARY(!result.empty());
CHECK_EQ(result.size(), ref_k.size());
CHECK_EQ(result.discard(), 0);
CHECK_EQ(result[0], doctest::Approx(-1.0));
CHECK_EQ(result[1], doctest::Approx(0.8));
CHECK_EQ(result[99], doctest::Approx(0.5));
CHECK_EQ(result.discard(), 2);
CHECK_UNARY(std::isnan(result[0]));
CHECK_UNARY(std::isnan(result[1]));
CHECK_EQ(result[2], doctest::Approx(0.8));
CHECK_EQ(result[99], doctest::Approx(-1));
}
//-----------------------------------------------------------------------------
@ -147,7 +148,11 @@ TEST_CASE("test_IC_export") {
CHECK_EQ(x1.discard(), x2.discard());
CHECK_EQ(x1.getResultNumber(), x2.getResultNumber());
for (size_t i = 0; i < x1.size(); ++i) {
CHECK_EQ(x1[i], doctest::Approx(x2[i]).epsilon(0.00001));
if (std::isnan(x1[i])) {
CHECK_UNARY(std::isnan(x2[i]));
} else {
CHECK_EQ(x1[i], doctest::Approx(x2[i]).epsilon(0.00001));
}
}
}
#endif /* #if HKU_SUPPORT_SERIALIZATION */

View File

@ -34,12 +34,14 @@ TEST_CASE("test_STDEV") {
Indicator ind = PRICELIST(d);
Indicator dev = STDEV(ind, 10);
CHECK_EQ(dev.discard(), 1);
CHECK_EQ(dev.size(), 15);
vector<price_t> expected{0, 0.707107, 1, 1.29099, 1.58114,
1.47196, 1.97605, 1.83225, 2.44949, 2.92309,
3.14289, 2.83039, 3.26769, 3.653, 4.00139};
for (size_t i = 0; i < dev.size(); i++) {
CHECK_UNARY(std::isnan(dev[0]));
for (size_t i = dev.discard(); i < dev.size(); i++) {
CHECK_EQ(dev[i], doctest::Approx(expected[i]).epsilon(0.0001));
}

View File

@ -0,0 +1,219 @@
/*
* test_ABS.cpp
*
* Created on: 201942
* Author: fasiondog
*/
#include "../../test_config.h"
#include <fstream>
#include <hikyuu/StockManager.h>
#include <hikyuu/indicator/crt/MA.h>
#include <hikyuu/indicator/crt/AMA.h>
#include <hikyuu/indicator/crt/EMA.h>
#include <hikyuu/indicator/crt/IC.h>
#include <hikyuu/indicator/crt/ROCR.h>
#include <hikyuu/indicator/crt/KDATA.h>
#include <hikyuu/trade_sys/factor/crt/MF_EqualWeight.h>
using namespace hku;
/**
* @defgroup test_MF_EqualWeight test_MF_EqualWeight
* @ingroup test_hikyuu_trade_sys_suite
* @{
*/
TEST_CASE("test_nan_sort_in_MF") {
PriceList t(10, Null<price_t>());
t[0] = 1;
t[2] = 3;
t[3] = 4;
t[8] = 5;
std::sort(t.begin(), t.end(), [](price_t a, price_t b) {
if (std::isnan(a) && std::isnan(b)) {
return false;
} else if (!std::isnan(a) && std::isnan(b)) {
return true;
} else if (std::isnan(a) && !std::isnan(b)) {
return false;
}
return a > b;
});
PriceList expect(10, Null<price_t>());
CHECK_EQ(t[0], 5);
CHECK_EQ(t[1], 4);
CHECK_EQ(t[2], 3);
CHECK_EQ(t[3], 1);
for (size_t i = 4, len = t.size(); i < len; i++) {
CHECK_UNARY(std::isnan(t[i]));
}
}
/** @par 检测点 */
TEST_CASE("test_MF_EqualWeight") {
StockManager& sm = StockManager::instance();
StockList stks{sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]};
Stock ref_stk = sm["sh000001"];
KQuery query = KQuery(-100);
KData ref_k = ref_stk.getKData(query);
DatetimeList ref_dates = ref_k.getDatetimeList();
IndicatorList src_inds{MA(CLOSE()), AMA(CLOSE(), EMA(CLOSE()))};
/** @arg 输入的股票列表中含有空股票 */
CHECK_THROWS_AS(MF_EqualWeight(src_inds, {Null<Stock>()}, query, ref_stk), std::exception);
/** @arg 输入的原始因子列表为空 */
CHECK_THROWS_AS(MF_EqualWeight({}, stks, query, ref_stk), std::exception);
/** @arg 输入的参考证券为空 */
CHECK_THROWS_AS(MF_EqualWeight({}, stks, query, Null<Stock>()), std::exception);
/** @arg 数据长度不足 */
CHECK_THROWS_AS(MF_EqualWeight(src_inds, stks, KQuery(-1), ref_stk), std::exception);
/** @arg 证券列表数量不足 */
CHECK_THROWS_AS(MF_EqualWeight(src_inds, {sm["sh600004"]}, KQuery(-2), ref_stk),
std::exception);
/** @arg 临界状态, 原始因子数量为1, 证券数量2, 数据长度为2 */
src_inds = {MA(CLOSE())};
stks = {sm["sh600005"], sm["sh600004"]};
query = KQuery(-2);
ref_k = ref_stk.getKData(query);
ref_dates = ref_k.getDatetimeList();
auto mf = MF_EqualWeight(src_inds, stks, query, ref_stk);
CHECK_EQ(mf->name(), "MF_EqualWeight");
CHECK_THROWS_AS(mf->getFactor(sm["sz000001"]), std::exception);
CHECK_EQ(mf->getDatetimeList(), ref_dates);
const auto& all_factors = mf->getAllFactors();
CHECK_EQ(all_factors.size(), 2);
auto ind1 = mf->getFactor(sm["sh600004"]);
auto ind2 = MA(CLOSE(sm["sh600004"].getKData(query)));
CHECK_UNARY(ind1.equal(ind2));
CHECK_UNARY(all_factors[1].equal(ind2));
ind1 = mf->getFactor(sm["sh600005"]);
ind2 = MA(CLOSE(sm["sh600005"].getKData(query)));
CHECK_UNARY(ind1.equal(ind2));
CHECK_UNARY(all_factors[0].equal(ind2));
auto ic1 = mf->getIC();
auto ic2 = IC(MA(CLOSE()), stks, query, 1, ref_stk);
CHECK_UNARY(ic1.equal(ic2));
CHECK_THROWS_AS(mf->getCross(Datetime(20111204)), std::exception);
auto cross = mf->getCross(Datetime(20111205));
CHECK_EQ(cross.size(), 2);
CHECK_EQ(cross[0].first, sm["sh600004"]);
CHECK_EQ(cross[0].second, doctest::Approx(6.85));
CHECK_EQ(cross[1].first, sm["sh600005"]);
CHECK_EQ(cross[1].second, doctest::Approx(3.13));
cross = mf->getCross(Datetime(20111206));
CHECK_EQ(cross.size(), 2);
CHECK_EQ(cross[0].first, sm["sh600004"]);
CHECK_EQ(cross[0].second, doctest::Approx(6.855));
CHECK_EQ(cross[1].first, sm["sh600005"]);
CHECK_EQ(cross[1].second, doctest::Approx(3.14));
// HKU_INFO("\n{}", mf->getAllCross());
/** @arg 原始因子数量为3, 证券数量4, 数据长度为20, 指定比较收益率 3 日 */
int ndays = 3;
src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)), EMA(ROCR(CLOSE(), ndays))};
stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]};
query = KQuery(-20);
ref_k = ref_stk.getKData(query);
ref_dates = ref_k.getDatetimeList();
mf = MF_EqualWeight(src_inds, stks, query, ref_stk, ndays);
CHECK_EQ(mf->name(), "MF_EqualWeight");
CHECK_THROWS_AS(mf->getFactor(sm["sh600000"]), std::exception);
auto stk = sm["sh600004"];
ind1 = MA(ROCR(CLOSE(stk.getKData(query)), ndays));
ind2 = AMA(ROCR(CLOSE(stk.getKData(query)), ndays));
auto ind3 = EMA(ROCR(CLOSE(stk.getKData(query)), ndays));
auto ind4 = mf->getFactor(stk);
CHECK_EQ(ind4.discard(), 3);
for (size_t i = 0; i < ind4.discard(); i++) {
CHECK_UNARY(std::isnan(ind4[i]));
}
for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) {
CHECK_EQ(ind4[i], doctest::Approx((ind1[i] + ind2[i] + ind3[i]) / 3.0));
}
}
//-----------------------------------------------------------------------------
// benchmark
//-----------------------------------------------------------------------------
#if ENABLE_BENCHMARK_TEST
TEST_CASE("test_MF_EqualWeight_benchmark") {
StockManager& sm = StockManager::instance();
int ndays = 3;
IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)),
EMA(ROCR(CLOSE(), ndays))};
StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]};
KQuery query = KQuery(0);
Stock ref_stk = sm["sh000001"];
auto ref_k = ref_stk.getKData(query);
auto ref_dates = ref_k.getDatetimeList();
int cycle = 10; // 测试循环次数
{
BENCHMARK_TIME_MSG(test_MF_EqualWeight_benchmark, cycle,
fmt::format("data len: {}", ref_k.size()));
SPEND_TIME_CONTROL(false);
for (int i = 0; i < cycle; i++) {
auto mf = MF_EqualWeight(src_inds, stks, query, ref_stk);
auto ic = mf->getIC();
}
}
}
#endif
//-----------------------------------------------------------------------------
// test export
//-----------------------------------------------------------------------------
#if HKU_SUPPORT_SERIALIZATION
/** @par 检测点 */
TEST_CASE("test_MF_EqualWeight_export") {
StockManager& sm = StockManager::instance();
int ndays = 3;
IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)),
EMA(ROCR(CLOSE(), ndays))};
StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]};
KQuery query = KQuery(0);
Stock ref_stk = sm["sh000001"];
auto ref_k = ref_stk.getKData(query);
auto ref_dates = ref_k.getDatetimeList();
string filename(sm.tmpdir());
filename += "/MF_EqualWeight.xml";
auto mf1 = MF_EqualWeight(src_inds, stks, query, ref_stk);
auto ic1 = mf1->getIC();
{
std::ofstream ofs(filename);
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(mf1);
}
MFPtr mf2;
{
std::ifstream ifs(filename);
boost::archive::xml_iarchive ia(ifs);
ia >> BOOST_SERIALIZATION_NVP(mf2);
}
CHECK_EQ(mf2->name(), mf1->name());
auto ic2 = mf2->getIC();
CHECK_EQ(ic1.size(), ic2.size());
CHECK_EQ(ic1.discard(), ic2.discard());
CHECK_UNARY(ic1.equal(ic2));
}
#endif /* #if HKU_SUPPORT_SERIALIZATION */
/** @} */

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-15
* Author: fasiondog
*/
#include "../../test_config.h"
#include <fstream>
#include <cmath>
#include <hikyuu/StockManager.h>
#include <hikyuu/indicator/crt/MA.h>
#include <hikyuu/indicator/crt/AMA.h>
#include <hikyuu/indicator/crt/EMA.h>
#include <hikyuu/indicator/crt/IC.h>
#include <hikyuu/indicator/crt/ICIR.h>
#include <hikyuu/indicator/crt/ROCR.h>
#include <hikyuu/indicator/crt/KDATA.h>
#include <hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h>
using namespace hku;
/**
* @defgroup test_MF_ICIRWeight test_MF_ICIRWeight
* @ingroup test_hikyuu_trade_sys_suite
* @{
*/
TEST_CASE("test_MF_ICIRWeight") {
StockManager& sm = StockManager::instance();
int ndays = 3;
int ic_rolling_n = 3;
Stock ref_stk = sm["sh000001"];
IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)),
EMA(ROCR(CLOSE(), ndays))};
StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]};
KQuery query = KQuery(-20);
KData ref_k = ref_stk.getKData(query);
DatetimeList ref_dates = ref_k.getDatetimeList();
auto mf = MF_ICIRWeight(src_inds, stks, query, ref_stk, ndays, ic_rolling_n);
CHECK_EQ(mf->name(), "MF_ICIRWeight");
CHECK_THROWS_AS(mf->getFactor(sm["sh600000"]), std::exception);
auto stk = sm["sh600004"];
auto ind1 = MA(ROCR(CLOSE(stk.getKData(query)), ndays));
auto ic1 = ICIR(IC(MA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n);
auto ind2 = AMA(ROCR(CLOSE(stk.getKData(query)), ndays));
auto ic2 = ICIR(IC(AMA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n);
auto ind3 = EMA(ROCR(CLOSE(stk.getKData(query)), ndays));
auto ic3 = ICIR(IC(EMA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n);
auto ind4 = mf->getFactor(stk);
for (size_t i = 0; i < ind4.discard(); i++) {
CHECK_UNARY(std::isnan(ind4[i]));
}
CHECK_EQ(ind4.discard(), std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard())));
for (size_t i = 0; i < ind4.discard(); i++) {
CHECK_UNARY(std::isnan(ind4[i]));
}
for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) {
Indicator::value_t w = (ic1[i] + ic2[i] + ic3[i]) / 3.0;
CHECK_EQ(ind4[i], doctest::Approx((ind1[i] * w + ind2[i] * w + ind3[i] * w) / 3.0));
}
}
//-----------------------------------------------------------------------------
// benchmark
//-----------------------------------------------------------------------------
#if ENABLE_BENCHMARK_TEST
TEST_CASE("test_MF_ICIRWeight_benchmark") {
StockManager& sm = StockManager::instance();
int ndays = 3;
IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)),
EMA(ROCR(CLOSE(), ndays))};
StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]};
KQuery query = KQuery(0);
Stock ref_stk = sm["sh000001"];
auto ref_k = ref_stk.getKData(query);
auto ref_dates = ref_k.getDatetimeList();
int cycle = 10; // 测试循环次数
{
BENCHMARK_TIME_MSG(test_MF_ICIRWeight_benchmark, cycle,
fmt::format("data len: {}", ref_k.size()));
SPEND_TIME_CONTROL(false);
for (int i = 0; i < cycle; i++) {
auto mf = MF_ICIRWeight(src_inds, stks, query, ref_stk);
auto ic = mf->getIC();
}
}
}
#endif
/** @} */

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-15
* Author: fasiondog
*/
#include "../../test_config.h"
#include <fstream>
#include <cmath>
#include <hikyuu/StockManager.h>
#include <hikyuu/indicator/crt/MA.h>
#include <hikyuu/indicator/crt/AMA.h>
#include <hikyuu/indicator/crt/EMA.h>
#include <hikyuu/indicator/crt/IC.h>
#include <hikyuu/indicator/crt/ROCR.h>
#include <hikyuu/indicator/crt/KDATA.h>
#include <hikyuu/trade_sys/factor/crt/MF_ICWeight.h>
using namespace hku;
/**
* @defgroup test_MF_ICWeight test_MF_ICWeight
* @ingroup test_hikyuu_trade_sys_suite
* @{
*/
TEST_CASE("test_MF_ICWeight") {
StockManager& sm = StockManager::instance();
int ndays = 3;
int ic_rolling_n = 3;
Stock ref_stk = sm["sh000001"];
IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)),
EMA(ROCR(CLOSE(), ndays))};
StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]};
KQuery query = KQuery(-20);
KData ref_k = ref_stk.getKData(query);
DatetimeList ref_dates = ref_k.getDatetimeList();
auto mf = MF_ICWeight(src_inds, stks, query, ref_stk, ndays, ic_rolling_n);
CHECK_EQ(mf->name(), "MF_ICWeight");
CHECK_THROWS_AS(mf->getFactor(sm["sh600000"]), std::exception);
auto stk = sm["sh600004"];
auto ind1 = MA(ROCR(CLOSE(stk.getKData(query)), ndays));
auto ic1 = MA(IC(MA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n);
auto ind2 = AMA(ROCR(CLOSE(stk.getKData(query)), ndays));
auto ic2 = MA(IC(AMA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n);
auto ind3 = EMA(ROCR(CLOSE(stk.getKData(query)), ndays));
auto ic3 = MA(IC(EMA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n);
auto ind4 = mf->getFactor(stk);
for (size_t i = 0; i < ind4.discard(); i++) {
CHECK_UNARY(std::isnan(ind4[i]));
}
CHECK_EQ(ind4.discard(), std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard())));
for (size_t i = 0; i < ind4.discard(); i++) {
CHECK_UNARY(std::isnan(ind4[i]));
}
for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) {
Indicator::value_t w = (ic1[i] + ic2[i] + ic3[i]) / 3.0;
CHECK_EQ(ind4[i], doctest::Approx((ind1[i] * w + ind2[i] * w + ind3[i] * w) / 3.0));
}
}
//-----------------------------------------------------------------------------
// benchmark
//-----------------------------------------------------------------------------
#if ENABLE_BENCHMARK_TEST
TEST_CASE("test_MF_ICWeight_benchmark") {
StockManager& sm = StockManager::instance();
int ndays = 3;
IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)),
EMA(ROCR(CLOSE(), ndays))};
StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]};
KQuery query = KQuery(0);
Stock ref_stk = sm["sh000001"];
auto ref_k = ref_stk.getKData(query);
auto ref_dates = ref_k.getDatetimeList();
int cycle = 10; // 测试循环次数
{
BENCHMARK_TIME_MSG(test_MF_ICWeight_benchmark, cycle,
fmt::format("data len: {}", ref_k.size()));
SPEND_TIME_CONTROL(false);
for (int i = 0; i < cycle; i++) {
auto mf = MF_ICWeight(src_inds, stks, query, ref_stk);
auto ic = mf->getIC();
}
}
}
#endif
/** @} */

View File

@ -167,6 +167,8 @@ set_context(self, stock, query)
:rtype: KData)")
.def("equal", &Indicator::equal)
.def("is_same", &Indicator::isSame)
.def("get_imp", &Indicator::getImp)
.def("__len__", &Indicator::size)

View File

@ -1711,7 +1711,7 @@ void export_Indicator_build_in(py::module& m) {
:param int n:
:param Stock ref_stk: 使 sh000300 300)");
m.def("ICIR", ICIR, py::arg("ic"), py::arg("n") = 10, R"(ICIR(ic[,n])
m.def("ICIR", ICIR, py::arg("ic"), py::arg("n") = 120, R"(ICIR(ic[,n])
IC IR = IC的多周期均值/IC的标准方差

View File

@ -72,7 +72,7 @@ void export_Environment(py::module& m) {
:param value:
:raises logic_error: Unsupported type! )")
.def("haveParam", &EnvironmentBase::haveParam, "是否存在指定参数")
.def("have_param", &EnvironmentBase::haveParam, "是否存在指定参数")
.def("is_valid", &EnvironmentBase::isValid, R"(is_valid(self, datetime)

View File

@ -0,0 +1,172 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-13
* Author: fasiondog
*/
#include <hikyuu/trade_sys/factor/build_in.h>
#include "../pybind_utils.h"
namespace py = pybind11;
using namespace hku;
class PyMultiFactor : public MultiFactorBase {
PY_CLONE(PyMultiFactor, MultiFactorBase)
public:
using MultiFactorBase::MultiFactorBase;
IndicatorList _calculate(const vector<IndicatorList>& all_stk_inds) {
PYBIND11_OVERLOAD_PURE_NAME(IndicatorList, MultiFactorBase, "_calculate", _calculate,
all_stk_inds);
}
};
void export_MultiFactor(py::module& m) {
py::class_<MultiFactorBase, MultiFactorPtr, PyMultiFactor>(m, "MultiFactor",
R"(市场环境判定策略基类
- _calculate :
- _clone :
- _reset : )")
.def(py::init<>())
.def("__str__", to_py_str<MultiFactorBase>)
.def("__repr__", to_py_str<MultiFactorBase>)
.def_property("name", py::overload_cast<>(&MultiFactorBase::name, py::const_),
py::overload_cast<const string&>(&MultiFactorBase::name),
py::return_value_policy::copy, "名称")
.def("get_query", &MultiFactorBase::getQuery, py::return_value_policy::copy)
.def("get_param", &MultiFactorBase::getParam<boost::any>, R"(get_param(self, name)
:param str name:
:return:
:raises out_of_range: )")
.def("set_param", &MultiFactorBase::setParam<boost::any>, R"(set_param(self, name, value)
:param str name:
:param value:
:raises logic_error: Unsupported type! )")
.def("have_param", &MultiFactorBase::haveParam, "是否存在指定参数")
.def("get_factor", &MultiFactorBase::getFactor, py::return_value_policy::copy)
.def("get_all_factors",
[](MultiFactorBase& self) {
// return vector_to_python_list<Indicator>()
auto factors = self.getAllFactors();
IndicatorList copy_factors;
copy_factors.reserve(factors.size());
for (const auto& factor : factors) {
copy_factors.emplace_back(factor.clone());
}
return vector_to_python_list<Indicator>(copy_factors);
})
.def("get_ic", &MultiFactorBase::getIC, py::arg("ndays") = 0)
.def("get_icir", &MultiFactorBase::getICIR, py::arg("ir_n"), py::arg("ic_n") = 0)
.def("clone", &MultiFactorBase::clone)
.def("get_cross",
[](MultiFactorBase& self, const Datetime& date) {
py::list ret;
auto cross = self.getCross(date);
for (const auto& item : cross) {
ret.append(py::make_tuple(item.first, item.second));
}
return ret;
})
.def("get_all_cross",
[](MultiFactorBase& self) {
py::list ret;
auto all_cross = self.getAllCross();
for (const auto& one_day : all_cross) {
py::list one;
for (const auto& item : one_day) {
one.append(py::make_tuple(item.first, item.second));
}
ret.append(std::move(one));
}
return ret;
})
DEF_PICKLE(MultiFactorPtr);
m.def(
"MF_EqualWeight",
[](const py::sequence& inds, const py::sequence& stks, const KQuery& query,
const Stock& ref_stk, int ic_n) {
IndicatorList c_inds = python_list_to_vector<Indicator>(inds);
StockList c_stks = python_list_to_vector<Stock>(stks);
return MF_EqualWeight(c_inds, c_stks, query, ref_stk, ic_n);
},
py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk"), py::arg("ic_n") = 5,
R"(MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5])
:param sequense(Indicator) inds:
:param sequense(stock) stks:
:param Query query:
:param Stock ref_stk:
:param int ic_n: IC N
:rtype: MultiFactorPtr)");
m.def(
"MF_ICWeight",
[](const py::sequence& inds, const py::sequence& stks, const KQuery& query,
const Stock& ref_stk, int ic_n, int ic_rolling_n) {
// MF_EqualWeight
IndicatorList c_inds = python_list_to_vector<Indicator>(inds);
StockList c_stks = python_list_to_vector<Stock>(stks);
return MF_ICWeight(c_inds, c_stks, query, ref_stk, ic_n);
},
py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk"), py::arg("ic_n") = 5,
py::arg("ic_rolling_n") = 120,
R"(MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120])
IC权重合成因子
:param sequense(Indicator) inds:
:param sequense(stock) stks:
:param Query query:
:param Stock ref_stk:
:param int ic_n: IC N
:param int ic_rolling_n: IC
:rtype: MultiFactorPtr)");
m.def(
"MF_ICIRWeight",
[](const py::sequence& inds, const py::sequence& stks, const KQuery& query,
const Stock& ref_stk, int ic_n, int ic_rolling_n) {
// MF_EqualWeight
IndicatorList c_inds = python_list_to_vector<Indicator>(inds);
StockList c_stks = python_list_to_vector<Stock>(stks);
return MF_ICIRWeight(c_inds, c_stks, query, ref_stk, ic_n);
},
py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk"), py::arg("ic_n") = 5,
py::arg("ic_rolling_n") = 120,
R"(MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120])
ICIR权重合成因子
:param sequense(Indicator) inds:
:param sequense(stock) stks:
:param Query query:
:param Stock ref_stk:
:param int ic_n: IC N
:param int ic_rolling_n: IC
:rtype: MultiFactorPtr)");
}

View File

@ -20,6 +20,7 @@ void export_System(py::module& m);
void export_Selector(py::module& m);
void export_Portfolio(py::module& m);
void export_AllocateFunds(py::module& m);
void export_MultiFactor(py::module& m);
void export_trade_sys_main(py::module& m) {
export_Environment(m);
@ -33,4 +34,5 @@ void export_trade_sys_main(py::module& m) {
export_Selector(m);
export_AllocateFunds(m);
export_Portfolio(m);
export_MultiFactor(m);
}