mirror of
https://gitee.com/fasiondog/hikyuu.git
synced 2024-12-04 21:07:57 +08:00
commit
c60c2f4975
@ -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)
|
||||
|
@ -133,6 +133,10 @@
|
||||
* @details 交易系统框架
|
||||
* @ingroup TradeSystem
|
||||
*
|
||||
* @defgroup MultiFactor MultiFactor 合成多因子
|
||||
* @details 合成多因子
|
||||
* @ingroup TradeSystem
|
||||
*
|
||||
* @defgroup SystemInstance SystemInstance 系统实例
|
||||
* @details 系统实例
|
||||
* @ingroup Hikyuu
|
||||
|
@ -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;
|
||||
|
@ -184,6 +184,17 @@ public:
|
||||
return m_imp ? m_imp->data(result_num) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个ind的值是否相等
|
||||
* @note operator==重载生成新的新的Indicator,此函数用于对两个ind进行值比较
|
||||
*/
|
||||
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";
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -16,7 +16,7 @@
|
||||
namespace hku {
|
||||
|
||||
/**
|
||||
* 变动率指标 (price - prePrice) / prevPrice
|
||||
* 变动率指标 (price - prePrice) / prevPrice N 日收益率 (本金之外的盈利)
|
||||
* @ingroup Indicator
|
||||
*/
|
||||
Indicator HKU_API ROCP(int n = 10);
|
||||
|
@ -16,7 +16,7 @@
|
||||
namespace hku {
|
||||
|
||||
/**
|
||||
* 变动率指标 (price / prevPrice)
|
||||
* 变动率指标 (price / prevPrice), N 日累积收益率 (包含本金)
|
||||
* @ingroup Indicator
|
||||
*/
|
||||
Indicator HKU_API ROCR(int n = 10);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
389
hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp
Normal file
389
hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp
Normal 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
|
232
hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h
Normal file
232
hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h
Normal 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
|
12
hikyuu_cpp/hikyuu/trade_sys/factor/build_in.h
Normal file
12
hikyuu_cpp/hikyuu/trade_sys/factor/build_in.h
Normal 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"
|
25
hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h
Normal file
25
hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h
Normal 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
|
28
hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h
Normal file
28
hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h
Normal 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);
|
||||
|
||||
}
|
28
hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h
Normal file
28
hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h
Normal 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);
|
||||
|
||||
}
|
@ -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
|
@ -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
|
103
hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp
Normal file
103
hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp
Normal 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
|
24
hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h
Normal file
24
hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h
Normal 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
|
104
hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp
Normal file
104
hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp
Normal 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
|
24
hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h
Normal file
24
hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h
Normal 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
|
12
hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleFactorSignal.h
Normal file
12
hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleFactorSignal.h
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2024 hikyuu.org
|
||||
*
|
||||
* Created on: 2024-03-12
|
||||
* Author: fasiondog
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../SignalBase.h"
|
||||
|
||||
namespace hku {}
|
@ -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 */
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* test_ABS.cpp
|
||||
*
|
||||
* Created on: 2019年4月2日
|
||||
* 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 */
|
||||
|
||||
/** @} */
|
@ -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
|
||||
|
||||
/** @} */
|
@ -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
|
||||
|
||||
/** @} */
|
@ -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)
|
||||
|
||||
|
@ -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的标准方差
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
172
hikyuu_pywrap/trade_sys/_MultiFactor.cpp
Normal file
172
hikyuu_pywrap/trade_sys/_MultiFactor.cpp
Normal 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)");
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user