Merge pull request #198 from fasiondog/feature/se

调整 PF/AF/SE,适配多因子
This commit is contained in:
fasiondog 2024-03-23 17:05:58 +08:00 committed by GitHub
commit b933865567
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 1147 additions and 1084 deletions

View File

@ -448,23 +448,26 @@
:param KData kdata: k线数据
:rtype: Indicator
.. py:function:: IC(ind, stks, query, n, ref_stk)
.. py:function:: IC(ind, stks, query, ref_stk[, n=1])
计算指定的因子相对于参考证券的 IC (实际为 RankIC
:param sequence(stock)|Block stks 证券组合
:param sequence | Block stks 证券组合
:param Query query: 查询条件
:param int n: 时间窗口
:param Stock ref_stk: 参照证券,通常使用 sh000300 沪深300
:param int n: 时间窗口(对应的 n 日收益率)
:rtype: Indicator
.. py:function:: ICIR(ic[,n])
.. py:function:: ICIR(ind, stks, query, ref_stk[, n=1, rolling_n=120])
计算 IC 因子 IR = IC的多周期均值/IC的标准方差
:param Indicator: ic 已经计算出的 ic 值
:param int n: 时间窗口
:param sequence | Block stks 证券组合
:param Query query: 查询条件
:param Stock ref_stk: 参照证券,通常使用 sh000300 沪深300
:param int n: 时间窗口(对应的 n 日收益率)
:param int rolling_n: 滚动周期
:rtype: Indicator

View File

@ -149,7 +149,7 @@ def se_add_stock_list(self, stk_list, proto_sys):
SelectorBase.add_stock_list = se_add_stock_list
def crtSE(calculate, get_selected_on_close, get_selected_on_open, is_match_af=None, params={}, name='crtSE', clone=None):
def crtSE(calculate, get_selected, is_match_af=None, params={}, name='crtSE', clone=None):
"""
@ -163,8 +163,7 @@ def crtSE(calculate, get_selected_on_close, get_selected_on_open, is_match_af=No
"""
meta_x = type(name, (SelectorBase, ), {'__init__': part_init})
meta_x._calculate = calculate
meta_x.get_selected_on_close = get_selected_on_close
meta_x.get_selected_on_open = get_selected_on_open
meta_x.get_selected = get_selected
meta_x.is_match_af = lambda self, af: True if is_match_af is None else is_match_af
meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone
return meta_x(name, params)

View File

@ -178,7 +178,7 @@ std::string HKU_API getLocalTime();
#define HKU_CHECK(expr, ...) \
do { \
if (!(expr)) { \
throw hku::exception(fmt::format("CHECK({}) {} [{}] ({}:{})", #expr, \
throw hku::exception(fmt::format("HKU_CHECK({}) {} [{}] ({}:{})", #expr, \
fmt::format(__VA_ARGS__), HKU_FUNCTION, __FILE__, \
__LINE__)); \
} \
@ -191,8 +191,8 @@ std::string HKU_API getLocalTime();
#define HKU_CHECK_THROW(expr, except, ...) \
do { \
if (!(expr)) { \
throw except(fmt::format("CHECK({}) {} [{}] ({}:{})", #expr, fmt::format(__VA_ARGS__), \
HKU_FUNCTION, __FILE__, __LINE__)); \
throw except(fmt::format("HKU_CHECK({}) {} [{}] ({}:{})", #expr, \
fmt::format(__VA_ARGS__), HKU_FUNCTION, __FILE__, __LINE__)); \
} \
} while (0)
@ -202,19 +202,19 @@ std::string HKU_API getLocalTime();
if (!(expr)) { \
std::string errmsg = fmt::format(__VA_ARGS__); \
errmsg = fmt::format("{}\n {}", errmsg, to_string(boost::stacktrace::stacktrace())); \
throw hku::exception(fmt::format("CHECK({}) {} [{}] ({}:{})", #expr, errmsg, \
throw hku::exception(fmt::format("HKU_CHECK({}) {} [{}] ({}:{})", #expr, errmsg, \
HKU_FUNCTION, __FILE__, __LINE__)); \
} \
} while (0)
#define HKU_CHECK_THROW(expr, except, ...) \
do { \
if (!(expr)) { \
std::string errmsg = fmt::format(__VA_ARGS__); \
errmsg = fmt::format("{}\n {}", errmsg, to_string(boost::stacktrace::stacktrace())); \
throw except(fmt::format("CHECK({}) {} [{}] ({}:{})", #expr, errmsg, HKU_FUNCTION, \
__FILE__, __LINE__)); \
} \
#define HKU_CHECK_THROW(expr, except, ...) \
do { \
if (!(expr)) { \
std::string errmsg = fmt::format(__VA_ARGS__); \
errmsg = fmt::format("{}\n {}", errmsg, to_string(boost::stacktrace::stacktrace())); \
throw except(fmt::format("HKU_CHECK({}) {} [{}] ({}:{})", #expr, errmsg, HKU_FUNCTION, \
__FILE__, __LINE__)); \
} \
} while (0)
#endif // #ifndef HKU_ENABLE_STACK_TRACE
@ -229,35 +229,11 @@ std::string HKU_API getLocalTime();
* false hku::exception
* @note HKU_DISABLE_ASSERT
*/
#define HKU_ASSERT(expr) \
do { \
if (!(expr)) { \
std::string err_msg( \
fmt::format("ASSERT({})\n{}", #expr, to_string(boost::stacktrace::stacktrace()))); \
throw hku::exception( \
fmt::format("{} [{}] ({}:{})", err_msg, HKU_FUNCTION, __FILE__, __LINE__)); \
} \
} while (0)
/**
* false hku::exception ,
* @note HKU_DISABLE_ASSERT
*/
#define HKU_ASSERT_M(expr, ...) \
do { \
if (!(expr)) { \
std::string err_msg(fmt::format("ASSERT({}) {}\n{}", #expr, fmt::format(__VA_ARGS__), \
to_string(boost::stacktrace::stacktrace()))); \
throw hku::exception( \
fmt::format("{} [{}] ({}:{})", err_msg, HKU_FUNCTION, __FILE__, __LINE__)); \
} \
} while (0)
#else
#define HKU_ASSERT(expr) \
do { \
if (!(expr)) { \
std::string err_msg(fmt::format("ASSERT({})", #expr)); \
std::string err_msg(fmt::format("HKU_ASSERT({})\n{}", #expr, \
to_string(boost::stacktrace::stacktrace()))); \
throw hku::exception( \
fmt::format("{} [{}] ({}:{})", err_msg, HKU_FUNCTION, __FILE__, __LINE__)); \
} \
@ -267,13 +243,39 @@ std::string HKU_API getLocalTime();
* false hku::exception ,
* @note HKU_DISABLE_ASSERT
*/
#define HKU_ASSERT_M(expr, ...) \
do { \
if (!(expr)) { \
std::string err_msg(fmt::format("ASSERT({}) {}", #expr, fmt::format(__VA_ARGS__))); \
throw hku::exception( \
fmt::format("{} [{}] ({}:{})", err_msg, HKU_FUNCTION, __FILE__, __LINE__)); \
} \
#define HKU_ASSERT_M(expr, ...) \
do { \
if (!(expr)) { \
std::string err_msg(fmt::format("HKU_ASSERT({}) {}\n{}", #expr, \
fmt::format(__VA_ARGS__), \
to_string(boost::stacktrace::stacktrace()))); \
throw hku::exception( \
fmt::format("{} [{}] ({}:{})", err_msg, HKU_FUNCTION, __FILE__, __LINE__)); \
} \
} while (0)
#else
#define HKU_ASSERT(expr) \
do { \
if (!(expr)) { \
std::string err_msg(fmt::format("HKU_ASSERT({})", #expr)); \
throw hku::exception( \
fmt::format("{} [{}] ({}:{})", err_msg, HKU_FUNCTION, __FILE__, __LINE__)); \
} \
} while (0)
/**
* false hku::exception ,
* @note HKU_DISABLE_ASSERT
*/
#define HKU_ASSERT_M(expr, ...) \
do { \
if (!(expr)) { \
std::string err_msg( \
fmt::format("HKU_ASSERT({}) {}", #expr, fmt::format(__VA_ARGS__))); \
throw hku::exception( \
fmt::format("{} [{}] ({}:{})", err_msg, HKU_FUNCTION, __FILE__, __LINE__)); \
} \
} while (0)
#endif // #ifndef HKU_ENABLE_STACK_TRACE
#endif /* #if HKU_DISABLE_ASSERT */

View File

@ -16,25 +16,25 @@ namespace hku {
* @brief IC RankIC
* @param stks
* @param query
* @param n
* @param ref_stk sh000300 300
* @param n ( n )
* @return Indicator
* @ingroup Indicator
*/
Indicator HKU_API IC(const StockList& stks, const KQuery& query, int n = 1,
const Stock& ref_stk = getStock("sh000300"));
Indicator HKU_API IC(const StockList& stks, const KQuery& query,
const Stock& ref_stk = getStock("sh000300"), int n = 1);
Indicator HKU_API IC(const Block& blk, const KQuery& query, int n = 1,
const Stock& ref_stk = getStock("sh000300"));
Indicator HKU_API IC(const Block& blk, const KQuery& query,
const Stock& ref_stk = getStock("sh000300"), int n = 1);
inline Indicator IC(const Indicator& ind, const StockList& stks, const KQuery& query, int n = 1,
const Stock& ref_stk = getStock("sh000300")) {
return IC(stks, query, n, ref_stk)(ind);
inline Indicator IC(const Indicator& ind, const StockList& stks, const KQuery& query,
const Stock& ref_stk = getStock("sh000300"), int n = 1) {
return IC(stks, query, ref_stk, n)(ind);
}
inline Indicator IC(const Indicator& ind, const Block& blk, const KQuery& query, int n = 1,
const Stock& ref_stk = getStock("sh000300")) {
return IC(blk, query, n, ref_stk)(ind);
inline Indicator IC(const Indicator& ind, const Block& blk, const KQuery& query,
const Stock& ref_stk = getStock("sh000300"), int n = 1) {
return IC(blk, query, ref_stk, n)(ind);
}
} // namespace hku

View File

@ -13,18 +13,34 @@
namespace hku {
/**
* @brief IR
* @brief ICIR RankIC
* @details IR:(Information Ratio,IR)=
* IC的多周期均值/IC的标准方差Alpha的能力
* @param ic ic
* @param n
* @param stks
* @param query
* @param ref_stk sh000300 300
* @param n IC对应的N日收益率
* @param rolling_n
* @return Indicator
* @ingroup Indicator
*/
inline Indicator ICIR(const Indicator& ic, int n = 120) {
Indicator x = MA(ic, n) / STDEV(ic, n);
x.name("IR");
inline Indicator ICIR(const Indicator& ind, const StockList& stks, const KQuery& query,
const Stock& ref_stk = getStock("sh000300"), int n = 1, int rolling_n = 120) {
Indicator ic = IC(ind, stks, query, ref_stk, n);
Indicator x = MA(ic, rolling_n) / STDEV(ic, rolling_n);
x.name("ICIR");
x.setParam<int>("n", n);
x.setParam<int>("rolling_n", rolling_n);
return x;
}
inline Indicator ICIR(const Indicator& ind, const Block& blk, const KQuery& query,
const Stock& ref_stk = getStock("sh000300"), int n = 1, int rolling_n = 120) {
Indicator ic = IC(ind, blk, query, ref_stk, n);
Indicator x = MA(ic, rolling_n) / STDEV(ic, rolling_n);
x.name("ICIR");
x.setParam<int>("n", n);
x.setParam<int>("rolling_n", rolling_n);
return x;
}

View File

@ -80,14 +80,8 @@ void IIc::_calculate(const Indicator& inputInd) {
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();
}
}
m_discard = discard;
@ -107,15 +101,22 @@ void IIc::_calculate(const Indicator& inputInd) {
auto ic = hku::SPEARMAN(a, b, stk_count);
dst[i] = ic[ic.size() - 1];
}
for (size_t i = m_discard; i < days_total; i++) {
if (!std::isnan(dst[i])) {
m_discard = i;
break;
}
}
}
Indicator HKU_API IC(const StockList& stks, const KQuery& query, int n, const Stock& ref_stk) {
Indicator HKU_API IC(const StockList& stks, const KQuery& query, const Stock& ref_stk, int n) {
return Indicator(make_shared<IIc>(stks, query, n, ref_stk));
}
Indicator HKU_API IC(const Block& blk, const KQuery& query, int n, const Stock& ref_stk) {
Indicator HKU_API IC(const Block& blk, const KQuery& query, const Stock& ref_stk, int n) {
StockList stks = blk.getAllStocks();
return IC(stks, query, n, ref_stk);
return IC(stks, query, ref_stk, n);
}
} // namespace hku

View File

@ -38,8 +38,10 @@ void IMa::_calculate(const Indicator& indicator) {
if (n <= 0) {
price_t sum = 0.0;
for (size_t i = m_discard; i < total; i++) {
sum += src[i];
dst[i] = sum / (i - m_discard + 1);
if (!std::isnan(src[i])) {
sum += src[i];
dst[i] = sum / (i - m_discard + 1);
}
}
return;
}
@ -49,13 +51,17 @@ void IMa::_calculate(const Indicator& indicator) {
size_t count = 1;
size_t first_end = startPos + n >= total ? total : startPos + n;
for (size_t i = startPos; i < first_end; ++i) {
sum += src[i];
dst[i] = sum / count++;
if (!std::isnan(src[i])) {
sum += src[i];
dst[i] = sum / count++;
}
}
for (size_t i = first_end; i < total; ++i) {
sum = src[i] + sum - src[i - n];
dst[i] = sum / n;
if (!std::isnan(src[i]) && !std::isnan(src[i - n])) {
sum = src[i] + sum - src[i - n];
dst[i] = sum / n;
}
}
}

View File

@ -48,24 +48,46 @@ void IStdev::_calculate(const Indicator& data) {
size_t first_end = start_pos + n >= total ? total : start_pos + n;
price_t k = src[start_pos];
for (size_t i = start_pos; i < first_end; i++) {
num++;
price_t d = src[i] - k;
ex += d;
price_t d_pow = std::pow(d, 2);
pow_buf[i] = d_pow;
ex2 += d_pow;
dst[i] = num == 1 ? 0. : std::sqrt((ex2 - std::pow(ex, 2) / num) / (num - 1));
if (!std::isnan(src[i])) {
num++;
price_t d = src[i] - k;
ex += d;
price_t d_pow = std::pow(d, 2);
pow_buf[i] = d_pow;
ex2 += d_pow;
// dst[i] = num == 1 ? 0. : std::sqrt((ex2 - std::pow(ex, 2) / num) / (num - 1));
if (num > 1) {
dst[i] = std::sqrt((ex2 - std::pow(ex, 2) / num) / (num - 1));
}
}
}
for (size_t i = first_end; i < total; i++) {
ex -= src[i - n] - k;
ex2 -= pow_buf[i - n];
price_t d = src[i] - k;
ex += d;
price_t d_pow = std::pow(d, 2);
pow_buf[i] = d_pow;
ex2 += d_pow;
dst[i] = std::sqrt((ex2 - std::pow(ex, 2) / n) / (n - 1));
if (!std::isnan(src[i])) {
size_t j = i - n;
for (; j < i; j++) {
if (!std::isnan(src[j])) {
break;
}
}
if (j == i) {
continue;
}
// ex -= src[i - n] - k;
// ex2 -= pow_buf[i - n];
ex -= src[j] - k;
ex2 -= pow_buf[j];
price_t d = src[i] - k;
ex += d;
price_t d_pow = std::pow(d, 2);
pow_buf[i] = d_pow;
ex2 += d_pow;
size_t num = i - j;
if (num != 1) {
dst[i] = std::sqrt((ex2 - std::pow(ex, 2) / num) / (num - 1));
}
// dst[i] = std::sqrt((ex2 - std::pow(ex, 2) / n) / (n - 1));
}
}
// 排除第一位的0值

View File

@ -31,8 +31,10 @@ string TradeManager::str() const {
<< " params: " << getParameter() << strip << " name: " << name() << strip
<< " init_date: " << initDatetime() << strip << " init_cash: " << initCash() << strip
<< " firstDatetime: " << firstDatetime() << strip << " lastDatetime: " << lastDatetime()
<< strip << " TradeCostFunc: " << costFunc() << strip << " current cash: " << currentCash()
<< strip << " current market_value: " << funds.market_value << strip
<< strip << " TradeCostFunc: " << costFunc() << strip << " current total funds: "
<< funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value << strip
<< " current cash: " << currentCash() << strip
<< " current market_value: " << funds.market_value << strip
<< " current short_market_value: " << funds.short_market_value << strip
<< " current base_cash: " << funds.base_cash << strip
<< " current base_asset: " << funds.base_asset << strip
@ -1831,7 +1833,8 @@ bool TradeManager::_add_buy_tr(const TradeRecord& tr) {
price_t money = roundEx(tr.realPrice * tr.number * tr.stock.unit(), precision);
HKU_WARN_IF_RETURN(m_cash < roundEx(money + tr.cost.total, precision), false,
"Don't have enough money!");
"Don't have enough money! {} < {}, {}", m_cash,
roundEx(money + tr.cost.total, precision), tr);
m_cash = roundEx(m_cash - money - tr.cost.total, precision);
new_tr.cash = m_cash;

View File

@ -113,33 +113,7 @@ TradeRecord::TradeRecord(const Stock& stock, const Datetime& datetime, BUSINESS
from(from) {}
HKU_API std::ostream& operator<<(std::ostream& os, const TradeRecord& record) {
Stock stock = record.stock;
string market_code(""), name("");
if (!stock.isNull()) {
market_code = stock.market_code();
name = stock.name();
}
string strip(", ");
os << std::fixed;
os.precision(4);
os << "Trade(" << record.datetime << strip << market_code << strip << name << strip
<< getBusinessName(record.business) << strip << record.planPrice << strip
<< record.realPrice;
if (std::isnan(record.goalPrice)) {
os << strip << "NULL";
} else {
os << strip << record.goalPrice;
}
os << strip << record.number << strip << record.cost.commission << strip << record.cost.stamptax
<< strip << record.cost.transferfee << strip << record.cost.others << strip
<< record.cost.total << strip << record.stoploss << strip << record.cash << strip
<< getSystemPartName(record.from) << ")";
os.unsetf(std::ostream::floatfield);
os.precision();
os << record.toString();
return os;
}
@ -150,27 +124,20 @@ string TradeRecord::toString() const {
name = stock.name();
}
string strip(", ");
std::stringstream os;
os << std::fixed;
os.precision(4);
os << "Trade(" << datetime << strip << market_code << strip << name << strip
<< getBusinessName(business) << strip << planPrice << strip << realPrice;
// if (goalPrice == Null<price_t>()) {
if (std::isnan(goalPrice)) {
os << strip << "nan";
} else {
os << strip << goalPrice;
}
os << strip << goalPrice << strip << number << strip << cost.commission << strip
<< cost.stamptax << strip << cost.transferfee << strip << cost.others << strip << cost.total
<< strip << stoploss << strip << cash << strip << getSystemPartName(from) << ")";
os.unsetf(std::ostream::floatfield);
os.precision();
return os.str();
#if HKU_OS_WINDOWS
return fmt::format(
"Trade({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})", datetime, market_code,
StockManager::instance().runningInPython() && StockManager::instance().pythonInJupyter()
? name
: UTF8ToGB(name),
getBusinessName(business), planPrice, realPrice, goalPrice, number, cost.commission,
cost.stamptax, cost.transferfee, cost.others, getSystemPartName(from));
#else
return fmt::format("Trade({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})", datetime,
market_code, name, getBusinessName(business), planPrice, realPrice,
goalPrice, number, cost.commission, cost.stamptax, cost.transferfee,
cost.others, getSystemPartName(from));
#endif
}
bool TradeRecord::isNull() const {

View File

@ -28,35 +28,43 @@ HKU_API std::ostream& operator<<(std::ostream& os, const AFPtr& af) {
AllocateFundsBase::AllocateFundsBase() : m_name("AllocateMoneyBase"), m_reserve_percent(0) {
// 是否调整之前已经持仓策略的持仓。不调整时,仅使用总账户当前剩余资金进行分配,否则将使用总市值进行分配
// 注意:无论是否调整已持仓策略,权重比例都是相对于总资产,不是针对剩余现金余额
// 仅针对剩余现金比例调整没有意义,即使分配由于交易成本原因可能也无法完成实际交易
// adjust_running_sys: True - 主动根据资产分配对已持仓策略进行增减仓
// adjust_running_sys: False - 不会根据当前分配权重对已持仓策略进行强制加减仓
setParam<bool>("adjust_running_sys", false);
setParam<int>("max_sys_num", 1000000); // 允许运行的最大系统实例数
setParam<double>("weight_unit", 0.0001); // 最小权重单位
// 是否过滤子类返回的比例权重列表中的 0 值包含小于0和 nan 值
// 如:子类返回权重比例列表 [6, 2, 0, 0, 0], 则
// 过滤 0 值,则实际调整后的权重为 Xi / sum(Xi)[6/8, 2/8]
// 不过滤m 设为非零元素个数n为总元素个数(Xi / Sum(Xi)) * (m / n)
// [(6/8)*(2/5), (2/8)*(2/5), 0, 0, 0]
// 即保留分为5份后仅在2份中保持相对比例
setParam<bool>("filter_zero_weight", false);
setParam<double>("default_reserve_percent", 0.0); // 默认保留不参与重分配的资产比例
setParam<bool>("trace", false); // 打印跟踪
}
AllocateFundsBase::AllocateFundsBase(const string& name)
: m_name("AllocateMoneyBase"), m_reserve_percent(0) {
setParam<bool>("adjust_running_sys", false);
setParam<int>("max_sys_num", 100000); // 最大系统实例数
setParam<double>("weight_unit", 0.0001); // 最小权重单位
setParam<bool>("filter_zero_weight", false);
setParam<double>("default_reserve_percent", 0.0); // 默认保留不参与重分配的资产比例
setParam<bool>("trace", false); // 打印跟踪
}
AllocateFundsBase::~AllocateFundsBase() {}
void AllocateFundsBase::reset() {
m_reserve_percent = getParam<double>("default_reserve_percent");
_reset();
// 参数检查
HKU_ERROR_IF(getParam<int>("max_sys_num") <= 0, R"(AF param["max_sys_num"]({}) need > 0!)",
getParam<int>("max_sys_num"));
HKU_ERROR_IF(
getParam<double>("default_reserve_percent") >= 1.0,
R"(AF param(default_reserve_percent)({}) >= 1.0, No asset adjustments will be made!)");
HKU_CHECK(getParam<double>("default_reserve_percent") >= 0.0,
R"(Invalid AF param["default_reserve_percent"] ({}))",
getParam<double>("default_reserve_percent"));
double default_reserve_percent = getParam<double>("default_reserve_percent");
HKU_CHECK(default_reserve_percent >= 0.0 && default_reserve_percent < 1.0,
R"(AF param(default_reserve_percent)({}) >= 1.0, No asset adjustments will be made!)",
default_reserve_percent);
m_reserve_percent = default_reserve_percent;
_reset();
}
AFPtr AllocateFundsBase::clone() {
@ -78,11 +86,11 @@ AFPtr AllocateFundsBase::clone() {
p->m_query = m_query;
p->m_reserve_percent = m_reserve_percent;
/* m_tm, m_shadow_tm 由 PF 运行时指定,不需要 clone
/* m_tm, m_cash_tm 由 PF 运行时指定,不需要 clone
if (m_tm)
p->m_tm = m_tm->clone();
if (m_shadow_tm)
p->m_shadow_tm = m_shadow_tm->clone();*/
if (m_cash_tm)
p->m_cash_tm = m_cash_tm->clone();*/
return p;
}
@ -92,403 +100,372 @@ void AllocateFundsBase::setReservePercent(double percent) {
m_reserve_percent = percent;
}
void AllocateFundsBase::adjustFunds(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list,
const SystemList& ignore_list) {
void AllocateFundsBase::_check_weight(const SystemWeightList& sw_list) {
price_t sum_weight = 0.0;
for (const auto& sw : sw_list) {
HKU_CHECK(!std::isnan(sw.weight) && sw.weight >= 0.0 && sw.weight <= 1.0,
"Invalid weight ({}, {})", sw.sys->name(), sw.weight);
sum_weight += sw.weight;
}
HKU_CHECK(sum_weight <= 1.001, "The cumulative weight exceeds 1! sum_weight: {}", sum_weight);
}
SystemWeightList AllocateFundsBase::adjustFunds(const Datetime& date,
const SystemWeightList& se_list,
const std::unordered_set<SYSPtr>& running_list) {
SystemWeightList result;
if (getParam<bool>("adjust_running_sys")) {
_adjust_with_running(date, se_list, running_list, ignore_list);
result = _adjust_with_running(date, se_list, running_list);
} else {
_adjust_without_running(date, se_list, running_list);
}
return result;
}
price_t AllocateFundsBase::_getTotalFunds(const Datetime& date,
const std::list<SYSPtr>& running_list) {
price_t total_value = 0;
// 降序排列 SystemWeightList并将权重调整为总权重为 1
static void adjustWeight(SystemWeightList& sw_list, double can_allocate_weight, bool filter_zero) {
std::sort(sw_list.begin(), sw_list.end(), [](const SystemWeight& a, const SystemWeight& b) {
if (std::isnan(a.weight) && std::isnan(b.weight)) {
return false;
} else if (!std::isnan(a.weight) && std::isnan(b.weight)) {
return true;
} else if (std::isnan(a.weight) && !std::isnan(b.weight)) {
return false;
}
return a.weight > b.weight;
});
// 计算运行中的子系统总资产净值
for (auto& sub_sys : running_list) {
TMPtr sub_tm = sub_sys->getTM();
KQuery sub_query = sub_sys->getTO().getQuery();
FundsRecord funds = sub_tm->getFunds(date, sub_query.kType());
total_value +=
funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value;
SystemWeightList new_list;
new_list.reserve(sw_list.size());
price_t sum = 0.0;
for (size_t i = 0, total = sw_list.size(); i < total; i++) {
const auto& item = sw_list[i];
if (std::isnan(item.weight) || item.weight <= 0.0) {
break;
}
sum += item.weight;
new_list.emplace_back(item);
}
// 加上当前总账户现金余额
m_shadow_tm->updateWithWeight(date);
total_value = roundDown(total_value + m_shadow_tm->cash(date, m_query.kType()),
m_shadow_tm->getParam<int>("precision"));
return total_value;
double per_weight =
filter_zero ? 1.0 / sum : (new_list.size() * can_allocate_weight) / (sum * sw_list.size());
for (size_t i = 0, total = new_list.size(); i < total; i++) {
new_list[i].weight = new_list[i].weight * per_weight;
}
sw_list.swap(new_list);
}
bool AllocateFundsBase::_returnAssets(const SYSPtr& sys, const Datetime& date) {
KData kdata = sys->getTO();
size_t pos = kdata.getPos(date);
HKU_IF_RETURN(pos == Null<size_t>(), false);
// 只对现金余额进行分配,此时的权重是针对余额的
void AllocateFundsBase::_adjust_without_running(const Datetime& date,
const SystemWeightList& se_list,
const std::unordered_set<SYSPtr>& running_set) {
HKU_CHECK(m_reserve_percent >= 0.0 && m_reserve_percent < 1.0,
"Invalid reserve_percent({}) in AF, Calculations that will cause errors!",
m_reserve_percent);
KRecord record = kdata.getKRecord(pos);
Stock stock = kdata.getStock();
KRecord srcRecord = stock.getKRecord(kdata.startPos() + pos);
bool trace = getParam<bool>("trace");
HKU_INFO_IF(trace, "[AF] {} _adjust_without_running", date);
// 如果存在持仓则卖出
TMPtr tm = sys->getTM();
if (tm->have(stock)) {
TradeRecord tr = sys->sell(record, srcRecord, PART_ALLOCATEFUNDS);
if (!tr.isNull()) {
m_tm->addTradeRecord(tr);
// 获取计划分配的资产权重因为不调整已运行系统实现占位将所有运行中的系统以0比例权重加在选中系统之前
SystemWeightList new_se_list;
for (const auto& running_sw : running_set) {
new_se_list.emplace_back(running_sw, 0.0);
}
for (const auto& sw : se_list) {
if (running_set.find(sw.sys) == running_set.end()) {
new_se_list.emplace_back(sw);
}
}
// 回收当前子账号资金至总账户
price_t cash = tm->cash(date, kdata.getQuery().kType());
if (cash > 0.0 && tm->checkout(date, cash)) {
m_shadow_tm->checkin(date, cash);
}
return true;
}
// 调整运行中子系统持仓
void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list,
const SystemList& ignore_list) {
// 计算当前选中系统列表的权重
SystemWeightList sw_list = _allocateWeight(date, se_list);
HKU_IF_RETURN(new_se_list.size() == 0, void());
SystemWeightList sw_list = _allocateWeight(date, new_se_list);
HKU_IF_RETURN(sw_list.size() == 0, void());
// 构建实际分配权重大于零的的系统集合权重小于等于0的将被忽略
std::unordered_set<System*> selected_sets;
for (auto& sw : sw_list) {
if (sw.getWeight() > 0.0) {
selected_sets.insert(sw.getSYS().get());
}
}
std::unordered_set<System*> ignore_sets;
for (auto& sys : ignore_list) {
ignore_sets.insert(sys.get());
}
std::unordered_set<System*> selected_running_sets;
// 如果当前运行的系统不在实际的选中系统集合且不在忽略列表中,则强制清仓卖出,并回收资产
for (auto& sys : running_list) {
// 不在忽略列表中
if (ignore_sets.find(sys.get()) == ignore_sets.end()) {
// 当前持仓的系统仍旧被选中,记入仍被选中的运行系统列表
if (selected_sets.find(sys.get()) != selected_sets.end()) {
selected_running_sets.insert(sys.get());
} else {
_returnAssets(sys, date);
}
}
}
// 按权重升序排序注意无法保证等权重的相对顺序即使用stable_sort也一样后面要倒序遍历
std::sort(
sw_list.begin(), sw_list.end(),
std::bind(std::less<double>(), std::bind(&SystemWeight::m_weight, std::placeholders::_1),
std::bind(&SystemWeight::m_weight, std::placeholders::_2)));
// 过滤掉超出允许范围的系统
// 按权重从大到小遍历,构建不超过最大允许的运行子系统数的新权重列表(此时按从大到小顺序存放)
// 同时,将超出最大允许的运行子系统数范围外的运行中子系统清仓回收资金
int max_num = getParam<int>("max_sys_num");
std::list<SystemWeight> new_sw_list; // 存放新的权重列表
size_t count = 0;
for (auto iter = sw_list.rbegin(); iter != sw_list.rend(); ++iter) {
// 忽略小于等于零的权重
if (iter->getWeight() <= 0.0) {
break;
}
// 小于最大允许运行数,直接保存至新的权重列表
if (count < max_num) {
new_sw_list.emplace_back(std::move(*iter));
count++;
continue;
}
// 处理超出允许的最大系统数范围外的系统,尝试强制清仓,但不在放入权重列表(即后续不参与资金分配)
if (selected_running_sets.find(iter->getSYS().get()) != selected_running_sets.end()) {
_returnAssets(iter->getSYS(), date);
}
}
// 账户资金精度
int precision = m_shadow_tm->getParam<int>("precision");
// 获取当前总账户资产净值,并计算每单位权重代表的资金
price_t total_funds = _getTotalFunds(date, running_list);
// 计算需保留的资产
HKU_ERROR_IF(m_reserve_percent < 0.0,
"Invalid reserve_percent({}) in AF, Calculations that will cause errors!",
m_reserve_percent);
price_t reserve_funds = roundUp(total_funds * m_reserve_percent, precision);
// 计算每单位权重资产
double weight_unit = getParam<double>("weight_unit");
HKU_CHECK(weight_unit > 0, "Invalid weight_unit! {}", weight_unit);
price_t per_weight_funds = total_funds * weight_unit;
// 计算可分配现金
price_t can_allocate_cash =
roundDown(m_shadow_tm->cash(date, m_query.kType()) - reserve_funds, precision);
if (can_allocate_cash < 0.0) {
can_allocate_cash = 0.0;
}
// 缓存需要进一步处理的系统及其待补充资金的列表
std::list<std::pair<SYSPtr, price_t>> wait_list;
// 按权重从大到小遍历
// 1.如果子系统当前资产已经等于期望被分配的资产则不做处理
// 2.如果子系统当前资产小于期望被分配的资产,则尝试补充资金,否则放入等待列表
// 3.如果当前资产大于期望分配的资产,则子账户是否有现金可以取出抵扣,否则卖掉部分股票
for (auto iter = new_sw_list.begin(); iter != new_sw_list.end(); ++iter) {
// 选中系统的分配权重已经小于等于0则退出
if (iter->getWeight() <= 0.0) {
break;
}
auto& sys = iter->getSYS();
// 如果在忽略列表中,则跳过
if (ignore_sets.find(sys.get()) != ignore_sets.end()) {
continue;
}
// 获取系统账户的当前资产市值
TMPtr tm = sys->getTM();
// 更新子账号权息数据
tm->updateWithWeight(date);
FundsRecord funds = tm->getFunds(date, m_query.kType());
price_t funds_value =
funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value;
price_t will_funds_value = (iter->getWeight() / weight_unit) * per_weight_funds;
if (funds_value == will_funds_value) {
// 如果当前资产已经等于期望分配的资产,则忽略
continue;
} else if (funds_value < will_funds_value) {
// 如果当前资产小于期望分配的资产,则补充现金
price_t will_cash = roundUp(will_funds_value - funds_value, precision);
// 如果当前可用于分配的资金大于期望的资金,则尝试从总账户中将现金补充进子账户中
if (will_cash > 0.0 && will_cash <= can_allocate_cash) {
if (m_shadow_tm->checkout(date, will_cash)) {
tm->checkin(date, will_cash);
can_allocate_cash = roundDown(can_allocate_cash - will_cash, precision);
}
} else {
// 如果当前可用于分配的资金已经不足,则先全部转入子账户,并调整该系统实例权重。
// 同时,将该系统实例放入带重分配列表中,等有需要腾出仓位的系统卖出后,再重新分配补充现金
if (can_allocate_cash > 0.0 && m_shadow_tm->checkout(date, can_allocate_cash)) {
tm->checkin(date, can_allocate_cash);
can_allocate_cash = 0.0;
}
// 缓存至等待列表,以便后续处理
price_t reserve_cash = roundUp(will_cash - can_allocate_cash, precision);
if (reserve_cash > 0.0) {
wait_list.push_back(std::make_pair(sys, reserve_cash));
}
}
} else {
// 如果当前资产大于期望分配的资产,则子账户是否有现金可以取出抵扣,否则卖掉部分股票
price_t will_return_cash = roundDown(funds_value - will_funds_value, precision);
if (will_return_cash <= 0.0) {
continue;
}
price_t sub_cash = tm->currentCash();
if (sub_cash >= will_return_cash) {
if (tm->checkout(date, will_return_cash)) {
m_shadow_tm->checkin(date, will_return_cash);
can_allocate_cash = roundDown(can_allocate_cash + will_return_cash, precision);
}
} else {
// 计算需要卖出股票换取的资金
price_t need_cash = will_return_cash - sub_cash;
// 计算并卖出部分股票以获取剩下需要返还的资金
Stock stock = sys->getStock();
PositionRecord position = tm->getPosition(date, stock);
if (position.number > 0) {
KData kdata = sys->getTO();
size_t pos = kdata.getPos(date);
if (pos == Null<size_t>()) {
continue;
}
KRecord k = kdata.getKRecord(pos);
KRecord srcK = stock.getKRecord(kdata.startPos() + pos);
TradeRecord tr;
double need_sell_num = sys->getParam<bool>("sell_delay")
? need_cash / k.closePrice
: need_cash / k.openPrice;
if (position.number <= need_sell_num) {
// 如果当前持仓数小于等于需要卖出的数量,则全部卖出
tr = sys->sellForce(k, srcK, position.number, PART_ALLOCATEFUNDS);
} else {
if (position.number - need_sell_num >= stock.minTradeNumber()) {
// 如果按需要卖出数量卖出后,可能剩余的数量大于等于最小交易数则按需要卖出的数量卖出
tr = sys->sellForce(k, srcK, need_sell_num, PART_ALLOCATEFUNDS);
} else {
// 如果按需要卖出的数量卖出后,剩余的持仓数小于最小交易数量则全部卖出
tr = sys->sellForce(k, srcK, position.number, PART_ALLOCATEFUNDS);
}
}
if (!tr.isNull()) {
m_tm->addTradeRecord(tr);
}
// 卖出后,尝试将资金取出转移至总账户
sub_cash = tm->currentCash();
if (sub_cash > 0 && tm->checkout(date, sub_cash)) {
m_shadow_tm->checkin(date, sub_cash);
can_allocate_cash = roundDown(can_allocate_cash + sub_cash, precision);
}
}
}
}
}
// 部分调整仓位的股票被卖出后,再次将资金分配至等待资金的子系统
can_allocate_cash = m_shadow_tm->currentCash();
for (auto iter = wait_list.begin(); iter != wait_list.end(); ++iter) {
// 如果可分配的现金不足或选中系统的分配权重已经小于等于0则退出
if (can_allocate_cash <= 0.0) {
break;
}
// 获取系统账户的当前资产市值
SYSPtr sys = iter->first;
TMPtr tm = sys->getTM();
price_t need_cash = iter->second;
if (need_cash <= can_allocate_cash) {
if (m_shadow_tm->checkout(date, need_cash)) {
tm->checkin(date, need_cash);
can_allocate_cash = roundDown(can_allocate_cash - need_cash, precision);
}
} else {
if (m_shadow_tm->checkout(date, can_allocate_cash)) {
tm->checkin(date, can_allocate_cash);
can_allocate_cash = 0.0;
}
}
}
}
void AllocateFundsBase::_adjust_without_running(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list) {
HKU_IF_RETURN(se_list.size() == 0, void());
// 如果运行中的系统数已大于等于允许的最大系统数,直接返回
int max_num = getParam<int>("max_sys_num");
HKU_IF_RETURN(running_list.size() >= max_num, void());
// 计算选中系统中当前正在运行中的子系统
std::unordered_set<System*> hold_sets;
for (auto& sys : running_list) {
hold_sets.insert(sys.get());
}
// 计算不包含运行中系统的子系统列表
SystemList pure_se_list;
for (auto& sys : se_list) {
if (hold_sets.find(sys.get()) == hold_sets.end()) {
pure_se_list.push_back(sys);
}
}
// 获取计划分配的资产权重
SystemWeightList sw_list = _allocateWeight(date, pure_se_list);
HKU_IF_RETURN(sw_list.size() == 0, void());
// 按权重升序排序注意无法保证等权重的相对顺序即使用stable_sort也一样后面要倒序遍历
std::sort(
sw_list.begin(), sw_list.end(),
std::bind(std::less<double>(), std::bind(&SystemWeight::m_weight, std::placeholders::_1),
std::bind(&SystemWeight::m_weight, std::placeholders::_2)));
// 检测是否有信号发生,过滤掉没有发生信号的系统 以及 权重为 0 的系统
SystemWeightList new_sw_list;
auto sw_iter = sw_list.rbegin();
for (; sw_iter != sw_list.rend(); ++sw_iter) {
if (sw_iter->getWeight() <= 0.0)
break;
if (sw_iter->getSYS()->getSG()->shouldBuy(date)) {
new_sw_list.emplace_back(*sw_iter);
}
}
// 总账号资金精度
int precision = m_shadow_tm->getParam<int>("precision");
// 获取当前总资产市值
price_t total_funds = _getTotalFunds(date, running_list);
// 计算需保留的资产
HKU_ERROR_IF(m_reserve_percent < 0.0,
"Invalid reserve_percent({}) in AF, Calculations that will cause errors!",
m_reserve_percent);
// 获取当前总资产市值,计算剩余可分配权重与现金
int precision = m_tm->getParam<int>("precision");
FundsRecord funds = m_tm->getFunds(date, m_query.kType()); // 总资产从总账户获取
price_t total_funds =
funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value;
price_t reserve_funds = total_funds * m_reserve_percent;
// 如果当前现金小于等于需保留的资产,则直接返回
HKU_IF_RETURN(m_shadow_tm->cash(date, m_query.kType()) <= reserve_funds, void());
// 计算可用于分配的现金
price_t can_allocate_cash =
roundDown(m_shadow_tm->cash(date, m_query.kType()) - reserve_funds, precision);
if (can_allocate_cash <= 0.0) {
return;
price_t can_allocate_cash = m_cash_tm->currentCash(); // 可分配资金从资金账户中获取
if (can_allocate_cash + reserve_funds > total_funds) {
can_allocate_cash = roundDown(total_funds - reserve_funds, precision);
}
double can_allocate_weight = 1.0 - m_reserve_percent;
HKU_INFO_IF(trace,
"can_allocate_weight: {:<.4f}, can_allocate_cash: {:<.2f}, current cash: {:<.2f}, "
"total funds: {:<.2f}, "
"reserved funds: {:<.2f}",
can_allocate_weight, can_allocate_cash, funds.cash, total_funds, reserve_funds);
HKU_IF_RETURN(can_allocate_cash <= 1.0, void());
// 调整权重(累积权重和归一)并按降序排列, 并过滤掉 0 值和 Nan 值
adjustWeight(sw_list, can_allocate_weight, getParam<bool>("filter_zero_weight"));
// 遍历选中子系统列表,并将剩余现金按权重比例转入子账户
double sum_weight = 0.0; // 由于不调整已运行系统,已运行系统实际占用比例可能和要求的比例不一致
for (auto iter = sw_list.begin(), end_iter = sw_list.end(); iter != end_iter; ++iter) {
if (can_allocate_cash <= 1.0 || sum_weight >= can_allocate_weight) {
break;
}
// 如果是运行中系统,不使用计算的权重,更新累积权重和
if (running_set.find(iter->sys) != running_set.cend()) {
FundsRecord sub_funds = m_tm->getFunds(date, m_query.kType());
price_t sub_total_funds = sub_funds.cash + sub_funds.market_value +
sub_funds.borrow_asset - sub_funds.short_market_value;
sum_weight += sub_total_funds / total_funds;
continue;
}
// 计算实际可用的权重
price_t current_weight =
iter->weight + sum_weight > 1.0 ? iter->weight + sum_weight - 1.0 : iter->weight;
// 再次遍历选中子系统列表,并将剩余现金按权重比例转入子账户
double weight_unit = getParam<double>("weight_unit");
price_t per_cash = total_funds * weight_unit; // 每单位权重资金
size_t can_run_count = 0;
for (auto sw_iter = new_sw_list.begin(), end_iter = new_sw_list.end(); sw_iter != end_iter;
++sw_iter) {
// 该系统期望分配的资金
price_t will_cash = roundUp(per_cash * (sw_iter->getWeight() / weight_unit), precision);
price_t will_cash = roundUp(total_funds * current_weight, precision);
if (will_cash <= 0.0) {
continue;
}
// 计算实际可分配的资金
// 计算子账户实际可获取的的资金
price_t need_cash = will_cash <= can_allocate_cash ? will_cash : can_allocate_cash;
// 检测是否可以发生交易,不能的话,忽略
auto krecord = sw_iter->getSYS()->getTO().getKRecord(date);
if (krecord.closePrice < need_cash) {
// 如果需要的资金连一手都买不了,直接忽略跳过
KRecord krecord =
iter->sys->getStock().getKRecord(date, iter->sys->getTO().getQuery().kType());
if (krecord.isValid() &&
need_cash < (krecord.closePrice * iter->sys->getStock().minTradeNumber())) {
continue;
}
// 尝试从总账户中取出资金存入子账户
TMPtr sub_tm = sw_iter->getSYS()->getTM();
if (m_shadow_tm->checkout(date, need_cash)) {
TMPtr sub_tm = iter->sys->getTM();
if (m_cash_tm->checkout(date, need_cash)) {
sub_tm->checkin(date, need_cash);
can_allocate_cash = roundDown(can_allocate_cash - need_cash, precision);
if (can_allocate_cash <= 0.0) {
continue;
}
HKU_INFO_IF(trace, "[AF] ({}, {}, weight: {:<.4f}) fetched cash: {}", iter->sys->name(),
iter->sys->getStock().market_code(), current_weight, need_cash);
// 如果超出允许运行的最大系统数,跳出循环
can_run_count++;
if (can_run_count > max_num) {
break;
}
// 计算剩余的可用于分配的资金
can_allocate_cash = roundDown(can_allocate_cash - need_cash, precision);
sum_weight += current_weight;
} else {
HKU_DEBUG_IF(trace, "[AF] {} failed to fetch cash from total account ({})!",
iter->sys->name(), m_cash_tm->currentCash());
}
}
}
SystemWeightList AllocateFundsBase::_adjust_with_running(
const Datetime& date, const SystemWeightList& se_list,
const std::unordered_set<SYSPtr>& running_set) {
HKU_CHECK(m_reserve_percent >= 0.0 && m_reserve_percent < 1.0,
"Invalid reserve_percent({}) in AF, Calculations that will cause errors!",
m_reserve_percent);
SystemWeightList delay_list;
bool trace = getParam<bool>("trace");
HKU_INFO_IF(trace, "[AF] {} _adjust_with_running", date);
HKU_IF_RETURN(se_list.size() == 0, delay_list);
// 获取计划分配的资产权重
SystemWeightList sw_list = _allocateWeight(date, se_list);
HKU_IF_RETURN(sw_list.size() == 0, delay_list);
// 按权重降序排列
double can_allocate_weight = 1.0 - m_reserve_percent;
adjustWeight(sw_list, can_allocate_weight, getParam<bool>("filter_zero_weight"));
//-----------------------------------------------------------------
// 先将已不在 sw_list 中的运行系统进行清仓,回收可分配资金
//-----------------------------------------------------------------
std::unordered_set<SYSPtr> running_in_sw_list;
for (const auto& sw : sw_list) {
if (running_set.find(sw.sys) != running_set.cend()) {
running_in_sw_list.insert(sw.sys);
}
}
for (const auto& sys : running_set) {
if (running_in_sw_list.find(sys) == running_in_sw_list.cend()) {
if (sys->getParam<bool>("buy_delay")) {
delay_list.emplace_back(sys, MAX_DOUBLE);
} else {
// 非延迟卖出的系统,立即强制卖出并回收资金
auto tr = sys->sellForceOnClose(date, MAX_DOUBLE, PART_ALLOCATEFUNDS);
HKU_DEBUG_IF(trace && tr.isNull(), "[AF] failed to sell: {}", sys->name());
if (!tr.isNull()) {
auto sub_tm = sys->getTM();
auto sub_cash = sub_tm->currentCash();
if (sub_tm->checkout(date, sub_cash)) {
m_cash_tm->checkin(date, sub_cash);
m_tm->addTradeRecord(tr); // 向总账户加入交易记录
HKU_INFO_IF(trace, "[AF] Adjust sell: {}, recycle cash: {:<.2f}",
sys->name(), sub_cash);
}
}
}
}
}
//-----------------------------------------------------------------
// 对于仍在选中系统中的运行系统,根据其权重进行减仓处理,回收可分配资金
//-----------------------------------------------------------------
// 获取当前总资产市值,计算需保留的资产
int precision = m_cash_tm->getParam<int>("precision");
FundsRecord funds = m_tm->getFunds(date, m_query.kType());
price_t total_funds =
funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value;
price_t reserve_funds = roundEx(total_funds * m_reserve_percent, precision);
std::unordered_set<SYSPtr> reduced_running_set; // 缓存已执行过减仓的运行中系统
for (auto iter = sw_list.begin(), end_iter = sw_list.end(); iter != end_iter; ++iter) {
// 如果当前系统是运行中的系统
if (running_set.find(iter->sys) != running_set.cend()) {
TMPtr sub_tm = iter->sys->getTM();
const KQuery& query = iter->sys->getTO().getQuery();
FundsRecord sub_funds = sub_tm->getFunds(date, query.kType());
price_t sub_total_funds = sub_funds.cash + sub_funds.market_value +
sub_funds.borrow_asset - sub_funds.short_market_value;
price_t sub_will_funds = total_funds * iter->weight;
if (sub_total_funds > sub_will_funds) {
reduced_running_set.insert(iter->sys); // 缓存执行了减仓的系统
price_t need_back_funds = sub_total_funds - sub_will_funds;
Stock stock = iter->sys->getStock();
KRecord krecord = stock.getKRecord(date, query.kType());
size_t need_back_shou =
size_t(need_back_funds / krecord.closePrice / stock.minTradeNumber());
if (need_back_shou > 0) {
size_t need_back_num = need_back_shou * stock.minTradeNumber();
size_t hold_num = sub_tm->getHoldNumber(date, stock);
if (hold_num - need_back_num < stock.minTradeNumber()) {
need_back_num = hold_num;
}
if (iter->sys->getParam<bool>("buy_delay")) {
delay_list.emplace_back(iter->sys, need_back_num);
} else {
auto tr =
iter->sys->sellForceOnClose(date, need_back_num, PART_ALLOCATEFUNDS);
if (!tr.isNull()) {
auto sub_tm = iter->sys->getTM();
auto sub_cash = sub_tm->currentCash();
if (sub_tm->checkout(date, sub_cash)) {
m_cash_tm->checkin(date, sub_cash);
m_tm->addTradeRecord(tr); // 向总账户加入交易记录
}
}
}
}
}
}
}
//-----------------------------------------------------------------
// 遍历当前选中系统,按指定权重分配资金
//-----------------------------------------------------------------
// 计算可用于分配的现金, 小于等于需保留的资产,则直接返回
price_t current_cash = m_cash_tm->currentCash();
price_t can_allocate_cash = roundDown(current_cash - reserve_funds, precision);
HKU_INFO_IF(trace,
"can_allocate_weight: {:<.4f}, can_allocate_cash: {:<.2f}, current cash: {:<.2f}, "
"total funds: {:<.2f}, "
"reserved funds: {:<.2f}",
can_allocate_weight, can_allocate_cash, funds.cash, total_funds, reserve_funds);
HKU_IF_RETURN(can_allocate_cash < 1.0, delay_list);
// 遍历选中子系统列表,并调整资产
price_t sum_weight = 0.0;
for (auto iter = sw_list.begin(), end_iter = sw_list.end(); iter != end_iter; ++iter) {
if (sum_weight >= can_allocate_weight || can_allocate_cash < 1.0) {
break;
}
// 计算实际可用的权重
price_t current_weight = iter->weight + sum_weight > can_allocate_weight
? iter->weight + sum_weight - can_allocate_weight
: iter->weight;
// 系统期望分配的资产额
price_t will_funds = roundUp(total_funds * current_weight, precision);
// 如果该系统是当前运行中系统
if (running_set.find(iter->sys) != running_set.cend()) {
auto sub_tm = iter->sys->getTM();
const KQuery& query = iter->sys->getTO().getQuery();
FundsRecord sub_funds = sub_tm->getFunds(date, query.kType());
price_t sub_total_funds = sub_funds.cash + sub_funds.market_value +
sub_funds.borrow_asset - sub_funds.short_market_value;
// 如果是已经执行过减仓的系统
if (reduced_running_set.find(iter->sys) != reduced_running_set.cend()) {
// 剩余可分配资金不变,已占用权重按实际权重累积
sum_weight += sub_total_funds / total_funds;
} else {
// 未执行过减仓的系统,需要予以相应资金分配
if (sub_total_funds >= will_funds) {
sum_weight += sub_total_funds / total_funds;
} else {
price_t need_cash = will_funds - sub_total_funds;
if (need_cash > can_allocate_cash) {
need_cash = can_allocate_cash;
}
// 如果期望的资金连一手都买不起,则跳过
const KQuery& query = iter->sys->getTO().getQuery();
auto krecord = iter->sys->getStock().getKRecord(date, query.kType());
if (krecord.isValid() &&
need_cash < krecord.closePrice * iter->sys->getStock().minTradeNumber()) {
continue;
}
if (m_cash_tm->checkout(date, need_cash)) {
sub_tm->checkin(date, need_cash);
can_allocate_cash = roundDown(can_allocate_cash - need_cash, precision);
// 更新已分配的累积权重
sum_weight += (sub_total_funds + need_cash) / total_funds;
}
}
}
} else {
// 非运行中的系统
// 计算子账户实际可获取的的资金
price_t need_cash = will_funds <= can_allocate_cash ? will_funds : can_allocate_cash;
// 如果期望的资金连一手都买不起,则跳过
const KQuery& query = iter->sys->getTO().getQuery();
auto krecord = iter->sys->getStock().getKRecord(date, query.kType());
if (krecord.isValid() &&
need_cash < krecord.closePrice * iter->sys->getStock().minTradeNumber()) {
continue;
}
// 尝试从资金账户中取出资金存入子账户
TMPtr sub_tm = iter->sys->getTM();
if (m_cash_tm->checkout(date, need_cash)) {
sub_tm->checkin(date, need_cash);
HKU_INFO_IF(trace, "[AF] {} fetched cash: {}", iter->sys->name(), need_cash);
// 更新剩余可分配资金
can_allocate_cash = roundDown(can_allocate_cash - need_cash, precision);
// 更新已分配的累积权重
sum_weight += iter->weight;
} else {
HKU_DEBUG_IF(trace, "[AF] {} failed to fetch cash from total account!",
iter->sys->name());
}
}
}
return delay_list;
}
} /* namespace hku */

View File

@ -10,7 +10,7 @@
#define TRADE_SYS_ALLOCATEFUNDS_ALLOCATEFUNDSBASE_H_
#include "../../utilities/Parameter.h"
#include "../allocatefunds/SystemWeight.h"
#include "../selector/SystemWeight.h"
namespace hku {
@ -42,15 +42,15 @@ public:
void name(const string& name);
/**
*
* PF
* @param date
* @param se_list
* @param running_list
* @param ignore_list
* @return
* @return
*/
void adjustFunds(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list, const SystemList& ignore_list);
SystemWeightList adjustFunds(const Datetime& date, const SystemWeightList& se_list,
const std::unordered_set<SYSPtr>& running_list);
/** 获取交易账户 */
const TMPtr& getTM() const;
@ -59,9 +59,9 @@ public:
void setTM(const TMPtr&);
/** 设置 Portfolio 的影子账户, 仅由 Portfolio 调用 */
void setShadowTM(const TMPtr&);
void setCashTM(const TMPtr&);
const TMPtr& getShadowTM(const TMPtr&) const;
const TMPtr& getCashTM(const TMPtr&) const;
/** 获取关联查询条件 */
const KQuery& getQuery() const;
@ -95,34 +95,31 @@ public:
/**
*
* @details _allocateWeight
*
* @details _allocateWeight
* @param date
* @param se_list
* @return
* @return
*/
virtual SystemWeightList _allocateWeight(const Datetime& date, const SystemList& se_list) = 0;
virtual SystemWeightList _allocateWeight(const Datetime& date,
const SystemWeightList& se_list) = 0;
private:
/* 同时调整已运行中的子系统(已分配资金或已持仓) */
void _adjust_with_running(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list, const SystemList& ignore_list);
SystemWeightList _adjust_with_running(const Datetime& date, const SystemWeightList& se_list,
const std::unordered_set<SYSPtr>& running_list);
/* 不调整已在运行中的子系统 */
void _adjust_without_running(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list);
void _adjust_without_running(const Datetime& date, const SystemWeightList& se_list,
const std::unordered_set<SYSPtr>& running_list);
/* 计算当前的资产总值 */
price_t _getTotalFunds(const Datetime& date, const std::list<SYSPtr>& running_list);
/* 回收系统资产 */
bool _returnAssets(const SYSPtr& sys, const Datetime& date);
/* 检查分配的权重是否在 0 和 1 之间,如果存在错误,抛出异常,仅在 trace 时生效*/
void _check_weight(const SystemWeightList&);
private:
string m_name; // 组件名称
KQuery m_query; // 查询条件
TMPtr m_tm; // 运行期由PF设定PF的实际账户
TMPtr m_shadow_tm; // 运行期由PF设定tm 的影子账户,由于协调分配资金
string m_name; // 组件名称
KQuery m_query; // 查询条件
TMPtr m_tm; // 运行期由PF设定PF的实际账户
TMPtr m_cash_tm; // 运行期由PF设定tm 的影子账户,由于协调分配资金
double m_reserve_percent; // 保留资产比例,不参与资产分配
//============================================
@ -137,7 +134,6 @@ private:
ar& BOOST_SERIALIZATION_NVP(m_params);
ar& BOOST_SERIALIZATION_NVP(m_query);
ar& BOOST_SERIALIZATION_NVP(m_reserve_percent);
ar& BOOST_SERIALIZATION_NVP(m_tm);
}
template <class Archive>
@ -146,7 +142,6 @@ private:
ar& BOOST_SERIALIZATION_NVP(m_params);
ar& BOOST_SERIALIZATION_NVP(m_query);
ar& BOOST_SERIALIZATION_NVP(m_reserve_percent);
ar& BOOST_SERIALIZATION_NVP(m_tm);
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
@ -187,7 +182,7 @@ public: \
virtual AFPtr _clone() override { \
return AFPtr(new classname()); \
} \
virtual SystemWeightList _allocateWeight(const Datetime&, const SystemList&) override;
virtual SystemWeightList _allocateWeight(const Datetime&, const SystemWeightList&) override;
typedef shared_ptr<AllocateFundsBase> AllocateFundsPtr;
typedef shared_ptr<AllocateFundsBase> AFPtr;
@ -211,12 +206,12 @@ inline void AllocateFundsBase::setTM(const TMPtr& tm) {
m_tm = tm;
}
inline void AllocateFundsBase::setShadowTM(const TMPtr& tm) {
m_shadow_tm = tm;
inline void AllocateFundsBase::setCashTM(const TMPtr& tm) {
m_cash_tm = tm;
}
inline const TMPtr& AllocateFundsBase::getShadowTM(const TMPtr&) const {
return m_shadow_tm;
inline const TMPtr& AllocateFundsBase::getCashTM(const TMPtr&) const {
return m_cash_tm;
}
inline const KQuery& AllocateFundsBase::getQuery() const {

View File

@ -1,111 +0,0 @@
/*
* SystemWeight.h
*
* Created on: 2018129
* Author: fasiondog
*/
#pragma once
#ifndef TRADE_SYS_ALLOCATEFUNDS_SYSTEMWEIGHT_H_
#define TRADE_SYS_ALLOCATEFUNDS_SYSTEMWEIGHT_H_
#include "../system/System.h"
namespace hku {
/**
* [0.0, 1.0]
* @details
* @ingroup AllocateFunds
*/
class HKU_API SystemWeight {
public:
/** 默认构造函数缺省权重为1.0 */
SystemWeight() : m_weight(1.0) {}
/**
* @brief Construct a new System Weight object
*
* @param sys
* @param weight [0.0, 1.0]
*/
SystemWeight(const SystemPtr& sys, double weight) : m_sys(sys), m_weight(1.0) {
HKU_CHECK_THROW(weight >= 0.0 && weight <= 1.0, std::out_of_range,
"weigth ({}) is out of range [0, 1]!", weight);
m_weight = weight;
}
SystemWeight(const SystemWeight&) = default;
SystemWeight(SystemWeight&& sw) : m_sys(std::move(sw.m_sys)), m_weight(sw.m_weight) {}
SystemWeight& operator=(const SystemWeight& other) = default;
SystemWeight& operator=(SystemWeight&& other);
/** 析构函数 */
virtual ~SystemWeight() {}
/** 修改对应的系统 */
void setSYS(const SystemPtr& sys) {
m_sys = sys;
}
/** 获取对应的系统 */
const SystemPtr& getSYS() const {
return m_sys;
}
/** 设置权重值,须在[0,1]之间 */
void setWeight(double weight) {
HKU_CHECK_THROW(weight >= 0.0 && weight <= 1.0, std::out_of_range,
"weigth ({}) is out of range [0, 1]!", weight);
m_weight = weight;
}
/** 获取权重值 */
double getWeight() const {
return m_weight;
}
public:
SystemPtr m_sys;
double m_weight;
private:
//============================================
// 序列化支持
//============================================
#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_sys);
ar& BOOST_SERIALIZATION_NVP(m_weight);
}
template <class Archive>
void load(Archive& ar, const unsigned int version) {
ar& BOOST_SERIALIZATION_NVP(m_sys);
ar& BOOST_SERIALIZATION_NVP(m_weight);
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
#endif /* HKU_SUPPORT_SERIALIZATION */
};
typedef vector<SystemWeight> SystemWeightList;
HKU_API std::ostream& operator<<(std::ostream&, const SystemWeight&);
inline bool operator==(const SystemWeight& d1, const SystemWeight& d2) {
return d1.getSYS() == d2.getSYS() && d1.getWeight() == d2.getWeight();
}
} /* namespace hku */
#if FMT_VERSION >= 90000
template <>
struct fmt::formatter<hku::SystemWeight> : ostream_formatter {};
#endif
#endif /* TRADE_SYS_ALLOCATEFUNDS_SYSTEMWEIGHT_H_ */

View File

@ -18,11 +18,11 @@ EqualWeightAllocateFunds::EqualWeightAllocateFunds() : AllocateFundsBase("AF_Equ
EqualWeightAllocateFunds::~EqualWeightAllocateFunds() {}
SystemWeightList EqualWeightAllocateFunds ::_allocateWeight(const Datetime& date,
const SystemList& se_list) {
const SystemWeightList& se_list) {
SystemWeightList result;
double weight = 1.0 / se_list.size();
price_t weight = 1 / se_list.size();
for (auto iter = se_list.begin(); iter != se_list.end(); ++iter) {
result.emplace_back(*iter, weight);
result.emplace_back(iter->sys, weight);
}
return result;

View File

@ -20,11 +20,11 @@ FixedWeightAllocateFunds::FixedWeightAllocateFunds() : AllocateFundsBase("AF_Fix
FixedWeightAllocateFunds::~FixedWeightAllocateFunds() {}
SystemWeightList FixedWeightAllocateFunds ::_allocateWeight(const Datetime& date,
const SystemList& se_list) {
const SystemWeightList& se_list) {
SystemWeightList result;
double weight = getParam<double>("weight");
price_t weight = getParam<double>("weight");
for (auto iter = se_list.begin(); iter != se_list.end(); ++iter) {
result.emplace_back(*iter, weight);
result.emplace_back(iter->sys, weight);
}
return result;

View File

@ -243,7 +243,12 @@ Indicator MultiFactorBase::getIC(int ndays) {
}
Indicator MultiFactorBase::getICIR(int ic_n, int ir_n) {
return ICIR(getIC(ic_n), ir_n);
Indicator ic = getIC(ic_n);
Indicator x = MA(ic, ir_n) / STDEV(ic, ir_n);
x.name("ICIR");
x.setParam<int>("n", ic_n);
x.setParam<int>("rolling_n", ir_n);
return x;
}
IndicatorList MultiFactorBase::_getAllReturns(int ndays) const {

View File

@ -39,54 +39,42 @@ IndicatorList ICIRMultiFactor::_calculate(const vector<IndicatorList>& all_stk_i
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);
icir[ii] = ICIR(m_inds[ii], m_stks, m_query, m_ref_stk, ic_n, 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);
// 以 ICIR 为权重,计算加权后的合成因子
IndicatorList all_factors(stk_count);
PriceList new_values(days_total, 0.0);
PriceList sum_weight(days_total, 0.0);
for (size_t si = 0; si < stk_count; si++) {
memset(new_values.data(), 0, sizeof(price_t) * days_total);
memset(sum_weight.data(), 0, sizeof(price_t) * days_total);
for (size_t di = 0; di < discard; di++) {
new_values[di] = Null<price_t>();
}
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];
new_values[di] += all_stk_inds[si][ii][di] * icir[ii][di];
sum_weight[di] += std::abs(icir[ii][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 (!std::isnan(new_values[di]) && sum_weight[di] != 0.0) {
new_values[di] = new_values[di] / sum_weight[di];
}
if (di == days_total - 1 && std::isnan(all_factors[si][di])) {
all_factors[si].setDiscard(di);
}
all_factors[si] = PRICELIST(new_values);
all_factors[si].name("ICIR");
const auto* data = all_factors[si].data();
for (size_t di = discard; di < days_total; di++) {
if (!std::isnan(data[di])) {
all_factors[si].setDiscard(discard);
}
}
}

View File

@ -35,59 +35,46 @@ IndicatorList ICMultiFactor::_calculate(const vector<IndicatorList>& all_stk_ind
int ic_n = getParam<int>("ic_n");
int ic_rolling_n = getParam<int>("ic_rolling_n");
// 计算每个原始因子的IC值
// 计算每个原始因子的滚动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);
ic[ii] = MA(IC(m_inds[ii], m_stks, m_query, m_ref_stk, ic_n), 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 权重计算合成因子
// 以滚动 IC 为权重,计算加权后的合成因子
IndicatorList all_factors(stk_count);
PriceList new_values(days_total, 0);
PriceList new_values(days_total, 0.0);
PriceList sum_weight(days_total, 0.0);
for (size_t si = 0; si < stk_count; si++) {
memset(new_values.data(), 0, sizeof(price_t) * days_total);
memset(sum_weight.data(), 0, sizeof(price_t) * days_total);
for (size_t di = 0; di < discard; di++) {
new_values[di] = Null<price_t>();
}
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];
new_values[di] += all_stk_inds[si][ii][di] * ic[ii][di];
sum_weight[di] += std::abs(ic[ii][di]);
}
}
for (size_t di = discard; di < days_total; di++) {
new_values[di] =
(countByDate[di] == 0) ? Null<value_t>() : new_values[di] / countByDate[di];
if (!std::isnan(new_values[di]) && sum_weight[di] != 0.0) {
new_values[di] = new_values[di] / sum_weight[di];
}
}
all_factors[si] = PRICELIST(new_values);
all_factors[si].name("IC");
// 更新 discard
const auto* data = all_factors[si].data();
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);
if (!std::isnan(data[di])) {
all_factors[si].setDiscard(discard);
}
}
}

View File

@ -112,7 +112,8 @@ double MoneyManagerBase::getBuyNumber(const Datetime& datetime, const Stock& sto
double min_trade = stock.minTradeNumber();
if (n < min_trade) {
HKU_TRACE("Ignore! Is less than the minimum number of transactions({})", min_trade);
HKU_TRACE("Ignore! Is less than the minimum number of transactions({}<{}) {}", n, min_trade,
stock.market_code());
return 0;
}

View File

@ -33,11 +33,13 @@ HKU_API std::ostream& operator<<(std::ostream& os, const PortfolioPtr& pf) {
Portfolio::Portfolio()
: m_name("Portfolio"), m_query(Null<KQuery>()), m_is_ready(false), m_need_calculate(true) {
setParam<bool>("trace", false); // 打印跟踪
setParam<int>("adjust_cycle", 1); // 调仓周期
setParam<bool>("trace", false); // 打印跟踪
}
Portfolio::Portfolio(const string& name)
: m_name(name), m_query(Null<KQuery>()), m_is_ready(false), m_need_calculate(true) {
setParam<int>("adjust_cycle", 1); // 调仓周期
setParam<bool>("trace", false);
}
@ -49,6 +51,7 @@ Portfolio::Portfolio(const TradeManagerPtr& tm, const SelectorPtr& se, const AFP
m_query(Null<KQuery>()),
m_is_ready(false),
m_need_calculate(true) {
setParam<int>("adjust_cycle", 1); // 调仓周期
setParam<bool>("trace", false);
}
@ -59,14 +62,13 @@ void Portfolio::reset() {
m_pro_sys_list.clear();
m_real_sys_list.clear();
m_running_sys_set.clear();
m_running_sys_list.clear();
m_tmp_selected_list_on_open.clear();
m_tmp_selected_list_on_close.clear();
m_delay_adjust_sys_list.clear();
m_tmp_selected_list.clear();
m_tmp_will_remove_sys.clear();
if (m_tm)
m_tm->reset();
if (m_shadow_tm)
m_shadow_tm->reset();
if (m_cash_tm)
m_cash_tm->reset();
if (m_se)
m_se->reset();
if (m_af)
@ -88,8 +90,8 @@ PortfolioPtr Portfolio::clone() {
p->m_af = m_af->clone();
if (m_tm)
p->m_tm = m_tm->clone();
if (m_shadow_tm)
p->m_shadow_tm = m_shadow_tm->clone();
if (m_cash_tm)
p->m_cash_tm = m_cash_tm->clone();
return p;
}
@ -118,9 +120,9 @@ bool Portfolio::_readyForRun() {
reset();
// 将影子账户指定给资产分配器
m_shadow_tm = m_tm->clone();
m_cash_tm = m_tm->clone();
m_af->setTM(m_tm);
m_af->setShadowTM(m_shadow_tm);
m_af->setCashTM(m_cash_tm);
// 为资金分配器设置关联查询条件
m_af->setQuery(m_query);
@ -144,7 +146,7 @@ bool Portfolio::_readyForRun() {
// 为内部实际执行的系统创建初始资金为0的子账户
sys->setTM(pro_tm->clone());
sys->getTM()->name(fmt::format("TM_SUB{}", i));
sys->name(fmt::format("PF_Real_{}_{}", i, sys->name()));
sys->name(fmt::format("PF_Real_{}_{}_{}", i, sys->name(), sys->getStock().market_code()));
HKU_CHECK(sys->readyForRun() && pro_sys->readyForRun(),
"Exist invalid system, it could not ready for run!");
@ -160,132 +162,262 @@ bool Portfolio::_readyForRun() {
return true;
}
void Portfolio::_runMoment(const Datetime& date) {
// 当前日期小于账户建立日期,直接忽略
HKU_IF_RETURN(date < m_shadow_tm->initDatetime(), void());
void Portfolio::run(const KQuery& query, int adjust_cycle, bool force) {
HKU_CHECK(adjust_cycle > 0, "Invalid param adjust_cycle! {}", adjust_cycle);
setParam<int>("adjust_cycle", adjust_cycle);
_runMomentOnOpen(date);
_runMomentOnClose(date);
// 释放掉临时数据占用的内存
m_running_sys_set = std::unordered_set<System*>();
m_running_sys_list = std::list<SYSPtr>();
m_tmp_selected_list_on_open = SystemList();
m_tmp_selected_list_on_close = SystemList();
m_tmp_will_remove_sys = SystemList();
}
void Portfolio::_runMomentOnOpen(const Datetime& date) {
// 从选股策略获取当前选中的系统列表
m_tmp_selected_list_on_open = m_se->getSelectedOnOpen(date);
// 资产分配算法调整各子系统资产分配,忽略上一周期收盘时选中的系统
m_af->adjustFunds(date, m_tmp_selected_list_on_open, m_running_sys_list,
m_tmp_selected_list_on_close);
// 由于系统的交易指令可能被延迟执行,需要保存并判断
// 遍历当前运行中的子系统,如果已没有分配资金和持仓,则放入待移除列表
m_tmp_will_remove_sys.clear();
int precision = m_shadow_tm->getParam<int>("precision");
for (auto& running_sys : m_running_sys_list) {
Stock stock = running_sys->getStock();
TMPtr sub_tm = running_sys->getTM();
PositionRecord position = sub_tm->getPosition(date, stock);
price_t cash = sub_tm->cash(date, m_query.kType());
// 已没有持仓且没有现金,则放入待移除列表
if (position.number == 0 && cash <= precision) {
if (cash != 0) {
sub_tm->checkout(date, cash);
m_shadow_tm->checkin(date, cash);
}
m_tmp_will_remove_sys.push_back(running_sys);
}
}
// 依据待移除列表将系统从运行中系统列表里删除
for (auto& sub_sys : m_tmp_will_remove_sys) {
m_running_sys_list.remove(sub_sys);
m_running_sys_set.erase(sub_sys.get());
}
// 遍历本次选择的系统列表,如果存在分配资金且不在运行中列表内,则加入运行列表
for (auto& sub_sys : m_tmp_selected_list_on_open) {
price_t cash = sub_sys->getTM()->cash(date, m_query.kType());
if (cash > 0.0 && m_running_sys_set.find(sub_sys.get()) == m_running_sys_set.end()) {
m_running_sys_list.push_back(sub_sys);
m_running_sys_set.insert(sub_sys.get());
}
}
// 在开盘时执行所有运行中的非延迟交易系统系统
for (auto& sub_sys : m_running_sys_list) {
if (!sub_sys->getParam<bool>("buy_delay")) {
auto tr = sub_sys->runMoment(date);
if (!tr.isNull()) {
m_tm->addTradeRecord(tr);
}
}
}
}
void Portfolio::_runMomentOnClose(const Datetime& date) {
// 从选股策略获取当前选中的系统列表
m_tmp_selected_list_on_close = m_se->getSelectedOnClose(date);
// 资产分配算法调整各子系统资产分配,忽略开盘时选中的系统
m_af->adjustFunds(date, m_tmp_selected_list_on_close, m_running_sys_list,
m_tmp_selected_list_on_open);
if (getParam<bool>("trace") &&
(!m_tmp_selected_list_on_open.empty() || !m_tmp_selected_list_on_close.empty())) {
HKU_INFO("{} ===========================================================", date);
for (auto& sys : m_tmp_selected_list_on_open) {
HKU_INFO("select on open: {}, cash: {}", sys->getTO().getStock(),
sys->getTM()->cash(date, m_query.kType()));
}
for (auto& sys : m_tmp_selected_list_on_close) {
HKU_INFO("select on close: {}, cash: {}", sys->getTO().getStock(),
sys->getTM()->cash(date, m_query.kType()));
}
}
// 如果选中的系统不在已有列表中,且账户已经被分配了资金,则将其加入运行系统,并执行
for (auto& sys : m_tmp_selected_list_on_close) {
if (m_running_sys_set.find(sys.get()) == m_running_sys_set.end()) {
TMPtr tm = sys->getTM();
if (tm->cash(date, m_query.kType()) > 0.0) {
m_running_sys_list.push_back(sys);
m_running_sys_set.insert(sys.get());
}
}
}
// 执行所有非延迟运行中系统
for (auto& sub_sys : m_running_sys_list) {
if (sub_sys->getParam<bool>("buy_delay")) {
auto tr = sub_sys->runMoment(date);
if (!tr.isNull()) {
m_tm->addTradeRecord(tr);
}
}
}
}
void Portfolio::run(const KQuery& query, bool force) {
setQuery(query);
if (force) {
m_need_calculate = true;
}
HKU_IF_RETURN(!m_need_calculate, void());
HKU_ERROR_IF_RETURN(!_readyForRun(), void(),
"readyForRun fails, check to see if a valid TradeManager, Selector, or "
"AllocateFunds instance have been specified.");
HKU_CHECK(_readyForRun(),
"readyForRun fails, check to see if a valid TradeManager, Selector, or "
"AllocateFunds instance have been specified.");
DatetimeList datelist = StockManager::instance().getTradingCalendar(query);
for (const auto& date : datelist) {
_runMoment(date);
HKU_IF_RETURN(datelist.empty(), void());
size_t cur_adjust_ix = 0;
for (size_t i = 0, total = datelist.size(); i < total; i++) {
bool adjust = false;
if (i == cur_adjust_ix) {
adjust = true;
cur_adjust_ix += adjust_cycle;
}
const auto& date = datelist[i];
_runMoment(date, adjust);
}
m_need_calculate = false;
// 释放掉临时数据占用的内存
m_tmp_selected_list = SystemWeightList();
m_tmp_will_remove_sys = SystemWeightList();
}
void Portfolio::_runMoment(const Datetime& date, bool adjust) {
// 当前日期小于账户建立日期,直接忽略
HKU_IF_RETURN(date < m_cash_tm->initDatetime(), void());
bool trace = getParam<bool>("trace");
HKU_INFO_IF(trace, "{} ===========================================================", date);
HKU_INFO_IF(trace && adjust, "[PF] Position adjustment will be made today.");
HKU_INFO_IF(trace, "[PF] current running system size: {}", m_running_sys_set.size());
//---------------------------------------------------
// 开盘前处理各个子账户、资金账户、总账户之间可能的误差
//---------------------------------------------------
int precision = m_tm->getParam<int>("precision");
// 更新所有运行中系统的权息
price_t sum_cash = 0.0;
for (auto& running_sys : m_running_sys_set) {
TMPtr sub_tm = running_sys->getTM();
sub_tm->updateWithWeight(date);
sum_cash += sub_tm->currentCash();
}
// 开盘前,调整账户权息,并进行轧差处理(平衡 sub_sys, cash_tm, tm 之间的误差)
m_tm->updateWithWeight(date);
HKU_INFO_IF(trace, "[PF] The sum cash of sub_tm: {}, cash tm: {}, tm cash: {}", sum_cash,
m_cash_tm->currentCash(), m_tm->currentCash());
sum_cash += m_cash_tm->currentCash();
price_t diff = roundEx(std::abs(m_tm->currentCash() - sum_cash), precision);
if (diff > 0.) {
if (m_tm->currentCash() > sum_cash) {
m_cash_tm->checkin(date, diff);
} else if (m_tm->currentCash() < sum_cash) {
if (!m_cash_tm->checkout(date, diff)) {
m_tm->checkin(date, diff);
}
}
HKU_INFO_IF(trace, "After compensate: the sum cash of sub_tm: {}, cash tm: {}, tm cash: {}",
sum_cash, m_cash_tm->currentCash(), m_tm->currentCash());
}
//----------------------------------------------------------------------
// 跟踪打印执行调仓前的资产情况
//----------------------------------------------------------------------
if (trace) {
auto funds = m_tm->getFunds(date, m_query.kType());
HKU_INFO("[PF] [beforce adjust] - total funds: {}, cash: {}, market_value: {}",
funds.cash + funds.market_value, funds.cash, funds.market_value);
}
//----------------------------------------------------------------------
// 开盘时,优先处理上一交易日确定的需延迟调仓卖出的系统,在开盘时先卖出调整
//----------------------------------------------------------------------
for (auto& sys : m_delay_adjust_sys_list) {
auto tr = sys.sys->sellForceOnOpen(date, sys.weight, PART_PORTFOLIO);
HKU_DEBUG_IF(trace && tr.isNull(), "[PF] Failed to force sell: {}", sys.sys->name());
if (!tr.isNull()) {
HKU_INFO_IF(trace, "[PF] Delay adjust sell: {}", tr);
m_tm->addTradeRecord(tr);
// 卖出后,尝试将资金取出转移至影子总账户
TMPtr sub_tm = sys.sys->getTM();
auto sub_cash = sub_tm->currentCash();
if (sub_cash > 0.0 && sub_tm->checkout(date, sub_cash)) {
m_cash_tm->checkin(date, sub_cash);
}
}
}
// 清空,避免循环至下一个非调仓日时被重复处理
m_delay_adjust_sys_list.clear();
//---------------------------------------------------------------
// 遍历当前运行中的子系统,如果已没有分配资金和持仓,则回收
//---------------------------------------------------------------
m_tmp_will_remove_sys.clear();
for (auto& running_sys : m_running_sys_set) {
Stock stock = running_sys->getStock();
TMPtr sub_tm = running_sys->getTM();
PositionRecord position = sub_tm->getPosition(date, stock);
price_t cash = sub_tm->currentCash();
price_t min_cash = 1.0;
KRecord krecord = stock.getKRecord(date);
if (krecord.isValid()) {
min_cash = krecord.openPrice * stock.minTradeNumber();
}
// 如果系统的剩余资金小于交易一手的资金,则回收资金
if (cash != 0 && cash <= min_cash) {
sub_tm->checkout(date, cash);
m_cash_tm->checkin(date, cash);
HKU_INFO_IF(trace, "Recycle cash ({:<.2f}) from {}, current cash: {}", cash,
running_sys->name(), m_cash_tm->currentCash());
// 如果已经没有持仓,则回收
if (position.number == 0) {
m_tmp_will_remove_sys.emplace_back(running_sys, 0.);
HKU_INFO_IF(trace, "[PF] Recycle running sys: {}", running_sys->name());
}
}
}
// 依据待移除列表将系统从运行中系统列表里删除
for (auto& sub_sys : m_tmp_will_remove_sys) {
HKU_INFO_IF(trace, "Recycling system {}", sub_sys.sys->name());
m_running_sys_set.erase(sub_sys.sys);
}
//---------------------------------------------------
// 调仓日,进行资金分配调整
//---------------------------------------------------
if (adjust) {
// 从选股策略获取选中的系统列表
m_tmp_selected_list = m_se->getSelected(date);
if (trace && !m_tmp_selected_list.empty()) {
for (auto& sys : m_tmp_selected_list) {
HKU_INFO("[PF] select: {}, score: {:<.4f}", sys.sys->name(), sys.weight);
}
}
// 资产分配算法调整各子系统资产分配
m_delay_adjust_sys_list = m_af->adjustFunds(date, m_tmp_selected_list, m_running_sys_set);
// 如果选中的系统不在已有列表中,且账户已经被分配了资金,则将其加入运行系统,并执行
for (auto& sys : m_tmp_selected_list) {
if (m_running_sys_set.find(sys.sys) == m_running_sys_set.end()) {
if (sys.sys->getTM()->cash(date, m_query.kType()) > 0.0) {
m_running_sys_set.insert(sys.sys);
}
}
}
// 从已运行系统列表中立即移除已没有持仓且没有资金的非延迟买入的系统
m_tmp_will_remove_sys.clear();
for (auto& sys : m_running_sys_set) {
auto sub_tm = sys->getTM();
if (!sys->getParam<bool>("buy_delay") && sub_tm->currentCash() < 1.0 &&
0 == sub_tm->getHoldNumber(date, sys->getStock())) {
m_tmp_will_remove_sys.emplace_back(sys, 0.0);
}
}
for (auto& sw : m_tmp_will_remove_sys) {
m_running_sys_set.erase(sw.sys);
}
}
//----------------------------------------------------------------------
// 跟踪打印执行调仓后的资产情况
//----------------------------------------------------------------------
if (trace) {
auto funds = m_tm->getFunds(date, m_query.kType());
HKU_INFO("[PF] [after adjust] - total funds: {}, cash: {}, market_value: {}",
funds.cash + funds.market_value, funds.cash, funds.market_value);
}
//----------------------------------------------------------------------------
// 执行所有运行中的系统,无论是延迟还是非延迟,当天运行中的系统都需要被执行一次
//----------------------------------------------------------------------------
for (auto& sub_sys : m_running_sys_set) {
auto tr = sub_sys->runMoment(date);
if (!tr.isNull()) {
HKU_INFO_IF(trace, "[PF] {}", tr);
m_tm->addTradeRecord(tr);
}
}
//----------------------------------------------------------------------
// 跟踪各个子系统执行后的资产情况
//----------------------------------------------------------------------
if (trace) {
auto funds = m_tm->getFunds(date, m_query.kType());
HKU_INFO("[PF] [after run] - total funds: {}, cash: {}, market_value: {}",
funds.cash + funds.market_value, funds.cash, funds.market_value);
}
//----------------------------------------------------------------------
// 跟踪打印持仓情况
//----------------------------------------------------------------------
if (trace && !m_running_sys_set.empty()) {
HKU_INFO(
"+------------+------------+------------+--------------+--------------+-------------+");
HKU_INFO(
"| code | name | position | market value | remain cash | close price |");
HKU_INFO(
"+------------+------------+------------+--------------+--------------+-------------+");
size_t count = 0;
for (const auto& sys : m_running_sys_set) {
Stock stk = sys->getStock();
auto funds = sys->getTM()->getFunds(date, m_query.kType());
size_t position = sys->getTM()->getHoldNumber(date, stk);
#if HKU_OS_WINDOWS
auto stk_name = StockManager::instance().runningInPython() &&
StockManager::instance().pythonInJupyter()
? stk.name()
: UTF8ToGB(stk.name());
if (stk_name.size() < 11) {
for (size_t i = 0, total = 11 - stk_name.size(); i < total; i++) {
stk_name.push_back(' ');
}
}
HKU_INFO("| {:<11}| {}| {:<11}| {:<13.2f}| {:<13.2f}| {:<12.2f}|", stk.market_code(),
stk_name, position, funds.market_value, funds.cash,
stk.getKRecord(date, m_query.kType()).closePrice);
#else
auto stk_name = stk.name();
HKU_INFO("| {:<11}| {:<11}| {:<11}| {:<13.2f}| {:<13.2f}| {:<12.2f}|",
stk.market_code(), stk_name, position, funds.market_value, funds.cash,
stk.getKRecord(date, m_query.kType()).closePrice);
#endif
HKU_INFO(
"+------------+------------+------------+--------------+--------------+-------------"
"+");
if (++count >= 10) {
break;
}
}
}
}
} /* namespace hku */

View File

@ -55,7 +55,7 @@ public:
* @param query
* @param force
*/
void run(const KQuery& query, bool force = false);
void run(const KQuery& query, int adjust_cycle = 1, bool force = false);
/** 修改查询条件 */
void setQuery(const KQuery& query);
@ -99,14 +99,12 @@ private:
/** 运行前准备 */
bool _readyForRun();
void _runMoment(const Datetime& datetime);
void _runMomentOnOpen(const Datetime& datetime);
void _runMomentOnClose(const Datetime& datetime);
void _runMoment(const Datetime& date, bool adjust);
protected:
string m_name;
TMPtr m_tm;
TMPtr m_shadow_tm;
TMPtr m_cash_tm; // 仅仅负责内部资金的管理(即只需要 checkout 到子账号, 从账户checkin现金
SEPtr m_se;
AFPtr m_af;
@ -118,11 +116,10 @@ protected:
SystemList m_real_sys_list; // 所有实际运行的子系统列表
// 用于中间计算的临时数据
std::unordered_set<System*> m_running_sys_set; // 当前仍在运行的子系统集合
std::list<SYSPtr> m_running_sys_list; // 当前仍在运行的子系统列表
SystemList m_tmp_selected_list_on_open;
SystemList m_tmp_selected_list_on_close;
SystemList m_tmp_will_remove_sys;
std::unordered_set<SYSPtr> m_running_sys_set;
SystemWeightList m_delay_adjust_sys_list; // 延迟调仓卖出的系统列表
SystemWeightList m_tmp_selected_list;
SystemWeightList m_tmp_will_remove_sys;
//============================================
// 序列化支持
@ -135,7 +132,7 @@ private:
ar& BOOST_SERIALIZATION_NVP(m_name);
ar& BOOST_SERIALIZATION_NVP(m_params);
ar& BOOST_SERIALIZATION_NVP(m_tm);
ar& BOOST_SERIALIZATION_NVP(m_shadow_tm);
ar& BOOST_SERIALIZATION_NVP(m_cash_tm);
ar& BOOST_SERIALIZATION_NVP(m_se);
ar& BOOST_SERIALIZATION_NVP(m_af);
ar& BOOST_SERIALIZATION_NVP(m_query);
@ -148,7 +145,7 @@ private:
ar& BOOST_SERIALIZATION_NVP(m_name);
ar& BOOST_SERIALIZATION_NVP(m_params);
ar& BOOST_SERIALIZATION_NVP(m_tm);
ar& BOOST_SERIALIZATION_NVP(m_shadow_tm);
ar& BOOST_SERIALIZATION_NVP(m_cash_tm);
ar& BOOST_SERIALIZATION_NVP(m_se);
ar& BOOST_SERIALIZATION_NVP(m_af);
ar& BOOST_SERIALIZATION_NVP(m_query);

View File

@ -6,7 +6,6 @@
*/
#include "SelectorBase.h"
#include "../portfolio/Portfolio.h"
namespace hku {

View File

@ -10,9 +10,10 @@
#define TRADE_SYS_SELECTOR_SELECTORBASE_H_
#include "../system/System.h"
#include "../allocatefunds/AllocateFundsBase.h"
#include "../../KData.h"
#include "../../utilities/Parameter.h"
#include "hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h"
#include "SystemWeight.h"
namespace hku {
@ -96,11 +97,8 @@ public:
/** 子类计算接口 */
virtual void _calculate() = 0;
/** 子类获取指定时刻开盘时选中的标的 */
virtual SystemList getSelectedOnOpen(Datetime date) = 0;
/** 子类获取指定时刻收盘时选中的标的 */
virtual SystemList getSelectedOnClose(Datetime date) = 0;
virtual SystemWeightList getSelected(Datetime date) = 0;
virtual bool isMatchAF(const AFPtr& af) = 0;
@ -166,14 +164,13 @@ private: \
#define SELECTOR_NO_PRIVATE_MEMBER_SERIALIZATION
#endif
#define SELECTOR_IMP(classname) \
public: \
virtual SelectorPtr _clone() override { \
return SelectorPtr(new classname()); \
} \
virtual SystemList getSelectedOnOpen(Datetime date) override; \
virtual SystemList getSelectedOnClose(Datetime date) override; \
virtual bool isMatchAF(const AFPtr& af) override; \
#define SELECTOR_IMP(classname) \
public: \
virtual SelectorPtr _clone() override { \
return SelectorPtr(new classname()); \
} \
virtual SystemWeightList getSelected(Datetime date) override; \
virtual bool isMatchAF(const AFPtr& af) override; \
virtual void _calculate() override;
/**

View File

@ -5,7 +5,7 @@
* Author: fasiondog
*/
#include "../allocatefunds/SystemWeight.h"
#include "SystemWeight.h"
namespace hku {
@ -15,16 +15,16 @@ HKU_API std::ostream& operator<<(std::ostream& os, const SystemWeight& sw) {
string name("NULL");
string stk_name("(Stock(NULL))");
if (sw.getSYS()) {
name = sw.getSYS()->name();
if (sw.sys) {
name = sw.sys->name();
Stock stk = sw.getSYS()->getStock();
Stock stk = sw.sys->getStock();
if (!stk.isNull()) {
stk_name = "(Stock(" + stk.market_code() + "))";
}
}
os << "SystemWeight(sys: " << name << stk_name << ", weight: " << sw.getWeight() << ")";
os << "SystemWeight(sys: " << name << stk_name << ", weight: " << sw.weight << ")";
os.unsetf(std::ostream::floatfield);
os.precision();
@ -33,8 +33,8 @@ HKU_API std::ostream& operator<<(std::ostream& os, const SystemWeight& sw) {
SystemWeight& SystemWeight::operator=(SystemWeight&& other) {
if (this != &other) {
m_sys = std::move(other.m_sys);
m_weight = other.m_weight;
sys = std::move(other.sys);
weight = other.weight;
}
return *this;
}

View File

@ -0,0 +1,70 @@
/*
* SystemWeight.h
*
* Created on: 2018129
* Author: fasiondog
*/
#pragma once
#ifndef TRADE_SYS_ALLOCATEFUNDS_SYSTEMWEIGHT_H_
#define TRADE_SYS_ALLOCATEFUNDS_SYSTEMWEIGHT_H_
#include "../system/System.h"
namespace hku {
/**
* [0.0, 1.0]
* @details
* @ingroup Selector
*/
struct HKU_API SystemWeight {
SystemPtr sys;
price_t weight{1.0};
SystemWeight() = default;
SystemWeight(const SystemPtr& sys_, double weight_) : sys(sys_), weight(weight_) {}
SystemWeight(const SystemWeight&) = default;
SystemWeight(SystemWeight&& sw) : sys(std::move(sw.sys)), weight(sw.weight) {}
SystemWeight& operator=(const SystemWeight& other) = default;
SystemWeight& operator=(SystemWeight&& other);
private:
//============================================
// 序列化支持
//============================================
#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(sys);
ar& BOOST_SERIALIZATION_NVP(weight);
}
template <class Archive>
void load(Archive& ar, const unsigned int version) {
ar& BOOST_SERIALIZATION_NVP(sys);
ar& BOOST_SERIALIZATION_NVP(weight);
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
#endif /* HKU_SUPPORT_SERIALIZATION */
};
typedef vector<SystemWeight> SystemWeightList;
HKU_API std::ostream& operator<<(std::ostream&, const SystemWeight&);
inline bool operator==(const SystemWeight& d1, const SystemWeight& d2) {
return d1.sys == d2.sys && std::abs(d1.weight - d2.weight) < 0.0001;
}
} /* namespace hku */
#if FMT_VERSION >= 90000
template <>
struct fmt::formatter<hku::SystemWeight> : ostream_formatter {};
#endif
#endif /* TRADE_SYS_ALLOCATEFUNDS_SYSTEMWEIGHT_H_ */

View File

@ -21,22 +21,10 @@ bool FixedSelector::isMatchAF(const AFPtr& af) {
return true;
}
SystemList FixedSelector::getSelectedOnOpen(Datetime date) {
SystemList result;
SystemWeightList FixedSelector::getSelected(Datetime date) {
SystemWeightList result;
for (auto& sys : m_real_sys_list) {
if (!sys->getParam<bool>("buy_delay")) {
result.emplace_back(sys);
}
}
return result;
}
SystemList FixedSelector::getSelectedOnClose(Datetime date) {
SystemList result;
for (auto& sys : m_real_sys_list) {
if (sys->getParam<bool>("buy_delay")) {
result.emplace_back(sys);
}
result.emplace_back(sys, 1.0);
}
return result;
}

View File

@ -24,14 +24,9 @@ bool SignalSelector::isMatchAF(const AFPtr& af) {
return true;
}
SystemList SignalSelector::getSelectedOnOpen(Datetime date) {
auto iter = m_sys_dict_on_open.find(date);
return iter != m_sys_dict_on_open.end() ? iter->second : SystemList();
}
SystemList SignalSelector::getSelectedOnClose(Datetime date) {
auto iter = m_sys_dict_on_close.find(date);
return iter != m_sys_dict_on_close.end() ? iter->second : SystemList();
SystemWeightList SignalSelector::getSelected(Datetime date) {
auto iter = m_sys_dict.find(date);
return iter != m_sys_dict.end() ? iter->second : SystemWeightList();
}
void SignalSelector::_calculate() {
@ -40,14 +35,12 @@ void SignalSelector::_calculate() {
auto& sys = m_real_sys_list[i];
auto sg = sys->getSG();
auto dates = sg->getBuySignal();
unordered_map<Datetime, SystemList>* date_dict;
date_dict = sys->getParam<bool>("buy_delay") ? &m_sys_dict_on_close : &m_sys_dict_on_open;
for (auto& date : dates) {
auto iter = date_dict->find(date);
if (iter != date_dict->end()) {
iter->second.emplace_back(sys);
auto iter = m_sys_dict.find(date);
if (iter != m_sys_dict.end()) {
iter->second.emplace_back(sys, 1.0);
} else {
(*date_dict)[date] = {sys};
m_sys_dict[date] = {SystemWeight(sys, 1.0)};
}
}
}

View File

@ -20,13 +20,11 @@ public:
virtual ~SignalSelector();
virtual void _reset() override {
m_sys_dict_on_open.clear();
m_sys_dict_on_close.clear();
m_sys_dict.clear();
}
private:
unordered_map<Datetime, SystemList> m_sys_dict_on_open;
unordered_map<Datetime, SystemList> m_sys_dict_on_close;
unordered_map<Datetime, SystemWeightList> m_sys_dict;
};
} // namespace hku

View File

@ -671,43 +671,35 @@ void System::_submitBuyRequest(const KRecord& today, const KRecord& src_today, P
src_today.closePrice - m_buyRequest.stoploss, m_buyRequest.from);
}
TradeRecord System::sellForce(const KRecord& today, const KRecord& src_today, double num,
Part from) {
HKU_ASSERT_M(from == PART_ALLOCATEFUNDS || from == PART_PORTFOLIO,
"Only Allocator or Portfolis can perform this operation!");
TradeRecord result;
if (getParam<bool>("sell_delay")) {
if (m_sellRequest.valid) {
if (m_sellRequest.count > getParam<int>("max_delay_count")) {
// 超出最大延迟次数,清除买入请求
m_sellRequest.clear();
return result;
}
m_sellRequest.count++;
TradeRecord System::_sellForce(const Datetime& date, double num, Part from, bool on_open) {
TradeRecord record;
size_t pos = m_kdata.getPos(date);
HKU_TRACE_IF_RETURN(pos == Null<size_t>(), record,
"Failed to sellForce {}, the day {} could'nt sell!", m_stock.market_code(),
date);
} else {
m_sellRequest.valid = true;
m_sellRequest.business = BUSINESS_SELL;
m_sellRequest.count = 1;
}
const auto& krecord = m_kdata.getKRecord(pos);
const auto& src_krecord =
m_stock.getKRecord(m_kdata.startPos() + pos, m_kdata.getQuery().kType());
PositionRecord position = m_tm->getPosition(today.datetime, m_stock);
m_sellRequest.from = from;
m_sellRequest.datetime = today.datetime;
m_sellRequest.stoploss = position.stoploss;
m_sellRequest.goal = position.goalPrice;
m_sellRequest.number = num;
return result;
PositionRecord position = m_tm->getPosition(date, m_stock);
price_t realPrice =
_getRealSellPrice(krecord.datetime, on_open ? src_krecord.openPrice : src_krecord.closePrice);
} else {
PositionRecord position = m_tm->getPosition(today.datetime, m_stock);
price_t realPrice = _getRealSellPrice(today.datetime, src_today.closePrice);
TradeRecord record = m_tm->sell(today.datetime, m_stock, realPrice, num, position.stoploss,
position.goalPrice, src_today.closePrice, from);
m_trade_list.push_back(record);
_sellNotifyAll(record);
return record;
double min_num = m_stock.minTradeNumber();
double real_sell_num = num;
if (real_sell_num >= position.number) {
real_sell_num = position.number;
} else if (min_num > 1) {
real_sell_num = static_cast<int64_t>(num / min_num) * min_num;
}
record =
m_tm->sell(date, m_stock, realPrice, real_sell_num, position.stoploss, position.goalPrice,
on_open ? src_krecord.openPrice : src_krecord.closePrice, from);
m_trade_list.push_back(record);
_sellNotifyAll(record);
return record;
}
TradeRecord System::_sell(const KRecord& today, const KRecord& src_today, Part from) {

View File

@ -211,8 +211,17 @@ public:
return _sell(today, src_today, from);
}
// 强制卖出,用于资金分配管理器和资产组合指示系统进行强制卖出操作
TradeRecord sellForce(const KRecord& today, const KRecord& src_today, double num, Part from);
// 强制以开盘价卖出,仅供 PF/AF 内部调用
TradeRecord sellForceOnOpen(const Datetime& date, double num, Part from) {
HKU_ASSERT(from == PART_ALLOCATEFUNDS || from == PART_PORTFOLIO);
return _sellForce(date, num, from, true);
}
// 强制以收盘价卖出,仅供 PF/AF 内部调用
TradeRecord sellForceOnClose(const Datetime& date, double num, Part from) {
HKU_ASSERT(from == PART_ALLOCATEFUNDS || from == PART_PORTFOLIO);
return _sellForce(date, num, from, false);
}
private:
bool _environmentIsValid(const Datetime& datetime);
@ -264,6 +273,9 @@ private:
TradeRecord _runMoment(const KRecord& record, const KRecord& src_record);
// Portfolio | AllocateFunds 指示立即进行强制卖出,以便对 buy_delay 的系统进行资金调整
TradeRecord _sellForce(const Datetime& date, double num, Part from, bool on_open);
protected:
TradeManagerPtr m_tm;
MoneyManagerPtr m_mm;

View File

@ -7,7 +7,7 @@ target("hikyuu")
-- set_kind("shared")
-- end
add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time")
add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time", "log_level")
add_packages("boost", "fmt", "spdlog", "flatbuffers", "nng", "nlohmann_json", "cpp-httplib")
if is_plat("windows", "linux", "cross") then
@ -76,6 +76,7 @@ target("hikyuu")
add_headerfiles("../(hikyuu/**.h)|**doc.h")
-- add files
-- add_files("./**.cpp|data_driver/**.cpp|utilities/db_connect/mysql/*.cpp")
add_files("./**.cpp|data_driver/**.cpp|utilities/db_connect/mysql/*.cpp")
add_files("./data_driver/*.cpp")
if get_config("hdf5") or get_config("sqlite") then

View File

@ -31,38 +31,38 @@ TEST_CASE("test_IC") {
Indicator result;
/** @arg 传入非法 n */
result = IC(MA(CLOSE()), stks, query, -1, ref_stk);
result = IC(MA(CLOSE()), stks, query, ref_stk, -1);
CHECK_EQ(result.name(), "IC");
CHECK_UNARY(result.empty());
/** @arg 传入的 ref_stk 为 null */
result = IC(stks, query, 1, Stock())(MA(CLOSE()));
result = IC(stks, query, Stock(), 1)(MA(CLOSE()));
CHECK_EQ(result.name(), "IC");
CHECK_UNARY(result.empty());
/** @arg 传入空的 stks */
result = IC(MA(CLOSE()), StockList(), query, 1, ref_stk);
result = IC(MA(CLOSE()), StockList(), query, ref_stk, 1);
CHECK_EQ(result.name(), "IC");
CHECK_UNARY(!result.empty());
CHECK_EQ(result.size(), ref_k.size());
CHECK_EQ(result.discard(), result.size());
/** @arg 传入的 stks 数量不足需要大于等于2 */
result = IC(MA(CLOSE()), {sm["sh600004"]}, query, 1, ref_stk);
result = IC(MA(CLOSE()), {sm["sh600004"]}, query, ref_stk, 1);
CHECK_EQ(result.name(), "IC");
CHECK_UNARY(!result.empty());
CHECK_EQ(result.size(), ref_k.size());
CHECK_EQ(result.discard(), result.size());
/** @arg ref_stk 数据长度不足 */
result = IC(MA(CLOSE()), stks, KQuery(-1), 1, ref_stk);
result = IC(MA(CLOSE()), stks, KQuery(-1), ref_stk, 1);
CHECK_EQ(result.name(), "IC");
CHECK_UNARY(!result.empty());
CHECK_EQ(result.size(), 1);
CHECK_EQ(result.discard(), result.size());
/** @arg 传入的 stks 中夹杂有 null stock实际的 stks 长度小于2 */
result = IC(MA(CLOSE()), {sm["sh600004"], Stock()}, KQuery(-2), 1, ref_stk);
result = IC(MA(CLOSE()), {sm["sh600004"], Stock()}, KQuery(-2), ref_stk, 1);
CHECK_EQ(result.name(), "IC");
CHECK_UNARY(!result.empty());
CHECK_EQ(result.size(), 2);
@ -71,7 +71,7 @@ TEST_CASE("test_IC") {
CHECK_UNARY(std::isnan(result[1]));
/** @arg 传入的 stks 长度为2query 的长度为2*/
result = IC(MA(CLOSE()), {sm["sh600004"], sm["sh600005"]}, KQuery(-2), 1, ref_stk);
result = IC(MA(CLOSE()), {sm["sh600004"], sm["sh600005"]}, KQuery(-2), ref_stk, 1);
CHECK_EQ(result.name(), "IC");
CHECK_UNARY(!result.empty());
CHECK_EQ(result.size(), 2);
@ -80,13 +80,12 @@ TEST_CASE("test_IC") {
CHECK_UNARY(std::isnan(result[1]));
/** @arg 正常执行 */
result = IC(MA(CLOSE()), stks, query, 1, ref_stk);
result = IC(MA(CLOSE()), stks, query, ref_stk, 1);
CHECK_EQ(result.name(), "IC");
CHECK_UNARY(!result.empty());
CHECK_EQ(result.size(), ref_k.size());
CHECK_EQ(result.discard(), 2);
CHECK_EQ(result.discard(), 1);
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));
}
@ -108,7 +107,7 @@ TEST_CASE("test_IC_benchmark") {
BENCHMARK_TIME_MSG(test_IC_benchmark, cycle, fmt::format("data len: {}", ref_k.size()));
SPEND_TIME_CONTROL(false);
for (int i = 0; i < cycle; i++) {
Indicator ind = IC(MA(CLOSE()), stks, query, 1, ref_stk);
Indicator ind = IC(MA(CLOSE()), stks, query, ref_stk, 1);
}
}
}
@ -128,7 +127,7 @@ TEST_CASE("test_IC_export") {
Stock ref_stk = sm["sh000001"];
KQuery query = KQuery(-200);
Indicator x1 = IC(stks, query, 1, ref_stk)(MA(CLOSE()));
Indicator x1 = IC(stks, query, ref_stk, 1)(MA(CLOSE()));
{
std::ofstream ofs(filename);

View File

@ -30,7 +30,7 @@ TEST_CASE("test_AllocateFunds") {
TMPtr subtm = crtTM(Datetime(200101010000L), 0);
SYSPtr sys = SYS_Simple(subtm->clone());
SystemList se_list;
SystemWeightList se_list;
SystemList hold_list;
SystemList ac_list;
SystemWeightList sw_list;
@ -48,9 +48,9 @@ TEST_CASE("test_AllocateFunds") {
/** @arg 出入的se_list、hold_list均为空 */
CHECK_EQ(se_list.size(), 0);
CHECK_EQ(hold_list.size(), 0);
sw_list = af->_allocateWeight(Datetime(201802100000L), se_list);
// sw_list = af->_allocateWeight(Datetime(201802100000L), se_list);
// ac_list = af->getAllocatedSystemList(Datetime(201802100000L), se_list, hold_list);
CHECK_EQ(sw_list.size(), 0);
// CHECK_EQ(sw_list.size(), 0);
// CHECK_EQ(ac_list.size(), 0);
/** @arg 最大持仓系统数小于0 */

View File

@ -6,7 +6,7 @@
*/
#include "doctest/doctest.h"
#include <hikyuu/trade_sys/allocatefunds/SystemWeight.h>
// #include <hikyuu/trade_sys/allocatefunds/SystemWeight.h>
// #include<hikyuu / StockManager.h>
#include <hikyuu/trade_manage/crt/crtTM.h>
#include <hikyuu/trade_sys/system/crt/SYS_Simple.h>
@ -21,16 +21,16 @@ using namespace hku;
/** @par 检测点,不自动调仓 */
TEST_CASE("test_SystemWeight") {
SystemWeight w;
CHECK_EQ(w.getWeight(), 1.0);
CHECK_THROWS(w.setWeight(1.1));
w.setWeight(0.9);
CHECK_EQ(w.getWeight(), 0.9);
// SystemWeight w;
// CHECK_EQ(w.getWeight(), 1.0);
// // CHECK_THROWS(w.setWeight(1.1));
// w.setWeight(0.9);
// CHECK_EQ(w.getWeight(), 0.9);
TMPtr tm = crtTM(Datetime(200101010000L), 100000);
SYSPtr sys = SYS_Simple(tm);
// TMPtr tm = crtTM(Datetime(200101010000L), 100000);
// SYSPtr sys = SYS_Simple(tm);
CHECK_THROWS(SystemWeight(sys, 1.1));
// CHECK_THROWS(SystemWeight(sys, 1.1));
// HKU_INFO("{}", SystemWeight(sys, 0.2));
}

View File

@ -100,7 +100,7 @@ TEST_CASE("test_MF_EqualWeight") {
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);
auto ic2 = IC(MA(CLOSE()), stks, query, ref_stk, 1);
CHECK_UNARY(ic1.equal(ic2));
CHECK_THROWS_AS(mf->getScore(Datetime(20111204)), std::exception);

View File

@ -16,6 +16,7 @@
#include <hikyuu/indicator/crt/ICIR.h>
#include <hikyuu/indicator/crt/ROCR.h>
#include <hikyuu/indicator/crt/KDATA.h>
#include <hikyuu/indicator/crt/STDEV.h>
#include <hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h>
using namespace hku;
@ -43,11 +44,11 @@ TEST_CASE("test_MF_ICIRWeight") {
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 ic1 = ICIR(MA(ROCR(CLOSE(), ndays)), stks, query, ref_stk, ndays, 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 ic2 = ICIR(AMA(ROCR(CLOSE(), ndays)), stks, query, ref_stk, ndays, 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 ic3 = ICIR(EMA(ROCR(CLOSE(), ndays)), stks, query, ref_stk, ndays, ic_rolling_n);
auto ind4 = mf->getFactor(stk);
for (size_t i = 0; i < ind4.discard(); i++) {
@ -58,9 +59,15 @@ TEST_CASE("test_MF_ICIRWeight") {
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));
Indicator::value_t w = (ind1[i] * ic1[i] + ind2[i] * ic2[i] + ind3[i] * ic3[i]) /
(std::abs(ic1[i]) + std::abs(ic2[i]) + std::abs(ic3[i]));
// HKU_INFO("{}: {}, {}, {}, {}, {}", i, w, ind4[i], ind1[i], ma_ic1[i], stdev_ic1[i]);
// HKU_INFO("{}: {}, {}", i, w, ind4[i]);
if (!std::isnan(ind4[i]) && !std::isnan(w)) {
CHECK_EQ(ind4[i], doctest::Approx(w));
}
}
// HKU_INFO("{}", ind4);
}
//-----------------------------------------------------------------------------

View File

@ -15,6 +15,7 @@
#include <hikyuu/indicator/crt/IC.h>
#include <hikyuu/indicator/crt/ROCR.h>
#include <hikyuu/indicator/crt/KDATA.h>
#include <hikyuu/indicator/crt/PRICELIST.h>
#include <hikyuu/trade_sys/factor/crt/MF_ICWeight.h>
using namespace hku;
@ -42,11 +43,11 @@ TEST_CASE("test_MF_ICWeight") {
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 ic1 = MA(IC(MA(ROCR(CLOSE(), ndays)), stks, query, ref_stk, ndays), 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 ic2 = MA(IC(AMA(ROCR(CLOSE(), ndays)), stks, query, ref_stk, ndays), 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 ic3 = MA(IC(EMA(ROCR(CLOSE(), ndays)), stks, query, ref_stk, ndays), ic_rolling_n);
auto ind4 = mf->getFactor(stk);
for (size_t i = 0; i < ind4.discard(); i++) {
@ -57,9 +58,12 @@ TEST_CASE("test_MF_ICWeight") {
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));
Indicator::value_t w = (ind1[i] * ic1[i] + ind2[i] * ic2[i] + ind3[i] * ic3[i]) /
(std::abs(ic1[i]) + std::abs(ic2[i]) + std::abs(ic3[i]));
// HKU_INFO("{}: {}, {}", i, w, ind4[i]);
CHECK_EQ(ind4[i], doctest::Approx(w));
}
// HKU_INFO("{}", ind4);
}
//-----------------------------------------------------------------------------

View File

@ -29,24 +29,24 @@ TEST_CASE("test_SE_Fixed") {
SYSPtr sys = SYS_Simple();
SEPtr se = SE_Fixed();
/** @arg 试图加入一个不存在的stock */
se->addStock(Stock(), sys);
SystemList result = se->getSelectedOnOpen(Datetime(200001010000L));
CHECK_EQ(result.size(), 0);
// /** @arg 试图加入一个不存在的stock */
// se->addStock(Stock(), sys);
// SystemWeightList result = se->getSelectedOnOpen(Datetime(200001010000L));
// CHECK_EQ(result.size(), 0);
/** @arg 试图加入一个空的系统策略原型 */
se->addStock(sm["sh600000"], SYSPtr());
result = se->getSelectedOnOpen(Datetime(200001010000L));
CHECK_EQ(result.size(), 0);
// /** @arg 试图加入一个空的系统策略原型 */
// se->addStock(sm["sh600000"], SYSPtr());
// result = se->getSelectedOnOpen(Datetime(200001010000L));
// CHECK_EQ(result.size(), 0);
/** @arg 试图加入一个缺少MM的系统策略原型 */
SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10));
MMPtr mm = MM_FixedCount(100);
CHECK_UNARY(!se->addStock(sm["sh600000"], sys));
// /** @arg 试图加入一个缺少MM的系统策略原型 */
// SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10));
// MMPtr mm = MM_FixedCount(100);
// CHECK_UNARY(!se->addStock(sm["sh600000"], sys));
/* @arg 试图加入一个未指定SG的系统原型 */
sys->setMM(mm);
CHECK_UNARY(!se->addStock(sm["sh600000"], sys));
// /* @arg 试图加入一个未指定SG的系统原型 */
// sys->setMM(mm);
// CHECK_UNARY(!se->addStock(sm["sh600000"], sys));
// 目前必须有PF指定实际执行的子系统下面代码无法执行
// /** @arg getSelectedSystemList */

View File

@ -59,7 +59,7 @@ target("unit-test")
set_kind("binary")
set_default(false)
add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time")
add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time", "log_level")
add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3")
if get_config("mysql") then
@ -106,7 +106,7 @@ target("small-test")
set_kind("binary")
set_default(false)
add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time")
add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time", "log_level")
add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3")
if get_config("mysql") then

View File

@ -1739,36 +1739,59 @@ void export_Indicator_build_in(py::module& m) {
m.def(
"IC",
[](const Indicator& ind, const py::object& stks, const KQuery& query, int n,
const Stock& ref_stk) {
[](const Indicator& ind, const py::object& stks, const KQuery& query, const Stock& ref_stk,
int n) {
if (py::isinstance<Block>(stks)) {
const auto& blk = stks.cast<Block&>();
return IC(ind, blk, query, n, ref_stk);
return IC(ind, blk, query, ref_stk, n);
}
if (py::isinstance<py::sequence>(stks)) {
StockList c_stks = python_list_to_vector<Stock>(stks);
return IC(ind, c_stks, query, n, ref_stk);
return IC(ind, c_stks, query, ref_stk, n);
}
HKU_THROW("Input stks must be Block or sequenc(Stock)!");
},
py::arg("ind"), py::arg("stks"), py::arg("query"), py::arg("n"), py::arg("ref_stk"),
R"(IC(ind, stks, query, n, ref_stk)
py::arg("ind"), py::arg("stks"), py::arg("query"), py::arg("ref_stk"), py::arg("n") = 1,
R"(IC(ind, stks, query, ref_stk[, n=1])
IC RankIC
:param Indicator ind:
:param sequence(stock)|Block stks
:param Query query:
:param int n:
:param Stock ref_stk: 使 sh000300 300)");
:param Stock ref_stk: 使 sh000300 300
:param int n: )");
m.def("ICIR", ICIR, py::arg("ic"), py::arg("n") = 120, R"(ICIR(ic[,n])
m.def(
"ICIR",
[](const Indicator& ind, const py::object& stks, const KQuery& query, const Stock& ref_stk,
int n, int rolling_n) {
if (py::isinstance<Block>(stks)) {
const auto& blk = stks.cast<Block&>();
return ICIR(ind, blk, query, ref_stk, n, rolling_n);
}
if (py::isinstance<py::sequence>(stks)) {
StockList c_stks = python_list_to_vector<Stock>(stks);
return ICIR(ind, c_stks, query, ref_stk, n, rolling_n);
}
HKU_THROW("Input stks must be Block or sequenc(Stock)!");
},
py::arg("ind"), py::arg("stks"), py::arg("query"), py::arg("ref_stk"), py::arg("n") = 1,
py::arg("rolling_n") = 120,
R"(ICIR(ind, stks, query, ref_stk[, n=1, rolling_n=120])
IC IR = IC的多周期均值/IC的标准方差
:param Indicator: ic ic
:param int n: )");
:param Indicator ind:
:param sequence(stock)|Block stks
:param Query query:
:param Stock ref_stk: 使 sh000300 300
:param int n: IC时对应的 n
:param int rolling_n: )");
m.def("ZSCORE", ZSCORE_1, py::arg("out_extreme") = false, py::arg("nsigma") = 3.0,
py::arg("recursive") = false);

View File

@ -416,7 +416,7 @@ void export_TradeManager(py::module& m) {
:param ktype: K线类型
:rtype: float)")
.def("get_funds", getFunds_1, py::arg("ktype"))
.def("get_funds", getFunds_1, py::arg("ktype") = KQuery::DAY)
.def("get_funds", getFunds_2, py::arg("datetime"), py::arg("ktype") = KQuery::DAY,
R"(get_funds(self, [datetime, ktype = Query.DAY])

View File

@ -25,29 +25,17 @@ public:
PYBIND11_OVERLOAD(void, AllocateFundsBase, _reset, );
}
SystemWeightList _allocateWeight(const Datetime& date, const SystemList& se_list) override {
SystemWeightList _allocateWeight(const Datetime& date,
const SystemWeightList& se_list) override {
PYBIND11_OVERLOAD_PURE_NAME(SystemWeightList, AllocateFundsBase, "_allocate_weight",
_allocateWeight, date, se_list);
}
};
void export_AllocateFunds(py::module& m) {
py::class_<SystemWeight>(m, "SystemWeight",
"系统权重系数结构,在资产分配时,指定对应系统的资产占比系数")
.def(py::init<>())
.def(py::init<const SystemPtr&, price_t>())
.def("__str__", to_py_str<SystemWeight>)
.def("__repr__", to_py_str<SystemWeight>)
.def_property("sys", py::overload_cast<>(&SystemWeight::getSYS, py::const_),
py::overload_cast<const SystemPtr&>(&SystemWeight::setSYS),
"对应的 System 实例")
.def_property("weight", &SystemWeight::getWeight, &SystemWeight::setWeight,
"对应的权重系数,有效范围为 [0, 1]")
DEF_PICKLE(SystemWeight);
py::class_<AllocateFundsBase, AFPtr, PyAllocateFundsBase>(m, "AllocateFundsBase",
R"(资产分配算法基类, 子类接口:
py::class_<AllocateFundsBase, AFPtr, PyAllocateFundsBase>(
m, "AllocateFundsBase",
R"(资产分配算法基类, 子类接口:
- _allocateWeight :
- _clone :
@ -62,6 +50,8 @@ void export_AllocateFunds(py::module& m) {
.def_property("query", py::overload_cast<>(&AllocateFundsBase::getQuery, py::const_),
py::overload_cast<const KQuery&>(&AllocateFundsBase::setQuery),
py::return_value_policy::copy, "设置或获取查询条件")
.def_property_readonly("tm", py::overload_cast<>(&AllocateFundsBase::getTM, py::const_),
py::return_value_policy::copy)
.def("get_param", &AllocateFundsBase::getParam<boost::any>, R"(get_param(self, name)
@ -85,7 +75,8 @@ void export_AllocateFunds(py::module& m) {
.def("clone", &AllocateFundsBase::clone, "克隆操作")
.def("_reset", &AllocateFundsBase::_reset, "子类复位操作实现")
.def("_allocate_weight", &AllocateFundsBase::_allocateWeight, py::arg("date"),
py::arg("se_list"), R"(_allocate_weight(self, date, se_list)
py::arg("se_list"),
R"(_allocate_weight(self, date, se_list)

View File

@ -170,7 +170,7 @@ void export_MultiFactor(py::module& m) {
// 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);
return MF_ICWeight(c_inds, c_stks, query, ref_stk, ic_n, ic_rolling_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,
@ -193,7 +193,7 @@ void export_MultiFactor(py::module& m) {
// 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);
return MF_ICIRWeight(c_inds, c_stks, query, ref_stk, ic_n, ic_rolling_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,

View File

@ -57,12 +57,15 @@ void export_Portfolio(py::module& m) {
.def("reset", &Portfolio::reset, "复位操作")
.def("clone", &Portfolio::clone, "克隆操作")
.def("run", &Portfolio::run, py::arg("query"), py::arg("force") = false, R"(run(self, query)
.def("run", &Portfolio::run, py::arg("query"), py::arg("adjust_cycle") = 1,
py::arg("force") = false,
R"(run(self, query[, adjust_cycle=1, force=false])
PF在第二次执行时
:param Query query:
:param int adjust_cycle:
:param bool force: )")
DEF_PICKLE(Portfolio);

View File

@ -25,14 +25,9 @@ public:
PYBIND11_OVERLOAD_PURE(void, SelectorBase, _calculate, );
}
SystemList getSelectedOnOpen(Datetime date) override {
PYBIND11_OVERLOAD_PURE_NAME(SystemList, SelectorBase, "get_selected_on_open",
getSelectedOnOpen, date);
}
SystemList getSelectedOnClose(Datetime date) override {
PYBIND11_OVERLOAD_PURE_NAME(SystemList, SelectorBase, "get_selected_on_close",
getSelectedOnClose, date);
SystemWeightList getSelected(Datetime date) override {
PYBIND11_OVERLOAD_PURE_NAME(SystemWeightList, SelectorBase, "get_selected", getSelected,
date);
}
bool isMatchAF(const AFPtr& af) override {
@ -41,12 +36,22 @@ public:
};
void export_Selector(py::module& m) {
py::class_<SystemWeight>(m, "SystemWeight",
"系统权重系数结构,在资产分配时,指定对应系统的资产占比系数")
.def(py::init<>())
.def(py::init<const SystemPtr&, price_t>())
.def("__str__", to_py_str<SystemWeight>)
.def("__repr__", to_py_str<SystemWeight>)
.def_readwrite("sys", &SystemWeight::sys, "对应的 System 实例")
.def_readwrite("weight", &SystemWeight::weight)
DEF_PICKLE(SystemWeight);
py::class_<SelectorBase, SEPtr, PySelectorBase>(
m, "SelectorBase",
R"(选择器策略基类,实现标的、系统策略的评估和选取算法,自定义选择器策略子类接口:
- get_selected_on_open -
- get_selected_on_close -
- get_selected -
- _calculate -
- _reset -
- _clone - )")
@ -118,19 +123,10 @@ void export_Selector(py::module& m) {
:param AllocateFundsBase af: )")
.def("get_selected_on_open", &SelectorBase::getSelectedOnOpen,
R"(get_selected_on_open(self, datetime)
.def("get_selected", &SelectorBase::getSelected,
R"(get_selected(self, datetime)
:param Datetime datetime:
:return:
:rtype: SystemList)")
.def("get_selected_on_close", &SelectorBase::getSelectedOnClose,
R"(get_selected_on_close(self, datetime)
:param Datetime datetime:
:return:

View File

@ -10,7 +10,7 @@ target("core")
-- end
-- add_options("stacktrace")
add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time")
add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time", "log_level")
add_deps("hikyuu")
add_packages("boost", "fmt", "spdlog", "flatbuffers", "pybind11", "cpp-httplib")